feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit 286de54384
6841 changed files with 1848356 additions and 0 deletions
@@ -0,0 +1,109 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Pezkuwi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! Coretime pallet benchmarking.
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use frame_benchmarking::v2::*;
use frame_support::traits::OriginTrait;
use pallet_broker::CoreIndex as BrokerCoreIndex;
#[benchmarks]
mod benchmarks {
use super::*;
use assigner_coretime::PartsOf57600;
#[benchmark]
fn request_revenue_at() {
let root_origin = <T as frame_system::Config>::RuntimeOrigin::root();
let mhr = <T as on_demand::Config>::MaxHistoricalRevenue::get();
frame_system::Pallet::<T>::set_block_number((mhr + 2).into());
let minimum_balance = <T as on_demand::Config>::Currency::minimum_balance();
let rev: BoundedVec<
<<T as on_demand::Config>::Currency as frame_support::traits::Currency<
T::AccountId,
>>::Balance,
T::MaxHistoricalRevenue,
> = BoundedVec::try_from((1..=mhr).map(|v| minimum_balance * v.into()).collect::<Vec<_>>())
.unwrap();
on_demand::Revenue::<T>::put(rev);
crate::paras::Heads::<T>::insert(ParaId::from(T::BrokerId::get()), vec![1, 2, 3]);
<T as on_demand::Config>::Currency::make_free_balance_be(
&<on_demand::Pallet<T>>::account_id(),
minimum_balance * (mhr * (mhr + 1)).into(),
);
#[extrinsic_call]
_(root_origin as <T as frame_system::Config>::RuntimeOrigin, mhr + 1)
}
#[benchmark]
fn request_core_count() {
// Setup
let root_origin = <T as frame_system::Config>::RuntimeOrigin::root();
#[extrinsic_call]
_(
root_origin as <T as frame_system::Config>::RuntimeOrigin,
// random core count
100,
)
}
#[benchmark]
fn assign_core(s: Linear<1, 100>) {
// Setup
let root_origin = <T as frame_system::Config>::RuntimeOrigin::root();
// Use parameterized assignment count
let mut assignments: Vec<(CoreAssignment, PartsOf57600)> = vec![0u16; s as usize - 1]
.into_iter()
.enumerate()
.map(|(index, parts)| {
(CoreAssignment::Task(index as u32), PartsOf57600::new_saturating(parts))
})
.collect();
// Parts must add up to exactly 57600. Here we add all the parts in one assignment, as
// it won't effect the weight and splitting up the parts into even groupings may not
// work for every value `s`.
assignments.push((CoreAssignment::Task(s as u32), PartsOf57600::FULL));
let core_index: BrokerCoreIndex = 0;
#[extrinsic_call]
_(
root_origin as <T as frame_system::Config>::RuntimeOrigin,
core_index,
BlockNumberFor::<T>::from(5u32),
assignments,
Some(BlockNumberFor::<T>::from(20u32)),
)
}
#[benchmark]
fn credit_account() {
// Setup
let root_origin = <T as frame_system::Config>::RuntimeOrigin::root();
let who: T::AccountId = whitelisted_caller();
#[extrinsic_call]
_(root_origin as <T as frame_system::Config>::RuntimeOrigin, who, 1_000_000u32.into())
}
}
@@ -0,0 +1,316 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Pezkuwi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! Migrations for the Coretime pallet.
pub use v_coretime::{GetLegacyLease, MigrateToCoretime};
mod v_coretime {
use crate::{
assigner_coretime, configuration,
coretime::{mk_coretime_call, Config, PartsOf57600, WeightInfo},
};
use alloc::{vec, vec::Vec};
#[cfg(feature = "try-runtime")]
use codec::Decode;
#[cfg(feature = "try-runtime")]
use codec::Encode;
use core::{iter, result};
#[cfg(feature = "try-runtime")]
use frame_support::ensure;
use frame_support::{
traits::{OnRuntimeUpgrade, PalletInfoAccess, StorageVersion},
weights::Weight,
};
use frame_system::pallet_prelude::BlockNumberFor;
use pallet_broker::{CoreAssignment, CoreMask, ScheduleItem};
use pezkuwi_primitives::{CoreIndex, Id as ParaId};
use pezkuwi_teyrchain_primitives::primitives::IsSystem;
use sp_arithmetic::traits::SaturatedConversion;
use sp_core::Get;
use sp_runtime::BoundedVec;
use xcm::prelude::{
send_xcm, Instruction, Junction, Location, SendError, SendXcm, WeightLimit, Xcm,
};
/// Return information about a legacy lease of a teyrchain.
pub trait GetLegacyLease<N> {
/// If teyrchain is a lease holding teyrchain, return the block at which the lease expires.
fn get_teyrchain_lease_in_blocks(para: ParaId) -> Option<N>;
// All teyrchains holding a lease, no matter if there are gaps in the slots or not.
fn get_all_teyrchains_with_leases() -> Vec<ParaId>;
}
/// Migrate a chain to use coretime.
///
/// This assumes that the `Coretime` and the `AssignerCoretime` pallets are added at the same
/// time to a runtime.
pub struct MigrateToCoretime<T, SendXcm, LegacyLease, const TIMESLICE_PERIOD: u32>(
core::marker::PhantomData<(T, SendXcm, LegacyLease)>,
);
impl<
T: Config,
XcmSender: SendXcm,
LegacyLease: GetLegacyLease<BlockNumberFor<T>>,
const TIMESLICE_PERIOD: u32,
> MigrateToCoretime<T, XcmSender, LegacyLease, TIMESLICE_PERIOD>
{
fn already_migrated() -> bool {
// We are using the assigner coretime because the coretime pallet doesn't has any
// storage data. But both pallets are introduced at the same time, so this is fine.
let name_hash = assigner_coretime::Pallet::<T>::name_hash();
let mut next_key = name_hash.to_vec();
let storage_version_key = StorageVersion::storage_key::<assigner_coretime::Pallet<T>>();
loop {
match sp_io::storage::next_key(&next_key) {
// StorageVersion is initialized before, so we need to ignore it.
Some(key) if &key == &storage_version_key => {
next_key = key;
},
// If there is any other key with the prefix of the pallet,
// we already have executed the migration.
Some(key) if key.starts_with(&name_hash) => {
log::info!("`MigrateToCoretime` already executed!");
return true;
},
// Any other key/no key means that we did not yet have migrated.
None | Some(_) => return false,
}
}
}
}
impl<
T: Config + crate::dmp::Config,
XcmSender: SendXcm,
LegacyLease: GetLegacyLease<BlockNumberFor<T>>,
const TIMESLICE_PERIOD: u32,
> OnRuntimeUpgrade for MigrateToCoretime<T, XcmSender, LegacyLease, TIMESLICE_PERIOD>
{
fn on_runtime_upgrade() -> Weight {
if Self::already_migrated() {
return Weight::zero();
}
log::info!("Migrating existing teyrchains to coretime.");
migrate_to_coretime::<T, XcmSender, LegacyLease, TIMESLICE_PERIOD>()
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::DispatchError> {
if Self::already_migrated() {
return Ok(Vec::new());
}
let legacy_paras = LegacyLease::get_all_teyrchains_with_leases();
let config = configuration::ActiveConfig::<T>::get();
let total_core_count = config.scheduler_params.num_cores + legacy_paras.len() as u32;
let dmp_queue_size =
crate::dmp::Pallet::<T>::dmq_contents(T::BrokerId::get().into()).len() as u32;
let total_core_count = total_core_count as u32;
Ok((total_core_count, dmp_queue_size).encode())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::DispatchError> {
if state.is_empty() {
return Ok(());
}
log::trace!("Running post_upgrade()");
let (prev_core_count, prev_dmp_queue_size) =
<(u32, u32)>::decode(&mut &state[..]).unwrap();
let dmp_queue_size =
crate::dmp::Pallet::<T>::dmq_contents(T::BrokerId::get().into()).len() as u32;
let config = configuration::ActiveConfig::<T>::get();
let new_core_count = config.scheduler_params.num_cores;
ensure!(new_core_count == prev_core_count, "Total number of cores need to not change.");
ensure!(
dmp_queue_size > prev_dmp_queue_size,
"There should have been enqueued at least one DMP messages."
);
Ok(())
}
}
// Migrate to Coretime.
//
// NOTE: Also migrates `num_cores` config value in configuration::ActiveConfig.
fn migrate_to_coretime<
T: Config,
XcmSender: SendXcm,
LegacyLease: GetLegacyLease<BlockNumberFor<T>>,
const TIMESLICE_PERIOD: u32,
>() -> Weight {
let legacy_paras = LegacyLease::get_all_teyrchains_with_leases();
let legacy_count = legacy_paras.len() as u32;
let now = frame_system::Pallet::<T>::block_number();
for (core, para_id) in legacy_paras.into_iter().enumerate() {
let r = assigner_coretime::Pallet::<T>::assign_core(
CoreIndex(core as u32),
now,
vec![(CoreAssignment::Task(para_id.into()), PartsOf57600::FULL)],
None,
);
if let Err(err) = r {
log::error!(
"Creating assignment for existing para failed: {:?}, error: {:?}",
para_id,
err
);
}
}
let config = configuration::ActiveConfig::<T>::get();
for on_demand in 0..config.scheduler_params.num_cores {
let core = CoreIndex(legacy_count.saturating_add(on_demand as _));
let r = assigner_coretime::Pallet::<T>::assign_core(
core,
now,
vec![(CoreAssignment::Pool, PartsOf57600::FULL)],
None,
);
if let Err(err) = r {
log::error!("Creating assignment for existing on-demand core, failed: {:?}", err);
}
}
let total_cores = config.scheduler_params.num_cores + legacy_count;
configuration::ActiveConfig::<T>::mutate(|c| {
c.scheduler_params.num_cores = total_cores;
});
if let Err(err) = migrate_send_assignments_to_coretime_chain::<
T,
XcmSender,
LegacyLease,
TIMESLICE_PERIOD,
>() {
log::error!("Sending legacy chain data to coretime chain failed: {:?}", err);
}
let single_weight = <T as Config>::WeightInfo::assign_core(1);
single_weight
.saturating_mul(u64::from(
legacy_count.saturating_add(config.scheduler_params.num_cores),
))
// Second read from sending assignments to the coretime chain.
.saturating_add(T::DbWeight::get().reads_writes(2, 1))
}
fn migrate_send_assignments_to_coretime_chain<
T: Config,
XcmSender: SendXcm,
LegacyLease: GetLegacyLease<BlockNumberFor<T>>,
const TIMESLICE_PERIOD: u32,
>() -> result::Result<(), SendError> {
let legacy_paras = LegacyLease::get_all_teyrchains_with_leases();
let legacy_paras_count = legacy_paras.len();
let (system_chains, lease_holding): (Vec<_>, Vec<_>) =
legacy_paras.into_iter().partition(IsSystem::is_system);
let reservations = system_chains.into_iter().map(|p| {
let schedule = BoundedVec::truncate_from(vec![ScheduleItem {
mask: CoreMask::complete(),
assignment: CoreAssignment::Task(p.into()),
}]);
mk_coretime_call::<T>(crate::coretime::CoretimeCalls::Reserve(schedule))
});
let mut leases = lease_holding.into_iter().filter_map(|p| {
log::trace!(target: "coretime-migration", "Preparing sending of lease holding para {:?}", p);
let Some(valid_until) = LegacyLease::get_teyrchain_lease_in_blocks(p) else {
log::error!("Lease holding chain with no lease information?!");
return None
};
let valid_until: u32 = match valid_until.try_into() {
Ok(val) => val,
Err(_) => {
log::error!("Converting block number to u32 failed!");
return None
},
};
let time_slice = valid_until.div_ceil(TIMESLICE_PERIOD);
log::trace!(target: "coretime-migration", "Sending of lease holding para {:?}, valid_until: {:?}, time_slice: {:?}", p, valid_until, time_slice);
Some(mk_coretime_call::<T>(crate::coretime::CoretimeCalls::SetLease(p.into(), time_slice)))
});
let core_count: u16 = configuration::ActiveConfig::<T>::get()
.scheduler_params
.num_cores
.saturated_into();
let set_core_count = iter::once(mk_coretime_call::<T>(
crate::coretime::CoretimeCalls::NotifyCoreCount(core_count),
));
let pool = (legacy_paras_count..core_count.into()).map(|_| {
let schedule = BoundedVec::truncate_from(vec![ScheduleItem {
mask: CoreMask::complete(),
assignment: CoreAssignment::Pool,
}]);
// Reserved cores will come before lease cores, so cores will change their assignments
// when coretime chain sends us their assign_core calls -> Good test.
mk_coretime_call::<T>(crate::coretime::CoretimeCalls::Reserve(schedule))
});
let message_content = iter::once(Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
});
let reservation_content = message_content.clone().chain(reservations).collect();
let leases_content_1 = message_content
.clone()
.chain(leases.by_ref().take(legacy_paras_count / 2)) // split in two messages to avoid overweighted XCM
.collect();
let leases_content_2 = message_content.clone().chain(leases).collect();
let set_core_count_content = message_content.clone().chain(set_core_count).collect();
// If `pool_content` is empty don't send a blank XCM message
let messages = if core_count as usize > legacy_paras_count {
let pool_content = message_content.clone().chain(pool).collect();
vec![
Xcm(reservation_content),
Xcm(pool_content),
Xcm(leases_content_1),
Xcm(leases_content_2),
Xcm(set_core_count_content),
]
} else {
vec![
Xcm(reservation_content),
Xcm(leases_content_1),
Xcm(leases_content_2),
Xcm(set_core_count_content),
]
};
for message in messages {
send_xcm::<XcmSender>(
Location::new(0, Junction::Teyrchain(T::BrokerId::get())),
message,
)?;
}
Ok(())
}
}
@@ -0,0 +1,395 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Pezkuwi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! Extrinsics implementing the relay chain side of the Coretime interface.
//!
//! <https://github.com/polkadot-fellows/RFCs/blob/main/text/0005-coretime-interface.md>
use alloc::{vec, vec::Vec};
use core::result;
use frame_support::{
pallet_prelude::*,
traits::{defensive_prelude::*, Currency},
};
use frame_system::pallet_prelude::*;
pub use pallet::*;
use pallet_broker::{CoreAssignment, CoreIndex as BrokerCoreIndex};
use pezkuwi_primitives::{Balance, BlockNumber, CoreIndex, Id as ParaId};
use sp_arithmetic::traits::SaturatedConversion;
use sp_runtime::traits::TryConvert;
use xcm::prelude::*;
use xcm_executor::traits::TransactAsset;
use crate::{
assigner_coretime::{self, PartsOf57600},
initializer::{OnNewSession, SessionChangeNotification},
on_demand,
origin::{ensure_teyrchain, Origin},
};
mod benchmarking;
pub mod migration;
const LOG_TARGET: &str = "runtime::teyrchains::coretime";
pub trait WeightInfo {
fn request_core_count() -> Weight;
fn request_revenue_at() -> Weight;
fn credit_account() -> Weight;
fn assign_core(s: u32) -> Weight;
}
/// A weight info that is only suitable for testing.
pub struct TestWeightInfo;
impl WeightInfo for TestWeightInfo {
fn request_core_count() -> Weight {
Weight::MAX
}
fn request_revenue_at() -> Weight {
Weight::MAX
}
fn credit_account() -> Weight {
Weight::MAX
}
fn assign_core(_s: u32) -> Weight {
Weight::MAX
}
}
/// Shorthand for the Balance type the runtime is using.
pub type BalanceOf<T> = <<T as on_demand::Config>::Currency as Currency<
<T as frame_system::Config>::AccountId,
>>::Balance;
/// Broker pallet index on the coretime chain. Used to
///
/// construct remote calls. The codec index must correspond to the index of `Broker` in the
/// `construct_runtime` of the coretime chain.
#[derive(Encode, Decode)]
enum BrokerRuntimePallets {
#[codec(index = 50)]
Broker(CoretimeCalls),
}
/// Call encoding for the calls needed from the Broker pallet.
#[derive(Encode, Decode)]
enum CoretimeCalls {
#[codec(index = 1)]
Reserve(pallet_broker::Schedule),
#[codec(index = 3)]
SetLease(pallet_broker::TaskId, pallet_broker::Timeslice),
#[codec(index = 19)]
NotifyCoreCount(u16),
#[codec(index = 20)]
NotifyRevenue((BlockNumber, Balance)),
#[codec(index = 99)]
SwapLeases(ParaId, ParaId),
}
#[frame_support::pallet]
pub mod pallet {
use crate::configuration;
use sp_runtime::traits::TryConvert;
use xcm::latest::InteriorLocation;
use xcm_executor::traits::TransactAsset;
use super::*;
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config + assigner_coretime::Config + on_demand::Config {
type RuntimeOrigin: From<<Self as frame_system::Config>::RuntimeOrigin>
+ Into<result::Result<Origin, <Self as Config>::RuntimeOrigin>>;
#[allow(deprecated)]
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// The ParaId of the coretime chain.
#[pallet::constant]
type BrokerId: Get<u32>;
/// The coretime chain pot location.
#[pallet::constant]
type BrokerPotLocation: Get<InteriorLocation>;
/// Something that provides the weight of this pallet.
type WeightInfo: WeightInfo;
/// The XCM sender.
type SendXcm: SendXcm;
/// The asset transactor.
type AssetTransactor: TransactAsset;
/// AccountId to Location converter
type AccountToLocation: for<'a> TryConvert<&'a Self::AccountId, Location>;
/// Maximum weight for any XCM transact call that should be executed on the coretime chain.
///
/// Basically should be `max_weight(set_leases, reserve, notify_core_count)`.
type MaxXcmTransactWeight: Get<Weight>;
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// The broker chain has asked for revenue information for a specific block.
RevenueInfoRequested { when: BlockNumberFor<T> },
/// A core has received a new assignment from the broker chain.
CoreAssigned { core: CoreIndex },
}
#[pallet::error]
pub enum Error<T> {
/// The paraid making the call is not the coretime brokerage system teyrchain.
NotBroker,
/// Requested revenue information `when` parameter was in the future from the current
/// block height.
RequestedFutureRevenue,
/// Failed to transfer assets to the coretime chain
AssetTransferFailed,
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Request the configuration to be updated with the specified number of cores. Warning:
/// Since this only schedules a configuration update, it takes two sessions to come into
/// effect.
///
/// - `origin`: Root or the Coretime Chain
/// - `count`: total number of cores
#[pallet::weight(<T as Config>::WeightInfo::request_core_count())]
#[pallet::call_index(1)]
pub fn request_core_count(origin: OriginFor<T>, count: u16) -> DispatchResult {
// Ignore requests not coming from the coretime chain or root.
Self::ensure_root_or_para(origin, <T as Config>::BrokerId::get().into())?;
configuration::Pallet::<T>::set_coretime_cores_unchecked(u32::from(count))
}
/// Request to claim the instantaneous coretime sales revenue starting from the block it was
/// last claimed until and up to the block specified. The claimed amount value is sent back
/// to the Coretime chain in a `notify_revenue` message. At the same time, the amount is
/// teleported to the Coretime chain.
#[pallet::weight(<T as Config>::WeightInfo::request_revenue_at())]
#[pallet::call_index(2)]
pub fn request_revenue_at(origin: OriginFor<T>, when: BlockNumber) -> DispatchResult {
// Ignore requests not coming from the Coretime Chain or Root.
Self::ensure_root_or_para(origin, <T as Config>::BrokerId::get().into())?;
Self::notify_revenue(when)
}
#[pallet::weight(<T as Config>::WeightInfo::credit_account())]
#[pallet::call_index(3)]
pub fn credit_account(
origin: OriginFor<T>,
who: T::AccountId,
amount: BalanceOf<T>,
) -> DispatchResult {
// Ignore requests not coming from the coretime chain or root.
Self::ensure_root_or_para(origin, <T as Config>::BrokerId::get().into())?;
on_demand::Pallet::<T>::credit_account(who, amount.saturated_into());
Ok(())
}
/// Receive instructions from the `ExternalBrokerOrigin`, detailing how a specific core is
/// to be used.
///
/// Parameters:
/// -`origin`: The `ExternalBrokerOrigin`, assumed to be the coretime chain.
/// -`core`: The core that should be scheduled.
/// -`begin`: The starting blockheight of the instruction.
/// -`assignment`: How the blockspace should be utilised.
/// -`end_hint`: An optional hint as to when this particular set of instructions will end.
// The broker pallet's `CoreIndex` definition is `u16` but on the relay chain it's `struct
// CoreIndex(u32)`
#[pallet::call_index(4)]
#[pallet::weight(<T as Config>::WeightInfo::assign_core(assignment.len() as u32))]
pub fn assign_core(
origin: OriginFor<T>,
core: BrokerCoreIndex,
begin: BlockNumberFor<T>,
assignment: Vec<(CoreAssignment, PartsOf57600)>,
end_hint: Option<BlockNumberFor<T>>,
) -> DispatchResult {
// Ignore requests not coming from the coretime chain or root.
Self::ensure_root_or_para(origin, T::BrokerId::get().into())?;
let core = u32::from(core).into();
<assigner_coretime::Pallet<T>>::assign_core(core, begin, assignment, end_hint)?;
Self::deposit_event(Event::<T>::CoreAssigned { core });
Ok(())
}
}
}
impl<T: Config> Pallet<T> {
/// Ensure the origin is one of Root or the `para` itself.
fn ensure_root_or_para(
origin: <T as frame_system::Config>::RuntimeOrigin,
id: ParaId,
) -> DispatchResult {
if let Ok(caller_id) = ensure_teyrchain(<T as Config>::RuntimeOrigin::from(origin.clone()))
{
// Check if matching para id...
ensure!(caller_id == id, Error::<T>::NotBroker);
} else {
// Check if root...
ensure_root(origin.clone())?;
}
Ok(())
}
pub fn initializer_on_new_session(notification: &SessionChangeNotification<BlockNumberFor<T>>) {
let old_core_count = notification.prev_config.scheduler_params.num_cores;
let new_core_count = notification.new_config.scheduler_params.num_cores;
if new_core_count != old_core_count {
let core_count: u16 = new_core_count.saturated_into();
let message = Xcm(vec![
Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
},
mk_coretime_call::<T>(crate::coretime::CoretimeCalls::NotifyCoreCount(core_count)),
]);
if let Err(err) = send_xcm::<T::SendXcm>(
Location::new(0, [Junction::Teyrchain(T::BrokerId::get())]),
message,
) {
log::error!(target: LOG_TARGET, "Sending `NotifyCoreCount` to coretime chain failed: {:?}", err);
}
}
}
/// Provide the amount of revenue accumulated from Instantaneous Coretime Sales from Relay-chain
/// block number last_until to until, not including until itself. last_until is defined as being
/// the until argument of the last notify_revenue message sent, or zero for the first call. If
/// revenue is None, this indicates that the information is no longer available. This explicitly
/// disregards the possibility of multiple teyrchains requesting and being notified of revenue
/// information.
///
/// The Relay-chain must be configured to ensure that only a single revenue information
/// destination exists.
pub fn notify_revenue(until: BlockNumber) -> DispatchResult {
let now = <frame_system::Pallet<T>>::block_number();
let until_bnf: BlockNumberFor<T> = until.into();
// When cannot be in the future.
ensure!(until_bnf <= now, Error::<T>::RequestedFutureRevenue);
let amount = <on_demand::Pallet<T>>::claim_revenue_until(until_bnf);
log::debug!(target: LOG_TARGET, "Revenue info requested: {:?}", amount);
let raw_revenue: Balance = amount.try_into().map_err(|_| {
log::error!(target: LOG_TARGET, "Converting on demand revenue for `NotifyRevenue` failed");
Error::<T>::AssetTransferFailed
})?;
do_notify_revenue::<T>(until, raw_revenue).map_err(|err| {
log::error!(target: LOG_TARGET, "notify_revenue failed: {err:?}");
Error::<T>::AssetTransferFailed
})?;
Ok(())
}
// Handle legacy swaps in coretime. Notifies coretime chain that a lease swap has occurred via
// XCM message. This function is meant to be used in an implementation of `OnSwap` trait.
pub fn on_legacy_lease_swap(one: ParaId, other: ParaId) {
let message = Xcm(vec![
Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
},
mk_coretime_call::<T>(crate::coretime::CoretimeCalls::SwapLeases(one, other)),
]);
if let Err(err) = send_xcm::<T::SendXcm>(
Location::new(0, [Junction::Teyrchain(T::BrokerId::get())]),
message,
) {
log::error!(target: LOG_TARGET, "Sending `SwapLeases` to coretime chain failed: {:?}", err);
}
}
}
impl<T: Config> OnNewSession<BlockNumberFor<T>> for Pallet<T> {
fn on_new_session(notification: &SessionChangeNotification<BlockNumberFor<T>>) {
Self::initializer_on_new_session(notification);
}
}
fn mk_coretime_call<T: Config>(call: crate::coretime::CoretimeCalls) -> Instruction<()> {
Instruction::Transact {
origin_kind: OriginKind::Superuser,
fallback_max_weight: Some(T::MaxXcmTransactWeight::get()),
call: BrokerRuntimePallets::Broker(call).encode().into(),
}
}
fn do_notify_revenue<T: Config>(when: BlockNumber, raw_revenue: Balance) -> Result<(), XcmError> {
let dest = Junction::Teyrchain(T::BrokerId::get()).into_location();
let mut message = vec![Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
}];
let asset = Asset { id: Location::here().into(), fun: Fungible(raw_revenue) };
let dummy_xcm_context = XcmContext { origin: None, message_id: [0; 32], topic: None };
if raw_revenue > 0 {
let on_demand_pot =
T::AccountToLocation::try_convert(&<on_demand::Pallet<T>>::account_id()).map_err(
|err| {
log::error!(
target: LOG_TARGET,
"Failed to convert on-demand pot account to XCM location: {err:?}",
);
XcmError::InvalidLocation
},
)?;
let withdrawn = T::AssetTransactor::withdraw_asset(&asset, &on_demand_pot, None)?;
T::AssetTransactor::can_check_out(&dest, &asset, &dummy_xcm_context)?;
let assets_reanchored = Into::<Assets>::into(withdrawn)
.reanchored(&dest, &Here.into())
.defensive_map_err(|_| XcmError::ReanchorFailed)?;
message.extend(
[
ReceiveTeleportedAsset(assets_reanchored),
DepositAsset {
assets: Wild(AllCounted(1)),
beneficiary: T::BrokerPotLocation::get().into_location(),
},
]
.into_iter(),
);
}
message.push(mk_coretime_call::<T>(CoretimeCalls::NotifyRevenue((when, raw_revenue))));
send_xcm::<T::SendXcm>(dest.clone(), Xcm(message))?;
if raw_revenue > 0 {
T::AssetTransactor::check_out(&dest, &asset, &dummy_xcm_context);
}
Ok(())
}