pezkuwi_runtime_common/assigned_slots/
mod.rs

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