1pub mod migration;
25
26use alloc::vec::Vec;
27use core::result;
28use frame_support::{
29 dispatch::DispatchResult,
30 ensure,
31 pallet_prelude::Weight,
32 traits::{Currency, Get, ReservableCurrency},
33};
34use frame_system::{self, ensure_root, ensure_signed, pallet_prelude::BlockNumberFor};
35use polkadot_primitives::{
36 HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID, MIN_CODE_SIZE,
37};
38use polkadot_runtime_parachains::{
39 configuration, ensure_parachain,
40 paras::{self, ParaGenesisArgs, UpgradeStrategy},
41 Origin, ParaLifecycle,
42};
43
44use crate::traits::{OnSwap, Registrar};
45use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
46pub use pallet::*;
47use polkadot_runtime_parachains::paras::{OnNewHead, ParaKind};
48use scale_info::TypeInfo;
49use sp_runtime::{
50 traits::{CheckedSub, Saturating, Zero},
51 Debug,
52};
53
54#[derive(
55 Encode,
56 Decode,
57 Clone,
58 PartialEq,
59 Eq,
60 Default,
61 Debug,
62 TypeInfo,
63 MaxEncodedLen,
64 DecodeWithMemTracking,
65)]
66pub struct ParaInfo<Account, Balance> {
67 pub manager: Account,
69 pub deposit: Balance,
71 pub locked: Option<bool>,
74}
75
76impl<Account, Balance> ParaInfo<Account, Balance> {
77 pub fn is_locked(&self) -> bool {
79 self.locked.unwrap_or(false)
80 }
81}
82
83type BalanceOf<T> =
84 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
85
86pub trait WeightInfo {
87 fn reserve() -> Weight;
88 fn register() -> Weight;
89 fn force_register() -> Weight;
90 fn deregister() -> Weight;
91 fn swap() -> Weight;
92 fn schedule_code_upgrade(b: u32) -> Weight;
93 fn set_current_head(b: u32) -> Weight;
94}
95
96pub struct TestWeightInfo;
97impl WeightInfo for TestWeightInfo {
98 fn reserve() -> Weight {
99 Weight::zero()
100 }
101 fn register() -> Weight {
102 Weight::zero()
103 }
104 fn force_register() -> Weight {
105 Weight::zero()
106 }
107 fn deregister() -> Weight {
108 Weight::zero()
109 }
110 fn swap() -> Weight {
111 Weight::zero()
112 }
113 fn schedule_code_upgrade(_b: u32) -> Weight {
114 Weight::zero()
115 }
116 fn set_current_head(_b: u32) -> Weight {
117 Weight::zero()
118 }
119}
120
121#[frame_support::pallet]
122pub mod pallet {
123 use super::*;
124 use frame_support::pallet_prelude::*;
125 use frame_system::pallet_prelude::*;
126
127 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
129
130 #[pallet::pallet]
131 #[pallet::without_storage_info]
132 #[pallet::storage_version(STORAGE_VERSION)]
133 pub struct Pallet<T>(_);
134
135 #[pallet::config]
136 #[pallet::disable_frame_system_supertrait_check]
137 pub trait Config: configuration::Config + paras::Config {
138 #[allow(deprecated)]
140 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
141
142 type RuntimeOrigin: From<<Self as frame_system::Config>::RuntimeOrigin>
147 + Into<result::Result<Origin, <Self as Config>::RuntimeOrigin>>;
148
149 type Currency: ReservableCurrency<Self::AccountId>;
151
152 type OnSwap: crate::traits::OnSwap;
154
155 #[pallet::constant]
158 type ParaDeposit: Get<BalanceOf<Self>>;
159
160 #[pallet::constant]
162 type DataDepositPerByte: Get<BalanceOf<Self>>;
163
164 type WeightInfo: WeightInfo;
166 }
167
168 #[pallet::event]
169 #[pallet::generate_deposit(pub(super) fn deposit_event)]
170 pub enum Event<T: Config> {
171 Registered { para_id: ParaId, manager: T::AccountId },
172 Deregistered { para_id: ParaId },
173 Reserved { para_id: ParaId, who: T::AccountId },
174 Swapped { para_id: ParaId, other_id: ParaId },
175 }
176
177 #[pallet::error]
178 pub enum Error<T> {
179 NotRegistered,
181 AlreadyRegistered,
183 NotOwner,
185 CodeTooLarge,
187 HeadDataTooLarge,
189 NotParachain,
191 NotParathread,
193 CannotDeregister,
195 CannotDowngrade,
197 CannotUpgrade,
199 ParaLocked,
202 NotReserved,
204 InvalidCode,
206 CannotSwap,
209 NotImplemented,
211 }
212
213 #[pallet::storage]
215 pub(super) type PendingSwap<T> = StorageMap<_, Twox64Concat, ParaId, ParaId>;
216
217 #[pallet::storage]
222 pub type Paras<T: Config> =
223 StorageMap<_, Twox64Concat, ParaId, ParaInfo<T::AccountId, BalanceOf<T>>>;
224
225 #[pallet::storage]
227 pub type NextFreeParaId<T> = StorageValue<_, ParaId, ValueQuery>;
228
229 #[pallet::genesis_config]
230 pub struct GenesisConfig<T: Config> {
231 #[serde(skip)]
232 pub _config: core::marker::PhantomData<T>,
233 pub next_free_para_id: ParaId,
234 }
235
236 impl<T: Config> Default for GenesisConfig<T> {
237 fn default() -> Self {
238 GenesisConfig { next_free_para_id: LOWEST_PUBLIC_ID, _config: Default::default() }
239 }
240 }
241
242 #[pallet::genesis_build]
243 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
244 fn build(&self) {
245 NextFreeParaId::<T>::put(self.next_free_para_id);
246 }
247 }
248
249 #[pallet::hooks]
250 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
251
252 #[pallet::call]
253 impl<T: Config> Pallet<T> {
254 #[pallet::call_index(0)]
275 #[pallet::weight(<T as Config>::WeightInfo::register())]
276 pub fn register(
277 origin: OriginFor<T>,
278 id: ParaId,
279 genesis_head: HeadData,
280 validation_code: ValidationCode,
281 ) -> DispatchResult {
282 let who = ensure_signed(origin)?;
283 Self::do_register(who, None, id, genesis_head, validation_code, true)?;
284 Ok(())
285 }
286
287 #[pallet::call_index(1)]
294 #[pallet::weight(<T as Config>::WeightInfo::force_register())]
295 pub fn force_register(
296 origin: OriginFor<T>,
297 who: T::AccountId,
298 deposit: BalanceOf<T>,
299 id: ParaId,
300 genesis_head: HeadData,
301 validation_code: ValidationCode,
302 ) -> DispatchResult {
303 ensure_root(origin)?;
304 Self::do_register(who, Some(deposit), id, genesis_head, validation_code, false)
305 }
306
307 #[pallet::call_index(2)]
312 #[pallet::weight(<T as Config>::WeightInfo::deregister())]
313 pub fn deregister(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
314 Self::ensure_root_para_or_owner(origin, id)?;
315 Self::do_deregister(id)
316 }
317
318 #[pallet::call_index(3)]
331 #[pallet::weight(<T as Config>::WeightInfo::swap())]
332 pub fn swap(origin: OriginFor<T>, id: ParaId, other: ParaId) -> DispatchResult {
333 Self::ensure_root_para_or_owner(origin, id)?;
334
335 if id == other {
338 PendingSwap::<T>::remove(id);
339 return Ok(());
340 }
341
342 let id_lifecycle =
344 paras::Pallet::<T>::lifecycle(id).ok_or(Error::<T>::NotRegistered)?;
345
346 if PendingSwap::<T>::get(other) == Some(id) {
347 let other_lifecycle =
348 paras::Pallet::<T>::lifecycle(other).ok_or(Error::<T>::NotRegistered)?;
349 if id_lifecycle == ParaLifecycle::Parachain &&
352 other_lifecycle == ParaLifecycle::Parathread
353 {
354 Self::do_thread_and_chain_swap(id, other);
355 } else if id_lifecycle == ParaLifecycle::Parathread &&
356 other_lifecycle == ParaLifecycle::Parachain
357 {
358 Self::do_thread_and_chain_swap(other, id);
359 } else if id_lifecycle == ParaLifecycle::Parachain &&
360 other_lifecycle == ParaLifecycle::Parachain
361 {
362 T::OnSwap::on_swap(id, other);
366 } else {
367 return Err(Error::<T>::CannotSwap.into());
368 }
369 Self::deposit_event(Event::<T>::Swapped { para_id: id, other_id: other });
370 PendingSwap::<T>::remove(other);
371 } else {
372 PendingSwap::<T>::insert(id, other);
373 }
374
375 Ok(())
376 }
377
378 #[pallet::call_index(4)]
383 #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
384 pub fn remove_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
385 Self::ensure_root_or_para(origin, para)?;
386 <Self as Registrar>::remove_lock(para);
387 Ok(())
388 }
389
390 #[pallet::call_index(5)]
408 #[pallet::weight(<T as Config>::WeightInfo::reserve())]
409 pub fn reserve(origin: OriginFor<T>) -> DispatchResult {
410 let who = ensure_signed(origin)?;
411 let id = NextFreeParaId::<T>::get().max(LOWEST_PUBLIC_ID);
412 Self::do_reserve(who, None, id)?;
413 NextFreeParaId::<T>::set(id + 1);
414 Ok(())
415 }
416
417 #[pallet::call_index(6)]
423 #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
424 pub fn add_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
425 Self::ensure_root_para_or_owner(origin, para)?;
426 <Self as Registrar>::apply_lock(para);
427 Ok(())
428 }
429
430 #[pallet::call_index(7)]
442 #[pallet::weight(<T as Config>::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))]
443 pub fn schedule_code_upgrade(
444 origin: OriginFor<T>,
445 para: ParaId,
446 new_code: ValidationCode,
447 ) -> DispatchResult {
448 Self::ensure_root_para_or_owner(origin, para)?;
449 polkadot_runtime_parachains::schedule_code_upgrade::<T>(
450 para,
451 new_code,
452 UpgradeStrategy::ApplyAtExpectedBlock,
453 )?;
454 Ok(())
455 }
456
457 #[pallet::call_index(8)]
462 #[pallet::weight(<T as Config>::WeightInfo::set_current_head(new_head.0.len() as u32))]
463 pub fn set_current_head(
464 origin: OriginFor<T>,
465 para: ParaId,
466 new_head: HeadData,
467 ) -> DispatchResult {
468 Self::ensure_root_para_or_owner(origin, para)?;
469 polkadot_runtime_parachains::set_current_head::<T>(para, new_head);
470 Ok(())
471 }
472 }
473}
474
475impl<T: Config> Registrar for Pallet<T> {
476 type AccountId = T::AccountId;
477
478 fn manager_of(id: ParaId) -> Option<T::AccountId> {
480 Some(Paras::<T>::get(id)?.manager)
481 }
482
483 fn parachains() -> Vec<ParaId> {
486 paras::Parachains::<T>::get()
487 }
488
489 fn is_parathread(id: ParaId) -> bool {
491 paras::Pallet::<T>::is_parathread(id)
492 }
493
494 fn is_parachain(id: ParaId) -> bool {
496 paras::Pallet::<T>::is_parachain(id)
497 }
498
499 fn apply_lock(id: ParaId) {
501 Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(true)));
502 }
503
504 fn remove_lock(id: ParaId) {
506 Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(false)));
507 }
508
509 fn register(
514 manager: T::AccountId,
515 id: ParaId,
516 genesis_head: HeadData,
517 validation_code: ValidationCode,
518 ) -> DispatchResult {
519 Self::do_register(manager, None, id, genesis_head, validation_code, false)
520 }
521
522 fn deregister(id: ParaId) -> DispatchResult {
524 Self::do_deregister(id)
525 }
526
527 fn make_parachain(id: ParaId) -> DispatchResult {
529 ensure!(
531 paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parathread),
532 Error::<T>::NotParathread
533 );
534 polkadot_runtime_parachains::schedule_parathread_upgrade::<T>(id)
535 .map_err(|_| Error::<T>::CannotUpgrade)?;
536
537 Ok(())
538 }
539
540 fn make_parathread(id: ParaId) -> DispatchResult {
542 ensure!(
544 paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parachain),
545 Error::<T>::NotParachain
546 );
547 polkadot_runtime_parachains::schedule_parachain_downgrade::<T>(id)
548 .map_err(|_| Error::<T>::CannotDowngrade)?;
549 Ok(())
550 }
551
552 #[cfg(any(feature = "runtime-benchmarks", test))]
553 fn worst_head_data() -> HeadData {
554 let max_head_size = configuration::ActiveConfig::<T>::get().max_head_data_size;
555 assert!(max_head_size > 0, "max_head_data can't be zero for generating worst head data.");
556 alloc::vec![0u8; max_head_size as usize].into()
557 }
558
559 #[cfg(any(feature = "runtime-benchmarks", test))]
560 fn worst_validation_code() -> ValidationCode {
561 let max_code_size = configuration::ActiveConfig::<T>::get().max_code_size;
562 assert!(max_code_size > 0, "max_code_size can't be zero for generating worst code data.");
563 let validation_code = alloc::vec![0u8; max_code_size as usize];
564 validation_code.into()
565 }
566
567 #[cfg(any(feature = "runtime-benchmarks", test))]
568 fn execute_pending_transitions() {
569 use polkadot_runtime_parachains::shared;
570 shared::Pallet::<T>::set_session_index(shared::Pallet::<T>::scheduled_session());
571 paras::Pallet::<T>::test_on_new_session();
572 }
573}
574
575impl<T: Config> registrar_primitives::ParachainRegistrar for Pallet<T> {
586 type AccountId = T::AccountId;
587
588 fn check_onboarding(head_len: u32, code_len: u32) -> Result<(), ()> {
589 let config = configuration::ActiveConfig::<T>::get();
590 Self::validate_onboarding_sizes(&config, head_len as usize, code_len as usize)
591 .map_err(|_| ())
592 }
593
594 fn is_registered(para_id: u32) -> bool {
595 let id = ParaId::from(para_id);
596 Paras::<T>::contains_key(id) || paras::Pallet::<T>::lifecycle(id).is_some()
597 }
598
599 fn register(
600 manager: T::AccountId,
601 para_id: u32,
602 genesis_head: Vec<u8>,
603 validation_code: Vec<u8>,
604 ) -> DispatchResult {
605 Self::do_register(
606 manager,
607 Some(BalanceOf::<T>::zero()),
608 ParaId::from(para_id),
609 HeadData(genesis_head),
610 ValidationCode(validation_code),
611 false,
612 )
613 }
614
615 fn deregister(_para_id: u32) -> DispatchResult {
616 Err(Error::<T>::NotImplemented.into())
618 }
619
620 fn check_head_data(_head_len: u32) -> Result<(), ()> {
621 Err(())
623 }
624
625 fn set_current_head(_para_id: u32, _head: Vec<u8>) {
626 }
628
629 fn check_code_upgrade(_para_id: u32, _code_len: u32) -> Result<(), ()> {
630 Err(())
632 }
633
634 fn schedule_code_upgrade(_para_id: u32, _validation_code: Vec<u8>) -> DispatchResult {
635 Err(Error::<T>::NotImplemented.into())
637 }
638}
639
640impl<T: Config> Pallet<T> {
641 fn ensure_root_para_or_owner(
644 origin: <T as frame_system::Config>::RuntimeOrigin,
645 id: ParaId,
646 ) -> DispatchResult {
647 if let Ok(who) = ensure_signed(origin.clone()) {
648 let para_info = Paras::<T>::get(id).ok_or(Error::<T>::NotRegistered)?;
649
650 if para_info.manager == who {
651 ensure!(!para_info.is_locked(), Error::<T>::ParaLocked);
652 return Ok(());
653 }
654 }
655
656 Self::ensure_root_or_para(origin, id)
657 }
658
659 fn ensure_root_or_para(
661 origin: <T as frame_system::Config>::RuntimeOrigin,
662 id: ParaId,
663 ) -> DispatchResult {
664 if ensure_root(origin.clone()).is_ok() {
665 return Ok(());
666 }
667
668 let caller_id = ensure_parachain(<T as Config>::RuntimeOrigin::from(origin))?;
669 ensure!(caller_id == id, Error::<T>::NotOwner);
671
672 Ok(())
673 }
674
675 fn do_reserve(
676 who: T::AccountId,
677 deposit_override: Option<BalanceOf<T>>,
678 id: ParaId,
679 ) -> DispatchResult {
680 ensure!(!Paras::<T>::contains_key(id), Error::<T>::AlreadyRegistered);
681 ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
682
683 let deposit = deposit_override.unwrap_or_else(T::ParaDeposit::get);
684 <T as Config>::Currency::reserve(&who, deposit)?;
685 let info = ParaInfo { manager: who.clone(), deposit, locked: None };
686
687 Paras::<T>::insert(id, info);
688 Self::deposit_event(Event::<T>::Reserved { para_id: id, who });
689 Ok(())
690 }
691
692 fn do_register(
695 who: T::AccountId,
696 deposit_override: Option<BalanceOf<T>>,
697 id: ParaId,
698 genesis_head: HeadData,
699 validation_code: ValidationCode,
700 ensure_reserved: bool,
701 ) -> DispatchResult {
702 let deposited = if let Some(para_data) = Paras::<T>::get(id) {
703 ensure!(para_data.manager == who, Error::<T>::NotOwner);
704 ensure!(!para_data.is_locked(), Error::<T>::ParaLocked);
705 para_data.deposit
706 } else {
707 ensure!(!ensure_reserved, Error::<T>::NotReserved);
708 Default::default()
709 };
710 ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
711 let (genesis, deposit) =
712 Self::validate_onboarding_data(genesis_head, validation_code, ParaKind::Parathread)?;
713 let deposit = deposit_override.unwrap_or(deposit);
714
715 if let Some(additional) = deposit.checked_sub(&deposited) {
716 <T as Config>::Currency::reserve(&who, additional)?;
717 } else if let Some(rebate) = deposited.checked_sub(&deposit) {
718 <T as Config>::Currency::unreserve(&who, rebate);
719 };
720 let info = ParaInfo { manager: who.clone(), deposit, locked: None };
721
722 Paras::<T>::insert(id, info);
723 let res = polkadot_runtime_parachains::schedule_para_initialize::<T>(id, genesis);
725 debug_assert!(res.is_ok());
726 Self::deposit_event(Event::<T>::Registered { para_id: id, manager: who });
727 Ok(())
728 }
729
730 fn do_deregister(id: ParaId) -> DispatchResult {
732 match paras::Pallet::<T>::lifecycle(id) {
733 Some(ParaLifecycle::Parathread) | None => {},
735 _ => return Err(Error::<T>::NotParathread.into()),
736 }
737 polkadot_runtime_parachains::schedule_para_cleanup::<T>(id)
738 .map_err(|_| Error::<T>::CannotDeregister)?;
739
740 if let Some(info) = Paras::<T>::take(&id) {
741 <T as Config>::Currency::unreserve(&info.manager, info.deposit);
742 }
743
744 PendingSwap::<T>::remove(id);
745 Self::deposit_event(Event::<T>::Deregistered { para_id: id });
746 Ok(())
747 }
748
749 fn validate_onboarding_data(
753 genesis_head: HeadData,
754 validation_code: ValidationCode,
755 para_kind: ParaKind,
756 ) -> Result<(ParaGenesisArgs, BalanceOf<T>), sp_runtime::DispatchError> {
757 let config = configuration::ActiveConfig::<T>::get();
758 Self::validate_onboarding_sizes(&config, genesis_head.0.len(), validation_code.0.len())?;
759
760 let per_byte_fee = T::DataDepositPerByte::get();
761 let deposit = T::ParaDeposit::get()
762 .saturating_add(per_byte_fee.saturating_mul((genesis_head.0.len() as u32).into()))
763 .saturating_add(per_byte_fee.saturating_mul(config.max_code_size.into()));
764
765 Ok((ParaGenesisArgs { genesis_head, validation_code, para_kind }, deposit))
766 }
767
768 fn validate_onboarding_sizes(
770 config: &configuration::HostConfiguration<BlockNumberFor<T>>,
771 head_len: usize,
772 code_len: usize,
773 ) -> DispatchResult {
774 ensure!(code_len >= MIN_CODE_SIZE as usize, Error::<T>::InvalidCode);
775 ensure!(code_len <= config.max_code_size as usize, Error::<T>::CodeTooLarge);
776 ensure!(head_len <= config.max_head_data_size as usize, Error::<T>::HeadDataTooLarge);
777 Ok(())
778 }
779
780 fn do_thread_and_chain_swap(to_downgrade: ParaId, to_upgrade: ParaId) {
783 let res1 = polkadot_runtime_parachains::schedule_parachain_downgrade::<T>(to_downgrade);
784 debug_assert!(res1.is_ok());
785 let res2 = polkadot_runtime_parachains::schedule_parathread_upgrade::<T>(to_upgrade);
786 debug_assert!(res2.is_ok());
787 T::OnSwap::on_swap(to_upgrade, to_downgrade);
788 }
789}
790
791impl<T: Config> OnNewHead for Pallet<T> {
792 fn on_new_head(id: ParaId, _head: &HeadData) -> Weight {
793 let mut writes = 0;
795 if let Some(mut info) = Paras::<T>::get(id) {
796 if info.locked.is_none() {
797 info.locked = Some(true);
798 Paras::<T>::insert(id, info);
799 writes += 1;
800 }
801 }
802 T::DbWeight::get().reads_writes(1, writes)
803 }
804}
805
806#[cfg(test)]
807mod mock;
808
809#[cfg(test)]
810mod tests;
811
812#[cfg(feature = "runtime-benchmarks")]
813mod benchmarking;