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