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