1pub mod migration;
26
27use crate::traits::{LeaseError, Leaser, Registrar};
28use alloc::{vec, vec::Vec};
29use frame_support::{
30 pallet_prelude::*,
31 traits::{Currency, ReservableCurrency},
32 weights::Weight,
33};
34use frame_system::pallet_prelude::*;
35pub use pallet::*;
36use polkadot_primitives::Id as ParaId;
37use sp_runtime::traits::{CheckedConversion, CheckedSub, Saturating, Zero};
38
39type BalanceOf<T> =
40 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
41type LeasePeriodOf<T> = BlockNumberFor<T>;
42
43pub trait WeightInfo {
44 fn force_lease() -> Weight;
45 fn manage_lease_period_start(c: u32, t: u32) -> Weight;
46 fn clear_all_leases() -> Weight;
47 fn trigger_onboard() -> Weight;
48}
49
50pub struct TestWeightInfo;
51impl WeightInfo for TestWeightInfo {
52 fn force_lease() -> Weight {
53 Weight::zero()
54 }
55 fn manage_lease_period_start(_c: u32, _t: u32) -> Weight {
56 Weight::zero()
57 }
58 fn clear_all_leases() -> Weight {
59 Weight::zero()
60 }
61 fn trigger_onboard() -> Weight {
62 Weight::zero()
63 }
64}
65
66#[frame_support::pallet]
67pub mod pallet {
68 use super::*;
69
70 #[pallet::pallet]
71 #[pallet::without_storage_info]
72 pub struct Pallet<T>(_);
73
74 #[pallet::config]
75 pub trait Config: frame_system::Config {
76 #[allow(deprecated)]
78 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
79
80 type Currency: ReservableCurrency<Self::AccountId>;
82
83 type Registrar: Registrar<AccountId = Self::AccountId>;
85
86 #[pallet::constant]
88 type LeasePeriod: Get<BlockNumberFor<Self>>;
89
90 #[pallet::constant]
92 type LeaseOffset: Get<BlockNumberFor<Self>>;
93
94 type ForceOrigin: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin>;
96
97 type WeightInfo: WeightInfo;
99 }
100
101 #[pallet::storage]
118 pub type Leases<T: Config> =
119 StorageMap<_, Twox64Concat, ParaId, Vec<Option<(T::AccountId, BalanceOf<T>)>>, ValueQuery>;
120
121 #[pallet::event]
122 #[pallet::generate_deposit(pub(super) fn deposit_event)]
123 pub enum Event<T: Config> {
124 NewLeasePeriod { lease_period: LeasePeriodOf<T> },
126 Leased {
130 para_id: ParaId,
131 leaser: T::AccountId,
132 period_begin: LeasePeriodOf<T>,
133 period_count: LeasePeriodOf<T>,
134 extra_reserved: BalanceOf<T>,
135 total_amount: BalanceOf<T>,
136 },
137 }
138
139 #[pallet::error]
140 pub enum Error<T> {
141 ParaNotOnboarding,
143 LeaseError,
145 }
146
147 #[pallet::hooks]
148 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
149 fn on_initialize(n: BlockNumberFor<T>) -> Weight {
150 if let Some((lease_period, first_block)) = Self::lease_period_index(n) {
151 if first_block {
153 return Self::manage_lease_period_start(lease_period);
154 }
155 }
156
157 Weight::zero()
159 }
160 }
161
162 #[pallet::call]
163 impl<T: Config> Pallet<T> {
164 #[pallet::call_index(0)]
169 #[pallet::weight(T::WeightInfo::force_lease())]
170 pub fn force_lease(
171 origin: OriginFor<T>,
172 para: ParaId,
173 leaser: T::AccountId,
174 amount: BalanceOf<T>,
175 period_begin: LeasePeriodOf<T>,
176 period_count: LeasePeriodOf<T>,
177 ) -> DispatchResult {
178 T::ForceOrigin::ensure_origin(origin)?;
179 Self::lease_out(para, &leaser, amount, period_begin, period_count)
180 .map_err(|_| Error::<T>::LeaseError)?;
181 Ok(())
182 }
183
184 #[pallet::call_index(1)]
188 #[pallet::weight(T::WeightInfo::clear_all_leases())]
189 pub fn clear_all_leases(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
190 T::ForceOrigin::ensure_origin(origin)?;
191 let deposits = Self::all_deposits_held(para);
192
193 for (who, deposit) in deposits {
195 let err_amount = T::Currency::unreserve(&who, deposit);
196 debug_assert!(err_amount.is_zero());
197 }
198
199 Leases::<T>::remove(para);
200 Ok(())
201 }
202
203 #[pallet::call_index(2)]
211 #[pallet::weight(T::WeightInfo::trigger_onboard())]
212 pub fn trigger_onboard(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
213 ensure_signed(origin)?;
214 let leases = Leases::<T>::get(para);
215 match leases.first() {
216 Some(Some(_lease_info)) => T::Registrar::make_parachain(para)?,
219 Some(None) | None => return Err(Error::<T>::ParaNotOnboarding.into()),
221 };
222 Ok(())
223 }
224 }
225}
226
227impl<T: Config> Pallet<T> {
228 fn manage_lease_period_start(lease_period_index: LeasePeriodOf<T>) -> Weight {
233 Self::deposit_event(Event::<T>::NewLeasePeriod { lease_period: lease_period_index });
234
235 let old_parachains = T::Registrar::parachains();
236
237 let mut parachains = Vec::new();
239 for (para, mut lease_periods) in Leases::<T>::iter() {
240 if lease_periods.is_empty() {
241 continue;
242 }
243 if lease_periods.len() == 1 {
246 if let Some((who, value)) = &lease_periods[0] {
252 T::Currency::unreserve(&who, *value);
253 }
254
255 Leases::<T>::remove(para);
257 } else {
258 let maybe_ended_lease = lease_periods.remove(0);
263
264 Leases::<T>::insert(para, &lease_periods);
265
266 if let Some(ended_lease) = maybe_ended_lease {
268 let now_held = Self::deposit_held(para, &ended_lease.0);
271
272 if let Some(rebate) = ended_lease.1.checked_sub(&now_held) {
275 T::Currency::unreserve(&ended_lease.0, rebate);
276 }
277 }
278
279 if lease_periods[0].is_some() {
281 parachains.push(para);
282 }
283 }
284 }
285 parachains.sort();
286
287 for para in parachains.iter() {
288 if old_parachains.binary_search(para).is_err() {
289 let res = T::Registrar::make_parachain(*para);
291 debug_assert!(res.is_ok());
292 }
293 }
294
295 for para in old_parachains.iter() {
296 if parachains.binary_search(para).is_err() {
297 let res = T::Registrar::make_parathread(*para);
299 debug_assert!(res.is_ok());
300 }
301 }
302
303 T::WeightInfo::manage_lease_period_start(
304 old_parachains.len() as u32,
305 parachains.len() as u32,
306 )
307 }
308
309 fn all_deposits_held(para: ParaId) -> Vec<(T::AccountId, BalanceOf<T>)> {
313 let mut tracker = alloc::collections::btree_map::BTreeMap::new();
314 Leases::<T>::get(para).into_iter().for_each(|lease| match lease {
315 Some((who, amount)) => match tracker.get(&who) {
316 Some(prev_amount) =>
317 if amount > *prev_amount {
318 tracker.insert(who, amount);
319 },
320 None => {
321 tracker.insert(who, amount);
322 },
323 },
324 None => {},
325 });
326
327 tracker.into_iter().collect()
328 }
329}
330
331impl<T: Config> crate::traits::OnSwap for Pallet<T> {
332 fn on_swap(one: ParaId, other: ParaId) {
333 Leases::<T>::mutate(one, |x| Leases::<T>::mutate(other, |y| core::mem::swap(x, y)))
334 }
335}
336
337impl<T: Config> Leaser<BlockNumberFor<T>> for Pallet<T> {
338 type AccountId = T::AccountId;
339 type LeasePeriod = BlockNumberFor<T>;
340 type Currency = T::Currency;
341
342 fn lease_out(
343 para: ParaId,
344 leaser: &Self::AccountId,
345 amount: <Self::Currency as Currency<Self::AccountId>>::Balance,
346 period_begin: Self::LeasePeriod,
347 period_count: Self::LeasePeriod,
348 ) -> Result<(), LeaseError> {
349 let now = frame_system::Pallet::<T>::block_number();
350 let (current_lease_period, _) =
351 Self::lease_period_index(now).ok_or(LeaseError::NoLeasePeriod)?;
352 let offset = period_begin
355 .checked_sub(¤t_lease_period)
356 .and_then(|x| x.checked_into::<usize>())
357 .ok_or(LeaseError::AlreadyEnded)?;
358
359 Leases::<T>::try_mutate(para, |d| {
367 if d.len() < offset {
369 d.resize_with(offset, || None);
370 }
371 let period_count_usize =
372 period_count.checked_into::<usize>().ok_or(LeaseError::AlreadyEnded)?;
373 for i in offset..(offset + period_count_usize) {
375 if d.len() > i {
376 if d[i] == None {
379 d[i] = Some((leaser.clone(), amount));
380 } else {
381 return Err(LeaseError::AlreadyLeased);
386 }
387 } else if d.len() == i {
388 d.push(Some((leaser.clone(), amount)));
390 } else {
391 }
394 }
395
396 let maybe_additional = amount.checked_sub(&Self::deposit_held(para, &leaser));
399 if let Some(ref additional) = maybe_additional {
400 T::Currency::reserve(&leaser, *additional)
401 .map_err(|_| LeaseError::ReserveFailed)?;
402 }
403
404 let reserved = maybe_additional.unwrap_or_default();
405
406 if current_lease_period == period_begin {
410 let _ = T::Registrar::make_parachain(para);
412 }
413
414 Self::deposit_event(Event::<T>::Leased {
415 para_id: para,
416 leaser: leaser.clone(),
417 period_begin,
418 period_count,
419 extra_reserved: reserved,
420 total_amount: amount,
421 });
422
423 Ok(())
424 })
425 }
426
427 fn deposit_held(
428 para: ParaId,
429 leaser: &Self::AccountId,
430 ) -> <Self::Currency as Currency<Self::AccountId>>::Balance {
431 Leases::<T>::get(para)
432 .into_iter()
433 .map(|lease| match lease {
434 Some((who, amount)) =>
435 if &who == leaser {
436 amount
437 } else {
438 Zero::zero()
439 },
440 None => Zero::zero(),
441 })
442 .max()
443 .unwrap_or_else(Zero::zero)
444 }
445
446 #[cfg(any(feature = "runtime-benchmarks", test))]
447 fn lease_period_length() -> (BlockNumberFor<T>, BlockNumberFor<T>) {
448 (T::LeasePeriod::get(), T::LeaseOffset::get())
449 }
450
451 fn lease_period_index(b: BlockNumberFor<T>) -> Option<(Self::LeasePeriod, bool)> {
452 let offset_block_now = b.checked_sub(&T::LeaseOffset::get())?;
454 let lease_period = offset_block_now / T::LeasePeriod::get();
455 let at_begin = (offset_block_now % T::LeasePeriod::get()).is_zero();
456
457 Some((lease_period, at_begin))
458 }
459
460 fn already_leased(
461 para_id: ParaId,
462 first_period: Self::LeasePeriod,
463 last_period: Self::LeasePeriod,
464 ) -> bool {
465 let now = frame_system::Pallet::<T>::block_number();
466 let (current_lease_period, _) = match Self::lease_period_index(now) {
467 Some(clp) => clp,
468 None => return true,
469 };
470
471 let start_period = first_period.max(current_lease_period);
473 let offset = match (start_period - current_lease_period).checked_into::<usize>() {
476 Some(offset) => offset,
477 None => return true,
478 };
479
480 let period_count = match last_period.saturating_sub(start_period).checked_into::<usize>() {
482 Some(period_count) => period_count,
483 None => return true,
484 };
485
486 let leases = Leases::<T>::get(para_id);
489 for slot in offset..=offset + period_count {
490 if let Some(Some(_)) = leases.get(slot) {
491 return true;
493 }
494 }
495
496 false
498 }
499}
500
501#[cfg(test)]
503mod tests {
504 use super::*;
505
506 use crate::{mock::TestRegistrar, slots};
507 use frame_support::{assert_noop, assert_ok, derive_impl, parameter_types};
508 use frame_system::EnsureRoot;
509 use pallet_balances;
510 use polkadot_primitives::BlockNumber;
511 use polkadot_primitives_test_helpers::{dummy_head_data, dummy_validation_code};
512 use sp_core::H256;
513 use sp_runtime::{
514 traits::{BlakeTwo256, IdentityLookup},
515 BuildStorage,
516 };
517
518 type Block = frame_system::mocking::MockBlockU32<Test>;
519
520 frame_support::construct_runtime!(
521 pub enum Test
522 {
523 System: frame_system,
524 Balances: pallet_balances,
525 Slots: slots,
526 }
527 );
528
529 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
530 impl frame_system::Config for Test {
531 type BaseCallFilter = frame_support::traits::Everything;
532 type BlockWeights = ();
533 type BlockLength = ();
534 type RuntimeOrigin = RuntimeOrigin;
535 type RuntimeCall = RuntimeCall;
536 type Nonce = u64;
537 type Hash = H256;
538 type Hashing = BlakeTwo256;
539 type AccountId = u64;
540 type Lookup = IdentityLookup<Self::AccountId>;
541 type Block = Block;
542 type RuntimeEvent = RuntimeEvent;
543 type DbWeight = ();
544 type Version = ();
545 type PalletInfo = PalletInfo;
546 type AccountData = pallet_balances::AccountData<u64>;
547 type OnNewAccount = ();
548 type OnKilledAccount = ();
549 type SystemWeightInfo = ();
550 type SS58Prefix = ();
551 type OnSetCode = ();
552 type MaxConsumers = frame_support::traits::ConstU32<16>;
553 }
554
555 #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
556 impl pallet_balances::Config for Test {
557 type AccountStore = System;
558 }
559
560 parameter_types! {
561 pub const LeasePeriod: BlockNumber = 10;
562 pub static LeaseOffset: BlockNumber = 0;
563 pub const ParaDeposit: u64 = 1;
564 }
565
566 impl Config for Test {
567 type RuntimeEvent = RuntimeEvent;
568 type Currency = Balances;
569 type Registrar = TestRegistrar<Test>;
570 type LeasePeriod = LeasePeriod;
571 type LeaseOffset = LeaseOffset;
572 type ForceOrigin = EnsureRoot<Self::AccountId>;
573 type WeightInfo = crate::slots::TestWeightInfo;
574 }
575
576 pub fn new_test_ext() -> sp_io::TestExternalities {
579 let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
580 pallet_balances::GenesisConfig::<Test> {
581 balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
582 ..Default::default()
583 }
584 .assimilate_storage(&mut t)
585 .unwrap();
586 t.into()
587 }
588
589 #[test]
590 fn basic_setup_works() {
591 new_test_ext().execute_with(|| {
592 System::run_to_block::<AllPalletsWithSystem>(1);
593 assert_eq!(Slots::lease_period_length(), (10, 0));
594 let now = System::block_number();
595 assert_eq!(Slots::lease_period_index(now).unwrap().0, 0);
596 assert_eq!(Slots::deposit_held(1.into(), &1), 0);
597
598 System::run_to_block::<AllPalletsWithSystem>(10);
599 let now = System::block_number();
600 assert_eq!(Slots::lease_period_index(now).unwrap().0, 1);
601 });
602 }
603
604 #[test]
605 fn lease_lifecycle_works() {
606 new_test_ext().execute_with(|| {
607 System::run_to_block::<AllPalletsWithSystem>(1);
608
609 assert_ok!(TestRegistrar::<Test>::register(
610 1,
611 ParaId::from(1_u32),
612 dummy_head_data(),
613 dummy_validation_code()
614 ));
615
616 assert_ok!(Slots::lease_out(1.into(), &1, 1, 1, 1));
617 assert_eq!(Slots::deposit_held(1.into(), &1), 1);
618 assert_eq!(Balances::reserved_balance(1), 1);
619
620 System::run_to_block::<AllPalletsWithSystem>(19);
621 assert_eq!(Slots::deposit_held(1.into(), &1), 1);
622 assert_eq!(Balances::reserved_balance(1), 1);
623
624 System::run_to_block::<AllPalletsWithSystem>(20);
625 assert_eq!(Slots::deposit_held(1.into(), &1), 0);
626 assert_eq!(Balances::reserved_balance(1), 0);
627
628 assert_eq!(
629 TestRegistrar::<Test>::operations(),
630 vec![(1.into(), 10, true), (1.into(), 20, false),]
631 );
632 });
633 }
634
635 #[test]
636 fn lease_interrupted_lifecycle_works() {
637 new_test_ext().execute_with(|| {
638 System::run_to_block::<AllPalletsWithSystem>(1);
639
640 assert_ok!(TestRegistrar::<Test>::register(
641 1,
642 ParaId::from(1_u32),
643 dummy_head_data(),
644 dummy_validation_code()
645 ));
646
647 assert_ok!(Slots::lease_out(1.into(), &1, 6, 1, 1));
648 assert_ok!(Slots::lease_out(1.into(), &1, 4, 3, 1));
649
650 System::run_to_block::<AllPalletsWithSystem>(19);
651 assert_eq!(Slots::deposit_held(1.into(), &1), 6);
652 assert_eq!(Balances::reserved_balance(1), 6);
653
654 System::run_to_block::<AllPalletsWithSystem>(20);
655 assert_eq!(Slots::deposit_held(1.into(), &1), 4);
656 assert_eq!(Balances::reserved_balance(1), 4);
657
658 System::run_to_block::<AllPalletsWithSystem>(39);
659 assert_eq!(Slots::deposit_held(1.into(), &1), 4);
660 assert_eq!(Balances::reserved_balance(1), 4);
661
662 System::run_to_block::<AllPalletsWithSystem>(40);
663 assert_eq!(Slots::deposit_held(1.into(), &1), 0);
664 assert_eq!(Balances::reserved_balance(1), 0);
665
666 assert_eq!(
667 TestRegistrar::<Test>::operations(),
668 vec![
669 (1.into(), 10, true),
670 (1.into(), 20, false),
671 (1.into(), 30, true),
672 (1.into(), 40, false),
673 ]
674 );
675 });
676 }
677
678 #[test]
679 fn lease_relayed_lifecycle_works() {
680 new_test_ext().execute_with(|| {
681 System::run_to_block::<AllPalletsWithSystem>(1);
682
683 assert_ok!(TestRegistrar::<Test>::register(
684 1,
685 ParaId::from(1_u32),
686 dummy_head_data(),
687 dummy_validation_code()
688 ));
689
690 assert!(Slots::lease_out(1.into(), &1, 6, 1, 1).is_ok());
691 assert!(Slots::lease_out(1.into(), &2, 4, 2, 1).is_ok());
692 assert_eq!(Slots::deposit_held(1.into(), &1), 6);
693 assert_eq!(Balances::reserved_balance(1), 6);
694 assert_eq!(Slots::deposit_held(1.into(), &2), 4);
695 assert_eq!(Balances::reserved_balance(2), 4);
696
697 System::run_to_block::<AllPalletsWithSystem>(19);
698 assert_eq!(Slots::deposit_held(1.into(), &1), 6);
699 assert_eq!(Balances::reserved_balance(1), 6);
700 assert_eq!(Slots::deposit_held(1.into(), &2), 4);
701 assert_eq!(Balances::reserved_balance(2), 4);
702
703 System::run_to_block::<AllPalletsWithSystem>(20);
704 assert_eq!(Slots::deposit_held(1.into(), &1), 0);
705 assert_eq!(Balances::reserved_balance(1), 0);
706 assert_eq!(Slots::deposit_held(1.into(), &2), 4);
707 assert_eq!(Balances::reserved_balance(2), 4);
708
709 System::run_to_block::<AllPalletsWithSystem>(29);
710 assert_eq!(Slots::deposit_held(1.into(), &1), 0);
711 assert_eq!(Balances::reserved_balance(1), 0);
712 assert_eq!(Slots::deposit_held(1.into(), &2), 4);
713 assert_eq!(Balances::reserved_balance(2), 4);
714
715 System::run_to_block::<AllPalletsWithSystem>(30);
716 assert_eq!(Slots::deposit_held(1.into(), &1), 0);
717 assert_eq!(Balances::reserved_balance(1), 0);
718 assert_eq!(Slots::deposit_held(1.into(), &2), 0);
719 assert_eq!(Balances::reserved_balance(2), 0);
720
721 assert_eq!(
722 TestRegistrar::<Test>::operations(),
723 vec![(1.into(), 10, true), (1.into(), 30, false),]
724 );
725 });
726 }
727
728 #[test]
729 fn lease_deposit_increase_works() {
730 new_test_ext().execute_with(|| {
731 System::run_to_block::<AllPalletsWithSystem>(1);
732
733 assert_ok!(TestRegistrar::<Test>::register(
734 1,
735 ParaId::from(1_u32),
736 dummy_head_data(),
737 dummy_validation_code()
738 ));
739
740 assert!(Slots::lease_out(1.into(), &1, 4, 1, 1).is_ok());
741 assert_eq!(Slots::deposit_held(1.into(), &1), 4);
742 assert_eq!(Balances::reserved_balance(1), 4);
743
744 assert!(Slots::lease_out(1.into(), &1, 6, 2, 1).is_ok());
745 assert_eq!(Slots::deposit_held(1.into(), &1), 6);
746 assert_eq!(Balances::reserved_balance(1), 6);
747
748 System::run_to_block::<AllPalletsWithSystem>(29);
749 assert_eq!(Slots::deposit_held(1.into(), &1), 6);
750 assert_eq!(Balances::reserved_balance(1), 6);
751
752 System::run_to_block::<AllPalletsWithSystem>(30);
753 assert_eq!(Slots::deposit_held(1.into(), &1), 0);
754 assert_eq!(Balances::reserved_balance(1), 0);
755
756 assert_eq!(
757 TestRegistrar::<Test>::operations(),
758 vec![(1.into(), 10, true), (1.into(), 30, false),]
759 );
760 });
761 }
762
763 #[test]
764 fn lease_deposit_decrease_works() {
765 new_test_ext().execute_with(|| {
766 System::run_to_block::<AllPalletsWithSystem>(1);
767
768 assert_ok!(TestRegistrar::<Test>::register(
769 1,
770 ParaId::from(1_u32),
771 dummy_head_data(),
772 dummy_validation_code()
773 ));
774
775 assert!(Slots::lease_out(1.into(), &1, 6, 1, 1).is_ok());
776 assert_eq!(Slots::deposit_held(1.into(), &1), 6);
777 assert_eq!(Balances::reserved_balance(1), 6);
778
779 assert!(Slots::lease_out(1.into(), &1, 4, 2, 1).is_ok());
780 assert_eq!(Slots::deposit_held(1.into(), &1), 6);
781 assert_eq!(Balances::reserved_balance(1), 6);
782
783 System::run_to_block::<AllPalletsWithSystem>(19);
784 assert_eq!(Slots::deposit_held(1.into(), &1), 6);
785 assert_eq!(Balances::reserved_balance(1), 6);
786
787 System::run_to_block::<AllPalletsWithSystem>(20);
788 assert_eq!(Slots::deposit_held(1.into(), &1), 4);
789 assert_eq!(Balances::reserved_balance(1), 4);
790
791 System::run_to_block::<AllPalletsWithSystem>(29);
792 assert_eq!(Slots::deposit_held(1.into(), &1), 4);
793 assert_eq!(Balances::reserved_balance(1), 4);
794
795 System::run_to_block::<AllPalletsWithSystem>(30);
796 assert_eq!(Slots::deposit_held(1.into(), &1), 0);
797 assert_eq!(Balances::reserved_balance(1), 0);
798
799 assert_eq!(
800 TestRegistrar::<Test>::operations(),
801 vec![(1.into(), 10, true), (1.into(), 30, false),]
802 );
803 });
804 }
805
806 #[test]
807 fn clear_all_leases_works() {
808 new_test_ext().execute_with(|| {
809 System::run_to_block::<AllPalletsWithSystem>(1);
810
811 assert_ok!(TestRegistrar::<Test>::register(
812 1,
813 ParaId::from(1_u32),
814 dummy_head_data(),
815 dummy_validation_code()
816 ));
817
818 let max_num = 5u32;
819
820 for i in 1u32..=max_num {
822 let j: u64 = i.into();
823 assert_ok!(Slots::lease_out(1.into(), &j, j * 10 - 1, i * i, i));
824 assert_eq!(Slots::deposit_held(1.into(), &j), j * 10 - 1);
825 assert_eq!(Balances::reserved_balance(j), j * 10 - 1);
826 }
827
828 assert_ok!(Slots::clear_all_leases(RuntimeOrigin::root(), 1.into()));
829
830 for i in 1u32..=max_num {
832 let j: u64 = i.into();
833 assert_eq!(Slots::deposit_held(1.into(), &j), 0);
834 assert_eq!(Balances::reserved_balance(j), 0);
835 }
836
837 assert!(Leases::<Test>::get(ParaId::from(1_u32)).is_empty());
839 });
840 }
841
842 #[test]
843 fn lease_out_current_lease_period() {
844 new_test_ext().execute_with(|| {
845 System::run_to_block::<AllPalletsWithSystem>(1);
846
847 assert_ok!(TestRegistrar::<Test>::register(
848 1,
849 ParaId::from(1_u32),
850 dummy_head_data(),
851 dummy_validation_code()
852 ));
853 assert_ok!(TestRegistrar::<Test>::register(
854 1,
855 ParaId::from(2_u32),
856 dummy_head_data(),
857 dummy_validation_code()
858 ));
859
860 System::run_to_block::<AllPalletsWithSystem>(20);
861 let now = System::block_number();
862 assert_eq!(Slots::lease_period_index(now).unwrap().0, 2);
863 assert!(Slots::lease_out(1.into(), &1, 1, 1, 1).is_err());
865 assert_ok!(Slots::lease_out(1.into(), &1, 1, 2, 1));
867 assert_ok!(Slots::lease_out(2.into(), &1, 1, 3, 1));
869
870 assert_eq!(TestRegistrar::<Test>::operations(), vec![(1.into(), 20, true),]);
871 });
872 }
873
874 #[test]
875 fn trigger_onboard_works() {
876 new_test_ext().execute_with(|| {
877 System::run_to_block::<AllPalletsWithSystem>(1);
878 assert_ok!(TestRegistrar::<Test>::register(
879 1,
880 ParaId::from(1_u32),
881 dummy_head_data(),
882 dummy_validation_code()
883 ));
884 assert_ok!(TestRegistrar::<Test>::register(
885 1,
886 ParaId::from(2_u32),
887 dummy_head_data(),
888 dummy_validation_code()
889 ));
890 assert_ok!(TestRegistrar::<Test>::register(
891 1,
892 ParaId::from(3_u32),
893 dummy_head_data(),
894 dummy_validation_code()
895 ));
896
897 Leases::<Test>::insert(ParaId::from(2_u32), vec![Some((0, 0))]);
901 Leases::<Test>::insert(ParaId::from(3_u32), vec![None, None, Some((0, 0))]);
903
904 assert_noop!(
906 Slots::trigger_onboard(RuntimeOrigin::signed(1), 1.into()),
907 Error::<Test>::ParaNotOnboarding
908 );
909
910 assert_ok!(Slots::trigger_onboard(RuntimeOrigin::signed(1), 2.into()));
912
913 assert_noop!(
915 Slots::trigger_onboard(RuntimeOrigin::signed(1), 3.into()),
916 Error::<Test>::ParaNotOnboarding
917 );
918
919 assert!(Slots::trigger_onboard(RuntimeOrigin::signed(1), 2.into()).is_err());
921
922 assert_eq!(TestRegistrar::<Test>::operations(), vec![(2.into(), 1, true),]);
923 });
924 }
925
926 #[test]
927 fn lease_period_offset_works() {
928 new_test_ext().execute_with(|| {
929 let (lpl, offset) = Slots::lease_period_length();
930 assert_eq!(offset, 0);
931 assert_eq!(Slots::lease_period_index(0), Some((0, true)));
932 assert_eq!(Slots::lease_period_index(1), Some((0, false)));
933 assert_eq!(Slots::lease_period_index(lpl - 1), Some((0, false)));
934 assert_eq!(Slots::lease_period_index(lpl), Some((1, true)));
935 assert_eq!(Slots::lease_period_index(lpl + 1), Some((1, false)));
936 assert_eq!(Slots::lease_period_index(2 * lpl - 1), Some((1, false)));
937 assert_eq!(Slots::lease_period_index(2 * lpl), Some((2, true)));
938 assert_eq!(Slots::lease_period_index(2 * lpl + 1), Some((2, false)));
939
940 LeaseOffset::set(5);
942 let (lpl, offset) = Slots::lease_period_length();
943 assert_eq!(offset, 5);
944 assert_eq!(Slots::lease_period_index(0), None);
945 assert_eq!(Slots::lease_period_index(1), None);
946 assert_eq!(Slots::lease_period_index(offset), Some((0, true)));
947 assert_eq!(Slots::lease_period_index(lpl), Some((0, false)));
948 assert_eq!(Slots::lease_period_index(lpl - 1 + offset), Some((0, false)));
949 assert_eq!(Slots::lease_period_index(lpl + offset), Some((1, true)));
950 assert_eq!(Slots::lease_period_index(lpl + offset + 1), Some((1, false)));
951 assert_eq!(Slots::lease_period_index(2 * lpl - 1 + offset), Some((1, false)));
952 assert_eq!(Slots::lease_period_index(2 * lpl + offset), Some((2, true)));
953 assert_eq!(Slots::lease_period_index(2 * lpl + offset + 1), Some((2, false)));
954 });
955 }
956}
957
958#[cfg(feature = "runtime-benchmarks")]
959mod benchmarking {
960 use super::*;
961 use frame_support::assert_ok;
962 use frame_system::RawOrigin;
963 use polkadot_runtime_parachains::paras;
964 use sp_runtime::traits::{Bounded, One};
965
966 use frame_benchmarking::v2::*;
967
968 use crate::slots::Pallet as Slots;
969
970 fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
971 let events = frame_system::Pallet::<T>::events();
972 let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
973 let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
975 assert_eq!(event, &system_event);
976 }
977
978 fn register_a_parathread<T: Config + paras::Config>(i: u32) -> (ParaId, T::AccountId) {
980 let para = ParaId::from(i);
981 let leaser: T::AccountId = account("leaser", i, 0);
982 T::Currency::make_free_balance_be(&leaser, BalanceOf::<T>::max_value());
983 let worst_head_data = T::Registrar::worst_head_data();
984 let worst_validation_code = T::Registrar::worst_validation_code();
985
986 assert_ok!(T::Registrar::register(
987 leaser.clone(),
988 para,
989 worst_head_data,
990 worst_validation_code.clone(),
991 ));
992 assert_ok!(paras::Pallet::<T>::add_trusted_validation_code(
993 frame_system::Origin::<T>::Root.into(),
994 worst_validation_code,
995 ));
996
997 T::Registrar::execute_pending_transitions();
998
999 (para, leaser)
1000 }
1001
1002 #[benchmarks(
1003 where T: paras::Config,
1004 )]
1005
1006 mod benchmarks {
1007 use super::*;
1008
1009 #[benchmark]
1010 fn force_lease() -> Result<(), BenchmarkError> {
1011 frame_system::Pallet::<T>::set_block_number(T::LeaseOffset::get() + One::one());
1013 let para = ParaId::from(1337);
1014 let leaser: T::AccountId = account("leaser", 0, 0);
1015 T::Currency::make_free_balance_be(&leaser, BalanceOf::<T>::max_value());
1016 let amount = T::Currency::minimum_balance();
1017 let period_begin = 69u32.into();
1018 let period_count = 3u32.into();
1019 let origin =
1020 T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
1021
1022 #[extrinsic_call]
1023 _(origin as T::RuntimeOrigin, para, leaser.clone(), amount, period_begin, period_count);
1024
1025 assert_last_event::<T>(
1026 Event::<T>::Leased {
1027 para_id: para,
1028 leaser,
1029 period_begin,
1030 period_count,
1031 extra_reserved: amount,
1032 total_amount: amount,
1033 }
1034 .into(),
1035 );
1036
1037 Ok(())
1038 }
1039
1040 #[benchmark]
1043 fn manage_lease_period_start(
1044 c: Linear<0, 100>,
1045 t: Linear<0, 100>,
1046 ) -> Result<(), BenchmarkError> {
1047 let period_begin = 1u32.into();
1048 let period_count = 4u32.into();
1049
1050 frame_system::Pallet::<T>::set_block_number(T::LeaseOffset::get() + One::one());
1052
1053 let paras_info = (0..t).map(|i| register_a_parathread::<T>(i)).collect::<Vec<_>>();
1055
1056 T::Registrar::execute_pending_transitions();
1057
1058 for (para, leaser) in paras_info {
1060 let amount = T::Currency::minimum_balance();
1061 let origin = T::ForceOrigin::try_successful_origin()
1062 .expect("ForceOrigin has no successful origin required for the benchmark");
1063 Slots::<T>::force_lease(origin, para, leaser, amount, period_begin, period_count)?;
1064 }
1065
1066 T::Registrar::execute_pending_transitions();
1067
1068 for i in 200..200 + c {
1070 let (para, _) = register_a_parathread::<T>(i);
1071 T::Registrar::make_parachain(para)?;
1072 }
1073
1074 T::Registrar::execute_pending_transitions();
1075
1076 for i in 0..t {
1077 assert!(T::Registrar::is_parathread(ParaId::from(i)));
1078 }
1079
1080 for i in 200..200 + c {
1081 assert!(T::Registrar::is_parachain(ParaId::from(i)));
1082 }
1083 #[block]
1084 {
1085 let _ = Slots::<T>::manage_lease_period_start(period_begin);
1086 }
1087
1088 T::Registrar::execute_pending_transitions();
1090 for i in 0..t {
1091 assert!(T::Registrar::is_parachain(ParaId::from(i)));
1092 }
1093 for i in 200..200 + c {
1094 assert!(T::Registrar::is_parathread(ParaId::from(i)));
1095 }
1096
1097 Ok(())
1098 }
1099
1100 #[benchmark]
1103 fn clear_all_leases() -> Result<(), BenchmarkError> {
1104 let max_people = 8;
1105 let (para, _) = register_a_parathread::<T>(1);
1106
1107 frame_system::Pallet::<T>::set_block_number(T::LeaseOffset::get() + One::one());
1109
1110 for i in 0..max_people {
1111 let leaser = account("lease_deposit", i, 0);
1112 let amount = T::Currency::minimum_balance();
1113 T::Currency::make_free_balance_be(&leaser, BalanceOf::<T>::max_value());
1114
1115 let period_count: LeasePeriodOf<T> = 4u32.into();
1117 let period_begin = period_count * i.into();
1118 let origin = T::ForceOrigin::try_successful_origin()
1119 .expect("ForceOrigin has no successful origin required for the benchmark");
1120 Slots::<T>::force_lease(origin, para, leaser, amount, period_begin, period_count)?;
1121 }
1122
1123 for i in 0..max_people {
1124 let leaser = account("lease_deposit", i, 0);
1125 assert_eq!(T::Currency::reserved_balance(&leaser), T::Currency::minimum_balance());
1126 }
1127
1128 let origin =
1129 T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
1130
1131 #[extrinsic_call]
1132 _(origin as T::RuntimeOrigin, para);
1133
1134 for i in 0..max_people {
1135 let leaser = account("lease_deposit", i, 0);
1136 assert_eq!(T::Currency::reserved_balance(&leaser), 0u32.into());
1137 }
1138
1139 Ok(())
1140 }
1141
1142 #[benchmark]
1143 fn trigger_onboard() -> Result<(), BenchmarkError> {
1144 let (para, _) = register_a_parathread::<T>(1);
1146 Leases::<T>::insert(
1147 para,
1148 vec![Some((
1149 account::<T::AccountId>("lease_insert", 0, 0),
1150 BalanceOf::<T>::default(),
1151 ))],
1152 );
1153 assert!(T::Registrar::is_parathread(para));
1154 let caller = whitelisted_caller();
1155
1156 #[extrinsic_call]
1157 _(RawOrigin::Signed(caller), para);
1158
1159 T::Registrar::execute_pending_transitions();
1160 assert!(T::Registrar::is_parachain(para));
1161 Ok(())
1162 }
1163
1164 impl_benchmark_test_suite!(
1165 Slots,
1166 crate::integration_tests::new_test_ext(),
1167 crate::integration_tests::Test,
1168 );
1169 }
1170}