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