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//!
20//! Registration is either local, with the deposit reserved here, or driven by a remote control
21//! plane that holds the deposit itself โ€” see the [`registrar_primitives::ParachainRegistrar`]
22//! impl.
23
24pub 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	/// The account that has placed a deposit for registering this para.
69	pub manager: Account,
70	/// The amount reserved by the `manager` account for the registration.
71	pub deposit: Balance,
72	/// Whether the para registration should be locked from being controlled by the manager.
73	/// None means the lock had not been explicitly set, and should be treated as false.
74	pub locked: Option<bool>,
75}
76
77impl<Account, Balance> ParaInfo<Account, Balance> {
78	/// Returns if the para is locked.
79	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	/// The in-code storage version.
129	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		/// The overarching event type.
140		#[allow(deprecated)]
141		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
142
143		/// The aggregated origin type must support the `parachains` origin. We require that we can
144		/// infallibly convert between this origin and the system origin, but in reality, they're
145		/// the same type, we just can't express that to the Rust type system without writing a
146		/// `where` clause everywhere.
147		type RuntimeOrigin: From<<Self as frame_system::Config>::RuntimeOrigin>
148			+ Into<result::Result<Origin, <Self as Config>::RuntimeOrigin>>;
149
150		/// The system's currency for on-demand parachain payment.
151		type Currency: ReservableCurrency<Self::AccountId>;
152
153		/// Runtime hook for when a lease holding parachain and on-demand parachain swap.
154		type OnSwap: crate::traits::OnSwap;
155
156		/// The deposit to be paid to run a on-demand parachain.
157		/// This should include the cost for storing the genesis head and validation code.
158		#[pallet::constant]
159		type ParaDeposit: Get<BalanceOf<Self>>;
160
161		/// The deposit to be paid per byte stored on chain.
162		#[pallet::constant]
163		type DataDepositPerByte: Get<BalanceOf<Self>>;
164
165		/// Weight Information for the Extrinsics in the Pallet
166		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		/// The ID is not registered.
181		NotRegistered,
182		/// The ID is already registered.
183		AlreadyRegistered,
184		/// The caller is not the owner of this Id.
185		NotOwner,
186		/// Invalid para code size.
187		CodeTooLarge,
188		/// Invalid para head data size.
189		HeadDataTooLarge,
190		/// Para is not a Parachain.
191		NotParachain,
192		/// Para is not a Parathread (on-demand parachain).
193		NotParathread,
194		/// Cannot deregister para
195		CannotDeregister,
196		/// Cannot schedule downgrade of lease holding parachain to on-demand parachain
197		CannotDowngrade,
198		/// Cannot schedule upgrade of on-demand parachain to lease holding parachain
199		CannotUpgrade,
200		/// Para is locked from manipulation by the manager. Must use parachain or relay chain
201		/// governance.
202		ParaLocked,
203		/// The ID given for registration has not been reserved.
204		NotReserved,
205		/// The validation code is invalid.
206		InvalidCode,
207		/// Cannot perform a parachain slot / lifecycle swap. Check that the state of both paras
208		/// are correct for the swap to work.
209		CannotSwap,
210		/// The operation is scaffolded but not implemented yet.
211		NotImplemented,
212	}
213
214	/// Pending swap operations.
215	#[pallet::storage]
216	pub(super) type PendingSwap<T> = StorageMap<_, Twox64Concat, ParaId, ParaId>;
217
218	/// Amount held on deposit for each para and the original depositor.
219	///
220	/// The given account ID is responsible for registering the code and initial head data, but may
221	/// only do so if it isn't yet registered. (After that, it's up to governance to do so.)
222	#[pallet::storage]
223	pub type Paras<T: Config> =
224		StorageMap<_, Twox64Concat, ParaId, ParaInfo<T::AccountId, BalanceOf<T>>>;
225
226	/// The next free `ParaId`.
227	#[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		/// Register head data and validation code for a reserved Para Id.
256		///
257		/// ## Arguments
258		/// - `origin`: Must be called by a `Signed` origin.
259		/// - `id`: The para ID. Must be owned/managed by the `origin` signing account.
260		/// - `genesis_head`: The genesis head data of the parachain/thread.
261		/// - `validation_code`: The initial validation code of the parachain/thread.
262		///
263		/// ## Deposits/Fees
264		/// The account with the originating signature must reserve a deposit.
265		///
266		/// The deposit is required to cover the costs associated with storing the genesis head
267		/// data and the validation code.
268		/// This accounts for the potential to store validation code of a size up to the
269		/// `max_code_size`, as defined in the configuration pallet
270		///
271		/// Anything already reserved previously for this para ID is accounted for.
272		///
273		/// ## Events
274		/// The `Registered` event is emitted in case of success.
275		#[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		/// Force the registration of a Para Id on the relay chain.
289		///
290		/// This function must be called by a Root origin.
291		///
292		/// The deposit taken can be specified for this registration. Any `ParaId`
293		/// can be registered, including sub-1000 IDs which are System Parachains.
294		///
295		/// Deprecated where registration runs through `pallet-registrar-para`: use its
296		/// `force_register` there, since a para registered here is unknown to that pallet.
297		#[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		/// Deregister a Para Id, freeing all data and returning any deposit.
312		///
313		/// The caller must be Root, the `para` owner, or the `para` itself. The para must be an
314		/// on-demand parachain.
315		#[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		/// Swap a lease holding parachain with another parachain, either on-demand or lease
323		/// holding.
324		///
325		/// The origin must be Root, the `para` owner, or the `para` itself.
326		///
327		/// The swap will happen only if there is already an opposite swap pending. If there is not,
328		/// the swap will be stored in the pending swaps map, ready for a later confirmatory swap.
329		///
330		/// The `ParaId`s remain mapped to the same head data and code so external code can rely on
331		/// `ParaId` to be a long-term identifier of a notional "parachain". However, their
332		/// scheduling info (i.e. whether they're an on-demand parachain or lease holding
333		/// parachain), auction information and the auction deposit are switched.
334		#[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` and `other` is the same id, we treat this as a "clear" function, and exit
340			// early, since swapping the same id would otherwise be a noop.
341			if id == other {
342				PendingSwap::<T>::remove(id);
343				return Ok(());
344			}
345
346			// Sanity check that `id` is even a para.
347			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				// identify which is a lease holding parachain and which is a parathread (on-demand
354				// parachain)
355				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					// If both chains are currently parachains, there is nothing funny we
367					// need to do for their lifecycle management, just swap the underlying
368					// data.
369					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		/// Remove a manager lock from a para. This will allow the manager of a
383		/// previously locked para to deregister or swap a para without using governance.
384		///
385		/// Can only be called by the Root origin or the parachain.
386		#[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		/// Reserve a Para Id on the relay chain.
395		///
396		/// This function will reserve a new Para Id to be owned/managed by the origin account.
397		/// The origin account is able to register head data and validation code using `register` to
398		/// create an on-demand parachain. Using the Slots pallet, an on-demand parachain can then
399		/// be upgraded to a lease holding parachain.
400		///
401		/// ## Arguments
402		/// - `origin`: Must be called by a `Signed` origin. Becomes the manager/owner of the new
403		///   para ID.
404		///
405		/// ## Deposits/Fees
406		/// The origin must reserve a deposit of `ParaDeposit` for the registration.
407		///
408		/// ## Events
409		/// The `Reserved` event is emitted in case of success, which provides the ID reserved for
410		/// use.
411		#[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		/// Add a manager lock from a para. This will prevent the manager of a
422		/// para to deregister or swap a para.
423		///
424		/// Can be called by Root, the parachain, or the parachain manager if the parachain is
425		/// unlocked.
426		#[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		/// Schedule a parachain upgrade.
435		///
436		/// This will kick off a check of `new_code` by all validators. After the majority of the
437		/// validators have reported on the validity of the code, the code will either be enacted
438		/// or the upgrade will be rejected. If the code will be enacted, the current code of the
439		/// parachain will be overwritten directly. This means that any PoV will be checked by this
440		/// new code. The parachain itself will not be informed explicitly that the validation code
441		/// has changed.
442		///
443		/// Can be called by Root, the parachain, or the parachain manager if the parachain is
444		/// unlocked.
445		#[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		/// Set the parachain's current head.
462		///
463		/// Can be called by Root, the parachain, or the parachain manager if the parachain is
464		/// unlocked.
465		#[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	/// Return the manager `AccountId` of a para if one exists.
483	fn manager_of(id: ParaId) -> Option<T::AccountId> {
484		Some(Paras::<T>::get(id)?.manager)
485	}
486
487	// All lease holding parachains. Ordered ascending by ParaId. On-demand parachains are not
488	// included.
489	fn parachains() -> Vec<ParaId> {
490		paras::Parachains::<T>::get()
491	}
492
493	// Return if a para is a parathread (on-demand parachain)
494	fn is_parathread(id: ParaId) -> bool {
495		paras::Pallet::<T>::is_parathread(id)
496	}
497
498	// Return if a para is a lease holding parachain
499	fn is_parachain(id: ParaId) -> bool {
500		paras::Pallet::<T>::is_parachain(id)
501	}
502
503	// Apply a lock to the parachain.
504	fn apply_lock(id: ParaId) {
505		Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(true)));
506	}
507
508	// Remove a lock from the parachain.
509	fn remove_lock(id: ParaId) {
510		Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(false)));
511	}
512
513	// Register a Para ID under control of `manager`.
514	//
515	// Note this is a backend registration API, so verification of ParaId
516	// is not done here to prevent.
517	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	// Deregister a Para ID, free any data, and return any deposits.
527	fn deregister(id: ParaId) -> DispatchResult {
528		Self::do_deregister(id)
529	}
530
531	// Upgrade a registered on-demand parachain into a lease holding parachain.
532	fn make_parachain(id: ParaId) -> DispatchResult {
533		// Para backend should think this is an on-demand parachain...
534		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	// Downgrade a registered para into a parathread (on-demand parachain).
545	fn make_parathread(id: ParaId) -> DispatchResult {
546		// Para backend should think this is a parachain...
547		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
579/// Exposes this pallet's registry to a remote registration control plane.
580///
581/// Registration can be driven by another pallet โ€” typically on another trusted chain โ€” that
582/// owns the manager relationship and holds the deposit. This impl is the seam: it does the
583/// registry work and nothing else, so where the deposit lives and how the request arrived
584/// are outside this pallet's concern.
585///
586/// The trait is stated in plain `u32`/`Vec<u8>` so its definition carries no dependency on
587/// Polkadot's parachain primitives; conversion to [`ParaId`], [`HeadData`] and [`ValidationCode`]
588/// happens here.
589impl<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	// No origin, manager or lock check: the parachain registrar does those before asking.
620	fn deregister(para_id: u32) -> DispatchResult {
621		let id = ParaId::from(para_id);
622		// Rolls back if `schedule_para_cleanup` fails after earlier writes.
623		with_storage_layer(|| {
624			// A retry after a lost answer: cleanup is already scheduled.
625			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		// The sample size the paras pallet's own benchmarks assume for per-para lists.
642		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		// A pending upgrade makes offboarding scan the active PVF votes. Its code is trusted so it
655		// gets no vote of its own, which would make offboarding fail.
656		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			// Unknown code, so each upgrade opens a vote of its own.
670			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		// TODO(ahm-v2): validate the code length and that an upgrade may be scheduled now.
698		Err(())
699	}
700
701	fn schedule_code_upgrade(_para_id: u32, _validation_code: Vec<u8>) -> DispatchResult {
702		// TODO(ahm-v2): schedule the validation code upgrade.
703		Err(Error::<T>::NotImplemented.into())
704	}
705}
706
707impl<T: Config> Pallet<T> {
708	/// Ensure the origin is one of Root, the `para` owner, or the `para` itself.
709	/// If the origin is the `para` owner, the `para` must be unlocked.
710	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	/// Ensure the origin is one of Root or the `para` itself.
727	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		// Check if matching para id...
737		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	/// Attempt to register a new Para Id under management of `who` in the
760	/// system with the given information.
761	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		// We check above that para has no lifecycle, so this should not fail.
791		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	/// Deregister a Para Id, freeing all data returning any deposit.
798	fn do_deregister(id: ParaId) -> DispatchResult {
799		match paras::Pallet::<T>::lifecycle(id) {
800			// Para must be a parathread (on-demand parachain), or not exist at all.
801			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	/// Verifies the onboarding data is valid for a para.
817	///
818	/// Returns `ParaGenesisArgs` and the deposit needed for the data.
819	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	/// Check onboarding head and code sizes against the given configuration.
836	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	/// Swap a lease holding parachain and parathread (on-demand parachain), which involves
848	/// scheduling an appropriate lifecycle update.
849	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		// mark the parachain locked if the locked value is not already set
861		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;