diff --git a/parachain/src/primitives.rs b/parachain/src/primitives.rs
index 39d59dcfe7a4..1a52a85db6d0 100644
--- a/parachain/src/primitives.rs
+++ b/parachain/src/primitives.rs
@@ -20,7 +20,7 @@
use sp_std::vec::Vec;
use frame_support::weights::Weight;
-use parity_scale_codec::{CompactAs, Decode, Encode};
+use parity_scale_codec::{CompactAs, Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_core::{RuntimeDebug, TypeId};
use sp_runtime::traits::Hash as _;
@@ -139,6 +139,7 @@ pub struct BlockData(#[cfg_attr(feature = "std", serde(with = "bytes"))] pub Vec
Encode,
Eq,
Hash,
+ MaxEncodedLen,
Ord,
PartialEq,
PartialOrd,
diff --git a/runtime/common/src/assigned_slots.rs b/runtime/common/src/assigned_slots.rs
new file mode 100644
index 000000000000..e0981a63d8c0
--- /dev/null
+++ b/runtime/common/src/assigned_slots.rs
@@ -0,0 +1,1266 @@
+// Copyright 2021 Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot 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.
+
+// Polkadot 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 Polkadot. If not, see .
+
+//! This pallet allows to assign permanent (long-lived) or temporary
+//! (short-lived) parachain slots to paras, leveraging the existing
+//! parachain slot lease mechanism. Temporary slots are given turns
+//! in a fair (though best-effort) manner.
+//! The dispatchables must be called from the configured origin
+//! (typically `Sudo` or a governance origin).
+//! This pallet should not be used on a production relay chain,
+//! only on a test relay chain (e.g. Rococo).
+
+use crate::{
+ slots::{self, Pallet as Slots, WeightInfo},
+ traits::{LeaseError, Leaser, Registrar},
+ MAXIMUM_BLOCK_WEIGHT,
+};
+use frame_support::{pallet_prelude::*, traits::Currency};
+use frame_system::pallet_prelude::*;
+pub use pallet::*;
+use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
+use primitives::v1::Id as ParaId;
+use runtime_parachains::{
+ configuration,
+ paras::{self},
+};
+use scale_info::TypeInfo;
+use sp_runtime::traits::{One, Saturating, Zero};
+use sp_std::prelude::*;
+
+/// Lease period an assigned slot should start from (current, or next one).
+#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)]
+pub enum SlotLeasePeriodStart {
+ Current,
+ Next,
+}
+
+/// Information about a temporary parachain slot.
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, MaxEncodedLen, RuntimeDebug, TypeInfo)]
+pub struct ParachainTemporarySlot {
+ /// Manager account of the para.
+ pub manager: AccountId,
+ /// Lease period the parachain slot should ideally start from,
+ /// As slot are allocated in a best-effort manner, this could be later,
+ /// but not earlier than the specified period.
+ pub period_begin: LeasePeriod,
+ /// Number of lease period the slot lease will last.
+ /// This is set to the value configured in `TemporarySlotLeasePeriodLength`.
+ pub period_count: LeasePeriod,
+ /// Last lease period this slot had a turn in (incl. current).
+ /// This is set to the beginning period of a slot.
+ pub last_lease: Option,
+ /// Number of leases this temporary slot had (incl. current).
+ pub lease_count: u32,
+}
+
+type BalanceOf = <<::Leaser as Leaser<::BlockNumber>>::Currency as Currency<
+ ::AccountId,
+>>::Balance;
+type LeasePeriodOf =
+ <::Leaser as Leaser<::BlockNumber>>::LeasePeriod;
+
+#[frame_support::pallet]
+pub mod pallet {
+ use super::*;
+
+ #[pallet::pallet]
+ #[pallet::generate_store(pub(super) trait Store)]
+ #[pallet::generate_storage_info]
+ pub struct Pallet(_);
+
+ #[pallet::config]
+ #[pallet::disable_frame_system_supertrait_check]
+ pub trait Config: configuration::Config + paras::Config + slots::Config {
+ /// The overarching event type.
+ type Event: From> + IsType<::Event>;
+
+ /// Origin for assigning slots.
+ type AssignSlotOrigin: EnsureOrigin<::Origin>;
+
+ /// The type representing the leasing system.
+ type Leaser: Leaser<
+ Self::BlockNumber,
+ AccountId = Self::AccountId,
+ LeasePeriod = Self::BlockNumber,
+ >;
+
+ /// The number of lease periods a permanent parachain slot lasts.
+ #[pallet::constant]
+ type PermanentSlotLeasePeriodLength: Get;
+
+ /// The number of lease periods a temporary parachain slot lasts.
+ #[pallet::constant]
+ type TemporarySlotLeasePeriodLength: Get;
+
+ /// The max number of permanent slots that can be assigned.
+ #[pallet::constant]
+ type MaxPermanentSlots: Get;
+
+ /// The max number of temporary slots that can be assigned.
+ #[pallet::constant]
+ type MaxTemporarySlots: Get;
+
+ /// The max number of temporary slots to be scheduled per lease periods.
+ #[pallet::constant]
+ type MaxTemporarySlotPerLeasePeriod: Get;
+ }
+
+ /// Assigned permanent slots, with their start lease period, and duration.
+ #[pallet::storage]
+ #[pallet::getter(fn permanent_slots)]
+ pub type PermanentSlots =
+ StorageMap<_, Twox64Concat, ParaId, (LeasePeriodOf, LeasePeriodOf), OptionQuery>;
+
+ /// Number of assigned (and active) permanent slots.
+ #[pallet::storage]
+ #[pallet::getter(fn permanent_slot_count)]
+ pub type PermanentSlotCount = StorageValue<_, u32, ValueQuery>;
+
+ /// Assigned temporary slots.
+ #[pallet::storage]
+ #[pallet::getter(fn temporary_slots)]
+ pub type TemporarySlots = StorageMap<
+ _,
+ Twox64Concat,
+ ParaId,
+ ParachainTemporarySlot>,
+ OptionQuery,
+ >;
+
+ /// Number of assigned temporary slots.
+ #[pallet::storage]
+ #[pallet::getter(fn temporary_slot_count)]
+ pub type TemporarySlotCount = StorageValue<_, u32, ValueQuery>;
+
+ /// Number of active temporary slots in current slot lease period.
+ #[pallet::storage]
+ #[pallet::getter(fn active_temporary_slot_count)]
+ pub type ActiveTemporarySlotCount = StorageValue<_, u32, ValueQuery>;
+
+ #[pallet::event]
+ #[pallet::generate_deposit(pub(super) fn deposit_event)]
+ pub enum Event {
+ /// A para was assigned a permanent parachain slot
+ PermanentSlotAssigned(ParaId),
+ /// A para was assigned a temporary parachain slot
+ TemporarySlotAssigned(ParaId),
+ }
+
+ #[pallet::error]
+ pub enum Error {
+ /// The specified parachain or parathread is not registered.
+ ParaDoesntExist,
+ /// Not a parathread.
+ NotParathread,
+ /// Cannot upgrade parathread.
+ CannotUpgrade,
+ /// Cannot downgrade parachain.
+ CannotDowngrade,
+ /// Permanent or Temporary slot already assigned.
+ SlotAlreadyAssigned,
+ /// Permanent or Temporary slot has not been assigned.
+ SlotNotAssigned,
+ /// An ongoing lease already exists.
+ OngoingLeaseExists,
+ // Maximum number of permanent slots exceeded
+ MaxPermanentSlotsExceeded,
+ // Maximum number of temporary slots exceeded
+ MaxTemporarySlotsExceeded,
+ }
+
+ #[pallet::hooks]
+ impl Hooks> for Pallet {
+ fn on_initialize(n: T::BlockNumber) -> Weight {
+ if let Some((lease_period, first_block)) = Self::lease_period_index(n) {
+ // If we're beginning a new lease period then handle that.
+ if first_block {
+ return Self::manage_lease_period_start(lease_period)
+ }
+ }
+
+ // We didn't return early above, so we didn't do anything.
+ 0
+ }
+ }
+
+ #[pallet::call]
+ impl Pallet {
+ // TODO: Benchmark this
+ /// Assign a permanent parachain slot and immediately create a lease for it.
+ #[pallet::weight(((MAXIMUM_BLOCK_WEIGHT / 10) as Weight, DispatchClass::Operational))]
+ pub fn assign_perm_parachain_slot(origin: OriginFor, id: ParaId) -> DispatchResult {
+ T::AssignSlotOrigin::ensure_origin(origin)?;
+
+ let manager = T::Registrar::manager_of(id).ok_or(Error::::ParaDoesntExist)?;
+
+ ensure!(T::Registrar::is_parathread(id), Error::::NotParathread,);
+
+ ensure!(
+ !Self::has_permanent_slot(id) && !Self::has_temporary_slot(id),
+ Error::::SlotAlreadyAssigned
+ );
+
+ let current_lease_period: T::BlockNumber = Self::current_lease_period_index();
+ ensure!(
+ !T::Leaser::already_leased(
+ id,
+ current_lease_period,
+ // Check current lease & next one
+ current_lease_period.saturating_add(
+ T::BlockNumber::from(2u32)
+ .saturating_mul(T::PermanentSlotLeasePeriodLength::get().into())
+ )
+ ),
+ Error::::OngoingLeaseExists
+ );
+
+ ensure!(
+ PermanentSlotCount::::get() < T::MaxPermanentSlots::get(),
+ Error::::MaxPermanentSlotsExceeded
+ );
+
+ // Permanent slot assignment fails if a lease cannot be created
+ Self::configure_slot_lease(
+ id,
+ manager,
+ current_lease_period,
+ T::PermanentSlotLeasePeriodLength::get().into(),
+ )
+ .map_err(|_| Error::::CannotUpgrade)?;
+
+ PermanentSlots::::insert(
+ id,
+ (
+ current_lease_period,
+ LeasePeriodOf::::from(T::PermanentSlotLeasePeriodLength::get()),
+ ),
+ );
+ >::mutate(|count| count.saturating_inc());
+
+ Self::deposit_event(Event::::PermanentSlotAssigned(id));
+ Ok(())
+ }
+
+ // TODO: Benchmark this
+ /// Assign a temporary parachain slot. The function tries to create a lease for it
+ /// immediately if `SlotLeasePeriodStart::Current` is specified, and if the number
+ /// of currently active temporary slots is below `MaxTemporarySlotPerLeasePeriod`.
+ #[pallet::weight(((MAXIMUM_BLOCK_WEIGHT / 10) as Weight, DispatchClass::Operational))]
+ pub fn assign_temp_parachain_slot(
+ origin: OriginFor,
+ id: ParaId,
+ lease_period_start: SlotLeasePeriodStart,
+ ) -> DispatchResult {
+ T::AssignSlotOrigin::ensure_origin(origin)?;
+
+ let manager = T::Registrar::manager_of(id).ok_or(Error::::ParaDoesntExist)?;
+
+ ensure!(T::Registrar::is_parathread(id), Error::::NotParathread);
+
+ ensure!(
+ !Self::has_permanent_slot(id) && !Self::has_temporary_slot(id),
+ Error::::SlotAlreadyAssigned
+ );
+
+ let current_lease_period: T::BlockNumber = Self::current_lease_period_index();
+ ensure!(
+ !T::Leaser::already_leased(
+ id,
+ current_lease_period,
+ // Check current lease & next one
+ current_lease_period.saturating_add(
+ T::BlockNumber::from(2u32)
+ .saturating_mul(T::TemporarySlotLeasePeriodLength::get().into())
+ )
+ ),
+ Error::::OngoingLeaseExists
+ );
+
+ ensure!(
+ TemporarySlotCount::::get() < T::MaxTemporarySlots::get(),
+ Error::::MaxTemporarySlotsExceeded
+ );
+
+ let mut temp_slot = ParachainTemporarySlot {
+ manager: manager.clone(),
+ period_begin: match lease_period_start {
+ SlotLeasePeriodStart::Current => current_lease_period,
+ SlotLeasePeriodStart::Next => current_lease_period + One::one(),
+ },
+ period_count: T::TemporarySlotLeasePeriodLength::get().into(),
+ last_lease: None,
+ lease_count: 0,
+ };
+
+ if lease_period_start == SlotLeasePeriodStart::Current &&
+ Self::active_temporary_slot_count() < T::MaxTemporarySlotPerLeasePeriod::get()
+ {
+ // Try to allocate slot directly
+ match Self::configure_slot_lease(
+ id,
+ manager,
+ temp_slot.period_begin,
+ temp_slot.period_count,
+ ) {
+ Ok(_) => {
+ ActiveTemporarySlotCount::::mutate(|count| count.saturating_inc());
+ temp_slot.last_lease = Some(temp_slot.period_begin);
+ temp_slot.lease_count += 1;
+ },
+ Err(err) => {
+ // Treat failed lease creation as warning .. slot will be allocated a lease
+ // in a subsequent lease period by the `allocate_temporary_slot_leases` function.
+ log::warn!(target: "assigned_slots",
+ "Failed to allocate a temp slot for para {:?} at period {:?}: {:?}",
+ id, current_lease_period, err
+ );
+ },
+ }
+ }
+
+ TemporarySlots::::insert(id, temp_slot);
+ >::mutate(|count| count.saturating_inc());
+
+ Self::deposit_event(Event::::TemporarySlotAssigned(id));
+
+ Ok(())
+ }
+
+ // TODO: Benchmark this
+ /// Unassign a permanent or temporary parachain slot
+ #[pallet::weight(((MAXIMUM_BLOCK_WEIGHT / 10) as Weight, DispatchClass::Operational))]
+ pub fn unassign_parachain_slot(origin: OriginFor, id: ParaId) -> DispatchResult {
+ T::AssignSlotOrigin::ensure_origin(origin.clone())?;
+
+ ensure!(
+ Self::has_permanent_slot(id) || Self::has_temporary_slot(id),
+ Error::::SlotNotAssigned
+ );
+
+ // Check & cache para status before we clear the lease
+ let is_parachain = Self::is_parachain(id);
+
+ // Remove perm or temp slot
+ Self::clear_slot_leases(origin.clone(), id)?;
+
+ if PermanentSlots::::contains_key(id) {
+ PermanentSlots::::remove(id);
+ >::mutate(|count| *count = count.saturating_sub(One::one()));
+ } else if TemporarySlots::::contains_key(id) {
+ TemporarySlots::::remove(id);
+ >::mutate(|count| *count = count.saturating_sub(One::one()));
+ if is_parachain {
+ >::mutate(|active_count| {
+ *active_count = active_count.saturating_sub(One::one())
+ });
+ }
+ }
+
+ // Force downgrade to parathread (if needed) before end of lease period
+ if is_parachain {
+ if let Err(err) = runtime_parachains::schedule_parachain_downgrade::(id) {
+ // Treat failed downgrade as warning .. slot lease has been cleared,
+ // so the parachain will be downgraded anyway by the slots pallet
+ // at the end of the lease period .
+ log::warn!(target: "assigned_slots",
+ "Failed to downgrade parachain {:?} at period {:?}: {:?}",
+ id, Self::current_lease_period_index(), err
+ );
+ }
+ }
+
+ Ok(())
+ }
+ }
+}
+
+impl Pallet {
+ /// Allocate temporary slot leases up to `MaxTemporarySlotPerLeasePeriod` per lease period.
+ /// Beyond the already active temporary slot leases, this function will activate more leases
+ /// in the following order of preference:
+ /// - Assigned slots that didn't have a turn yet, though their `period_begin` has passed.
+ /// - Assigned slots that already had one (or more) turn(s): they will be considered for the
+ /// current slot lease if they weren't active in the preceding one, and will be ranked by
+ /// total number of lease (lower first), and then when they last a turn (older ones first).
+ /// If any remaining ex-aequo, we just take the para ID in ascending order as discriminator.
+ ///
+ /// Assigned slots with a `period_begin` bigger than current lease period are not considered (yet).
+ ///
+ /// The function will call out to `Leaser::lease_out` to create the appropriate slot leases.
+ fn allocate_temporary_slot_leases(lease_period_index: LeasePeriodOf) -> DispatchResult {
+ let mut active_temp_slots = 0u32;
+ let mut pending_temp_slots = Vec::new();
+ TemporarySlots::::iter().for_each(|(para, slot)| {
+ match slot.last_lease {
+ Some(last_lease)
+ if last_lease <= lease_period_index &&
+ lease_period_index <
+ (last_lease.saturating_add(slot.period_count)) =>
+ {
+ // Active slot lease
+ active_temp_slots += 1;
+ }
+ Some(last_lease)
+ // Slot w/ past lease, only consider it every other slot lease period (times period_count)
+ if last_lease.saturating_add(slot.period_count.saturating_mul(2u32.into())) <= lease_period_index => {
+ pending_temp_slots.push((para, slot));
+ },
+ None if slot.period_begin <= lease_period_index => {
+ // Slot hasn't had a lease yet
+ pending_temp_slots.insert(0, (para, slot));
+ },
+ _ => {
+ // Slot not being considered for this lease period (will be for a subsequent one)
+ },
+ }
+ });
+
+ let mut newly_created_lease = 0u32;
+ if active_temp_slots < T::MaxTemporarySlotPerLeasePeriod::get() &&
+ !pending_temp_slots.is_empty()
+ {
+ // Sort by lease_count, favoring slots that had no or less turns first
+ // (then by last_lease index, and then Para ID)
+ pending_temp_slots.sort_by(|a, b| {
+ a.1.lease_count
+ .cmp(&b.1.lease_count)
+ .then_with(|| a.1.last_lease.cmp(&b.1.last_lease))
+ .then_with(|| a.0.cmp(&b.0))
+ });
+
+ let slots_to_be_upgraded = pending_temp_slots.iter().take(
+ (T::MaxTemporarySlotPerLeasePeriod::get().saturating_sub(active_temp_slots))
+ as usize,
+ );
+
+ for (id, temp_slot) in slots_to_be_upgraded {
+ TemporarySlots::::try_mutate::<_, _, Error, _>(id, |s| {
+ // Configure temp slot lease
+ Self::configure_slot_lease(
+ *id,
+ temp_slot.manager.clone(),
+ lease_period_index,
+ temp_slot.period_count,
+ )
+ .map_err(|_| Error::::CannotUpgrade)?;
+
+ // Update temp slot lease info in storage
+ *s = Some(ParachainTemporarySlot {
+ manager: temp_slot.manager.clone(),
+ period_begin: temp_slot.period_begin,
+ period_count: temp_slot.period_count,
+ last_lease: Some(lease_period_index),
+ lease_count: temp_slot.lease_count + 1,
+ });
+
+ newly_created_lease += 1;
+
+ Ok(())
+ })?;
+ }
+ }
+
+ ActiveTemporarySlotCount::::set(active_temp_slots + newly_created_lease);
+
+ Ok(())
+ }
+
+ /// Clear out all slot leases for both permanent & temporary slots.
+ /// The function merely calls out to `Slots::clear_all_leases`.
+ fn clear_slot_leases(origin: OriginFor, id: ParaId) -> DispatchResult {
+ Slots::::clear_all_leases(origin, id)
+ }
+
+ /// Create a parachain slot lease based on given params.
+ /// The function merely calls out to `Leaser::lease_out`.
+ fn configure_slot_lease(
+ para: ParaId,
+ manager: T::AccountId,
+ lease_period: LeasePeriodOf,
+ lease_duration: LeasePeriodOf,
+ ) -> Result<(), LeaseError> {
+ T::Leaser::lease_out(para, &manager, BalanceOf::::zero(), lease_period, lease_duration)
+ }
+
+ /// Returns whether a para has been assigned a permanent slot.
+ fn has_permanent_slot(id: ParaId) -> bool {
+ PermanentSlots::::contains_key(id)
+ }
+
+ /// Returns whether a para has been assigned temporary slot.
+ fn has_temporary_slot(id: ParaId) -> bool {
+ TemporarySlots::::contains_key(id)
+ }
+
+ /// Returns whether a para is currently a parachain.
+ fn is_parachain(id: ParaId) -> bool {
+ T::Registrar::is_parachain(id)
+ }
+
+ /// Returns current lease period index.
+ fn current_lease_period_index() -> LeasePeriodOf {
+ T::Leaser::lease_period_index(frame_system::Pallet::::block_number())
+ .and_then(|x| Some(x.0))
+ .unwrap()
+ }
+
+ /// Returns lease period index for block
+ fn lease_period_index(block: BlockNumberFor) -> Option<(LeasePeriodOf, bool)> {
+ T::Leaser::lease_period_index(block)
+ }
+
+ /// Handles start of a lease period.
+ fn manage_lease_period_start(lease_period_index: LeasePeriodOf) -> Weight {
+ // Note: leases that have ended in previous lease period, should have been cleaned in slots pallet.
+ if let Err(err) = Self::allocate_temporary_slot_leases(lease_period_index) {
+ log::error!(target: "assigned_slots",
+ "Allocating slots failed for lease period {:?}, with: {:?}",
+ lease_period_index, err
+ );
+ }
+ ::WeightInfo::force_lease() *
+ (T::MaxTemporarySlotPerLeasePeriod::get() as u64)
+ }
+}
+
+/// tests for this pallet
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::{assigned_slots, mock::TestRegistrar, slots};
+ use frame_support::{assert_noop, assert_ok, parameter_types};
+ use frame_system::EnsureRoot;
+ use pallet_balances;
+ use primitives::v1::{BlockNumber, Header};
+ use runtime_parachains::{
+ configuration as parachains_configuration, paras as parachains_paras,
+ shared as parachains_shared,
+ };
+ use sp_core::H256;
+ use sp_runtime::{
+ traits::{BlakeTwo256, IdentityLookup},
+ DispatchError::BadOrigin,
+ };
+
+ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic;
+ type Block = frame_system::mocking::MockBlock;
+
+ frame_support::construct_runtime!(
+ pub enum Test where
+ Block = Block,
+ NodeBlock = Block,
+ UncheckedExtrinsic = UncheckedExtrinsic,
+ {
+ System: frame_system::{Pallet, Call, Config, Storage, Event},
+ Balances: pallet_balances::{Pallet, Call, Storage, Config, Event},
+ Configuration: parachains_configuration::{Pallet, Call, Storage, Config},
+ ParasShared: parachains_shared::{Pallet, Call, Storage},
+ Parachains: parachains_paras::{Pallet, Call, Storage, Config, Event},
+ Slots: slots::{Pallet, Call, Storage, Event},
+ AssignedSlots: assigned_slots::{Pallet, Call, Storage, Event},
+ }
+ );
+
+ parameter_types! {
+ pub const BlockHashCount: u32 = 250;
+ }
+ impl frame_system::Config for Test {
+ type BaseCallFilter = frame_support::traits::Everything;
+ type BlockWeights = ();
+ type BlockLength = ();
+ type Origin = Origin;
+ type Call = Call;
+ type Index = u64;
+ type BlockNumber = BlockNumber;
+ type Hash = H256;
+ type Hashing = BlakeTwo256;
+ type AccountId = u64;
+ type Lookup = IdentityLookup;
+ type Header = Header;
+ type Event = Event;
+ type BlockHashCount = BlockHashCount;
+ type DbWeight = ();
+ type Version = ();
+ type PalletInfo = PalletInfo;
+ type AccountData = pallet_balances::AccountData;
+ type OnNewAccount = ();
+ type OnKilledAccount = ();
+ type SystemWeightInfo = ();
+ type SS58Prefix = ();
+ type OnSetCode = ();
+ }
+
+ parameter_types! {
+ pub const ExistentialDeposit: u64 = 1;
+ }
+
+ impl pallet_balances::Config for Test {
+ type Balance = u64;
+ type Event = Event;
+ type DustRemoval = ();
+ type ExistentialDeposit = ExistentialDeposit;
+ type AccountStore = System;
+ type WeightInfo = ();
+ type MaxLocks = ();
+ type MaxReserves = ();
+ type ReserveIdentifier = [u8; 8];
+ }
+
+ impl parachains_configuration::Config for Test {
+ type WeightInfo = parachains_configuration::TestWeightInfo;
+ }
+
+ impl parachains_paras::Config for Test {
+ type Event = Event;
+ type WeightInfo = parachains_paras::TestWeightInfo;
+ }
+
+ impl parachains_shared::Config for Test {}
+
+ parameter_types! {
+ pub const LeasePeriod: BlockNumber = 3;
+ pub static LeaseOffset: BlockNumber = 0;
+ pub const ParaDeposit: u64 = 1;
+ }
+
+ impl slots::Config for Test {
+ type Event = Event;
+ type Currency = Balances;
+ type Registrar = TestRegistrar;
+ type LeasePeriod = LeasePeriod;
+ type LeaseOffset = LeaseOffset;
+ type ForceOrigin = EnsureRoot;
+ type WeightInfo = crate::slots::TestWeightInfo;
+ }
+
+ parameter_types! {
+ pub const PermanentSlotLeasePeriodLength: u32 = 3;
+ pub const TemporarySlotLeasePeriodLength: u32 = 2;
+ pub const MaxPermanentSlots: u32 = 2;
+ pub const MaxTemporarySlots: u32 = 6;
+ pub const MaxTemporarySlotPerLeasePeriod: u32 = 2;
+ }
+
+ impl assigned_slots::Config for Test {
+ type Event = Event;
+ type AssignSlotOrigin = EnsureRoot;
+ type Leaser = Slots;
+ type PermanentSlotLeasePeriodLength = PermanentSlotLeasePeriodLength;
+ type TemporarySlotLeasePeriodLength = TemporarySlotLeasePeriodLength;
+ type MaxPermanentSlots = MaxPermanentSlots;
+ type MaxTemporarySlots = MaxTemporarySlots;
+ type MaxTemporarySlotPerLeasePeriod = MaxTemporarySlotPerLeasePeriod;
+ }
+
+ // This function basically just builds a genesis storage key/value store according to
+ // our desired mock up.
+ pub fn new_test_ext() -> sp_io::TestExternalities {
+ let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap();
+ pallet_balances::GenesisConfig:: {
+ balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
+ }
+ .assimilate_storage(&mut t)
+ .unwrap();
+ t.into()
+ }
+
+ fn run_to_block(n: BlockNumber) {
+ while System::block_number() < n {
+ let mut block = System::block_number();
+ // on_finalize hooks
+ AssignedSlots::on_finalize(block);
+ Slots::on_finalize(block);
+ Parachains::on_finalize(block);
+ ParasShared::on_finalize(block);
+ Configuration::on_finalize(block);
+ Balances::on_finalize(block);
+ System::on_finalize(block);
+ // Set next block
+ System::set_block_number(block + 1);
+ block = System::block_number();
+ // on_initialize hooks
+ System::on_initialize(block);
+ Balances::on_initialize(block);
+ Configuration::on_initialize(block);
+ ParasShared::on_initialize(block);
+ Parachains::on_initialize(block);
+ Slots::on_initialize(block);
+ AssignedSlots::on_initialize(block);
+ }
+ }
+
+ #[test]
+ fn basic_setup_works() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+ assert_eq!(AssignedSlots::current_lease_period_index(), 0);
+ assert_eq!(Slots::deposit_held(1.into(), &1), 0);
+
+ run_to_block(3);
+ assert_eq!(AssignedSlots::current_lease_period_index(), 1);
+ });
+ }
+
+ #[test]
+ fn assign_perm_slot_fails_for_unknown_para() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_noop!(
+ AssignedSlots::assign_perm_parachain_slot(Origin::root(), ParaId::from(1),),
+ Error::::ParaDoesntExist
+ );
+ });
+ }
+
+ #[test]
+ fn assign_perm_slot_fails_for_invalid_origin() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_noop!(
+ AssignedSlots::assign_perm_parachain_slot(Origin::signed(1), ParaId::from(1),),
+ BadOrigin
+ );
+ });
+ }
+
+ #[test]
+ fn assign_perm_slot_fails_when_not_parathread() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_ok!(TestRegistrar::::register(
+ 1,
+ ParaId::from(1),
+ Default::default(),
+ Default::default()
+ ));
+ assert_ok!(TestRegistrar::::make_parachain(ParaId::from(1)));
+
+ assert_noop!(
+ AssignedSlots::assign_perm_parachain_slot(Origin::root(), ParaId::from(1),),
+ Error::::NotParathread
+ );
+ });
+ }
+
+ #[test]
+ fn assign_perm_slot_fails_when_existing_lease() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_ok!(TestRegistrar::::register(
+ 1,
+ ParaId::from(1),
+ Default::default(),
+ Default::default()
+ ));
+
+ // Register lease in current lease period
+ assert_ok!(Slots::lease_out(ParaId::from(1), &1, 1, 1, 1));
+ // Try to assign a perm slot in current period fails
+ assert_noop!(
+ AssignedSlots::assign_perm_parachain_slot(Origin::root(), ParaId::from(1),),
+ Error::::OngoingLeaseExists
+ );
+
+ // Cleanup
+ assert_ok!(Slots::clear_all_leases(Origin::root(), 1.into()));
+
+ // Register lease for next lease period
+ assert_ok!(Slots::lease_out(ParaId::from(1), &1, 1, 2, 1));
+ // Should be detected and also fail
+ assert_noop!(
+ AssignedSlots::assign_perm_parachain_slot(Origin::root(), ParaId::from(1),),
+ Error::::OngoingLeaseExists
+ );
+ });
+ }
+
+ #[test]
+ fn assign_perm_slot_fails_when_max_perm_slots_exceeded() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_ok!(TestRegistrar::::register(
+ 1,
+ ParaId::from(1),
+ Default::default(),
+ Default::default()
+ ));
+
+ assert_ok!(TestRegistrar::::register(
+ 2,
+ ParaId::from(2),
+ Default::default(),
+ Default::default()
+ ));
+
+ assert_ok!(TestRegistrar::::register(
+ 3,
+ ParaId::from(3),
+ Default::default(),
+ Default::default()
+ ));
+
+ assert_ok!(AssignedSlots::assign_perm_parachain_slot(Origin::root(), ParaId::from(1),));
+ assert_ok!(AssignedSlots::assign_perm_parachain_slot(Origin::root(), ParaId::from(2),));
+ assert_eq!(AssignedSlots::permanent_slot_count(), 2);
+
+ assert_noop!(
+ AssignedSlots::assign_perm_parachain_slot(Origin::root(), ParaId::from(3),),
+ Error::::MaxPermanentSlotsExceeded
+ );
+ });
+ }
+
+ #[test]
+ fn assign_perm_slot_succeeds_for_parathread() {
+ new_test_ext().execute_with(|| {
+ let mut block = 1;
+ run_to_block(block);
+ assert_ok!(TestRegistrar::::register(
+ 1,
+ ParaId::from(1),
+ Default::default(),
+ Default::default()
+ ));
+
+ assert_eq!(AssignedSlots::permanent_slot_count(), 0);
+ assert_eq!(AssignedSlots::permanent_slots(ParaId::from(1)), None);
+
+ assert_ok!(AssignedSlots::assign_perm_parachain_slot(Origin::root(), ParaId::from(1),));
+
+ // Para is a parachain for PermanentSlotLeasePeriodLength * LeasePeriod blocks
+ while block < 9 {
+ println!("block #{}", block);
+
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), true);
+
+ assert_eq!(AssignedSlots::permanent_slot_count(), 1);
+ assert_eq!(AssignedSlots::has_permanent_slot(ParaId::from(1)), true);
+ assert_eq!(AssignedSlots::permanent_slots(ParaId::from(1)), Some((0, 3)));
+
+ assert_eq!(Slots::already_leased(ParaId::from(1), 0, 2), true);
+
+ block += 1;
+ run_to_block(block);
+ }
+
+ // Para lease ended, downgraded back to parathread
+ assert_eq!(TestRegistrar::::is_parathread(ParaId::from(1)), true);
+ assert_eq!(Slots::already_leased(ParaId::from(1), 0, 5), false);
+ });
+ }
+
+ #[test]
+ fn assign_temp_slot_fails_for_unknown_para() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_noop!(
+ AssignedSlots::assign_temp_parachain_slot(
+ Origin::root(),
+ ParaId::from(1),
+ SlotLeasePeriodStart::Current
+ ),
+ Error::::ParaDoesntExist
+ );
+ });
+ }
+
+ #[test]
+ fn assign_temp_slot_fails_for_invalid_origin() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_noop!(
+ AssignedSlots::assign_temp_parachain_slot(
+ Origin::signed(1),
+ ParaId::from(1),
+ SlotLeasePeriodStart::Current
+ ),
+ BadOrigin
+ );
+ });
+ }
+
+ #[test]
+ fn assign_temp_slot_fails_when_not_parathread() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_ok!(TestRegistrar::::register(
+ 1,
+ ParaId::from(1),
+ Default::default(),
+ Default::default()
+ ));
+ assert_ok!(TestRegistrar::::make_parachain(ParaId::from(1)));
+
+ assert_noop!(
+ AssignedSlots::assign_temp_parachain_slot(
+ Origin::root(),
+ ParaId::from(1),
+ SlotLeasePeriodStart::Current
+ ),
+ Error::::NotParathread
+ );
+ });
+ }
+
+ #[test]
+ fn assign_temp_slot_fails_when_existing_lease() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_ok!(TestRegistrar::::register(
+ 1,
+ ParaId::from(1),
+ Default::default(),
+ Default::default()
+ ));
+
+ // Register lease in current lease period
+ assert_ok!(Slots::lease_out(ParaId::from(1), &1, 1, 1, 1));
+ // Try to assign a perm slot in current period fails
+ assert_noop!(
+ AssignedSlots::assign_temp_parachain_slot(
+ Origin::root(),
+ ParaId::from(1),
+ SlotLeasePeriodStart::Current
+ ),
+ Error::::OngoingLeaseExists
+ );
+
+ // Cleanup
+ assert_ok!(Slots::clear_all_leases(Origin::root(), 1.into()));
+
+ // Register lease for next lease period
+ assert_ok!(Slots::lease_out(ParaId::from(1), &1, 1, 2, 1));
+ // Should be detected and also fail
+ assert_noop!(
+ AssignedSlots::assign_temp_parachain_slot(
+ Origin::root(),
+ ParaId::from(1),
+ SlotLeasePeriodStart::Current
+ ),
+ Error::::OngoingLeaseExists
+ );
+ });
+ }
+
+ #[test]
+ fn assign_temp_slot_fails_when_max_temp_slots_exceeded() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ // Register 6 paras & a temp slot for each
+ for n in 0..=5 {
+ assert_ok!(TestRegistrar::::register(
+ n,
+ ParaId::from(n as u32),
+ Default::default(),
+ Default::default()
+ ));
+
+ assert_ok!(AssignedSlots::assign_temp_parachain_slot(
+ Origin::root(),
+ ParaId::from(n as u32),
+ SlotLeasePeriodStart::Current
+ ));
+ }
+
+ assert_eq!(AssignedSlots::temporary_slot_count(), 6);
+
+ // Attempt to assign one more temp slot
+ assert_ok!(TestRegistrar::::register(
+ 7,
+ ParaId::from(7),
+ Default::default(),
+ Default::default()
+ ));
+ assert_noop!(
+ AssignedSlots::assign_temp_parachain_slot(
+ Origin::root(),
+ ParaId::from(7),
+ SlotLeasePeriodStart::Current
+ ),
+ Error::::MaxTemporarySlotsExceeded
+ );
+ });
+ }
+
+ #[test]
+ fn assign_temp_slot_succeeds_for_single_parathread() {
+ new_test_ext().execute_with(|| {
+ let mut block = 1;
+ run_to_block(block);
+ assert_ok!(TestRegistrar::::register(
+ 1,
+ ParaId::from(1),
+ Default::default(),
+ Default::default()
+ ));
+
+ assert_eq!(AssignedSlots::temporary_slots(ParaId::from(1)), None);
+
+ assert_ok!(AssignedSlots::assign_temp_parachain_slot(
+ Origin::root(),
+ ParaId::from(1),
+ SlotLeasePeriodStart::Current
+ ));
+ assert_eq!(AssignedSlots::temporary_slot_count(), 1);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 1);
+
+ // Block 1-5
+ // Para is a parachain for TemporarySlotLeasePeriodLength * LeasePeriod blocks
+ while block < 6 {
+ println!("block #{}", block);
+ println!("lease period #{}", AssignedSlots::current_lease_period_index());
+ println!("lease {:?}", Slots::lease(ParaId::from(1)));
+
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), true);
+
+ assert_eq!(AssignedSlots::has_temporary_slot(ParaId::from(1)), true);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 1);
+ assert_eq!(
+ AssignedSlots::temporary_slots(ParaId::from(1)),
+ Some(ParachainTemporarySlot {
+ manager: 1,
+ period_begin: 0,
+ period_count: 2, // TemporarySlotLeasePeriodLength
+ last_lease: Some(0),
+ lease_count: 1
+ })
+ );
+
+ assert_eq!(Slots::already_leased(ParaId::from(1), 0, 1), true);
+
+ block += 1;
+ run_to_block(block);
+ }
+
+ // Block 6
+ println!("block #{}", block);
+ println!("lease period #{}", AssignedSlots::current_lease_period_index());
+ println!("lease {:?}", Slots::lease(ParaId::from(1)));
+
+ // Para lease ended, downgraded back to parathread
+ assert_eq!(TestRegistrar::::is_parathread(ParaId::from(1)), true);
+ assert_eq!(Slots::already_leased(ParaId::from(1), 0, 3), false);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 0);
+
+ // Block 12
+ // Para should get a turn after TemporarySlotLeasePeriodLength * LeasePeriod blocks
+ run_to_block(12);
+ println!("block #{}", block);
+ println!("lease period #{}", AssignedSlots::current_lease_period_index());
+ println!("lease {:?}", Slots::lease(ParaId::from(1)));
+
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), true);
+ assert_eq!(Slots::already_leased(ParaId::from(1), 4, 5), true);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 1);
+ });
+ }
+
+ #[test]
+ fn assign_temp_slot_succeeds_for_multiple_parathreads() {
+ new_test_ext().execute_with(|| {
+ // Block 1, Period 0
+ run_to_block(1);
+
+ // Register 6 paras & a temp slot for each
+ // (3 slots in current lease period, 3 in the next one)
+ for n in 0..=5 {
+ assert_ok!(TestRegistrar::::register(
+ n,
+ ParaId::from(n as u32),
+ Default::default(),
+ Default::default()
+ ));
+
+ assert_ok!(AssignedSlots::assign_temp_parachain_slot(
+ Origin::root(),
+ ParaId::from(n as u32),
+ if (n % 2).is_zero() {
+ SlotLeasePeriodStart::Current
+ } else {
+ SlotLeasePeriodStart::Next
+ }
+ ));
+ }
+
+ // Block 1-5, Period 0-1
+ for n in 1..=5 {
+ if n > 1 {
+ run_to_block(n);
+ }
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(0)), true);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(2)), true);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(3)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(4)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(5)), false);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 2);
+ }
+
+ // Block 6-11, Period 2-3
+ for n in 6..=11 {
+ run_to_block(n);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(0)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), true);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(2)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(3)), true);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(4)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(5)), false);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 2);
+ }
+
+ // Block 12-17, Period 4-5
+ for n in 12..=17 {
+ run_to_block(n);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(0)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(2)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(3)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(4)), true);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(5)), true);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 2);
+ }
+
+ // Block 18-23, Period 6-7
+ for n in 18..=23 {
+ run_to_block(n);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(0)), true);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(2)), true);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(3)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(4)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(5)), false);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 2);
+ }
+
+ // Block 24-29, Period 8-9
+ for n in 24..=29 {
+ run_to_block(n);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(0)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), true);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(2)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(3)), true);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(4)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(5)), false);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 2);
+ }
+
+ // Block 30-35, Period 10-11
+ for n in 30..=35 {
+ run_to_block(n);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(0)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(2)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(3)), false);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(4)), true);
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(5)), true);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 2);
+ }
+ });
+ }
+
+ #[test]
+ fn unassign_slot_fails_for_unknown_para() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_noop!(
+ AssignedSlots::unassign_parachain_slot(Origin::root(), ParaId::from(1),),
+ Error::::SlotNotAssigned
+ );
+ });
+ }
+
+ #[test]
+ fn unassign_slot_fails_for_invalid_origin() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_noop!(
+ AssignedSlots::assign_perm_parachain_slot(Origin::signed(1), ParaId::from(1),),
+ BadOrigin
+ );
+ });
+ }
+
+ #[test]
+ fn unassign_perm_slot_succeeds() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_ok!(TestRegistrar::::register(
+ 1,
+ ParaId::from(1),
+ Default::default(),
+ Default::default()
+ ));
+
+ assert_ok!(AssignedSlots::assign_perm_parachain_slot(Origin::root(), ParaId::from(1),));
+
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), true);
+
+ assert_ok!(AssignedSlots::unassign_parachain_slot(Origin::root(), ParaId::from(1),));
+
+ assert_eq!(AssignedSlots::permanent_slot_count(), 0);
+ assert_eq!(AssignedSlots::has_permanent_slot(ParaId::from(1)), false);
+ assert_eq!(AssignedSlots::permanent_slots(ParaId::from(1)), None);
+
+ assert_eq!(Slots::already_leased(ParaId::from(1), 0, 2), false);
+ });
+ }
+
+ #[test]
+ fn unassign_temp_slot_succeeds() {
+ new_test_ext().execute_with(|| {
+ run_to_block(1);
+
+ assert_ok!(TestRegistrar::::register(
+ 1,
+ ParaId::from(1),
+ Default::default(),
+ Default::default()
+ ));
+
+ assert_ok!(AssignedSlots::assign_temp_parachain_slot(
+ Origin::root(),
+ ParaId::from(1),
+ SlotLeasePeriodStart::Current
+ ));
+
+ assert_eq!(TestRegistrar::::is_parachain(ParaId::from(1)), true);
+
+ assert_ok!(AssignedSlots::unassign_parachain_slot(Origin::root(), ParaId::from(1),));
+
+ assert_eq!(AssignedSlots::temporary_slot_count(), 0);
+ assert_eq!(AssignedSlots::active_temporary_slot_count(), 0);
+ assert_eq!(AssignedSlots::has_temporary_slot(ParaId::from(1)), false);
+ assert_eq!(AssignedSlots::temporary_slots(ParaId::from(1)), None);
+
+ assert_eq!(Slots::already_leased(ParaId::from(1), 0, 1), false);
+ });
+ }
+}
diff --git a/runtime/common/src/integration_tests.rs b/runtime/common/src/integration_tests.rs
index 70778023fbe4..aa5feee11b30 100644
--- a/runtime/common/src/integration_tests.rs
+++ b/runtime/common/src/integration_tests.rs
@@ -216,6 +216,7 @@ impl slots::Config for Test {
type Registrar = Registrar;
type LeasePeriod = LeasePeriod;
type LeaseOffset = LeaseOffset;
+ type ForceOrigin = EnsureRoot;
type WeightInfo = crate::slots::TestWeightInfo;
}
diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs
index 1f3dd2b237dd..9597d1a15bd1 100644
--- a/runtime/common/src/lib.rs
+++ b/runtime/common/src/lib.rs
@@ -18,6 +18,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
+pub mod assigned_slots;
pub mod auctions;
pub mod claims;
pub mod crowdloan;
diff --git a/runtime/common/src/slots.rs b/runtime/common/src/slots.rs
index 26ada547c7fe..687b8011f51e 100644
--- a/runtime/common/src/slots.rs
+++ b/runtime/common/src/slots.rs
@@ -87,6 +87,9 @@ pub mod pallet {
#[pallet::constant]
type LeaseOffset: Get;
+ /// The origin which may forcibly create or clear leases. Root can always do this.
+ type ForceOrigin: EnsureOrigin<::Origin>;
+
/// Weight Information for the Extrinsics in the Pallet
type WeightInfo: WeightInfo;
}
@@ -159,7 +162,7 @@ pub mod pallet {
/// Just a connect into the `lease_out` call, in case Root wants to force some lease to happen
/// independently of any other on-chain mechanism to use it.
///
- /// Can only be called by the Root origin.
+ /// The dispatch origin for this call must match `T::ForceOrigin`.
#[pallet::weight(T::WeightInfo::force_lease())]
pub fn force_lease(
origin: OriginFor,
@@ -169,7 +172,7 @@ pub mod pallet {
period_begin: LeasePeriodOf,
period_count: LeasePeriodOf,
) -> DispatchResult {
- ensure_root(origin)?;
+ T::ForceOrigin::ensure_origin(origin)?;
Self::lease_out(para, &leaser, amount, period_begin, period_count)
.map_err(|_| Error::::LeaseError)?;
Ok(())
@@ -177,10 +180,10 @@ pub mod pallet {
/// Clear all leases for a Para Id, refunding any deposits back to the original owners.
///
- /// Can only be called by the Root origin.
+ /// The dispatch origin for this call must match `T::ForceOrigin`.
#[pallet::weight(T::WeightInfo::clear_all_leases())]
pub fn clear_all_leases(origin: OriginFor, para: ParaId) -> DispatchResult {
- ensure_root(origin)?;
+ T::ForceOrigin::ensure_origin(origin)?;
let deposits = Self::all_deposits_held(para);
// Refund any deposits for these leases
@@ -495,6 +498,7 @@ mod tests {
use crate::{mock::TestRegistrar, slots};
use frame_support::{assert_noop, assert_ok, parameter_types};
+ use frame_system::EnsureRoot;
use pallet_balances;
use primitives::v1::{BlockNumber, Header};
use sp_core::H256;
@@ -572,6 +576,7 @@ mod tests {
type Registrar = TestRegistrar;
type LeasePeriod = LeasePeriod;
type LeaseOffset = LeaseOffset;
+ type ForceOrigin = EnsureRoot;
type WeightInfo = crate::slots::TestWeightInfo;
}
diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs
index d0422f88ef58..e00371a0f04d 100644
--- a/runtime/kusama/src/lib.rs
+++ b/runtime/kusama/src/lib.rs
@@ -1245,6 +1245,7 @@ impl slots::Config for Runtime {
type Registrar = Registrar;
type LeasePeriod = LeasePeriod;
type LeaseOffset = ();
+ type ForceOrigin = MoreThanHalfCouncil;
type WeightInfo = weights::runtime_common_slots::WeightInfo;
}
diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs
index f9372a8b79a7..547a30560139 100644
--- a/runtime/polkadot/src/lib.rs
+++ b/runtime/polkadot/src/lib.rs
@@ -1240,6 +1240,7 @@ impl slots::Config for Runtime {
type Registrar = Registrar;
type LeasePeriod = LeasePeriod;
type LeaseOffset = LeaseOffset;
+ type ForceOrigin = MoreThanHalfCouncil;
type WeightInfo = weights::runtime_common_slots::WeightInfo;
}
diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs
index acb0ee20dc49..ccbb3853e36c 100644
--- a/runtime/rococo/src/lib.rs
+++ b/runtime/rococo/src/lib.rs
@@ -43,8 +43,9 @@ use primitives::v1::{
ValidatorIndex,
};
use runtime_common::{
- auctions, crowdloan, impls::ToAuthor, paras_registrar, paras_sudo_wrapper, slots, xcm_sender,
- BlockHashCount, BlockLength, BlockWeights, RocksDbWeight, SlowAdjustingFeeUpdate,
+ assigned_slots, auctions, crowdloan, impls::ToAuthor, paras_registrar, paras_sudo_wrapper,
+ slots, xcm_sender, BlockHashCount, BlockLength, BlockWeights, RocksDbWeight,
+ SlowAdjustingFeeUpdate,
};
use runtime_parachains::{self, runtime_api_impl::v1 as runtime_api_impl};
use scale_info::TypeInfo;
@@ -239,11 +240,12 @@ construct_runtime! {
ParasDisputes: parachains_disputes,
// Parachain Onboarding Pallets
- Registrar: paras_registrar,
- Auctions: auctions,
- Crowdloan: crowdloan,
- Slots: slots,
- ParasSudoWrapper: paras_sudo_wrapper,
+ Registrar: paras_registrar::{Pallet, Call, Storage, Event, Config},
+ Auctions: auctions::{Pallet, Call, Storage, Event},
+ Crowdloan: crowdloan::{Pallet, Call, Storage, Event},
+ Slots: slots::{Pallet, Call, Storage, Event},
+ ParasSudoWrapper: paras_sudo_wrapper::{Pallet, Call},
+ AssignedSlots: assigned_slots::{Pallet, Call, Storage, Event},
// Sudo
Sudo: pallet_sudo,
@@ -786,6 +788,25 @@ impl parachains_initializer::Config for Runtime {
impl paras_sudo_wrapper::Config for Runtime {}
+parameter_types! {
+ pub const PermanentSlotLeasePeriodLength: u32 = 26;
+ pub const TemporarySlotLeasePeriodLength: u32 = 1;
+ pub const MaxPermanentSlots: u32 = 5;
+ pub const MaxTemporarySlots: u32 = 20;
+ pub const MaxTemporarySlotPerLeasePeriod: u32 = 5;
+}
+
+impl assigned_slots::Config for Runtime {
+ type Event = Event;
+ type AssignSlotOrigin = EnsureRoot;
+ type Leaser = Slots;
+ type PermanentSlotLeasePeriodLength = PermanentSlotLeasePeriodLength;
+ type TemporarySlotLeasePeriodLength = TemporarySlotLeasePeriodLength;
+ type MaxPermanentSlots = MaxPermanentSlots;
+ type MaxTemporarySlots = MaxTemporarySlots;
+ type MaxTemporarySlotPerLeasePeriod = MaxTemporarySlotPerLeasePeriod;
+}
+
parameter_types! {
pub const ParaDeposit: Balance = 5 * DOLLARS;
pub const DataDepositPerByte: Balance = deposit(0, 1);
@@ -1019,7 +1040,7 @@ impl auctions::Config for Runtime {
}
parameter_types! {
- pub const LeasePeriod: BlockNumber = 1 * DAYS;
+ pub const LeasePeriod: BlockNumber = 7 * DAYS;
}
impl slots::Config for Runtime {
@@ -1028,6 +1049,7 @@ impl slots::Config for Runtime {
type Registrar = Registrar;
type LeasePeriod = LeasePeriod;
type LeaseOffset = ();
+ type ForceOrigin = EnsureRoot;
type WeightInfo = slots::TestWeightInfo;
}
@@ -1108,8 +1130,9 @@ impl InstanceFilter for ProxyType {
fn filter(&self, c: &Call) -> bool {
match self {
ProxyType::Any => true,
- ProxyType::CancelProxy =>
- matches!(c, Call::Proxy(pallet_proxy::Call::reject_announcement { .. })),
+ ProxyType::CancelProxy => {
+ matches!(c, Call::Proxy(pallet_proxy::Call::reject_announcement { .. }))
+ },
ProxyType::Auction => matches!(
c,
Call::Auctions { .. } |
diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs
index a5cae34ca5a3..833887cb22dd 100644
--- a/runtime/westend/src/lib.rs
+++ b/runtime/westend/src/lib.rs
@@ -29,9 +29,10 @@ use primitives::v1::{
SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex,
};
use runtime_common::{
- auctions, crowdloan, impls::ToAuthor, paras_registrar, paras_sudo_wrapper, slots, xcm_sender,
- BlockHashCount, BlockLength, BlockWeights, CurrencyToVote, OffchainSolutionLengthLimit,
- OffchainSolutionWeightLimit, RocksDbWeight, SlowAdjustingFeeUpdate,
+ assigned_slots, auctions, crowdloan, impls::ToAuthor, paras_registrar, paras_sudo_wrapper,
+ slots, xcm_sender, BlockHashCount, BlockLength, BlockWeights, CurrencyToVote,
+ OffchainSolutionLengthLimit, OffchainSolutionWeightLimit, RocksDbWeight,
+ SlowAdjustingFeeUpdate,
};
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
@@ -862,6 +863,25 @@ impl parachains_initializer::Config for Runtime {
impl paras_sudo_wrapper::Config for Runtime {}
+parameter_types! {
+ pub const PermanentSlotLeasePeriodLength: u32 = 26;
+ pub const TemporarySlotLeasePeriodLength: u32 = 1;
+ pub const MaxPermanentSlots: u32 = 5;
+ pub const MaxTemporarySlots: u32 = 20;
+ pub const MaxTemporarySlotPerLeasePeriod: u32 = 5;
+}
+
+impl assigned_slots::Config for Runtime {
+ type Event = Event;
+ type AssignSlotOrigin = EnsureRoot;
+ type Leaser = Slots;
+ type PermanentSlotLeasePeriodLength = PermanentSlotLeasePeriodLength;
+ type TemporarySlotLeasePeriodLength = TemporarySlotLeasePeriodLength;
+ type MaxPermanentSlots = MaxPermanentSlots;
+ type MaxTemporarySlots = MaxTemporarySlots;
+ type MaxTemporarySlotPerLeasePeriod = MaxTemporarySlotPerLeasePeriod;
+}
+
parameter_types! {
pub const ParaDeposit: Balance = 2000 * CENTS;
pub const DataDepositPerByte: Balance = deposit(0, 1);
@@ -887,6 +907,7 @@ impl slots::Config for Runtime {
type Registrar = Registrar;
type LeasePeriod = LeasePeriod;
type LeaseOffset = ();
+ type ForceOrigin = EnsureRoot;
type WeightInfo = weights::runtime_common_slots::WeightInfo;
}
@@ -1112,6 +1133,7 @@ construct_runtime! {
ParasSudoWrapper: paras_sudo_wrapper::{Pallet, Call} = 62,
Auctions: auctions::{Pallet, Call, Storage, Event} = 63,
Crowdloan: crowdloan::{Pallet, Call, Storage, Event} = 64,
+ AssignedSlots: assigned_slots::{Pallet, Call, Storage, Event} = 65,
// Pallet for sending XCM.
XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 99,
diff --git a/scripts/gitlab/lingua.dic b/scripts/gitlab/lingua.dic
index 14d85a6d3e28..944779c8daad 100644
--- a/scripts/gitlab/lingua.dic
+++ b/scripts/gitlab/lingua.dic
@@ -280,6 +280,7 @@ typesystem
ubuntu/M
UDP
UI
+unassign
unconcluded
unfinalize/B
unfinalized