referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_common/paras_registrar/
mod.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Pallet to handle parachain registration and related fund management.
18//! In essence this is a simple wrapper around `paras`.
19
20pub mod migration;
21
22use alloc::{vec, vec::Vec};
23use core::result;
24use frame_support::{
25	dispatch::DispatchResult,
26	ensure,
27	pallet_prelude::Weight,
28	traits::{Currency, Get, ReservableCurrency},
29};
30use frame_system::{self, ensure_root, ensure_signed};
31use polkadot_primitives::{
32	HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID, MIN_CODE_SIZE,
33};
34use polkadot_runtime_parachains::{
35	configuration, ensure_parachain,
36	paras::{self, ParaGenesisArgs, UpgradeStrategy},
37	Origin, ParaLifecycle,
38};
39
40use crate::traits::{OnSwap, Registrar};
41use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
42pub use pallet::*;
43use polkadot_runtime_parachains::paras::{OnNewHead, ParaKind};
44use scale_info::TypeInfo;
45use sp_runtime::{
46	traits::{CheckedSub, Saturating},
47	RuntimeDebug,
48};
49
50#[derive(
51	Encode,
52	Decode,
53	Clone,
54	PartialEq,
55	Eq,
56	Default,
57	RuntimeDebug,
58	TypeInfo,
59	MaxEncodedLen,
60	DecodeWithMemTracking,
61)]
62pub struct ParaInfo<Account, Balance> {
63	/// The account that has placed a deposit for registering this para.
64	pub manager: Account,
65	/// The amount reserved by the `manager` account for the registration.
66	pub deposit: Balance,
67	/// Whether the para registration should be locked from being controlled by the manager.
68	/// None means the lock had not been explicitly set, and should be treated as false.
69	pub locked: Option<bool>,
70}
71
72impl<Account, Balance> ParaInfo<Account, Balance> {
73	/// Returns if the para is locked.
74	pub fn is_locked(&self) -> bool {
75		self.locked.unwrap_or(false)
76	}
77}
78
79type BalanceOf<T> =
80	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
81
82pub trait WeightInfo {
83	fn reserve() -> Weight;
84	fn register() -> Weight;
85	fn force_register() -> Weight;
86	fn deregister() -> Weight;
87	fn swap() -> Weight;
88	fn schedule_code_upgrade(b: u32) -> Weight;
89	fn set_current_head(b: u32) -> Weight;
90}
91
92pub struct TestWeightInfo;
93impl WeightInfo for TestWeightInfo {
94	fn reserve() -> Weight {
95		Weight::zero()
96	}
97	fn register() -> Weight {
98		Weight::zero()
99	}
100	fn force_register() -> Weight {
101		Weight::zero()
102	}
103	fn deregister() -> Weight {
104		Weight::zero()
105	}
106	fn swap() -> Weight {
107		Weight::zero()
108	}
109	fn schedule_code_upgrade(_b: u32) -> Weight {
110		Weight::zero()
111	}
112	fn set_current_head(_b: u32) -> Weight {
113		Weight::zero()
114	}
115}
116
117#[frame_support::pallet]
118pub mod pallet {
119	use super::*;
120	use frame_support::pallet_prelude::*;
121	use frame_system::pallet_prelude::*;
122
123	/// The in-code storage version.
124	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
125
126	#[pallet::pallet]
127	#[pallet::without_storage_info]
128	#[pallet::storage_version(STORAGE_VERSION)]
129	pub struct Pallet<T>(_);
130
131	#[pallet::config]
132	#[pallet::disable_frame_system_supertrait_check]
133	pub trait Config: configuration::Config + paras::Config {
134		/// The overarching event type.
135		#[allow(deprecated)]
136		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
137
138		/// The aggregated origin type must support the `parachains` origin. We require that we can
139		/// infallibly convert between this origin and the system origin, but in reality, they're
140		/// the same type, we just can't express that to the Rust type system without writing a
141		/// `where` clause everywhere.
142		type RuntimeOrigin: From<<Self as frame_system::Config>::RuntimeOrigin>
143			+ Into<result::Result<Origin, <Self as Config>::RuntimeOrigin>>;
144
145		/// The system's currency for on-demand parachain payment.
146		type Currency: ReservableCurrency<Self::AccountId>;
147
148		/// Runtime hook for when a lease holding parachain and on-demand parachain swap.
149		type OnSwap: crate::traits::OnSwap;
150
151		/// The deposit to be paid to run a on-demand parachain.
152		/// This should include the cost for storing the genesis head and validation code.
153		#[pallet::constant]
154		type ParaDeposit: Get<BalanceOf<Self>>;
155
156		/// The deposit to be paid per byte stored on chain.
157		#[pallet::constant]
158		type DataDepositPerByte: Get<BalanceOf<Self>>;
159
160		/// Weight Information for the Extrinsics in the Pallet
161		type WeightInfo: WeightInfo;
162	}
163
164	#[pallet::event]
165	#[pallet::generate_deposit(pub(super) fn deposit_event)]
166	pub enum Event<T: Config> {
167		Registered { para_id: ParaId, manager: T::AccountId },
168		Deregistered { para_id: ParaId },
169		Reserved { para_id: ParaId, who: T::AccountId },
170		Swapped { para_id: ParaId, other_id: ParaId },
171	}
172
173	#[pallet::error]
174	pub enum Error<T> {
175		/// The ID is not registered.
176		NotRegistered,
177		/// The ID is already registered.
178		AlreadyRegistered,
179		/// The caller is not the owner of this Id.
180		NotOwner,
181		/// Invalid para code size.
182		CodeTooLarge,
183		/// Invalid para head data size.
184		HeadDataTooLarge,
185		/// Para is not a Parachain.
186		NotParachain,
187		/// Para is not a Parathread (on-demand parachain).
188		NotParathread,
189		/// Cannot deregister para
190		CannotDeregister,
191		/// Cannot schedule downgrade of lease holding parachain to on-demand parachain
192		CannotDowngrade,
193		/// Cannot schedule upgrade of on-demand parachain to lease holding parachain
194		CannotUpgrade,
195		/// Para is locked from manipulation by the manager. Must use parachain or relay chain
196		/// governance.
197		ParaLocked,
198		/// The ID given for registration has not been reserved.
199		NotReserved,
200		/// The validation code is invalid.
201		InvalidCode,
202		/// Cannot perform a parachain slot / lifecycle swap. Check that the state of both paras
203		/// are correct for the swap to work.
204		CannotSwap,
205	}
206
207	/// Pending swap operations.
208	#[pallet::storage]
209	pub(super) type PendingSwap<T> = StorageMap<_, Twox64Concat, ParaId, ParaId>;
210
211	/// Amount held on deposit for each para and the original depositor.
212	///
213	/// The given account ID is responsible for registering the code and initial head data, but may
214	/// only do so if it isn't yet registered. (After that, it's up to governance to do so.)
215	#[pallet::storage]
216	pub type Paras<T: Config> =
217		StorageMap<_, Twox64Concat, ParaId, ParaInfo<T::AccountId, BalanceOf<T>>>;
218
219	/// The next free `ParaId`.
220	#[pallet::storage]
221	pub type NextFreeParaId<T> = StorageValue<_, ParaId, ValueQuery>;
222
223	#[pallet::genesis_config]
224	pub struct GenesisConfig<T: Config> {
225		#[serde(skip)]
226		pub _config: core::marker::PhantomData<T>,
227		pub next_free_para_id: ParaId,
228	}
229
230	impl<T: Config> Default for GenesisConfig<T> {
231		fn default() -> Self {
232			GenesisConfig { next_free_para_id: LOWEST_PUBLIC_ID, _config: Default::default() }
233		}
234	}
235
236	#[pallet::genesis_build]
237	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
238		fn build(&self) {
239			NextFreeParaId::<T>::put(self.next_free_para_id);
240		}
241	}
242
243	#[pallet::hooks]
244	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
245
246	#[pallet::call]
247	impl<T: Config> Pallet<T> {
248		/// Register head data and validation code for a reserved Para Id.
249		///
250		/// ## Arguments
251		/// - `origin`: Must be called by a `Signed` origin.
252		/// - `id`: The para ID. Must be owned/managed by the `origin` signing account.
253		/// - `genesis_head`: The genesis head data of the parachain/thread.
254		/// - `validation_code`: The initial validation code of the parachain/thread.
255		///
256		/// ## Deposits/Fees
257		/// The account with the originating signature must reserve a deposit.
258		///
259		/// The deposit is required to cover the costs associated with storing the genesis head
260		/// data and the validation code.
261		/// This accounts for the potential to store validation code of a size up to the
262		/// `max_code_size`, as defined in the configuration pallet
263		///
264		/// Anything already reserved previously for this para ID is accounted for.
265		///
266		/// ## Events
267		/// The `Registered` event is emitted in case of success.
268		#[pallet::call_index(0)]
269		#[pallet::weight(<T as Config>::WeightInfo::register())]
270		pub fn register(
271			origin: OriginFor<T>,
272			id: ParaId,
273			genesis_head: HeadData,
274			validation_code: ValidationCode,
275		) -> DispatchResult {
276			let who = ensure_signed(origin)?;
277			Self::do_register(who, None, id, genesis_head, validation_code, true)?;
278			Ok(())
279		}
280
281		/// Force the registration of a Para Id on the relay chain.
282		///
283		/// This function must be called by a Root origin.
284		///
285		/// The deposit taken can be specified for this registration. Any `ParaId`
286		/// can be registered, including sub-1000 IDs which are System Parachains.
287		#[pallet::call_index(1)]
288		#[pallet::weight(<T as Config>::WeightInfo::force_register())]
289		pub fn force_register(
290			origin: OriginFor<T>,
291			who: T::AccountId,
292			deposit: BalanceOf<T>,
293			id: ParaId,
294			genesis_head: HeadData,
295			validation_code: ValidationCode,
296		) -> DispatchResult {
297			ensure_root(origin)?;
298			Self::do_register(who, Some(deposit), id, genesis_head, validation_code, false)
299		}
300
301		/// Deregister a Para Id, freeing all data and returning any deposit.
302		///
303		/// The caller must be Root, the `para` owner, or the `para` itself. The para must be an
304		/// on-demand parachain.
305		#[pallet::call_index(2)]
306		#[pallet::weight(<T as Config>::WeightInfo::deregister())]
307		pub fn deregister(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
308			Self::ensure_root_para_or_owner(origin, id)?;
309			Self::do_deregister(id)
310		}
311
312		/// Swap a lease holding parachain with another parachain, either on-demand or lease
313		/// holding.
314		///
315		/// The origin must be Root, the `para` owner, or the `para` itself.
316		///
317		/// The swap will happen only if there is already an opposite swap pending. If there is not,
318		/// the swap will be stored in the pending swaps map, ready for a later confirmatory swap.
319		///
320		/// The `ParaId`s remain mapped to the same head data and code so external code can rely on
321		/// `ParaId` to be a long-term identifier of a notional "parachain". However, their
322		/// scheduling info (i.e. whether they're an on-demand parachain or lease holding
323		/// parachain), auction information and the auction deposit are switched.
324		#[pallet::call_index(3)]
325		#[pallet::weight(<T as Config>::WeightInfo::swap())]
326		pub fn swap(origin: OriginFor<T>, id: ParaId, other: ParaId) -> DispatchResult {
327			Self::ensure_root_para_or_owner(origin, id)?;
328
329			// If `id` and `other` is the same id, we treat this as a "clear" function, and exit
330			// early, since swapping the same id would otherwise be a noop.
331			if id == other {
332				PendingSwap::<T>::remove(id);
333				return Ok(());
334			}
335
336			// Sanity check that `id` is even a para.
337			let id_lifecycle =
338				paras::Pallet::<T>::lifecycle(id).ok_or(Error::<T>::NotRegistered)?;
339
340			if PendingSwap::<T>::get(other) == Some(id) {
341				let other_lifecycle =
342					paras::Pallet::<T>::lifecycle(other).ok_or(Error::<T>::NotRegistered)?;
343				// identify which is a lease holding parachain and which is a parathread (on-demand
344				// parachain)
345				if id_lifecycle == ParaLifecycle::Parachain &&
346					other_lifecycle == ParaLifecycle::Parathread
347				{
348					Self::do_thread_and_chain_swap(id, other);
349				} else if id_lifecycle == ParaLifecycle::Parathread &&
350					other_lifecycle == ParaLifecycle::Parachain
351				{
352					Self::do_thread_and_chain_swap(other, id);
353				} else if id_lifecycle == ParaLifecycle::Parachain &&
354					other_lifecycle == ParaLifecycle::Parachain
355				{
356					// If both chains are currently parachains, there is nothing funny we
357					// need to do for their lifecycle management, just swap the underlying
358					// data.
359					T::OnSwap::on_swap(id, other);
360				} else {
361					return Err(Error::<T>::CannotSwap.into());
362				}
363				Self::deposit_event(Event::<T>::Swapped { para_id: id, other_id: other });
364				PendingSwap::<T>::remove(other);
365			} else {
366				PendingSwap::<T>::insert(id, other);
367			}
368
369			Ok(())
370		}
371
372		/// Remove a manager lock from a para. This will allow the manager of a
373		/// previously locked para to deregister or swap a para without using governance.
374		///
375		/// Can only be called by the Root origin or the parachain.
376		#[pallet::call_index(4)]
377		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
378		pub fn remove_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
379			Self::ensure_root_or_para(origin, para)?;
380			<Self as Registrar>::remove_lock(para);
381			Ok(())
382		}
383
384		/// Reserve a Para Id on the relay chain.
385		///
386		/// This function will reserve a new Para Id to be owned/managed by the origin account.
387		/// The origin account is able to register head data and validation code using `register` to
388		/// create an on-demand parachain. Using the Slots pallet, an on-demand parachain can then
389		/// be upgraded to a lease holding parachain.
390		///
391		/// ## Arguments
392		/// - `origin`: Must be called by a `Signed` origin. Becomes the manager/owner of the new
393		///   para ID.
394		///
395		/// ## Deposits/Fees
396		/// The origin must reserve a deposit of `ParaDeposit` for the registration.
397		///
398		/// ## Events
399		/// The `Reserved` event is emitted in case of success, which provides the ID reserved for
400		/// use.
401		#[pallet::call_index(5)]
402		#[pallet::weight(<T as Config>::WeightInfo::reserve())]
403		pub fn reserve(origin: OriginFor<T>) -> DispatchResult {
404			let who = ensure_signed(origin)?;
405			let id = NextFreeParaId::<T>::get().max(LOWEST_PUBLIC_ID);
406			Self::do_reserve(who, None, id)?;
407			NextFreeParaId::<T>::set(id + 1);
408			Ok(())
409		}
410
411		/// Add a manager lock from a para. This will prevent the manager of a
412		/// para to deregister or swap a para.
413		///
414		/// Can be called by Root, the parachain, or the parachain manager if the parachain is
415		/// unlocked.
416		#[pallet::call_index(6)]
417		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
418		pub fn add_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
419			Self::ensure_root_para_or_owner(origin, para)?;
420			<Self as Registrar>::apply_lock(para);
421			Ok(())
422		}
423
424		/// Schedule a parachain upgrade.
425		///
426		/// This will kick off a check of `new_code` by all validators. After the majority of the
427		/// validators have reported on the validity of the code, the code will either be enacted
428		/// or the upgrade will be rejected. If the code will be enacted, the current code of the
429		/// parachain will be overwritten directly. This means that any PoV will be checked by this
430		/// new code. The parachain itself will not be informed explicitly that the validation code
431		/// has changed.
432		///
433		/// Can be called by Root, the parachain, or the parachain manager if the parachain is
434		/// unlocked.
435		#[pallet::call_index(7)]
436		#[pallet::weight(<T as Config>::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))]
437		pub fn schedule_code_upgrade(
438			origin: OriginFor<T>,
439			para: ParaId,
440			new_code: ValidationCode,
441		) -> DispatchResult {
442			Self::ensure_root_para_or_owner(origin, para)?;
443			polkadot_runtime_parachains::schedule_code_upgrade::<T>(
444				para,
445				new_code,
446				UpgradeStrategy::ApplyAtExpectedBlock,
447			)?;
448			Ok(())
449		}
450
451		/// Set the parachain's current head.
452		///
453		/// Can be called by Root, the parachain, or the parachain manager if the parachain is
454		/// unlocked.
455		#[pallet::call_index(8)]
456		#[pallet::weight(<T as Config>::WeightInfo::set_current_head(new_head.0.len() as u32))]
457		pub fn set_current_head(
458			origin: OriginFor<T>,
459			para: ParaId,
460			new_head: HeadData,
461		) -> DispatchResult {
462			Self::ensure_root_para_or_owner(origin, para)?;
463			polkadot_runtime_parachains::set_current_head::<T>(para, new_head);
464			Ok(())
465		}
466	}
467}
468
469impl<T: Config> Registrar for Pallet<T> {
470	type AccountId = T::AccountId;
471
472	/// Return the manager `AccountId` of a para if one exists.
473	fn manager_of(id: ParaId) -> Option<T::AccountId> {
474		Some(Paras::<T>::get(id)?.manager)
475	}
476
477	// All lease holding parachains. Ordered ascending by ParaId. On-demand parachains are not
478	// included.
479	fn parachains() -> Vec<ParaId> {
480		paras::Parachains::<T>::get()
481	}
482
483	// Return if a para is a parathread (on-demand parachain)
484	fn is_parathread(id: ParaId) -> bool {
485		paras::Pallet::<T>::is_parathread(id)
486	}
487
488	// Return if a para is a lease holding parachain
489	fn is_parachain(id: ParaId) -> bool {
490		paras::Pallet::<T>::is_parachain(id)
491	}
492
493	// Apply a lock to the parachain.
494	fn apply_lock(id: ParaId) {
495		Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(true)));
496	}
497
498	// Remove a lock from the parachain.
499	fn remove_lock(id: ParaId) {
500		Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(false)));
501	}
502
503	// Register a Para ID under control of `manager`.
504	//
505	// Note this is a backend registration API, so verification of ParaId
506	// is not done here to prevent.
507	fn register(
508		manager: T::AccountId,
509		id: ParaId,
510		genesis_head: HeadData,
511		validation_code: ValidationCode,
512	) -> DispatchResult {
513		Self::do_register(manager, None, id, genesis_head, validation_code, false)
514	}
515
516	// Deregister a Para ID, free any data, and return any deposits.
517	fn deregister(id: ParaId) -> DispatchResult {
518		Self::do_deregister(id)
519	}
520
521	// Upgrade a registered on-demand parachain into a lease holding parachain.
522	fn make_parachain(id: ParaId) -> DispatchResult {
523		// Para backend should think this is an on-demand parachain...
524		ensure!(
525			paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parathread),
526			Error::<T>::NotParathread
527		);
528		polkadot_runtime_parachains::schedule_parathread_upgrade::<T>(id)
529			.map_err(|_| Error::<T>::CannotUpgrade)?;
530
531		Ok(())
532	}
533
534	// Downgrade a registered para into a parathread (on-demand parachain).
535	fn make_parathread(id: ParaId) -> DispatchResult {
536		// Para backend should think this is a parachain...
537		ensure!(
538			paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parachain),
539			Error::<T>::NotParachain
540		);
541		polkadot_runtime_parachains::schedule_parachain_downgrade::<T>(id)
542			.map_err(|_| Error::<T>::CannotDowngrade)?;
543		Ok(())
544	}
545
546	#[cfg(any(feature = "runtime-benchmarks", test))]
547	fn worst_head_data() -> HeadData {
548		let max_head_size = configuration::ActiveConfig::<T>::get().max_head_data_size;
549		assert!(max_head_size > 0, "max_head_data can't be zero for generating worst head data.");
550		vec![0u8; max_head_size as usize].into()
551	}
552
553	#[cfg(any(feature = "runtime-benchmarks", test))]
554	fn worst_validation_code() -> ValidationCode {
555		let max_code_size = configuration::ActiveConfig::<T>::get().max_code_size;
556		assert!(max_code_size > 0, "max_code_size can't be zero for generating worst code data.");
557		let validation_code = vec![0u8; max_code_size as usize];
558		validation_code.into()
559	}
560
561	#[cfg(any(feature = "runtime-benchmarks", test))]
562	fn execute_pending_transitions() {
563		use polkadot_runtime_parachains::shared;
564		shared::Pallet::<T>::set_session_index(shared::Pallet::<T>::scheduled_session());
565		paras::Pallet::<T>::test_on_new_session();
566	}
567}
568
569impl<T: Config> Pallet<T> {
570	/// Ensure the origin is one of Root, the `para` owner, or the `para` itself.
571	/// If the origin is the `para` owner, the `para` must be unlocked.
572	fn ensure_root_para_or_owner(
573		origin: <T as frame_system::Config>::RuntimeOrigin,
574		id: ParaId,
575	) -> DispatchResult {
576		if let Ok(who) = ensure_signed(origin.clone()) {
577			let para_info = Paras::<T>::get(id).ok_or(Error::<T>::NotRegistered)?;
578
579			if para_info.manager == who {
580				ensure!(!para_info.is_locked(), Error::<T>::ParaLocked);
581				return Ok(())
582			}
583		}
584
585		Self::ensure_root_or_para(origin, id)
586	}
587
588	/// Ensure the origin is one of Root or the `para` itself.
589	fn ensure_root_or_para(
590		origin: <T as frame_system::Config>::RuntimeOrigin,
591		id: ParaId,
592	) -> DispatchResult {
593		if ensure_root(origin.clone()).is_ok() {
594			return Ok(())
595		}
596
597		let caller_id = ensure_parachain(<T as Config>::RuntimeOrigin::from(origin))?;
598		// Check if matching para id...
599		ensure!(caller_id == id, Error::<T>::NotOwner);
600
601		Ok(())
602	}
603
604	fn do_reserve(
605		who: T::AccountId,
606		deposit_override: Option<BalanceOf<T>>,
607		id: ParaId,
608	) -> DispatchResult {
609		ensure!(!Paras::<T>::contains_key(id), Error::<T>::AlreadyRegistered);
610		ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
611
612		let deposit = deposit_override.unwrap_or_else(T::ParaDeposit::get);
613		<T as Config>::Currency::reserve(&who, deposit)?;
614		let info = ParaInfo { manager: who.clone(), deposit, locked: None };
615
616		Paras::<T>::insert(id, info);
617		Self::deposit_event(Event::<T>::Reserved { para_id: id, who });
618		Ok(())
619	}
620
621	/// Attempt to register a new Para Id under management of `who` in the
622	/// system with the given information.
623	fn do_register(
624		who: T::AccountId,
625		deposit_override: Option<BalanceOf<T>>,
626		id: ParaId,
627		genesis_head: HeadData,
628		validation_code: ValidationCode,
629		ensure_reserved: bool,
630	) -> DispatchResult {
631		let deposited = if let Some(para_data) = Paras::<T>::get(id) {
632			ensure!(para_data.manager == who, Error::<T>::NotOwner);
633			ensure!(!para_data.is_locked(), Error::<T>::ParaLocked);
634			para_data.deposit
635		} else {
636			ensure!(!ensure_reserved, Error::<T>::NotReserved);
637			Default::default()
638		};
639		ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
640		let (genesis, deposit) =
641			Self::validate_onboarding_data(genesis_head, validation_code, ParaKind::Parathread)?;
642		let deposit = deposit_override.unwrap_or(deposit);
643
644		if let Some(additional) = deposit.checked_sub(&deposited) {
645			<T as Config>::Currency::reserve(&who, additional)?;
646		} else if let Some(rebate) = deposited.checked_sub(&deposit) {
647			<T as Config>::Currency::unreserve(&who, rebate);
648		};
649		let info = ParaInfo { manager: who.clone(), deposit, locked: None };
650
651		Paras::<T>::insert(id, info);
652		// We check above that para has no lifecycle, so this should not fail.
653		let res = polkadot_runtime_parachains::schedule_para_initialize::<T>(id, genesis);
654		debug_assert!(res.is_ok());
655		Self::deposit_event(Event::<T>::Registered { para_id: id, manager: who });
656		Ok(())
657	}
658
659	/// Deregister a Para Id, freeing all data returning any deposit.
660	fn do_deregister(id: ParaId) -> DispatchResult {
661		match paras::Pallet::<T>::lifecycle(id) {
662			// Para must be a parathread (on-demand parachain), or not exist at all.
663			Some(ParaLifecycle::Parathread) | None => {},
664			_ => return Err(Error::<T>::NotParathread.into()),
665		}
666		polkadot_runtime_parachains::schedule_para_cleanup::<T>(id)
667			.map_err(|_| Error::<T>::CannotDeregister)?;
668
669		if let Some(info) = Paras::<T>::take(&id) {
670			<T as Config>::Currency::unreserve(&info.manager, info.deposit);
671		}
672
673		PendingSwap::<T>::remove(id);
674		Self::deposit_event(Event::<T>::Deregistered { para_id: id });
675		Ok(())
676	}
677
678	/// Verifies the onboarding data is valid for a para.
679	///
680	/// Returns `ParaGenesisArgs` and the deposit needed for the data.
681	fn validate_onboarding_data(
682		genesis_head: HeadData,
683		validation_code: ValidationCode,
684		para_kind: ParaKind,
685	) -> Result<(ParaGenesisArgs, BalanceOf<T>), sp_runtime::DispatchError> {
686		let config = configuration::ActiveConfig::<T>::get();
687		ensure!(validation_code.0.len() >= MIN_CODE_SIZE as usize, Error::<T>::InvalidCode);
688		ensure!(validation_code.0.len() <= config.max_code_size as usize, Error::<T>::CodeTooLarge);
689		ensure!(
690			genesis_head.0.len() <= config.max_head_data_size as usize,
691			Error::<T>::HeadDataTooLarge
692		);
693
694		let per_byte_fee = T::DataDepositPerByte::get();
695		let deposit = T::ParaDeposit::get()
696			.saturating_add(per_byte_fee.saturating_mul((genesis_head.0.len() as u32).into()))
697			.saturating_add(per_byte_fee.saturating_mul(config.max_code_size.into()));
698
699		Ok((ParaGenesisArgs { genesis_head, validation_code, para_kind }, deposit))
700	}
701
702	/// Swap a lease holding parachain and parathread (on-demand parachain), which involves
703	/// scheduling an appropriate lifecycle update.
704	fn do_thread_and_chain_swap(to_downgrade: ParaId, to_upgrade: ParaId) {
705		let res1 = polkadot_runtime_parachains::schedule_parachain_downgrade::<T>(to_downgrade);
706		debug_assert!(res1.is_ok());
707		let res2 = polkadot_runtime_parachains::schedule_parathread_upgrade::<T>(to_upgrade);
708		debug_assert!(res2.is_ok());
709		T::OnSwap::on_swap(to_upgrade, to_downgrade);
710	}
711}
712
713impl<T: Config> OnNewHead for Pallet<T> {
714	fn on_new_head(id: ParaId, _head: &HeadData) -> Weight {
715		// mark the parachain locked if the locked value is not already set
716		let mut writes = 0;
717		if let Some(mut info) = Paras::<T>::get(id) {
718			if info.locked.is_none() {
719				info.locked = Some(true);
720				Paras::<T>::insert(id, info);
721				writes += 1;
722			}
723		}
724		T::DbWeight::get().reads_writes(1, writes)
725	}
726}
727
728#[cfg(test)]
729mod mock;
730
731#[cfg(test)]
732mod tests;
733
734#[cfg(feature = "runtime-benchmarks")]
735mod benchmarking;