polkadot_runtime_common/assigned_slots/
mod.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! This pallet allows to assign permanent (long-lived) or temporary
18//! (short-lived) parachain slots to paras, leveraging the existing
19//! parachain slot lease mechanism. Temporary slots are given turns
20//! in a fair (though best-effort) manner.
21//! The dispatchables must be called from the configured origin
22//! (typically `Sudo` or a governance origin).
23//! This pallet should not be used on a production relay chain,
24//! only on a test relay chain (e.g. Rococo).
25
26pub mod benchmarking;
27pub mod migration;
28
29use crate::{
30	slots::{self, Pallet as Slots, WeightInfo as SlotsWeightInfo},
31	traits::{LeaseError, Leaser, Registrar},
32};
33use alloc::vec::Vec;
34use codec::{Decode, Encode, MaxEncodedLen};
35use frame_support::{pallet_prelude::*, traits::Currency};
36use frame_system::pallet_prelude::*;
37pub use pallet::*;
38use polkadot_primitives::Id as ParaId;
39use polkadot_runtime_parachains::{
40	configuration,
41	paras::{self},
42};
43use scale_info::TypeInfo;
44use sp_runtime::traits::{One, Saturating, Zero};
45
46const LOG_TARGET: &str = "runtime::assigned_slots";
47
48/// Lease period an assigned slot should start from (current, or next one).
49#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)]
50pub enum SlotLeasePeriodStart {
51	Current,
52	Next,
53}
54
55/// Information about a temporary parachain slot.
56#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, MaxEncodedLen, RuntimeDebug, TypeInfo)]
57pub struct ParachainTemporarySlot<AccountId, LeasePeriod> {
58	/// Manager account of the para.
59	pub manager: AccountId,
60	/// Lease period the parachain slot should ideally start from,
61	/// As slot are allocated in a best-effort manner, this could be later,
62	/// but not earlier than the specified period.
63	pub period_begin: LeasePeriod,
64	/// Number of lease period the slot lease will last.
65	/// This is set to the value configured in `TemporarySlotLeasePeriodLength`.
66	pub period_count: LeasePeriod,
67	/// Last lease period this slot had a turn in (incl. current).
68	/// This is set to the beginning period of a slot.
69	pub last_lease: Option<LeasePeriod>,
70	/// Number of leases this temporary slot had (incl. current).
71	pub lease_count: u32,
72}
73
74pub trait WeightInfo {
75	fn assign_perm_parachain_slot() -> Weight;
76	fn assign_temp_parachain_slot() -> Weight;
77	fn unassign_parachain_slot() -> Weight;
78	fn set_max_permanent_slots() -> Weight;
79	fn set_max_temporary_slots() -> Weight;
80}
81
82pub struct TestWeightInfo;
83impl WeightInfo for TestWeightInfo {
84	fn assign_perm_parachain_slot() -> Weight {
85		Weight::zero()
86	}
87	fn assign_temp_parachain_slot() -> Weight {
88		Weight::zero()
89	}
90	fn unassign_parachain_slot() -> Weight {
91		Weight::zero()
92	}
93	fn set_max_permanent_slots() -> Weight {
94		Weight::zero()
95	}
96	fn set_max_temporary_slots() -> Weight {
97		Weight::zero()
98	}
99}
100
101type BalanceOf<T> = <<<T as Config>::Leaser as Leaser<BlockNumberFor<T>>>::Currency as Currency<
102	<T as frame_system::Config>::AccountId,
103>>::Balance;
104type LeasePeriodOf<T> = <<T as Config>::Leaser as Leaser<BlockNumberFor<T>>>::LeasePeriod;
105
106#[frame_support::pallet]
107pub mod pallet {
108	use super::*;
109
110	/// The in-code storage version.
111	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
112
113	#[pallet::pallet]
114	#[pallet::storage_version(STORAGE_VERSION)]
115	pub struct Pallet<T>(_);
116
117	#[pallet::config]
118	#[pallet::disable_frame_system_supertrait_check]
119	pub trait Config: configuration::Config + paras::Config + slots::Config {
120		/// The overarching event type.
121		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
122
123		/// Origin for assigning slots.
124		type AssignSlotOrigin: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin>;
125
126		/// The type representing the leasing system.
127		type Leaser: Leaser<
128			BlockNumberFor<Self>,
129			AccountId = Self::AccountId,
130			LeasePeriod = BlockNumberFor<Self>,
131		>;
132
133		/// The number of lease periods a permanent parachain slot lasts.
134		#[pallet::constant]
135		type PermanentSlotLeasePeriodLength: Get<u32>;
136
137		/// The number of lease periods a temporary parachain slot lasts.
138		#[pallet::constant]
139		type TemporarySlotLeasePeriodLength: Get<u32>;
140
141		/// The max number of temporary slots to be scheduled per lease periods.
142		#[pallet::constant]
143		type MaxTemporarySlotPerLeasePeriod: Get<u32>;
144
145		/// Weight Information for the Extrinsics in the Pallet
146		type WeightInfo: WeightInfo;
147	}
148
149	/// Assigned permanent slots, with their start lease period, and duration.
150	#[pallet::storage]
151	pub type PermanentSlots<T: Config> =
152		StorageMap<_, Twox64Concat, ParaId, (LeasePeriodOf<T>, LeasePeriodOf<T>), OptionQuery>;
153
154	/// Number of assigned (and active) permanent slots.
155	#[pallet::storage]
156	pub type PermanentSlotCount<T: Config> = StorageValue<_, u32, ValueQuery>;
157
158	/// Assigned temporary slots.
159	#[pallet::storage]
160	pub type TemporarySlots<T: Config> = StorageMap<
161		_,
162		Twox64Concat,
163		ParaId,
164		ParachainTemporarySlot<T::AccountId, LeasePeriodOf<T>>,
165		OptionQuery,
166	>;
167
168	/// Number of assigned temporary slots.
169	#[pallet::storage]
170	pub type TemporarySlotCount<T: Config> = StorageValue<_, u32, ValueQuery>;
171
172	/// Number of active temporary slots in current slot lease period.
173	#[pallet::storage]
174	pub type ActiveTemporarySlotCount<T: Config> = StorageValue<_, u32, ValueQuery>;
175
176	///  The max number of temporary slots that can be assigned.
177	#[pallet::storage]
178	pub type MaxTemporarySlots<T: Config> = StorageValue<_, u32, ValueQuery>;
179
180	/// The max number of permanent slots that can be assigned.
181	#[pallet::storage]
182	pub type MaxPermanentSlots<T: Config> = StorageValue<_, u32, ValueQuery>;
183
184	#[pallet::genesis_config]
185	#[derive(frame_support::DefaultNoBound)]
186	pub struct GenesisConfig<T: Config> {
187		pub max_temporary_slots: u32,
188		pub max_permanent_slots: u32,
189		#[serde(skip)]
190		pub _config: PhantomData<T>,
191	}
192
193	#[pallet::genesis_build]
194	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
195		fn build(&self) {
196			MaxPermanentSlots::<T>::put(&self.max_permanent_slots);
197			MaxTemporarySlots::<T>::put(&self.max_temporary_slots);
198		}
199	}
200
201	#[pallet::event]
202	#[pallet::generate_deposit(pub(super) fn deposit_event)]
203	pub enum Event<T: Config> {
204		/// A parachain was assigned a permanent parachain slot
205		PermanentSlotAssigned(ParaId),
206		/// A parachain was assigned a temporary parachain slot
207		TemporarySlotAssigned(ParaId),
208		/// The maximum number of permanent slots has been changed
209		MaxPermanentSlotsChanged { slots: u32 },
210		/// The maximum number of temporary slots has been changed
211		MaxTemporarySlotsChanged { slots: u32 },
212	}
213
214	#[pallet::error]
215	pub enum Error<T> {
216		/// The specified parachain is not registered.
217		ParaDoesntExist,
218		/// Not a parathread (on-demand parachain).
219		NotParathread,
220		/// Cannot upgrade on-demand parachain to lease holding
221		/// parachain.
222		CannotUpgrade,
223		/// Cannot downgrade lease holding parachain to
224		/// on-demand.
225		CannotDowngrade,
226		/// Permanent or Temporary slot already assigned.
227		SlotAlreadyAssigned,
228		/// Permanent or Temporary slot has not been assigned.
229		SlotNotAssigned,
230		/// An ongoing lease already exists.
231		OngoingLeaseExists,
232		// The maximum number of permanent slots exceeded
233		MaxPermanentSlotsExceeded,
234		// The maximum number of temporary slots exceeded
235		MaxTemporarySlotsExceeded,
236	}
237
238	#[pallet::hooks]
239	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
240		fn on_initialize(n: BlockNumberFor<T>) -> Weight {
241			if let Some((lease_period, first_block)) = Self::lease_period_index(n) {
242				// If we're beginning a new lease period then handle that.
243				if first_block {
244					return Self::manage_lease_period_start(lease_period)
245				}
246			}
247
248			// We didn't return early above, so we didn't do anything.
249			Weight::zero()
250		}
251	}
252
253	#[pallet::call]
254	impl<T: Config> Pallet<T> {
255		/// Assign a permanent parachain slot and immediately create a lease for it.
256		#[pallet::call_index(0)]
257		#[pallet::weight((<T as Config>::WeightInfo::assign_perm_parachain_slot(), DispatchClass::Operational))]
258		pub fn assign_perm_parachain_slot(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
259			T::AssignSlotOrigin::ensure_origin(origin)?;
260
261			let manager = T::Registrar::manager_of(id).ok_or(Error::<T>::ParaDoesntExist)?;
262
263			ensure!(T::Registrar::is_parathread(id), Error::<T>::NotParathread);
264
265			ensure!(
266				!Self::has_permanent_slot(id) && !Self::has_temporary_slot(id),
267				Error::<T>::SlotAlreadyAssigned
268			);
269
270			let current_lease_period: BlockNumberFor<T> = Self::current_lease_period_index();
271			ensure!(
272				!T::Leaser::already_leased(
273					id,
274					current_lease_period,
275					// Check current lease & next one
276					current_lease_period.saturating_add(
277						BlockNumberFor::<T>::from(2u32)
278							.saturating_mul(T::PermanentSlotLeasePeriodLength::get().into())
279					)
280				),
281				Error::<T>::OngoingLeaseExists
282			);
283
284			ensure!(
285				PermanentSlotCount::<T>::get() < MaxPermanentSlots::<T>::get(),
286				Error::<T>::MaxPermanentSlotsExceeded
287			);
288
289			// Permanent slot assignment fails if a lease cannot be created
290			Self::configure_slot_lease(
291				id,
292				manager,
293				current_lease_period,
294				T::PermanentSlotLeasePeriodLength::get().into(),
295			)
296			.map_err(|_| Error::<T>::CannotUpgrade)?;
297
298			PermanentSlots::<T>::insert(
299				id,
300				(
301					current_lease_period,
302					LeasePeriodOf::<T>::from(T::PermanentSlotLeasePeriodLength::get()),
303				),
304			);
305			PermanentSlotCount::<T>::mutate(|count| count.saturating_inc());
306
307			Self::deposit_event(Event::<T>::PermanentSlotAssigned(id));
308			Ok(())
309		}
310
311		/// Assign a temporary parachain slot. The function tries to create a lease for it
312		/// immediately if `SlotLeasePeriodStart::Current` is specified, and if the number
313		/// of currently active temporary slots is below `MaxTemporarySlotPerLeasePeriod`.
314		#[pallet::call_index(1)]
315		#[pallet::weight((<T as Config>::WeightInfo::assign_temp_parachain_slot(), DispatchClass::Operational))]
316		pub fn assign_temp_parachain_slot(
317			origin: OriginFor<T>,
318			id: ParaId,
319			lease_period_start: SlotLeasePeriodStart,
320		) -> DispatchResult {
321			T::AssignSlotOrigin::ensure_origin(origin)?;
322
323			let manager = T::Registrar::manager_of(id).ok_or(Error::<T>::ParaDoesntExist)?;
324
325			ensure!(T::Registrar::is_parathread(id), Error::<T>::NotParathread);
326
327			ensure!(
328				!Self::has_permanent_slot(id) && !Self::has_temporary_slot(id),
329				Error::<T>::SlotAlreadyAssigned
330			);
331
332			let current_lease_period: BlockNumberFor<T> = Self::current_lease_period_index();
333			ensure!(
334				!T::Leaser::already_leased(
335					id,
336					current_lease_period,
337					// Check current lease & next one
338					current_lease_period.saturating_add(
339						BlockNumberFor::<T>::from(2u32)
340							.saturating_mul(T::TemporarySlotLeasePeriodLength::get().into())
341					)
342				),
343				Error::<T>::OngoingLeaseExists
344			);
345
346			ensure!(
347				TemporarySlotCount::<T>::get() < MaxTemporarySlots::<T>::get(),
348				Error::<T>::MaxTemporarySlotsExceeded
349			);
350
351			let mut temp_slot = ParachainTemporarySlot {
352				manager: manager.clone(),
353				period_begin: match lease_period_start {
354					SlotLeasePeriodStart::Current => current_lease_period,
355					SlotLeasePeriodStart::Next => current_lease_period + One::one(),
356				},
357				period_count: T::TemporarySlotLeasePeriodLength::get().into(),
358				last_lease: None,
359				lease_count: 0,
360			};
361
362			if lease_period_start == SlotLeasePeriodStart::Current &&
363				ActiveTemporarySlotCount::<T>::get() < T::MaxTemporarySlotPerLeasePeriod::get()
364			{
365				// Try to allocate slot directly
366				match Self::configure_slot_lease(
367					id,
368					manager,
369					temp_slot.period_begin,
370					temp_slot.period_count,
371				) {
372					Ok(_) => {
373						ActiveTemporarySlotCount::<T>::mutate(|count| count.saturating_inc());
374						temp_slot.last_lease = Some(temp_slot.period_begin);
375						temp_slot.lease_count += 1;
376					},
377					Err(err) => {
378						// Treat failed lease creation as warning .. slot will be allocated a lease
379						// in a subsequent lease period by the `allocate_temporary_slot_leases`
380						// function.
381						log::warn!(
382							target: LOG_TARGET,
383							"Failed to allocate a temp slot for para {:?} at period {:?}: {:?}",
384							id,
385							current_lease_period,
386							err
387						);
388					},
389				}
390			}
391
392			TemporarySlots::<T>::insert(id, temp_slot);
393			TemporarySlotCount::<T>::mutate(|count| count.saturating_inc());
394
395			Self::deposit_event(Event::<T>::TemporarySlotAssigned(id));
396
397			Ok(())
398		}
399
400		/// Unassign a permanent or temporary parachain slot
401		#[pallet::call_index(2)]
402		#[pallet::weight((<T as Config>::WeightInfo::unassign_parachain_slot(), DispatchClass::Operational))]
403		pub fn unassign_parachain_slot(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
404			T::AssignSlotOrigin::ensure_origin(origin.clone())?;
405
406			ensure!(
407				Self::has_permanent_slot(id) || Self::has_temporary_slot(id),
408				Error::<T>::SlotNotAssigned
409			);
410
411			// Check & cache para status before we clear the lease
412			let is_parachain = Self::is_parachain(id);
413
414			// Remove perm or temp slot
415			Self::clear_slot_leases(origin.clone(), id)?;
416
417			if PermanentSlots::<T>::contains_key(id) {
418				PermanentSlots::<T>::remove(id);
419				PermanentSlotCount::<T>::mutate(|count| *count = count.saturating_sub(One::one()));
420			} else if TemporarySlots::<T>::contains_key(id) {
421				TemporarySlots::<T>::remove(id);
422				TemporarySlotCount::<T>::mutate(|count| *count = count.saturating_sub(One::one()));
423				if is_parachain {
424					ActiveTemporarySlotCount::<T>::mutate(|active_count| {
425						*active_count = active_count.saturating_sub(One::one())
426					});
427				}
428			}
429
430			// Force downgrade to on-demand parachain (if needed) before end of lease period
431			if is_parachain {
432				if let Err(err) = polkadot_runtime_parachains::schedule_parachain_downgrade::<T>(id)
433				{
434					// Treat failed downgrade as warning .. slot lease has been cleared,
435					// so the parachain will be downgraded anyway by the slots pallet
436					// at the end of the lease period .
437					log::warn!(
438						target: LOG_TARGET,
439						"Failed to downgrade parachain {:?} at period {:?}: {:?}",
440						id,
441						Self::current_lease_period_index(),
442						err
443					);
444				}
445			}
446
447			Ok(())
448		}
449
450		/// Sets the storage value [`MaxPermanentSlots`].
451		#[pallet::call_index(3)]
452		#[pallet::weight((<T as Config>::WeightInfo::set_max_permanent_slots(), DispatchClass::Operational))]
453		pub fn set_max_permanent_slots(origin: OriginFor<T>, slots: u32) -> DispatchResult {
454			ensure_root(origin)?;
455
456			MaxPermanentSlots::<T>::put(slots);
457
458			Self::deposit_event(Event::<T>::MaxPermanentSlotsChanged { slots });
459			Ok(())
460		}
461
462		/// Sets the storage value [`MaxTemporarySlots`].
463		#[pallet::call_index(4)]
464		#[pallet::weight((<T as Config>::WeightInfo::set_max_temporary_slots(), DispatchClass::Operational))]
465		pub fn set_max_temporary_slots(origin: OriginFor<T>, slots: u32) -> DispatchResult {
466			ensure_root(origin)?;
467
468			MaxTemporarySlots::<T>::put(slots);
469
470			Self::deposit_event(Event::<T>::MaxTemporarySlotsChanged { slots });
471			Ok(())
472		}
473	}
474}
475
476impl<T: Config> Pallet<T> {
477	/// Allocate temporary slot leases up to `MaxTemporarySlotPerLeasePeriod` per lease period.
478	/// Beyond the already active temporary slot leases, this function will activate more leases
479	/// in the following order of preference:
480	/// - Assigned slots that didn't have a turn yet, though their `period_begin` has passed.
481	/// - Assigned slots that already had one (or more) turn(s): they will be considered for the
482	/// current slot lease if they weren't active in the preceding one, and will be ranked by
483	/// total number of lease (lower first), and then when they last a turn (older ones first).
484	/// If any remaining ex-aequo, we just take the para ID in ascending order as discriminator.
485	///
486	/// Assigned slots with a `period_begin` bigger than current lease period are not considered
487	/// (yet).
488	///
489	/// The function will call out to `Leaser::lease_out` to create the appropriate slot leases.
490	fn allocate_temporary_slot_leases(lease_period_index: LeasePeriodOf<T>) -> DispatchResult {
491		let mut active_temp_slots = 0u32;
492		let mut pending_temp_slots = Vec::new();
493		TemporarySlots::<T>::iter().for_each(|(para, slot)| {
494				match slot.last_lease {
495					Some(last_lease)
496						if last_lease <= lease_period_index &&
497							lease_period_index <
498								(last_lease.saturating_add(slot.period_count)) =>
499					{
500						// Active slot lease
501						active_temp_slots += 1;
502					}
503					Some(last_lease)
504						// Slot w/ past lease, only consider it every other slot lease period (times period_count)
505						if last_lease.saturating_add(slot.period_count.saturating_mul(2u32.into())) <= lease_period_index => {
506							pending_temp_slots.push((para, slot));
507					},
508					None if slot.period_begin <= lease_period_index => {
509						// Slot hasn't had a lease yet
510						pending_temp_slots.insert(0, (para, slot));
511					},
512					_ => {
513						// Slot not being considered for this lease period (will be for a subsequent one)
514					},
515				}
516		});
517
518		let mut newly_created_lease = 0u32;
519		if active_temp_slots < T::MaxTemporarySlotPerLeasePeriod::get() &&
520			!pending_temp_slots.is_empty()
521		{
522			// Sort by lease_count, favoring slots that had no or less turns first
523			// (then by last_lease index, and then Para ID)
524			pending_temp_slots.sort_by(|a, b| {
525				a.1.lease_count
526					.cmp(&b.1.lease_count)
527					.then_with(|| a.1.last_lease.cmp(&b.1.last_lease))
528					.then_with(|| a.0.cmp(&b.0))
529			});
530
531			let slots_to_be_upgraded = pending_temp_slots.iter().take(
532				(T::MaxTemporarySlotPerLeasePeriod::get().saturating_sub(active_temp_slots))
533					as usize,
534			);
535
536			for (id, temp_slot) in slots_to_be_upgraded {
537				TemporarySlots::<T>::try_mutate::<_, _, Error<T>, _>(id, |s| {
538					// Configure temp slot lease
539					Self::configure_slot_lease(
540						*id,
541						temp_slot.manager.clone(),
542						lease_period_index,
543						temp_slot.period_count,
544					)
545					.map_err(|_| Error::<T>::CannotUpgrade)?;
546
547					// Update temp slot lease info in storage
548					*s = Some(ParachainTemporarySlot {
549						manager: temp_slot.manager.clone(),
550						period_begin: temp_slot.period_begin,
551						period_count: temp_slot.period_count,
552						last_lease: Some(lease_period_index),
553						lease_count: temp_slot.lease_count + 1,
554					});
555
556					newly_created_lease += 1;
557
558					Ok(())
559				})?;
560			}
561		}
562
563		ActiveTemporarySlotCount::<T>::set(active_temp_slots + newly_created_lease);
564
565		Ok(())
566	}
567
568	/// Clear out all slot leases for both permanent & temporary slots.
569	/// The function merely calls out to `Slots::clear_all_leases`.
570	fn clear_slot_leases(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
571		Slots::<T>::clear_all_leases(origin, id)
572	}
573
574	/// Create a parachain slot lease based on given params.
575	/// The function merely calls out to `Leaser::lease_out`.
576	fn configure_slot_lease(
577		para: ParaId,
578		manager: T::AccountId,
579		lease_period: LeasePeriodOf<T>,
580		lease_duration: LeasePeriodOf<T>,
581	) -> Result<(), LeaseError> {
582		T::Leaser::lease_out(para, &manager, BalanceOf::<T>::zero(), lease_period, lease_duration)
583	}
584
585	/// Returns whether a para has been assigned a permanent slot.
586	fn has_permanent_slot(id: ParaId) -> bool {
587		PermanentSlots::<T>::contains_key(id)
588	}
589
590	/// Returns whether a para has been assigned temporary slot.
591	fn has_temporary_slot(id: ParaId) -> bool {
592		TemporarySlots::<T>::contains_key(id)
593	}
594
595	/// Returns whether a para is currently a lease holding parachain.
596	fn is_parachain(id: ParaId) -> bool {
597		T::Registrar::is_parachain(id)
598	}
599
600	/// Returns current lease period index.
601	fn current_lease_period_index() -> LeasePeriodOf<T> {
602		T::Leaser::lease_period_index(frame_system::Pallet::<T>::block_number())
603			.and_then(|x| Some(x.0))
604			.unwrap()
605	}
606
607	/// Returns lease period index for block
608	fn lease_period_index(block: BlockNumberFor<T>) -> Option<(LeasePeriodOf<T>, bool)> {
609		T::Leaser::lease_period_index(block)
610	}
611
612	/// Handles start of a lease period.
613	fn manage_lease_period_start(lease_period_index: LeasePeriodOf<T>) -> Weight {
614		// Note: leases that have ended in previous lease period, should have been cleaned in slots
615		// pallet.
616		if let Err(err) = Self::allocate_temporary_slot_leases(lease_period_index) {
617			log::error!(
618				target: LOG_TARGET,
619				"Allocating slots failed for lease period {:?}, with: {:?}",
620				lease_period_index,
621				err
622			);
623		}
624		<T as slots::Config>::WeightInfo::force_lease() *
625			(T::MaxTemporarySlotPerLeasePeriod::get() as u64)
626	}
627}
628
629/// tests for this pallet
630#[cfg(test)]
631mod tests {
632	use super::*;
633
634	use crate::{assigned_slots, mock::TestRegistrar, slots};
635	use frame_support::{assert_noop, assert_ok, derive_impl, parameter_types};
636	use frame_system::EnsureRoot;
637	use pallet_balances;
638	use polkadot_primitives::BlockNumber;
639	use polkadot_primitives_test_helpers::{dummy_head_data, dummy_validation_code};
640	use polkadot_runtime_parachains::{
641		configuration as parachains_configuration, paras as parachains_paras,
642		shared as parachains_shared,
643	};
644	use sp_core::H256;
645	use sp_runtime::{
646		traits::{BlakeTwo256, IdentityLookup},
647		transaction_validity::TransactionPriority,
648		BuildStorage,
649		DispatchError::BadOrigin,
650	};
651
652	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
653	type Block = frame_system::mocking::MockBlockU32<Test>;
654
655	frame_support::construct_runtime!(
656		pub enum Test
657		{
658			System: frame_system,
659			Balances: pallet_balances,
660			Configuration: parachains_configuration,
661			ParasShared: parachains_shared,
662			Parachains: parachains_paras,
663			Slots: slots,
664			AssignedSlots: assigned_slots,
665		}
666	);
667
668	impl<C> frame_system::offchain::CreateTransactionBase<C> for Test
669	where
670		RuntimeCall: From<C>,
671	{
672		type Extrinsic = UncheckedExtrinsic;
673		type RuntimeCall = RuntimeCall;
674	}
675
676	impl<C> frame_system::offchain::CreateInherent<C> for Test
677	where
678		RuntimeCall: From<C>,
679	{
680		fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic {
681			UncheckedExtrinsic::new_bare(call)
682		}
683	}
684
685	#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
686	impl frame_system::Config for Test {
687		type BaseCallFilter = frame_support::traits::Everything;
688		type BlockWeights = ();
689		type BlockLength = ();
690		type RuntimeOrigin = RuntimeOrigin;
691		type RuntimeCall = RuntimeCall;
692		type Nonce = u64;
693		type Hash = H256;
694		type Hashing = BlakeTwo256;
695		type AccountId = u64;
696		type Lookup = IdentityLookup<Self::AccountId>;
697		type Block = Block;
698		type RuntimeEvent = RuntimeEvent;
699		type DbWeight = ();
700		type Version = ();
701		type PalletInfo = PalletInfo;
702		type AccountData = pallet_balances::AccountData<u64>;
703		type OnNewAccount = ();
704		type OnKilledAccount = ();
705		type SystemWeightInfo = ();
706		type SS58Prefix = ();
707		type OnSetCode = ();
708		type MaxConsumers = frame_support::traits::ConstU32<16>;
709	}
710
711	#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
712	impl pallet_balances::Config for Test {
713		type AccountStore = System;
714	}
715
716	impl parachains_configuration::Config for Test {
717		type WeightInfo = parachains_configuration::TestWeightInfo;
718	}
719
720	parameter_types! {
721		pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value();
722	}
723
724	impl parachains_paras::Config for Test {
725		type RuntimeEvent = RuntimeEvent;
726		type WeightInfo = parachains_paras::TestWeightInfo;
727		type UnsignedPriority = ParasUnsignedPriority;
728		type QueueFootprinter = ();
729		type NextSessionRotation = crate::mock::TestNextSessionRotation;
730		type OnNewHead = ();
731		type AssignCoretime = ();
732	}
733
734	impl parachains_shared::Config for Test {
735		type DisabledValidators = ();
736	}
737
738	parameter_types! {
739		pub const LeasePeriod: BlockNumber = 3;
740		pub static LeaseOffset: BlockNumber = 0;
741		pub const ParaDeposit: u64 = 1;
742	}
743
744	impl slots::Config for Test {
745		type RuntimeEvent = RuntimeEvent;
746		type Currency = Balances;
747		type Registrar = TestRegistrar<Test>;
748		type LeasePeriod = LeasePeriod;
749		type LeaseOffset = LeaseOffset;
750		type ForceOrigin = EnsureRoot<Self::AccountId>;
751		type WeightInfo = crate::slots::TestWeightInfo;
752	}
753
754	parameter_types! {
755		pub const PermanentSlotLeasePeriodLength: u32 = 3;
756		pub const TemporarySlotLeasePeriodLength: u32 = 2;
757		pub const MaxTemporarySlotPerLeasePeriod: u32 = 2;
758	}
759
760	impl assigned_slots::Config for Test {
761		type RuntimeEvent = RuntimeEvent;
762		type AssignSlotOrigin = EnsureRoot<Self::AccountId>;
763		type Leaser = Slots;
764		type PermanentSlotLeasePeriodLength = PermanentSlotLeasePeriodLength;
765		type TemporarySlotLeasePeriodLength = TemporarySlotLeasePeriodLength;
766		type MaxTemporarySlotPerLeasePeriod = MaxTemporarySlotPerLeasePeriod;
767		type WeightInfo = crate::assigned_slots::TestWeightInfo;
768	}
769
770	// This function basically just builds a genesis storage key/value store according to
771	// our desired mock up.
772	pub fn new_test_ext() -> sp_io::TestExternalities {
773		let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
774		pallet_balances::GenesisConfig::<Test> {
775			balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
776		}
777		.assimilate_storage(&mut t)
778		.unwrap();
779
780		crate::assigned_slots::GenesisConfig::<Test> {
781			max_temporary_slots: 6,
782			max_permanent_slots: 2,
783			_config: Default::default(),
784		}
785		.assimilate_storage(&mut t)
786		.unwrap();
787
788		t.into()
789	}
790
791	fn run_to_block(n: BlockNumber) {
792		while System::block_number() < n {
793			let mut block = System::block_number();
794			// on_finalize hooks
795			AssignedSlots::on_finalize(block);
796			Slots::on_finalize(block);
797			Parachains::on_finalize(block);
798			ParasShared::on_finalize(block);
799			Configuration::on_finalize(block);
800			Balances::on_finalize(block);
801			System::on_finalize(block);
802			// Set next block
803			System::set_block_number(block + 1);
804			block = System::block_number();
805			// on_initialize hooks
806			System::on_initialize(block);
807			Balances::on_initialize(block);
808			Configuration::on_initialize(block);
809			ParasShared::on_initialize(block);
810			Parachains::on_initialize(block);
811			Slots::on_initialize(block);
812			AssignedSlots::on_initialize(block);
813		}
814	}
815
816	#[test]
817	fn basic_setup_works() {
818		new_test_ext().execute_with(|| {
819			run_to_block(1);
820			assert_eq!(AssignedSlots::current_lease_period_index(), 0);
821			assert_eq!(Slots::deposit_held(1.into(), &1), 0);
822
823			run_to_block(3);
824			assert_eq!(AssignedSlots::current_lease_period_index(), 1);
825		});
826	}
827
828	#[test]
829	fn assign_perm_slot_fails_for_unknown_para() {
830		new_test_ext().execute_with(|| {
831			run_to_block(1);
832
833			assert_noop!(
834				AssignedSlots::assign_perm_parachain_slot(
835					RuntimeOrigin::root(),
836					ParaId::from(1_u32),
837				),
838				Error::<Test>::ParaDoesntExist
839			);
840		});
841	}
842
843	#[test]
844	fn assign_perm_slot_fails_for_invalid_origin() {
845		new_test_ext().execute_with(|| {
846			run_to_block(1);
847
848			assert_noop!(
849				AssignedSlots::assign_perm_parachain_slot(
850					RuntimeOrigin::signed(1),
851					ParaId::from(1_u32),
852				),
853				BadOrigin
854			);
855		});
856	}
857
858	#[test]
859	fn assign_perm_slot_fails_when_not_parathread() {
860		new_test_ext().execute_with(|| {
861			run_to_block(1);
862
863			assert_ok!(TestRegistrar::<Test>::register(
864				1,
865				ParaId::from(1_u32),
866				dummy_head_data(),
867				dummy_validation_code(),
868			));
869			assert_ok!(TestRegistrar::<Test>::make_parachain(ParaId::from(1_u32)));
870
871			assert_noop!(
872				AssignedSlots::assign_perm_parachain_slot(
873					RuntimeOrigin::root(),
874					ParaId::from(1_u32),
875				),
876				Error::<Test>::NotParathread
877			);
878		});
879	}
880
881	#[test]
882	fn assign_perm_slot_fails_when_existing_lease() {
883		new_test_ext().execute_with(|| {
884			run_to_block(1);
885
886			assert_ok!(TestRegistrar::<Test>::register(
887				1,
888				ParaId::from(1_u32),
889				dummy_head_data(),
890				dummy_validation_code(),
891			));
892
893			// Register lease in current lease period
894			assert_ok!(Slots::lease_out(ParaId::from(1_u32), &1, 1, 1, 1));
895			// Try to assign a perm slot in current period fails
896			assert_noop!(
897				AssignedSlots::assign_perm_parachain_slot(
898					RuntimeOrigin::root(),
899					ParaId::from(1_u32),
900				),
901				Error::<Test>::OngoingLeaseExists
902			);
903
904			// Cleanup
905			assert_ok!(Slots::clear_all_leases(RuntimeOrigin::root(), 1.into()));
906
907			// Register lease for next lease period
908			assert_ok!(Slots::lease_out(ParaId::from(1_u32), &1, 1, 2, 1));
909			// Should be detected and also fail
910			assert_noop!(
911				AssignedSlots::assign_perm_parachain_slot(
912					RuntimeOrigin::root(),
913					ParaId::from(1_u32),
914				),
915				Error::<Test>::OngoingLeaseExists
916			);
917		});
918	}
919
920	#[test]
921	fn assign_perm_slot_fails_when_max_perm_slots_exceeded() {
922		new_test_ext().execute_with(|| {
923			run_to_block(1);
924
925			assert_ok!(TestRegistrar::<Test>::register(
926				1,
927				ParaId::from(1_u32),
928				dummy_head_data(),
929				dummy_validation_code(),
930			));
931
932			assert_ok!(TestRegistrar::<Test>::register(
933				2,
934				ParaId::from(2_u32),
935				dummy_head_data(),
936				dummy_validation_code(),
937			));
938
939			assert_ok!(TestRegistrar::<Test>::register(
940				3,
941				ParaId::from(3_u32),
942				dummy_head_data(),
943				dummy_validation_code(),
944			));
945
946			assert_ok!(AssignedSlots::assign_perm_parachain_slot(
947				RuntimeOrigin::root(),
948				ParaId::from(1_u32),
949			));
950			assert_ok!(AssignedSlots::assign_perm_parachain_slot(
951				RuntimeOrigin::root(),
952				ParaId::from(2_u32),
953			));
954			assert_eq!(assigned_slots::PermanentSlotCount::<Test>::get(), 2);
955
956			assert_noop!(
957				AssignedSlots::assign_perm_parachain_slot(
958					RuntimeOrigin::root(),
959					ParaId::from(3_u32),
960				),
961				Error::<Test>::MaxPermanentSlotsExceeded
962			);
963		});
964	}
965
966	#[test]
967	fn assign_perm_slot_succeeds_for_parathread() {
968		new_test_ext().execute_with(|| {
969			let mut block = 1;
970			run_to_block(block);
971			assert_ok!(TestRegistrar::<Test>::register(
972				1,
973				ParaId::from(1_u32),
974				dummy_head_data(),
975				dummy_validation_code(),
976			));
977
978			assert_eq!(assigned_slots::PermanentSlotCount::<Test>::get(), 0);
979			assert_eq!(assigned_slots::PermanentSlots::<Test>::get(ParaId::from(1_u32)), None);
980
981			assert_ok!(AssignedSlots::assign_perm_parachain_slot(
982				RuntimeOrigin::root(),
983				ParaId::from(1_u32),
984			));
985
986			// Para is a lease holding parachain for PermanentSlotLeasePeriodLength * LeasePeriod
987			// blocks
988			while block < 9 {
989				println!("block #{}", block);
990
991				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), true);
992
993				assert_eq!(assigned_slots::PermanentSlotCount::<Test>::get(), 1);
994				assert_eq!(AssignedSlots::has_permanent_slot(ParaId::from(1_u32)), true);
995				assert_eq!(
996					assigned_slots::PermanentSlots::<Test>::get(ParaId::from(1_u32)),
997					Some((0, 3))
998				);
999
1000				assert_eq!(Slots::already_leased(ParaId::from(1_u32), 0, 2), true);
1001
1002				block += 1;
1003				run_to_block(block);
1004			}
1005
1006			// Para lease ended, downgraded back to parathread (on-demand parachain)
1007			assert_eq!(TestRegistrar::<Test>::is_parathread(ParaId::from(1_u32)), true);
1008			assert_eq!(Slots::already_leased(ParaId::from(1_u32), 0, 5), false);
1009		});
1010	}
1011
1012	#[test]
1013	fn assign_temp_slot_fails_for_unknown_para() {
1014		new_test_ext().execute_with(|| {
1015			run_to_block(1);
1016
1017			assert_noop!(
1018				AssignedSlots::assign_temp_parachain_slot(
1019					RuntimeOrigin::root(),
1020					ParaId::from(1_u32),
1021					SlotLeasePeriodStart::Current
1022				),
1023				Error::<Test>::ParaDoesntExist
1024			);
1025		});
1026	}
1027
1028	#[test]
1029	fn assign_temp_slot_fails_for_invalid_origin() {
1030		new_test_ext().execute_with(|| {
1031			run_to_block(1);
1032
1033			assert_noop!(
1034				AssignedSlots::assign_temp_parachain_slot(
1035					RuntimeOrigin::signed(1),
1036					ParaId::from(1_u32),
1037					SlotLeasePeriodStart::Current
1038				),
1039				BadOrigin
1040			);
1041		});
1042	}
1043
1044	#[test]
1045	fn assign_temp_slot_fails_when_not_parathread() {
1046		new_test_ext().execute_with(|| {
1047			run_to_block(1);
1048
1049			assert_ok!(TestRegistrar::<Test>::register(
1050				1,
1051				ParaId::from(1_u32),
1052				dummy_head_data(),
1053				dummy_validation_code(),
1054			));
1055			assert_ok!(TestRegistrar::<Test>::make_parachain(ParaId::from(1_u32)));
1056
1057			assert_noop!(
1058				AssignedSlots::assign_temp_parachain_slot(
1059					RuntimeOrigin::root(),
1060					ParaId::from(1_u32),
1061					SlotLeasePeriodStart::Current
1062				),
1063				Error::<Test>::NotParathread
1064			);
1065		});
1066	}
1067
1068	#[test]
1069	fn assign_temp_slot_fails_when_existing_lease() {
1070		new_test_ext().execute_with(|| {
1071			run_to_block(1);
1072
1073			assert_ok!(TestRegistrar::<Test>::register(
1074				1,
1075				ParaId::from(1_u32),
1076				dummy_head_data(),
1077				dummy_validation_code(),
1078			));
1079
1080			// Register lease in current lease period
1081			assert_ok!(Slots::lease_out(ParaId::from(1_u32), &1, 1, 1, 1));
1082			// Try to assign a perm slot in current period fails
1083			assert_noop!(
1084				AssignedSlots::assign_temp_parachain_slot(
1085					RuntimeOrigin::root(),
1086					ParaId::from(1_u32),
1087					SlotLeasePeriodStart::Current
1088				),
1089				Error::<Test>::OngoingLeaseExists
1090			);
1091
1092			// Cleanup
1093			assert_ok!(Slots::clear_all_leases(RuntimeOrigin::root(), 1.into()));
1094
1095			// Register lease for next lease period
1096			assert_ok!(Slots::lease_out(ParaId::from(1_u32), &1, 1, 2, 1));
1097			// Should be detected and also fail
1098			assert_noop!(
1099				AssignedSlots::assign_temp_parachain_slot(
1100					RuntimeOrigin::root(),
1101					ParaId::from(1_u32),
1102					SlotLeasePeriodStart::Current
1103				),
1104				Error::<Test>::OngoingLeaseExists
1105			);
1106		});
1107	}
1108
1109	#[test]
1110	fn assign_temp_slot_fails_when_max_temp_slots_exceeded() {
1111		new_test_ext().execute_with(|| {
1112			run_to_block(1);
1113
1114			// Register 6 paras & a temp slot for each
1115			for n in 0..=5 {
1116				assert_ok!(TestRegistrar::<Test>::register(
1117					n,
1118					ParaId::from(n as u32),
1119					dummy_head_data(),
1120					dummy_validation_code()
1121				));
1122
1123				assert_ok!(AssignedSlots::assign_temp_parachain_slot(
1124					RuntimeOrigin::root(),
1125					ParaId::from(n as u32),
1126					SlotLeasePeriodStart::Current
1127				));
1128			}
1129
1130			assert_eq!(assigned_slots::TemporarySlotCount::<Test>::get(), 6);
1131
1132			// Attempt to assign one more temp slot
1133			assert_ok!(TestRegistrar::<Test>::register(
1134				7,
1135				ParaId::from(7_u32),
1136				dummy_head_data(),
1137				dummy_validation_code(),
1138			));
1139			assert_noop!(
1140				AssignedSlots::assign_temp_parachain_slot(
1141					RuntimeOrigin::root(),
1142					ParaId::from(7_u32),
1143					SlotLeasePeriodStart::Current
1144				),
1145				Error::<Test>::MaxTemporarySlotsExceeded
1146			);
1147		});
1148	}
1149
1150	#[test]
1151	fn assign_temp_slot_succeeds_for_single_parathread() {
1152		new_test_ext().execute_with(|| {
1153			let mut block = 1;
1154			run_to_block(block);
1155			assert_ok!(TestRegistrar::<Test>::register(
1156				1,
1157				ParaId::from(1_u32),
1158				dummy_head_data(),
1159				dummy_validation_code(),
1160			));
1161
1162			assert_eq!(assigned_slots::TemporarySlots::<Test>::get(ParaId::from(1_u32)), None);
1163
1164			assert_ok!(AssignedSlots::assign_temp_parachain_slot(
1165				RuntimeOrigin::root(),
1166				ParaId::from(1_u32),
1167				SlotLeasePeriodStart::Current
1168			));
1169			assert_eq!(assigned_slots::TemporarySlotCount::<Test>::get(), 1);
1170			assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 1);
1171
1172			// Block 1-5
1173			// Para is a lease holding parachain for TemporarySlotLeasePeriodLength * LeasePeriod
1174			// blocks
1175			while block < 6 {
1176				println!("block #{}", block);
1177				println!("lease period #{}", AssignedSlots::current_lease_period_index());
1178				println!("lease {:?}", slots::Leases::<Test>::get(ParaId::from(1_u32)));
1179
1180				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), true);
1181
1182				assert_eq!(AssignedSlots::has_temporary_slot(ParaId::from(1_u32)), true);
1183				assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 1);
1184				assert_eq!(
1185					assigned_slots::TemporarySlots::<Test>::get(ParaId::from(1_u32)),
1186					Some(ParachainTemporarySlot {
1187						manager: 1,
1188						period_begin: 0,
1189						period_count: 2, // TemporarySlotLeasePeriodLength
1190						last_lease: Some(0),
1191						lease_count: 1
1192					})
1193				);
1194
1195				assert_eq!(Slots::already_leased(ParaId::from(1_u32), 0, 1), true);
1196
1197				block += 1;
1198				run_to_block(block);
1199			}
1200
1201			// Block 6
1202			println!("block #{}", block);
1203			println!("lease period #{}", AssignedSlots::current_lease_period_index());
1204			println!("lease {:?}", slots::Leases::<Test>::get(ParaId::from(1_u32)));
1205
1206			// Para lease ended, downgraded back to on-demand parachain
1207			assert_eq!(TestRegistrar::<Test>::is_parathread(ParaId::from(1_u32)), true);
1208			assert_eq!(Slots::already_leased(ParaId::from(1_u32), 0, 3), false);
1209			assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 0);
1210
1211			// Block 12
1212			// Para should get a turn after TemporarySlotLeasePeriodLength * LeasePeriod blocks
1213			run_to_block(12);
1214			println!("block #{}", block);
1215			println!("lease period #{}", AssignedSlots::current_lease_period_index());
1216			println!("lease {:?}", slots::Leases::<Test>::get(ParaId::from(1_u32)));
1217
1218			assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), true);
1219			assert_eq!(Slots::already_leased(ParaId::from(1_u32), 4, 5), true);
1220			assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 1);
1221		});
1222	}
1223
1224	#[test]
1225	fn assign_temp_slot_succeeds_for_multiple_parathreads() {
1226		new_test_ext().execute_with(|| {
1227			// Block 1, Period 0
1228			run_to_block(1);
1229
1230			// Register 6 paras & a temp slot for each
1231			// (3 slots in current lease period, 3 in the next one)
1232			for n in 0..=5 {
1233				assert_ok!(TestRegistrar::<Test>::register(
1234					n,
1235					ParaId::from(n as u32),
1236					dummy_head_data(),
1237					dummy_validation_code()
1238				));
1239
1240				assert_ok!(AssignedSlots::assign_temp_parachain_slot(
1241					RuntimeOrigin::root(),
1242					ParaId::from(n as u32),
1243					if (n % 2).is_zero() {
1244						SlotLeasePeriodStart::Current
1245					} else {
1246						SlotLeasePeriodStart::Next
1247					}
1248				));
1249			}
1250
1251			// Block 1-5, Period 0-1
1252			for n in 1..=5 {
1253				if n > 1 {
1254					run_to_block(n);
1255				}
1256				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), true);
1257				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), false);
1258				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), true);
1259				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(3_u32)), false);
1260				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(4_u32)), false);
1261				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(5_u32)), false);
1262				assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 2);
1263			}
1264
1265			// Block 6-11, Period 2-3
1266			for n in 6..=11 {
1267				run_to_block(n);
1268				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), false);
1269				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), true);
1270				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), false);
1271				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(3_u32)), true);
1272				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(4_u32)), false);
1273				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(5_u32)), false);
1274				assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 2);
1275			}
1276
1277			// Block 12-17, Period 4-5
1278			for n in 12..=17 {
1279				run_to_block(n);
1280				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), false);
1281				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), false);
1282				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), false);
1283				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(3_u32)), false);
1284				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(4_u32)), true);
1285				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(5_u32)), true);
1286				assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 2);
1287			}
1288
1289			// Block 18-23, Period 6-7
1290			for n in 18..=23 {
1291				run_to_block(n);
1292				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), true);
1293				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), false);
1294				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), true);
1295				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(3_u32)), false);
1296				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(4_u32)), false);
1297				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(5_u32)), false);
1298				assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 2);
1299			}
1300
1301			// Block 24-29, Period 8-9
1302			for n in 24..=29 {
1303				run_to_block(n);
1304				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), false);
1305				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), true);
1306				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), false);
1307				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(3_u32)), true);
1308				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(4_u32)), false);
1309				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(5_u32)), false);
1310				assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 2);
1311			}
1312
1313			// Block 30-35, Period 10-11
1314			for n in 30..=35 {
1315				run_to_block(n);
1316				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), false);
1317				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), false);
1318				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), false);
1319				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(3_u32)), false);
1320				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(4_u32)), true);
1321				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(5_u32)), true);
1322				assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 2);
1323			}
1324		});
1325	}
1326
1327	#[test]
1328	fn unassign_slot_fails_for_unknown_para() {
1329		new_test_ext().execute_with(|| {
1330			run_to_block(1);
1331
1332			assert_noop!(
1333				AssignedSlots::unassign_parachain_slot(RuntimeOrigin::root(), ParaId::from(1_u32),),
1334				Error::<Test>::SlotNotAssigned
1335			);
1336		});
1337	}
1338
1339	#[test]
1340	fn unassign_slot_fails_for_invalid_origin() {
1341		new_test_ext().execute_with(|| {
1342			run_to_block(1);
1343
1344			assert_noop!(
1345				AssignedSlots::assign_perm_parachain_slot(
1346					RuntimeOrigin::signed(1),
1347					ParaId::from(1_u32),
1348				),
1349				BadOrigin
1350			);
1351		});
1352	}
1353
1354	#[test]
1355	fn unassign_perm_slot_succeeds() {
1356		new_test_ext().execute_with(|| {
1357			run_to_block(1);
1358
1359			assert_ok!(TestRegistrar::<Test>::register(
1360				1,
1361				ParaId::from(1_u32),
1362				dummy_head_data(),
1363				dummy_validation_code(),
1364			));
1365
1366			assert_ok!(AssignedSlots::assign_perm_parachain_slot(
1367				RuntimeOrigin::root(),
1368				ParaId::from(1_u32),
1369			));
1370
1371			assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), true);
1372
1373			assert_ok!(AssignedSlots::unassign_parachain_slot(
1374				RuntimeOrigin::root(),
1375				ParaId::from(1_u32),
1376			));
1377
1378			assert_eq!(assigned_slots::PermanentSlotCount::<Test>::get(), 0);
1379			assert_eq!(AssignedSlots::has_permanent_slot(ParaId::from(1_u32)), false);
1380			assert_eq!(assigned_slots::PermanentSlots::<Test>::get(ParaId::from(1_u32)), None);
1381
1382			assert_eq!(Slots::already_leased(ParaId::from(1_u32), 0, 2), false);
1383		});
1384	}
1385
1386	#[test]
1387	fn unassign_temp_slot_succeeds() {
1388		new_test_ext().execute_with(|| {
1389			run_to_block(1);
1390
1391			assert_ok!(TestRegistrar::<Test>::register(
1392				1,
1393				ParaId::from(1_u32),
1394				dummy_head_data(),
1395				dummy_validation_code(),
1396			));
1397
1398			assert_ok!(AssignedSlots::assign_temp_parachain_slot(
1399				RuntimeOrigin::root(),
1400				ParaId::from(1_u32),
1401				SlotLeasePeriodStart::Current
1402			));
1403
1404			assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), true);
1405
1406			assert_ok!(AssignedSlots::unassign_parachain_slot(
1407				RuntimeOrigin::root(),
1408				ParaId::from(1_u32),
1409			));
1410
1411			assert_eq!(assigned_slots::TemporarySlotCount::<Test>::get(), 0);
1412			assert_eq!(assigned_slots::ActiveTemporarySlotCount::<Test>::get(), 0);
1413			assert_eq!(AssignedSlots::has_temporary_slot(ParaId::from(1_u32)), false);
1414			assert_eq!(assigned_slots::TemporarySlots::<Test>::get(ParaId::from(1_u32)), None);
1415
1416			assert_eq!(Slots::already_leased(ParaId::from(1_u32), 0, 1), false);
1417		});
1418	}
1419	#[test]
1420	fn set_max_permanent_slots_fails_for_no_root_origin() {
1421		new_test_ext().execute_with(|| {
1422			run_to_block(1);
1423
1424			assert_noop!(
1425				AssignedSlots::set_max_permanent_slots(RuntimeOrigin::signed(1), 5),
1426				BadOrigin
1427			);
1428		});
1429	}
1430	#[test]
1431	fn set_max_permanent_slots_succeeds() {
1432		new_test_ext().execute_with(|| {
1433			run_to_block(1);
1434
1435			assert_eq!(MaxPermanentSlots::<Test>::get(), 2);
1436			assert_ok!(AssignedSlots::set_max_permanent_slots(RuntimeOrigin::root(), 10),);
1437			assert_eq!(MaxPermanentSlots::<Test>::get(), 10);
1438		});
1439	}
1440
1441	#[test]
1442	fn set_max_temporary_slots_fails_for_no_root_origin() {
1443		new_test_ext().execute_with(|| {
1444			run_to_block(1);
1445
1446			assert_noop!(
1447				AssignedSlots::set_max_temporary_slots(RuntimeOrigin::signed(1), 5),
1448				BadOrigin
1449			);
1450		});
1451	}
1452	#[test]
1453	fn set_max_temporary_slots_succeeds() {
1454		new_test_ext().execute_with(|| {
1455			run_to_block(1);
1456
1457			assert_eq!(MaxTemporarySlots::<Test>::get(), 6);
1458			assert_ok!(AssignedSlots::set_max_temporary_slots(RuntimeOrigin::root(), 12),);
1459			assert_eq!(MaxTemporarySlots::<Test>::get(), 12);
1460		});
1461	}
1462}