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