pezkuwi_runtime_common/paras_registrar/
mod.rs

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