1pub mod migration;
25
26use alloc::vec::Vec;
27use core::{matches, result};
28use frame_support::{
29 dispatch::DispatchResult,
30 ensure,
31 pallet_prelude::Weight,
32 storage::with_storage_layer,
33 traits::{Currency, Get, ReservableCurrency},
34};
35use frame_system::{self, ensure_root, ensure_signed, pallet_prelude::BlockNumberFor};
36use polkadot_primitives::{
37 HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID, MIN_CODE_SIZE,
38};
39use polkadot_runtime_parachains::{
40 configuration, ensure_parachain,
41 paras::{self, ParaGenesisArgs, UpgradeStrategy},
42 Origin, ParaLifecycle,
43};
44
45use crate::traits::{OnSwap, Registrar};
46use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
47pub use pallet::*;
48use polkadot_runtime_parachains::paras::{OnNewHead, ParaKind};
49use scale_info::TypeInfo;
50use sp_runtime::{
51 traits::{CheckedSub, Saturating, Zero},
52 Debug,
53};
54
55#[derive(
56 Encode,
57 Decode,
58 Clone,
59 PartialEq,
60 Eq,
61 Default,
62 Debug,
63 TypeInfo,
64 MaxEncodedLen,
65 DecodeWithMemTracking,
66)]
67pub struct ParaInfo<Account, Balance> {
68 pub manager: Account,
70 pub deposit: Balance,
72 pub locked: Option<bool>,
75}
76
77impl<Account, Balance> ParaInfo<Account, Balance> {
78 pub fn is_locked(&self) -> bool {
80 self.locked.unwrap_or(false)
81 }
82}
83
84type BalanceOf<T> =
85 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
86
87pub trait WeightInfo {
88 fn reserve() -> Weight;
89 fn register() -> Weight;
90 fn force_register() -> Weight;
91 fn deregister() -> Weight;
92 fn swap() -> Weight;
93 fn schedule_code_upgrade(b: u32) -> Weight;
94 fn set_current_head(b: u32) -> Weight;
95}
96
97pub struct TestWeightInfo;
98impl WeightInfo for TestWeightInfo {
99 fn reserve() -> Weight {
100 Weight::zero()
101 }
102 fn register() -> Weight {
103 Weight::zero()
104 }
105 fn force_register() -> Weight {
106 Weight::zero()
107 }
108 fn deregister() -> Weight {
109 Weight::zero()
110 }
111 fn swap() -> Weight {
112 Weight::zero()
113 }
114 fn schedule_code_upgrade(_b: u32) -> Weight {
115 Weight::zero()
116 }
117 fn set_current_head(_b: u32) -> Weight {
118 Weight::zero()
119 }
120}
121
122#[frame_support::pallet]
123pub mod pallet {
124 use super::*;
125 use frame_support::pallet_prelude::*;
126 use frame_system::pallet_prelude::*;
127
128 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
130
131 #[pallet::pallet]
132 #[pallet::without_storage_info]
133 #[pallet::storage_version(STORAGE_VERSION)]
134 pub struct Pallet<T>(_);
135
136 #[pallet::config]
137 #[pallet::disable_frame_system_supertrait_check]
138 pub trait Config: configuration::Config + paras::Config {
139 #[allow(deprecated)]
141 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
142
143 type RuntimeOrigin: From<<Self as frame_system::Config>::RuntimeOrigin>
148 + Into<result::Result<Origin, <Self as Config>::RuntimeOrigin>>;
149
150 type Currency: ReservableCurrency<Self::AccountId>;
152
153 type OnSwap: crate::traits::OnSwap;
155
156 #[pallet::constant]
159 type ParaDeposit: Get<BalanceOf<Self>>;
160
161 #[pallet::constant]
163 type DataDepositPerByte: Get<BalanceOf<Self>>;
164
165 type WeightInfo: WeightInfo;
167 }
168
169 #[pallet::event]
170 #[pallet::generate_deposit(pub(super) fn deposit_event)]
171 pub enum Event<T: Config> {
172 Registered { para_id: ParaId, manager: T::AccountId },
173 Deregistered { para_id: ParaId },
174 Reserved { para_id: ParaId, who: T::AccountId },
175 Swapped { para_id: ParaId, other_id: ParaId },
176 }
177
178 #[pallet::error]
179 pub enum Error<T> {
180 NotRegistered,
182 AlreadyRegistered,
184 NotOwner,
186 CodeTooLarge,
188 HeadDataTooLarge,
190 NotParachain,
192 NotParathread,
194 CannotDeregister,
196 CannotDowngrade,
198 CannotUpgrade,
200 ParaLocked,
203 NotReserved,
205 InvalidCode,
207 CannotSwap,
210 NotImplemented,
212 }
213
214 #[pallet::storage]
216 pub(super) type PendingSwap<T> = StorageMap<_, Twox64Concat, ParaId, ParaId>;
217
218 #[pallet::storage]
223 pub type Paras<T: Config> =
224 StorageMap<_, Twox64Concat, ParaId, ParaInfo<T::AccountId, BalanceOf<T>>>;
225
226 #[pallet::storage]
228 pub type NextFreeParaId<T> = StorageValue<_, ParaId, ValueQuery>;
229
230 #[pallet::genesis_config]
231 pub struct GenesisConfig<T: Config> {
232 #[serde(skip)]
233 pub _config: core::marker::PhantomData<T>,
234 pub next_free_para_id: ParaId,
235 }
236
237 impl<T: Config> Default for GenesisConfig<T> {
238 fn default() -> Self {
239 GenesisConfig { next_free_para_id: LOWEST_PUBLIC_ID, _config: Default::default() }
240 }
241 }
242
243 #[pallet::genesis_build]
244 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
245 fn build(&self) {
246 NextFreeParaId::<T>::put(self.next_free_para_id);
247 }
248 }
249
250 #[pallet::hooks]
251 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
252
253 #[pallet::call]
254 impl<T: Config> Pallet<T> {
255 #[pallet::call_index(0)]
276 #[pallet::weight(<T as Config>::WeightInfo::register())]
277 pub fn register(
278 origin: OriginFor<T>,
279 id: ParaId,
280 genesis_head: HeadData,
281 validation_code: ValidationCode,
282 ) -> DispatchResult {
283 let who = ensure_signed(origin)?;
284 Self::do_register(who, None, id, genesis_head, validation_code, true)?;
285 Ok(())
286 }
287
288 #[pallet::call_index(1)]
298 #[pallet::weight(<T as Config>::WeightInfo::force_register())]
299 pub fn force_register(
300 origin: OriginFor<T>,
301 who: T::AccountId,
302 deposit: BalanceOf<T>,
303 id: ParaId,
304 genesis_head: HeadData,
305 validation_code: ValidationCode,
306 ) -> DispatchResult {
307 ensure_root(origin)?;
308 Self::do_register(who, Some(deposit), id, genesis_head, validation_code, false)
309 }
310
311 #[pallet::call_index(2)]
316 #[pallet::weight(<T as Config>::WeightInfo::deregister())]
317 pub fn deregister(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
318 Self::ensure_root_para_or_owner(origin, id)?;
319 Self::do_deregister(id)
320 }
321
322 #[pallet::call_index(3)]
335 #[pallet::weight(<T as Config>::WeightInfo::swap())]
336 pub fn swap(origin: OriginFor<T>, id: ParaId, other: ParaId) -> DispatchResult {
337 Self::ensure_root_para_or_owner(origin, id)?;
338
339 if id == other {
342 PendingSwap::<T>::remove(id);
343 return Ok(());
344 }
345
346 let id_lifecycle =
348 paras::Pallet::<T>::lifecycle(id).ok_or(Error::<T>::NotRegistered)?;
349
350 if PendingSwap::<T>::get(other) == Some(id) {
351 let other_lifecycle =
352 paras::Pallet::<T>::lifecycle(other).ok_or(Error::<T>::NotRegistered)?;
353 if id_lifecycle == ParaLifecycle::Parachain &&
356 other_lifecycle == ParaLifecycle::Parathread
357 {
358 Self::do_thread_and_chain_swap(id, other);
359 } else if id_lifecycle == ParaLifecycle::Parathread &&
360 other_lifecycle == ParaLifecycle::Parachain
361 {
362 Self::do_thread_and_chain_swap(other, id);
363 } else if id_lifecycle == ParaLifecycle::Parachain &&
364 other_lifecycle == ParaLifecycle::Parachain
365 {
366 T::OnSwap::on_swap(id, other);
370 } else {
371 return Err(Error::<T>::CannotSwap.into());
372 }
373 Self::deposit_event(Event::<T>::Swapped { para_id: id, other_id: other });
374 PendingSwap::<T>::remove(other);
375 } else {
376 PendingSwap::<T>::insert(id, other);
377 }
378
379 Ok(())
380 }
381
382 #[pallet::call_index(4)]
387 #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
388 pub fn remove_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
389 Self::ensure_root_or_para(origin, para)?;
390 <Self as Registrar>::remove_lock(para);
391 Ok(())
392 }
393
394 #[pallet::call_index(5)]
412 #[pallet::weight(<T as Config>::WeightInfo::reserve())]
413 pub fn reserve(origin: OriginFor<T>) -> DispatchResult {
414 let who = ensure_signed(origin)?;
415 let id = NextFreeParaId::<T>::get().max(LOWEST_PUBLIC_ID);
416 Self::do_reserve(who, None, id)?;
417 NextFreeParaId::<T>::set(id + 1);
418 Ok(())
419 }
420
421 #[pallet::call_index(6)]
427 #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
428 pub fn add_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
429 Self::ensure_root_para_or_owner(origin, para)?;
430 <Self as Registrar>::apply_lock(para);
431 Ok(())
432 }
433
434 #[pallet::call_index(7)]
446 #[pallet::weight(<T as Config>::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))]
447 pub fn schedule_code_upgrade(
448 origin: OriginFor<T>,
449 para: ParaId,
450 new_code: ValidationCode,
451 ) -> DispatchResult {
452 Self::ensure_root_para_or_owner(origin, para)?;
453 polkadot_runtime_parachains::schedule_code_upgrade::<T>(
454 para,
455 new_code,
456 UpgradeStrategy::ApplyAtExpectedBlock,
457 )?;
458 Ok(())
459 }
460
461 #[pallet::call_index(8)]
466 #[pallet::weight(<T as Config>::WeightInfo::set_current_head(new_head.0.len() as u32))]
467 pub fn set_current_head(
468 origin: OriginFor<T>,
469 para: ParaId,
470 new_head: HeadData,
471 ) -> DispatchResult {
472 Self::ensure_root_para_or_owner(origin, para)?;
473 polkadot_runtime_parachains::set_current_head::<T>(para, new_head);
474 Ok(())
475 }
476 }
477}
478
479impl<T: Config> Registrar for Pallet<T> {
480 type AccountId = T::AccountId;
481
482 fn manager_of(id: ParaId) -> Option<T::AccountId> {
484 Some(Paras::<T>::get(id)?.manager)
485 }
486
487 fn parachains() -> Vec<ParaId> {
490 paras::Parachains::<T>::get()
491 }
492
493 fn is_parathread(id: ParaId) -> bool {
495 paras::Pallet::<T>::is_parathread(id)
496 }
497
498 fn is_parachain(id: ParaId) -> bool {
500 paras::Pallet::<T>::is_parachain(id)
501 }
502
503 fn apply_lock(id: ParaId) {
505 Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(true)));
506 }
507
508 fn remove_lock(id: ParaId) {
510 Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(false)));
511 }
512
513 fn register(
518 manager: T::AccountId,
519 id: ParaId,
520 genesis_head: HeadData,
521 validation_code: ValidationCode,
522 ) -> DispatchResult {
523 Self::do_register(manager, None, id, genesis_head, validation_code, false)
524 }
525
526 fn deregister(id: ParaId) -> DispatchResult {
528 Self::do_deregister(id)
529 }
530
531 fn make_parachain(id: ParaId) -> DispatchResult {
533 ensure!(
535 paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parathread),
536 Error::<T>::NotParathread
537 );
538 polkadot_runtime_parachains::schedule_parathread_upgrade::<T>(id)
539 .map_err(|_| Error::<T>::CannotUpgrade)?;
540
541 Ok(())
542 }
543
544 fn make_parathread(id: ParaId) -> DispatchResult {
546 ensure!(
548 paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parachain),
549 Error::<T>::NotParachain
550 );
551 polkadot_runtime_parachains::schedule_parachain_downgrade::<T>(id)
552 .map_err(|_| Error::<T>::CannotDowngrade)?;
553 Ok(())
554 }
555
556 #[cfg(any(feature = "runtime-benchmarks", test))]
557 fn worst_head_data() -> HeadData {
558 let max_head_size = configuration::ActiveConfig::<T>::get().max_head_data_size;
559 assert!(max_head_size > 0, "max_head_data can't be zero for generating worst head data.");
560 alloc::vec![0u8; max_head_size as usize].into()
561 }
562
563 #[cfg(any(feature = "runtime-benchmarks", test))]
564 fn worst_validation_code() -> ValidationCode {
565 let max_code_size = configuration::ActiveConfig::<T>::get().max_code_size;
566 assert!(max_code_size > 0, "max_code_size can't be zero for generating worst code data.");
567 let validation_code = alloc::vec![0u8; max_code_size as usize];
568 validation_code.into()
569 }
570
571 #[cfg(any(feature = "runtime-benchmarks", test))]
572 fn execute_pending_transitions() {
573 use polkadot_runtime_parachains::shared;
574 shared::Pallet::<T>::set_session_index(shared::Pallet::<T>::scheduled_session());
575 paras::Pallet::<T>::test_on_new_session();
576 }
577}
578
579impl<T: Config> registrar_primitives::ParachainRegistrar for Pallet<T> {
590 type AccountId = T::AccountId;
591
592 fn check_onboarding(head_len: u32, code_len: u32) -> Result<(), ()> {
593 let config = configuration::ActiveConfig::<T>::get();
594 Self::validate_onboarding_sizes(&config, head_len as usize, code_len as usize)
595 .map_err(|_| ())
596 }
597
598 fn is_registered(para_id: u32) -> bool {
599 let id = ParaId::from(para_id);
600 Paras::<T>::contains_key(id) || paras::Pallet::<T>::lifecycle(id).is_some()
601 }
602
603 fn register(
604 manager: T::AccountId,
605 para_id: u32,
606 genesis_head: Vec<u8>,
607 validation_code: Vec<u8>,
608 ) -> DispatchResult {
609 Self::do_register(
610 manager,
611 Some(BalanceOf::<T>::zero()),
612 ParaId::from(para_id),
613 HeadData(genesis_head),
614 ValidationCode(validation_code),
615 false,
616 )
617 }
618
619 fn deregister(para_id: u32) -> DispatchResult {
621 let id = ParaId::from(para_id);
622 with_storage_layer(|| {
624 if matches!(
626 paras::Pallet::<T>::lifecycle(id),
627 Some(ParaLifecycle::OffboardingParathread) |
628 Some(ParaLifecycle::OffboardingParachain)
629 ) {
630 return Ok(());
631 }
632 Self::do_deregister(id)
633 })
634 }
635
636 #[cfg(feature = "runtime-benchmarks")]
637 fn setup_worst_case_deregister(para_id: u32) -> DispatchResult {
638 use frame_system::RawOrigin;
639 use polkadot_runtime_parachains::shared;
640
641 const OTHER_PARAS: u32 = 1024;
643
644 let code = Self::worst_validation_code();
645 <Self as registrar_primitives::ParachainRegistrar>::register(
646 frame_benchmarking::account("manager", 0, 0),
647 para_id,
648 Self::worst_head_data().0,
649 code.0.clone(),
650 )?;
651 paras::Pallet::<T>::add_trusted_validation_code(RawOrigin::Root.into(), code)?;
652 Self::execute_pending_transitions();
653
654 let now = frame_system::Pallet::<T>::block_number();
657 let upgrade = ValidationCode(alloc::vec![1u8; MIN_CODE_SIZE as usize]);
658 paras::Pallet::<T>::add_trusted_validation_code(RawOrigin::Root.into(), upgrade.clone())?;
659 paras::Pallet::<T>::force_schedule_code_upgrade(
660 RawOrigin::Root.into(),
661 ParaId::from(para_id),
662 upgrade,
663 now,
664 )?;
665
666 let others: Vec<ParaId> =
667 (1..=OTHER_PARAS).map(|i| ParaId::from(para_id.saturating_add(i))).collect();
668 for other in &others {
669 let mut code = alloc::vec![0u8; MIN_CODE_SIZE as usize];
671 code[..4].copy_from_slice(&u32::from(*other).to_le_bytes());
672 paras::Pallet::<T>::force_schedule_code_upgrade(
673 RawOrigin::Root.into(),
674 *other,
675 ValidationCode(code),
676 now,
677 )?;
678 }
679 paras::ActionsQueue::<T>::insert(shared::Pallet::<T>::scheduled_session(), others);
680 Ok(())
681 }
682
683 fn check_head_data(head_len: u32) -> Result<(), ()> {
684 let max = configuration::ActiveConfig::<T>::get().max_head_data_size;
685 if head_len <= max {
686 Ok(())
687 } else {
688 Err(())
689 }
690 }
691
692 fn set_current_head(para_id: u32, head: Vec<u8>) {
693 polkadot_runtime_parachains::set_current_head::<T>(ParaId::from(para_id), HeadData(head));
694 }
695
696 fn check_code_upgrade(_para_id: u32, _code_len: u32) -> Result<(), ()> {
697 Err(())
699 }
700
701 fn schedule_code_upgrade(_para_id: u32, _validation_code: Vec<u8>) -> DispatchResult {
702 Err(Error::<T>::NotImplemented.into())
704 }
705}
706
707impl<T: Config> Pallet<T> {
708 fn ensure_root_para_or_owner(
711 origin: <T as frame_system::Config>::RuntimeOrigin,
712 id: ParaId,
713 ) -> DispatchResult {
714 if let Ok(who) = ensure_signed(origin.clone()) {
715 let para_info = Paras::<T>::get(id).ok_or(Error::<T>::NotRegistered)?;
716
717 if para_info.manager == who {
718 ensure!(!para_info.is_locked(), Error::<T>::ParaLocked);
719 return Ok(());
720 }
721 }
722
723 Self::ensure_root_or_para(origin, id)
724 }
725
726 fn ensure_root_or_para(
728 origin: <T as frame_system::Config>::RuntimeOrigin,
729 id: ParaId,
730 ) -> DispatchResult {
731 if ensure_root(origin.clone()).is_ok() {
732 return Ok(());
733 }
734
735 let caller_id = ensure_parachain(<T as Config>::RuntimeOrigin::from(origin))?;
736 ensure!(caller_id == id, Error::<T>::NotOwner);
738
739 Ok(())
740 }
741
742 fn do_reserve(
743 who: T::AccountId,
744 deposit_override: Option<BalanceOf<T>>,
745 id: ParaId,
746 ) -> DispatchResult {
747 ensure!(!Paras::<T>::contains_key(id), Error::<T>::AlreadyRegistered);
748 ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
749
750 let deposit = deposit_override.unwrap_or_else(T::ParaDeposit::get);
751 <T as Config>::Currency::reserve(&who, deposit)?;
752 let info = ParaInfo { manager: who.clone(), deposit, locked: None };
753
754 Paras::<T>::insert(id, info);
755 Self::deposit_event(Event::<T>::Reserved { para_id: id, who });
756 Ok(())
757 }
758
759 fn do_register(
762 who: T::AccountId,
763 deposit_override: Option<BalanceOf<T>>,
764 id: ParaId,
765 genesis_head: HeadData,
766 validation_code: ValidationCode,
767 ensure_reserved: bool,
768 ) -> DispatchResult {
769 let deposited = if let Some(para_data) = Paras::<T>::get(id) {
770 ensure!(para_data.manager == who, Error::<T>::NotOwner);
771 ensure!(!para_data.is_locked(), Error::<T>::ParaLocked);
772 para_data.deposit
773 } else {
774 ensure!(!ensure_reserved, Error::<T>::NotReserved);
775 Default::default()
776 };
777 ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
778 let (genesis, deposit) =
779 Self::validate_onboarding_data(genesis_head, validation_code, ParaKind::Parathread)?;
780 let deposit = deposit_override.unwrap_or(deposit);
781
782 if let Some(additional) = deposit.checked_sub(&deposited) {
783 <T as Config>::Currency::reserve(&who, additional)?;
784 } else if let Some(rebate) = deposited.checked_sub(&deposit) {
785 <T as Config>::Currency::unreserve(&who, rebate);
786 };
787 let info = ParaInfo { manager: who.clone(), deposit, locked: None };
788
789 Paras::<T>::insert(id, info);
790 let res = polkadot_runtime_parachains::schedule_para_initialize::<T>(id, genesis);
792 debug_assert!(res.is_ok());
793 Self::deposit_event(Event::<T>::Registered { para_id: id, manager: who });
794 Ok(())
795 }
796
797 fn do_deregister(id: ParaId) -> DispatchResult {
799 match paras::Pallet::<T>::lifecycle(id) {
800 Some(ParaLifecycle::Parathread) | None => {},
802 _ => return Err(Error::<T>::NotParathread.into()),
803 }
804 polkadot_runtime_parachains::schedule_para_cleanup::<T>(id)
805 .map_err(|_| Error::<T>::CannotDeregister)?;
806
807 if let Some(info) = Paras::<T>::take(&id) {
808 <T as Config>::Currency::unreserve(&info.manager, info.deposit);
809 }
810
811 PendingSwap::<T>::remove(id);
812 Self::deposit_event(Event::<T>::Deregistered { para_id: id });
813 Ok(())
814 }
815
816 fn validate_onboarding_data(
820 genesis_head: HeadData,
821 validation_code: ValidationCode,
822 para_kind: ParaKind,
823 ) -> Result<(ParaGenesisArgs, BalanceOf<T>), sp_runtime::DispatchError> {
824 let config = configuration::ActiveConfig::<T>::get();
825 Self::validate_onboarding_sizes(&config, genesis_head.0.len(), validation_code.0.len())?;
826
827 let per_byte_fee = T::DataDepositPerByte::get();
828 let deposit = T::ParaDeposit::get()
829 .saturating_add(per_byte_fee.saturating_mul((genesis_head.0.len() as u32).into()))
830 .saturating_add(per_byte_fee.saturating_mul(config.max_code_size.into()));
831
832 Ok((ParaGenesisArgs { genesis_head, validation_code, para_kind }, deposit))
833 }
834
835 fn validate_onboarding_sizes(
837 config: &configuration::HostConfiguration<BlockNumberFor<T>>,
838 head_len: usize,
839 code_len: usize,
840 ) -> DispatchResult {
841 ensure!(code_len >= MIN_CODE_SIZE as usize, Error::<T>::InvalidCode);
842 ensure!(code_len <= config.max_code_size as usize, Error::<T>::CodeTooLarge);
843 ensure!(head_len <= config.max_head_data_size as usize, Error::<T>::HeadDataTooLarge);
844 Ok(())
845 }
846
847 fn do_thread_and_chain_swap(to_downgrade: ParaId, to_upgrade: ParaId) {
850 let res1 = polkadot_runtime_parachains::schedule_parachain_downgrade::<T>(to_downgrade);
851 debug_assert!(res1.is_ok());
852 let res2 = polkadot_runtime_parachains::schedule_parathread_upgrade::<T>(to_upgrade);
853 debug_assert!(res2.is_ok());
854 T::OnSwap::on_swap(to_upgrade, to_downgrade);
855 }
856}
857
858impl<T: Config> OnNewHead for Pallet<T> {
859 fn on_new_head(id: ParaId, _head: &HeadData) -> Weight {
860 let mut writes = 0;
862 if let Some(mut info) = Paras::<T>::get(id) {
863 if info.locked.is_none() {
864 info.locked = Some(true);
865 Paras::<T>::insert(id, info);
866 writes += 1;
867 }
868 }
869 T::DbWeight::get().reads_writes(1, writes)
870 }
871}
872
873#[cfg(test)]
874mod mock;
875
876#[cfg(test)]
877mod tests;
878
879#[cfg(feature = "runtime-benchmarks")]
880mod benchmarking;