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)]
295 #[pallet::weight(<T as Config>::WeightInfo::force_register())]
296 pub fn force_register(
297 origin: OriginFor<T>,
298 who: T::AccountId,
299 deposit: BalanceOf<T>,
300 id: ParaId,
301 genesis_head: HeadData,
302 validation_code: ValidationCode,
303 ) -> DispatchResult {
304 ensure_root(origin)?;
305 Self::do_register(who, Some(deposit), id, genesis_head, validation_code, false)
306 }
307
308 #[pallet::call_index(2)]
313 #[pallet::weight(<T as Config>::WeightInfo::deregister())]
314 pub fn deregister(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
315 Self::ensure_root_para_or_owner(origin, id)?;
316 Self::do_deregister(id)
317 }
318
319 #[pallet::call_index(3)]
332 #[pallet::weight(<T as Config>::WeightInfo::swap())]
333 pub fn swap(origin: OriginFor<T>, id: ParaId, other: ParaId) -> DispatchResult {
334 Self::ensure_root_para_or_owner(origin, id)?;
335
336 if id == other {
339 PendingSwap::<T>::remove(id);
340 return Ok(());
341 }
342
343 let id_lifecycle =
345 paras::Pallet::<T>::lifecycle(id).ok_or(Error::<T>::NotRegistered)?;
346
347 if PendingSwap::<T>::get(other) == Some(id) {
348 let other_lifecycle =
349 paras::Pallet::<T>::lifecycle(other).ok_or(Error::<T>::NotRegistered)?;
350 if id_lifecycle == ParaLifecycle::Parachain &&
353 other_lifecycle == ParaLifecycle::Parathread
354 {
355 Self::do_thread_and_chain_swap(id, other);
356 } else if id_lifecycle == ParaLifecycle::Parathread &&
357 other_lifecycle == ParaLifecycle::Parachain
358 {
359 Self::do_thread_and_chain_swap(other, id);
360 } else if id_lifecycle == ParaLifecycle::Parachain &&
361 other_lifecycle == ParaLifecycle::Parachain
362 {
363 T::OnSwap::on_swap(id, other);
367 } else {
368 return Err(Error::<T>::CannotSwap.into());
369 }
370 Self::deposit_event(Event::<T>::Swapped { para_id: id, other_id: other });
371 PendingSwap::<T>::remove(other);
372 } else {
373 PendingSwap::<T>::insert(id, other);
374 }
375
376 Ok(())
377 }
378
379 #[pallet::call_index(4)]
384 #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
385 pub fn remove_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
386 Self::ensure_root_or_para(origin, para)?;
387 <Self as Registrar>::remove_lock(para);
388 Ok(())
389 }
390
391 #[pallet::call_index(5)]
409 #[pallet::weight(<T as Config>::WeightInfo::reserve())]
410 pub fn reserve(origin: OriginFor<T>) -> DispatchResult {
411 let who = ensure_signed(origin)?;
412 let id = NextFreeParaId::<T>::get().max(LOWEST_PUBLIC_ID);
413 Self::do_reserve(who, None, id)?;
414 NextFreeParaId::<T>::set(id + 1);
415 Ok(())
416 }
417
418 #[pallet::call_index(6)]
424 #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
425 pub fn add_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
426 Self::ensure_root_para_or_owner(origin, para)?;
427 <Self as Registrar>::apply_lock(para);
428 Ok(())
429 }
430
431 #[pallet::call_index(7)]
443 #[pallet::weight(<T as Config>::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))]
444 pub fn schedule_code_upgrade(
445 origin: OriginFor<T>,
446 para: ParaId,
447 new_code: ValidationCode,
448 ) -> DispatchResult {
449 Self::ensure_root_para_or_owner(origin, para)?;
450 polkadot_runtime_parachains::schedule_code_upgrade::<T>(
451 para,
452 new_code,
453 UpgradeStrategy::ApplyAtExpectedBlock,
454 )?;
455 Ok(())
456 }
457
458 #[pallet::call_index(8)]
463 #[pallet::weight(<T as Config>::WeightInfo::set_current_head(new_head.0.len() as u32))]
464 pub fn set_current_head(
465 origin: OriginFor<T>,
466 para: ParaId,
467 new_head: HeadData,
468 ) -> DispatchResult {
469 Self::ensure_root_para_or_owner(origin, para)?;
470 polkadot_runtime_parachains::set_current_head::<T>(para, new_head);
471 Ok(())
472 }
473 }
474}
475
476impl<T: Config> Registrar for Pallet<T> {
477 type AccountId = T::AccountId;
478
479 fn manager_of(id: ParaId) -> Option<T::AccountId> {
481 Some(Paras::<T>::get(id)?.manager)
482 }
483
484 fn parachains() -> Vec<ParaId> {
487 paras::Parachains::<T>::get()
488 }
489
490 fn is_parathread(id: ParaId) -> bool {
492 paras::Pallet::<T>::is_parathread(id)
493 }
494
495 fn is_parachain(id: ParaId) -> bool {
497 paras::Pallet::<T>::is_parachain(id)
498 }
499
500 fn apply_lock(id: ParaId) {
502 Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(true)));
503 }
504
505 fn remove_lock(id: ParaId) {
507 Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(false)));
508 }
509
510 fn register(
515 manager: T::AccountId,
516 id: ParaId,
517 genesis_head: HeadData,
518 validation_code: ValidationCode,
519 ) -> DispatchResult {
520 Self::do_register(manager, None, id, genesis_head, validation_code, false)
521 }
522
523 fn deregister(id: ParaId) -> DispatchResult {
525 Self::do_deregister(id)
526 }
527
528 fn make_parachain(id: ParaId) -> DispatchResult {
530 ensure!(
532 paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parathread),
533 Error::<T>::NotParathread
534 );
535 polkadot_runtime_parachains::schedule_parathread_upgrade::<T>(id)
536 .map_err(|_| Error::<T>::CannotUpgrade)?;
537
538 Ok(())
539 }
540
541 fn make_parathread(id: ParaId) -> DispatchResult {
543 ensure!(
545 paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parachain),
546 Error::<T>::NotParachain
547 );
548 polkadot_runtime_parachains::schedule_parachain_downgrade::<T>(id)
549 .map_err(|_| Error::<T>::CannotDowngrade)?;
550 Ok(())
551 }
552
553 #[cfg(any(feature = "runtime-benchmarks", test))]
554 fn worst_head_data() -> HeadData {
555 let max_head_size = configuration::ActiveConfig::<T>::get().max_head_data_size;
556 assert!(max_head_size > 0, "max_head_data can't be zero for generating worst head data.");
557 alloc::vec![0u8; max_head_size as usize].into()
558 }
559
560 #[cfg(any(feature = "runtime-benchmarks", test))]
561 fn worst_validation_code() -> ValidationCode {
562 let max_code_size = configuration::ActiveConfig::<T>::get().max_code_size;
563 assert!(max_code_size > 0, "max_code_size can't be zero for generating worst code data.");
564 let validation_code = alloc::vec![0u8; max_code_size as usize];
565 validation_code.into()
566 }
567
568 #[cfg(any(feature = "runtime-benchmarks", test))]
569 fn execute_pending_transitions() {
570 use polkadot_runtime_parachains::shared;
571 shared::Pallet::<T>::set_session_index(shared::Pallet::<T>::scheduled_session());
572 paras::Pallet::<T>::test_on_new_session();
573 }
574}
575
576impl<T: Config> registrar_primitives::ParachainRegistrar for Pallet<T> {
587 type AccountId = T::AccountId;
588
589 fn check_onboarding(head_len: u32, code_len: u32) -> Result<(), ()> {
590 let config = configuration::ActiveConfig::<T>::get();
591 Self::validate_onboarding_sizes(&config, head_len as usize, code_len as usize)
592 .map_err(|_| ())
593 }
594
595 fn is_registered(para_id: u32) -> bool {
596 let id = ParaId::from(para_id);
597 Paras::<T>::contains_key(id) || paras::Pallet::<T>::lifecycle(id).is_some()
598 }
599
600 fn register(
601 manager: T::AccountId,
602 para_id: u32,
603 genesis_head: Vec<u8>,
604 validation_code: Vec<u8>,
605 ) -> DispatchResult {
606 Self::do_register(
607 manager,
608 Some(BalanceOf::<T>::zero()),
609 ParaId::from(para_id),
610 HeadData(genesis_head),
611 ValidationCode(validation_code),
612 false,
613 )
614 }
615
616 fn deregister(para_id: u32) -> DispatchResult {
618 let id = ParaId::from(para_id);
619 with_storage_layer(|| {
621 if matches!(
623 paras::Pallet::<T>::lifecycle(id),
624 Some(ParaLifecycle::OffboardingParathread) |
625 Some(ParaLifecycle::OffboardingParachain)
626 ) {
627 return Ok(());
628 }
629 Self::do_deregister(id)
630 })
631 }
632
633 #[cfg(feature = "runtime-benchmarks")]
634 fn setup_worst_case_deregister(para_id: u32) -> DispatchResult {
635 use frame_system::RawOrigin;
636 use polkadot_runtime_parachains::shared;
637
638 const OTHER_PARAS: u32 = 1024;
640
641 let code = Self::worst_validation_code();
642 <Self as registrar_primitives::ParachainRegistrar>::register(
643 frame_benchmarking::account("manager", 0, 0),
644 para_id,
645 Self::worst_head_data().0,
646 code.0.clone(),
647 )?;
648 paras::Pallet::<T>::add_trusted_validation_code(RawOrigin::Root.into(), code)?;
649 Self::execute_pending_transitions();
650
651 let now = frame_system::Pallet::<T>::block_number();
654 let upgrade = ValidationCode(alloc::vec![1u8; MIN_CODE_SIZE as usize]);
655 paras::Pallet::<T>::add_trusted_validation_code(RawOrigin::Root.into(), upgrade.clone())?;
656 paras::Pallet::<T>::force_schedule_code_upgrade(
657 RawOrigin::Root.into(),
658 ParaId::from(para_id),
659 upgrade,
660 now,
661 )?;
662
663 let others: Vec<ParaId> =
664 (1..=OTHER_PARAS).map(|i| ParaId::from(para_id.saturating_add(i))).collect();
665 for other in &others {
666 let mut code = alloc::vec![0u8; MIN_CODE_SIZE as usize];
668 code[..4].copy_from_slice(&u32::from(*other).to_le_bytes());
669 paras::Pallet::<T>::force_schedule_code_upgrade(
670 RawOrigin::Root.into(),
671 *other,
672 ValidationCode(code),
673 now,
674 )?;
675 }
676 paras::ActionsQueue::<T>::insert(shared::Pallet::<T>::scheduled_session(), others);
677 Ok(())
678 }
679
680 fn check_head_data(_head_len: u32) -> Result<(), ()> {
681 Err(())
683 }
684
685 fn set_current_head(_para_id: u32, _head: Vec<u8>) {
686 }
688
689 fn check_code_upgrade(_para_id: u32, _code_len: u32) -> Result<(), ()> {
690 Err(())
692 }
693
694 fn schedule_code_upgrade(_para_id: u32, _validation_code: Vec<u8>) -> DispatchResult {
695 Err(Error::<T>::NotImplemented.into())
697 }
698}
699
700impl<T: Config> Pallet<T> {
701 fn ensure_root_para_or_owner(
704 origin: <T as frame_system::Config>::RuntimeOrigin,
705 id: ParaId,
706 ) -> DispatchResult {
707 if let Ok(who) = ensure_signed(origin.clone()) {
708 let para_info = Paras::<T>::get(id).ok_or(Error::<T>::NotRegistered)?;
709
710 if para_info.manager == who {
711 ensure!(!para_info.is_locked(), Error::<T>::ParaLocked);
712 return Ok(());
713 }
714 }
715
716 Self::ensure_root_or_para(origin, id)
717 }
718
719 fn ensure_root_or_para(
721 origin: <T as frame_system::Config>::RuntimeOrigin,
722 id: ParaId,
723 ) -> DispatchResult {
724 if ensure_root(origin.clone()).is_ok() {
725 return Ok(());
726 }
727
728 let caller_id = ensure_parachain(<T as Config>::RuntimeOrigin::from(origin))?;
729 ensure!(caller_id == id, Error::<T>::NotOwner);
731
732 Ok(())
733 }
734
735 fn do_reserve(
736 who: T::AccountId,
737 deposit_override: Option<BalanceOf<T>>,
738 id: ParaId,
739 ) -> DispatchResult {
740 ensure!(!Paras::<T>::contains_key(id), Error::<T>::AlreadyRegistered);
741 ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
742
743 let deposit = deposit_override.unwrap_or_else(T::ParaDeposit::get);
744 <T as Config>::Currency::reserve(&who, deposit)?;
745 let info = ParaInfo { manager: who.clone(), deposit, locked: None };
746
747 Paras::<T>::insert(id, info);
748 Self::deposit_event(Event::<T>::Reserved { para_id: id, who });
749 Ok(())
750 }
751
752 fn do_register(
755 who: T::AccountId,
756 deposit_override: Option<BalanceOf<T>>,
757 id: ParaId,
758 genesis_head: HeadData,
759 validation_code: ValidationCode,
760 ensure_reserved: bool,
761 ) -> DispatchResult {
762 let deposited = if let Some(para_data) = Paras::<T>::get(id) {
763 ensure!(para_data.manager == who, Error::<T>::NotOwner);
764 ensure!(!para_data.is_locked(), Error::<T>::ParaLocked);
765 para_data.deposit
766 } else {
767 ensure!(!ensure_reserved, Error::<T>::NotReserved);
768 Default::default()
769 };
770 ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
771 let (genesis, deposit) =
772 Self::validate_onboarding_data(genesis_head, validation_code, ParaKind::Parathread)?;
773 let deposit = deposit_override.unwrap_or(deposit);
774
775 if let Some(additional) = deposit.checked_sub(&deposited) {
776 <T as Config>::Currency::reserve(&who, additional)?;
777 } else if let Some(rebate) = deposited.checked_sub(&deposit) {
778 <T as Config>::Currency::unreserve(&who, rebate);
779 };
780 let info = ParaInfo { manager: who.clone(), deposit, locked: None };
781
782 Paras::<T>::insert(id, info);
783 let res = polkadot_runtime_parachains::schedule_para_initialize::<T>(id, genesis);
785 debug_assert!(res.is_ok());
786 Self::deposit_event(Event::<T>::Registered { para_id: id, manager: who });
787 Ok(())
788 }
789
790 fn do_deregister(id: ParaId) -> DispatchResult {
792 match paras::Pallet::<T>::lifecycle(id) {
793 Some(ParaLifecycle::Parathread) | None => {},
795 _ => return Err(Error::<T>::NotParathread.into()),
796 }
797 polkadot_runtime_parachains::schedule_para_cleanup::<T>(id)
798 .map_err(|_| Error::<T>::CannotDeregister)?;
799
800 if let Some(info) = Paras::<T>::take(&id) {
801 <T as Config>::Currency::unreserve(&info.manager, info.deposit);
802 }
803
804 PendingSwap::<T>::remove(id);
805 Self::deposit_event(Event::<T>::Deregistered { para_id: id });
806 Ok(())
807 }
808
809 fn validate_onboarding_data(
813 genesis_head: HeadData,
814 validation_code: ValidationCode,
815 para_kind: ParaKind,
816 ) -> Result<(ParaGenesisArgs, BalanceOf<T>), sp_runtime::DispatchError> {
817 let config = configuration::ActiveConfig::<T>::get();
818 Self::validate_onboarding_sizes(&config, genesis_head.0.len(), validation_code.0.len())?;
819
820 let per_byte_fee = T::DataDepositPerByte::get();
821 let deposit = T::ParaDeposit::get()
822 .saturating_add(per_byte_fee.saturating_mul((genesis_head.0.len() as u32).into()))
823 .saturating_add(per_byte_fee.saturating_mul(config.max_code_size.into()));
824
825 Ok((ParaGenesisArgs { genesis_head, validation_code, para_kind }, deposit))
826 }
827
828 fn validate_onboarding_sizes(
830 config: &configuration::HostConfiguration<BlockNumberFor<T>>,
831 head_len: usize,
832 code_len: usize,
833 ) -> DispatchResult {
834 ensure!(code_len >= MIN_CODE_SIZE as usize, Error::<T>::InvalidCode);
835 ensure!(code_len <= config.max_code_size as usize, Error::<T>::CodeTooLarge);
836 ensure!(head_len <= config.max_head_data_size as usize, Error::<T>::HeadDataTooLarge);
837 Ok(())
838 }
839
840 fn do_thread_and_chain_swap(to_downgrade: ParaId, to_upgrade: ParaId) {
843 let res1 = polkadot_runtime_parachains::schedule_parachain_downgrade::<T>(to_downgrade);
844 debug_assert!(res1.is_ok());
845 let res2 = polkadot_runtime_parachains::schedule_parathread_upgrade::<T>(to_upgrade);
846 debug_assert!(res2.is_ok());
847 T::OnSwap::on_swap(to_upgrade, to_downgrade);
848 }
849}
850
851impl<T: Config> OnNewHead for Pallet<T> {
852 fn on_new_head(id: ParaId, _head: &HeadData) -> Weight {
853 let mut writes = 0;
855 if let Some(mut info) = Paras::<T>::get(id) {
856 if info.locked.is_none() {
857 info.locked = Some(true);
858 Paras::<T>::insert(id, info);
859 writes += 1;
860 }
861 }
862 T::DbWeight::get().reads_writes(1, writes)
863 }
864}
865
866#[cfg(test)]
867mod mock;
868
869#[cfg(test)]
870mod tests;
871
872#[cfg(feature = "runtime-benchmarks")]
873mod benchmarking;