referrerpolicy=no-referrer-when-downgrade

pallet_registrar_para/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Parachain registrar pallet
19//!
20//! The user-facing half of parachain registration. It hands out para ids, takes the manager's
21//! deposits as [`Consideration`] tickets, and coordinates the registration itself asynchronously
22//! with the chain that owns the parachain registry.
23//!
24//! Both directions of that coordination are abstract: requests go out through [`SendToRelay`],
25//! verdicts come back in through [`Pallet::receive`], gated by [`Config::RelayOrigin`]. Nothing
26//! here depends on XCM or on the other chain's extrinsics.
27//!
28//! ## Registration flow
29//!
30//! Registration takes two transactions on two chains. Sending a multi-megabyte validation code
31//! through the messaging layer would be wasteful, so this chain only commits to its hash and
32//! length and the blob is uploaded to the relay chain directly:
33//!
34//! 1. [`Pallet::reserve`] allocates a para id here and takes [`Config::ReservationConsideration`].
35//! 2. [`Pallet::register`] takes [`Config::RegistrationConsideration`] for the head data and the
36//!    *declared* code length, then asks the relay chain to accept the registration. Only the code
37//!    hash and length are sent.
38//! 3. The manager uploads the validation code to the relay chain, which accepts it only if it
39//!    matches the hash and length committed to in step 2.
40//! 4. The verdict arrives back as [`Pallet::receive`], which either finalises the registration or
41//!    releases the registration deposit.
42//!
43//! ## Giving up
44//!
45//! Nothing on the relay chain times a registration out, so a request whose code never turns up
46//! waits until the manager ends it with [`Pallet::cancel_registration`]. That asks the relay chain
47//! to drop the authorization and only releases the deposit once it confirms, which is what
48//! keeps a cancellation from freeing the deposit on a para that did register after all.
49//!
50//! ## Giving up a para id
51//!
52//! [`Pallet::deregister`] on an id that is only reserved releases the reservation right away, as
53//! the relay chain never heard of it. A registered para has to be dropped on the relay chain
54//! first, so both deposits stay held until it answers. If the answer never arrives, the manager
55//! calls [`Pallet::deregister`] again.
56//!
57//! ## Locking
58//!
59//! [`Pallet::add_lock`] shuts the manager out of a registered para, leaving it to the para's own
60//! governance. Only root or the para itself can lift it again with [`Pallet::remove_lock`].
61//!
62//! A para is also locked the first time the relay chain reports that it produced a head, which
63//! arrives as [`MessageToParaV1::HeadNoted`]. A lock lifted with [`Pallet::remove_lock`] outranks
64//! that, so it is never re-applied.
65//!
66//! Deposits only ever live on this chain; the relay chain takes nothing.
67
68#![cfg_attr(not(feature = "std"), no_std)]
69
70extern crate alloc;
71
72use alloc::vec::Vec;
73use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
74use frame_support::{
75	ensure,
76	traits::{Consideration, EnsureOrigin, Footprint},
77};
78use registrar_primitives::{
79	FailureReason, MessageToPara, MessageToParaV1, MessageToRelay, MessageToRelayV1, Outcome,
80	ParaId,
81};
82use scale_info::TypeInfo;
83use sp_core::H256;
84use sp_runtime::{
85	traits::{BlockNumberProvider, Saturating},
86	DispatchError, DispatchResult,
87};
88
89pub use pallet::*;
90pub use weights::WeightInfo;
91
92pub mod weights;
93
94#[cfg(feature = "runtime-benchmarks")]
95mod benchmarking;
96#[cfg(test)]
97mod mock;
98#[cfg(test)]
99mod tests;
100
101/// Block number used for registration deadlines.
102///
103/// On a parachain, configure [`Config::BlockNumberProvider`] to
104/// `cumulus_pallet_parachain_system::RelaychainDataProvider`, so deadlines are expressed in
105/// relay-chain blocks and keep their meaning through a stall in this chain's own block production.
106pub type ProvidedBlockNumberOf<T> =
107	<<T as Config>::BlockNumberProvider as BlockNumberProvider>::BlockNumber;
108
109/// Used to send an XCM `Transact` to the registrar pallet on the remote relay chain.
110pub trait SendToRelay {
111	/// The account id used to identify a registration's manager on both chains.
112	type AccountId;
113
114	/// Send `message` to the relay chain.
115	///
116	/// `Err(())` means the message could not be handed to the transport at all. Callers are
117	/// expected to fail the whole extrinsic, so nothing is left half-done.
118	#[allow(clippy::result_unit_err)]
119	fn send(message: MessageToRelay<Self::AccountId>) -> Result<(), ()>;
120}
121
122#[cfg(feature = "std")]
123impl SendToRelay for () {
124	type AccountId = sp_runtime::AccountId32;
125
126	fn send(_message: MessageToRelay<Self::AccountId>) -> Result<(), ()> {
127		Ok(())
128	}
129}
130
131/// Where a para id sits in the registration flow.
132#[derive(
133	Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
134)]
135pub enum RegistrationState<Ticket, BlockNumber> {
136	/// The para id is held by its manager, but nothing is registered on the relay chain yet.
137	Reserved,
138	/// The relay chain has been asked to register this para and has not reported back.
139	Pending {
140		/// The registration's [`Consideration`] ticket, returned if the registration fails.
141		ticket: Ticket,
142		/// The block from which the manager may give up on this registration.
143		///
144		/// Expressed in [`Config::BlockNumberProvider`] blocks. Long enough that a verdict already
145		/// on its way arrives first, so a cancellation is only ever sent for a registration that
146		/// really has gone quiet. Pushed out again by every [`Pallet::cancel_registration`], so a
147		/// cancellation that gets lost can be retried but not spammed.
148		cancellable_at: BlockNumber,
149		/// The request this state is waiting on; a response carrying any other id is stale.
150		message_id: u64,
151	},
152	/// The relay chain has onboarded this para.
153	Registered {
154		/// The registration's [`Consideration`] ticket, kept while the para is registered.
155		ticket: Ticket,
156	},
157	/// The relay chain has been asked to drop this para and has not reported back.
158	///
159	/// Both deposits stay held: only the relay chain knows whether the para really went away, and
160	/// a refusal puts it straight back to [`RegistrationState::Registered`].
161	///
162	/// There is nothing to cancel from here: if no answer turns up, the manager calls
163	/// [`Pallet::deregister`] again once `can_retry_after` has passed.
164	Deregistering {
165		/// The registration's [`Consideration`] ticket, released once the relay chain confirms.
166		ticket: Ticket,
167		/// The block from which the manager may send the [`MessageToRelayV1::Deregister`] again,
168		/// if the answer never arrived. Retrying reports the missing answer as
169		/// [`UnexpectedKind::ResponseNeverArrived`].
170		can_retry_after: BlockNumber,
171		/// The request this state is waiting on; a response carrying any other id is stale.
172		message_id: u64,
173	},
174}
175
176/// Everything this chain knows about one para id.
177#[derive(
178	Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo, MaxEncodedLen, Debug,
179)]
180pub struct ParaInfo<AccountId, ReservationTicket, RegistrationTicket, BlockNumber> {
181	/// The account that reserved the para id and controls it.
182	pub manager: AccountId,
183	/// The [`Consideration`] ticket for the para id itself.
184	pub reservation: ReservationTicket,
185	/// Where this para id sits in the registration flow.
186	pub state: RegistrationState<RegistrationTicket, BlockNumber>,
187	/// Whether the manager is locked out of controlling this para. `None` until the lock is set
188	/// for the first time, and read as unlocked.
189	pub locked: Option<bool>,
190}
191
192impl<AccountId, ReservationTicket, RegistrationTicket, BlockNumber>
193	ParaInfo<AccountId, ReservationTicket, RegistrationTicket, BlockNumber>
194{
195	/// Whether the manager is locked out of this para.
196	pub fn is_locked(&self) -> bool {
197		self.locked.unwrap_or(false)
198	}
199}
200
201/// The [`ParaInfo`] type as configured.
202pub type ParaInfoOf<T> = ParaInfo<
203	<T as frame_system::Config>::AccountId,
204	<T as Config>::ReservationConsideration,
205	<T as Config>::RegistrationConsideration,
206	ProvidedBlockNumberOf<T>,
207>;
208
209#[frame_support::pallet]
210pub mod pallet {
211	use super::*;
212	use frame_support::pallet_prelude::{DispatchResult, *};
213	use frame_system::pallet_prelude::*;
214
215	#[pallet::config]
216	pub trait Config: frame_system::Config {
217		/// The cost of reserving a para id. The footprint is a single zero-sized item, so a flat
218		/// price fits.
219		type ReservationConsideration: Consideration<Self::AccountId, Footprint>;
220
221		/// The cost of a registration, on top of the reservation. The footprint is one item sized
222		/// as head data plus *declared* code length, so a per-byte price fits.
223		type RegistrationConsideration: Consideration<Self::AccountId, Footprint>;
224
225		/// Sends messages to the relay chain.
226		type SendToRelay: SendToRelay<AccountId = Self::AccountId>;
227
228		/// An origin that is sure to be the relay chain's registrar pallet.
229		type RelayOrigin: EnsureOrigin<Self::RuntimeOrigin>;
230
231		/// An origin a parachain uses to act as itself, resolved to its para id.
232		type ParachainOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = ParaId>;
233
234		/// The lowest para id this pallet will hand out.
235		///
236		/// Mirrors the relay chain's `LOWEST_PUBLIC_ID`. Ids below it are reserved for system
237		/// parachains and are not obtainable here.
238		#[pallet::constant]
239		type FirstPublicParaId: Get<ParaId>;
240
241		/// The smallest validation code the relay chain will accept.
242		///
243		/// A local mirror of the relay chain's `MIN_CODE_SIZE`, used to fail early. The relay
244		/// chain checks the real thing against its own live configuration.
245		#[pallet::constant]
246		type MinCodeSize: Get<u32>;
247
248		/// The largest validation code the relay chain will accept.
249		///
250		/// A local mirror of the relay chain's `max_code_size`. See [`Config::MinCodeSize`].
251		#[pallet::constant]
252		type MaxCodeSize: Get<u32>;
253
254		/// The largest head data the relay chain will accept.
255		///
256		/// A local mirror of the relay chain's `max_head_data_size`. See [`Config::MinCodeSize`].
257		#[pallet::constant]
258		type MaxHeadDataSize: Get<u32>;
259
260		/// How long a manager waits for the relay chain before giving up on a registration.
261		///
262		/// Measured in [`Config::BlockNumberProvider`] blocks. Should comfortably cover a round
263		/// trip, so that a verdict that is merely slow lands before anybody tries to cancel.
264		#[pallet::constant]
265		type PendingDeadline: Get<ProvidedBlockNumberOf<Self>>;
266
267		/// Source of block numbers for registration deadlines.
268		///
269		/// On a parachain this should be
270		/// `cumulus_pallet_parachain_system::RelaychainDataProvider`, so
271		/// [`Config::PendingDeadline`] is in relay-chain blocks.
272		type BlockNumberProvider: BlockNumberProvider;
273
274		/// Weight information for the extrinsics in this pallet.
275		type WeightInfo: WeightInfo;
276	}
277
278	#[pallet::pallet]
279	pub struct Pallet<T>(_);
280
281	/// Hold reasons for runtimes that pay the considerations out of held funds.
282	#[pallet::composite_enum]
283	pub enum HoldReason {
284		/// Held for keeping a para id reserved.
285		#[codec(index = 0)]
286		ParaIdReservation,
287		/// Held for the head data and validation code of a registration.
288		#[codec(index = 1)]
289		Registration,
290	}
291
292	/// The next para id that [`Pallet::reserve`] will hand out.
293	#[pallet::storage]
294	pub type NextFreeParaId<T: Config> = StorageValue<_, ParaId, ValueQuery>;
295
296	/// The id the next message to the relay chain will carry.
297	///
298	/// One per message sent, echoed back in the relay chain's response, so a request, its
299	/// response and the events on both chains can be tied together.
300	#[pallet::storage]
301	pub type NextMessageId<T: Config> = StorageValue<_, u64, ValueQuery>;
302
303	/// Every para id reserved through this pallet, and what is happening with it.
304	#[pallet::storage]
305	pub type Paras<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, ParaInfoOf<T>>;
306
307	#[pallet::event]
308	#[pallet::generate_deposit(pub(super) fn deposit_event)]
309	pub enum Event<T: Config> {
310		/// A para id was reserved.
311		Reserved { para_id: ParaId, who: T::AccountId },
312		/// A registration was requested and the relay chain has been asked to accept it.
313		RegisterRequested { para_id: ParaId, message_id: u64, manager: T::AccountId },
314		/// The relay chain confirmed a registration.
315		Registered { para_id: ParaId, message_id: u64, manager: T::AccountId },
316		/// The relay chain rejected a registration. The registration consideration was returned.
317		RegistrationFailed {
318			para_id: ParaId,
319			message_id: u64,
320			manager: T::AccountId,
321			reason: FailureReason,
322		},
323		/// A manager gave up on a pending registration, and the relay chain has been asked to
324		/// drop the authorization. The consideration stays taken until it answers.
325		CancelRequested { para_id: ParaId, message_id: u64, manager: T::AccountId },
326		/// The relay chain confirmed a cancellation. The registration consideration was returned.
327		RegistrationCancelled { para_id: ParaId, message_id: u64, manager: T::AccountId },
328		/// A para id that was never registered was given up. The reservation was returned.
329		ReservationDropped { para_id: ParaId, manager: T::AccountId },
330		/// The relay chain has been asked to drop a para. Both considerations stay taken until it
331		/// answers.
332		DeregisterRequested { para_id: ParaId, message_id: u64, manager: T::AccountId },
333		/// The relay chain dropped the para. Every consideration was returned and the id is gone.
334		Deregistered { para_id: ParaId, message_id: u64, manager: T::AccountId },
335		/// The relay chain refused to drop the para. It stays registered, considerations included.
336		DeregistrationFailed {
337			para_id: ParaId,
338			message_id: u64,
339			manager: T::AccountId,
340			reason: FailureReason,
341		},
342		/// The manager is locked out of this para.
343		ParaLocked { para_id: ParaId },
344		/// The manager may control this para again.
345		ParaUnlocked { para_id: ParaId },
346		/// Something that should never happen did. The pallet carried on regardless.
347		Unexpected(UnexpectedKind),
348	}
349
350	/// A defensive check that failed, reported as [`Event::Unexpected`] so it is visible on chain
351	/// and not just in the node's logs.
352	#[derive(
353		Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
354	)]
355	pub enum UnexpectedKind {
356		/// A register response for a para id this pallet does not know.
357		RegisterResponseForUnknownPara { para_id: ParaId, message_id: u64 },
358		/// A register response for a para with no registration in flight.
359		RegisterResponseNotPending { para_id: ParaId, message_id: u64 },
360		/// A head was noted for a para id this pallet does not know.
361		HeadNotedForUnknownPara { para_id: ParaId },
362		/// A cancel response for a para id this pallet does not know.
363		CancelResponseForUnknownPara { para_id: ParaId, message_id: u64 },
364		/// The relay chain refused a cancellation for a reason that is not one of the refusals
365		/// this pallet knows how to settle.
366		CancelRefused { para_id: ParaId, message_id: u64, reason: FailureReason },
367		/// A head was noted for a para that is not registered.
368		HeadNotedForUnregisteredPara { para_id: ParaId },
369		/// A deregister response for a para id this pallet does not know.
370		DeregisterResponseForUnknownPara { para_id: ParaId, message_id: u64 },
371		/// A deregister response for a para with no deregistration in flight.
372		DeregisterResponseNotDeregistering { para_id: ParaId, message_id: u64 },
373		/// A response answering a request this pallet is no longer waiting on.
374		StaleResponse { para_id: ParaId, message_id: u64, expected: u64 },
375		/// The relay chain never answered this request and the manager gave up waiting.
376		ResponseNeverArrived { para_id: ParaId, message_id: u64 },
377		/// Releasing a deposit failed while applying a relay chain answer, so it is still held.
378		DepositNotReleased { para_id: ParaId, error: DispatchError },
379	}
380
381	#[pallet::error]
382	pub enum Error<T> {
383		/// The para id has not been reserved.
384		NotReserved,
385		/// The caller does not manage this para id.
386		NotOwner,
387		/// The para id is already registered, or a registration is already in flight for it.
388		AlreadyRegistered,
389		/// There is no registration in flight for this para id.
390		NotPending,
391		/// The manager may not abandon this registration yet.
392		CannotCancelYet,
393		/// The head data is larger than the relay chain will accept.
394		HeadDataTooLarge,
395		/// The validation code is larger than the relay chain will accept.
396		CodeTooLarge,
397		/// The validation code is smaller than the relay chain will accept.
398		CodeTooSmall,
399		/// The message could not be handed to the transport.
400		SendFailed,
401		/// There are no more para ids to hand out.
402		NoFreeParaId,
403		/// The para is locked, so the manager may not act on it.
404		ParaLocked,
405		/// The para is already locked.
406		AlreadyLocked,
407		/// The para is not locked.
408		NotLocked,
409		/// The para is not registered on the relay chain.
410		NotRegistered,
411		/// The call is scaffolded but not implemented yet.
412		Unimplemented,
413		/// The manager may not ask the relay chain to drop this para again yet.
414		CannotRetryYet,
415	}
416
417	#[pallet::hooks]
418	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
419		fn integrity_test() {
420			// Otherwise no validation code could ever pass `register`.
421			assert!(
422				T::MinCodeSize::get() <= T::MaxCodeSize::get(),
423				"MinCodeSize ({}) must not exceed MaxCodeSize ({})",
424				T::MinCodeSize::get(),
425				T::MaxCodeSize::get(),
426			);
427		}
428	}
429
430	#[pallet::call]
431	impl<T: Config> Pallet<T> {
432		/// Accept a report from the relay chain's registrar pallet.
433		///
434		/// Not callable by users: the origin must be the relay chain.
435		#[pallet::call_index(0)]
436		#[pallet::weight(T::WeightInfo::receive())]
437		pub fn receive(origin: OriginFor<T>, message: MessageToPara) -> DispatchResult {
438			T::RelayOrigin::ensure_origin_or_root(origin)?;
439
440			match message {
441				MessageToPara::V1(MessageToParaV1::RegisterResponse {
442					para_id,
443					message_id,
444					outcome,
445				}) => Self::on_register_response(para_id, message_id, outcome),
446				MessageToPara::V1(MessageToParaV1::CancelResponse {
447					para_id,
448					message_id,
449					outcome,
450				}) => Self::on_cancel_response(para_id, message_id, outcome),
451				MessageToPara::V1(MessageToParaV1::DeregisterResponse {
452					para_id,
453					message_id,
454					outcome,
455				}) => Self::on_deregister_response(para_id, message_id, outcome),
456				MessageToPara::V1(MessageToParaV1::CodeUpgradeResponse {
457					para_id,
458					message_id,
459					outcome,
460				}) => Self::on_code_upgrade_response(para_id, message_id, outcome),
461				MessageToPara::V1(MessageToParaV1::CodeUpgradeScheduled {
462					para_id,
463					message_id,
464				}) => Self::on_code_upgrade_scheduled(para_id, message_id),
465				MessageToPara::V1(MessageToParaV1::SetHeadResponse {
466					para_id,
467					message_id,
468					outcome,
469				}) => Self::on_set_head_response(para_id, message_id, outcome),
470				MessageToPara::V1(MessageToParaV1::HeadNoted { para_id }) => {
471					Self::on_head_noted(para_id)
472				},
473			}
474		}
475
476		/// Reserve the next free para id for the caller.
477		///
478		/// Takes [`Config::ReservationConsideration`]. The caller becomes the manager of the new
479		/// id and is the only account that may [`Pallet::register`] against it.
480		#[pallet::call_index(1)]
481		#[pallet::weight(T::WeightInfo::reserve())]
482		pub fn reserve(origin: OriginFor<T>) -> DispatchResult {
483			let who = ensure_signed(origin)?;
484
485			let para_id = NextFreeParaId::<T>::get().max(T::FirstPublicParaId::get());
486			let next = para_id.checked_add(1).ok_or(Error::<T>::NoFreeParaId)?;
487			ensure!(!Paras::<T>::contains_key(para_id), Error::<T>::AlreadyRegistered);
488
489			let reservation = T::ReservationConsideration::new(&who, Footprint::from_parts(1, 0))?;
490
491			Paras::<T>::insert(
492				para_id,
493				ParaInfo {
494					manager: who.clone(),
495					reservation,
496					state: RegistrationState::Reserved,
497					locked: None,
498				},
499			);
500			NextFreeParaId::<T>::put(next);
501
502			Self::deposit_event(Event::Reserved { para_id, who });
503			Ok(())
504		}
505
506		/// Ask the relay chain to register head data and validation code for a reserved para id.
507		///
508		/// The validation code itself stays here: only `code_hash` and `code_len` are sent. The
509		/// caller uploads the blob to the relay chain separately, which accepts it only if it
510		/// hashes to `code_hash` and is exactly `code_len` bytes long.
511		///
512		/// ## Costs
513		///
514		/// Takes [`Config::RegistrationConsideration`] for the head data and the *declared* code
515		/// length, on top of the para id reservation. It is returned if the relay chain rejects
516		/// the registration or if the caller later abandons it.
517		#[pallet::call_index(2)]
518		#[pallet::weight(T::WeightInfo::register(genesis_head.len() as u32))]
519		pub fn register(
520			origin: OriginFor<T>,
521			para_id: ParaId,
522			genesis_head: Vec<u8>,
523			code_len: u32,
524			code_hash: H256,
525		) -> DispatchResult {
526			let who = ensure_signed(origin)?;
527
528			let mut info = Paras::<T>::get(para_id).ok_or(Error::<T>::NotReserved)?;
529			ensure!(info.manager == who, Error::<T>::NotOwner);
530			ensure!(
531				matches!(info.state, RegistrationState::Reserved),
532				Error::<T>::AlreadyRegistered
533			);
534
535			let head_len = genesis_head.len() as u32;
536			ensure!(head_len <= T::MaxHeadDataSize::get(), Error::<T>::HeadDataTooLarge);
537			ensure!(code_len >= T::MinCodeSize::get(), Error::<T>::CodeTooSmall);
538			ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
539
540			let ticket = T::RegistrationConsideration::new(
541				&who,
542				Self::registration_footprint(head_len, code_len),
543			)?;
544
545			let cancellable_at = T::BlockNumberProvider::current_block_number()
546				.saturating_add(T::PendingDeadline::get());
547			let message_id = Self::next_message_id();
548			info.state = RegistrationState::Pending { ticket, cancellable_at, message_id };
549			Paras::<T>::insert(para_id, info);
550
551			// A transport failure returns `Err` and unwinds everything above, ticket included.
552			T::SendToRelay::send(MessageToRelay::V1(MessageToRelayV1::Register {
553				para_id,
554				message_id,
555				manager: who.clone(),
556				genesis_head,
557				code_hash,
558				code_len,
559			}))
560			.map_err(|()| Error::<T>::SendFailed)?;
561
562			Self::deposit_event(Event::RegisterRequested { para_id, message_id, manager: who });
563			Ok(())
564		}
565
566		/// Give up on a registration the relay chain never reported on.
567		///
568		/// Callable from [`Config::PendingDeadline`] blocks after the request. Nothing on the
569		/// relay chain abandons a registration on its own, so this is what ends one whose code
570		/// never turned up, and the manager pays for the round trip rather than every relay-chain
571		/// block paying for a sweep.
572		///
573		/// The deposit is not released here: the relay chain is asked to drop the authorization
574		/// first, and [`Pallet::receive`] releases the deposit when it confirms. Waiting for that
575		/// answer is the point. A registration that did go through, with a verdict that got lost on
576		/// the way here, must not have its deposit refunded, and only the relay chain knows
577		/// which of the two happened.
578		///
579		/// The para id itself stays reserved either way, so the manager can simply try again.
580		#[pallet::call_index(3)]
581		#[pallet::weight(T::WeightInfo::cancel_registration())]
582		pub fn cancel_registration(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
583			let who = ensure_signed(origin)?;
584
585			let mut info = Paras::<T>::get(para_id).ok_or(Error::<T>::NotReserved)?;
586			ensure!(info.manager == who, Error::<T>::NotOwner);
587			let RegistrationState::Pending { ticket, cancellable_at, message_id: awaited } =
588				info.state
589			else {
590				return Err(Error::<T>::NotPending.into());
591			};
592			let now = T::BlockNumberProvider::current_block_number();
593			ensure!(now >= cancellable_at, Error::<T>::CannotCancelYet);
594
595			// Getting here means the relay chain's answer to `awaited` never turned up. That should
596			// not happen, so say so on chain instead of quietly retrying.
597			Self::report_unexpected(UnexpectedKind::ResponseNeverArrived {
598				para_id,
599				message_id: awaited,
600			});
601
602			// Another deadline's grace before the manager may ask again, so a request that goes
603			// missing can be retried without the relay chain being asked once per block.
604			let message_id = Self::next_message_id();
605			info.state = RegistrationState::Pending {
606				ticket,
607				cancellable_at: now.saturating_add(T::PendingDeadline::get()),
608				message_id,
609			};
610			Paras::<T>::insert(para_id, info);
611
612			// A transport failure returns `Err` and unwinds the new deadline with it.
613			T::SendToRelay::send(MessageToRelay::V1(MessageToRelayV1::CancelRegistration {
614				para_id,
615				message_id,
616			}))
617			.map_err(|()| Error::<T>::SendFailed)?;
618
619			Self::deposit_event(Event::CancelRequested { para_id, message_id, manager: who });
620			Ok(())
621		}
622
623		/// Lock a registered para, keeping the manager out of it.
624		#[pallet::call_index(4)]
625		#[pallet::weight(T::WeightInfo::add_lock())]
626		pub fn add_lock(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
627			let mut info = Paras::<T>::get(para_id).ok_or(Error::<T>::NotReserved)?;
628			Self::ensure_root_para_or_manager(origin, para_id, &info)?;
629			ensure!(!info.is_locked(), Error::<T>::AlreadyLocked);
630			ensure!(
631				matches!(info.state, RegistrationState::Registered { .. }),
632				Error::<T>::NotRegistered
633			);
634
635			info.locked = Some(true);
636			Paras::<T>::insert(para_id, info);
637
638			Self::deposit_event(Event::ParaLocked { para_id });
639			Ok(())
640		}
641
642		/// Unlock a para, handing control back to the manager.
643		///
644		/// The manager is not accepted here: a lock it could lift would not be a lock.
645		#[pallet::call_index(5)]
646		#[pallet::weight(T::WeightInfo::remove_lock())]
647		pub fn remove_lock(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
648			Self::ensure_root_or_para(origin, para_id)?;
649			let mut info = Paras::<T>::get(para_id).ok_or(Error::<T>::NotReserved)?;
650			ensure!(info.is_locked(), Error::<T>::NotLocked);
651
652			info.locked = Some(false);
653			Paras::<T>::insert(para_id, info);
654
655			Self::deposit_event(Event::ParaUnlocked { para_id });
656			Ok(())
657		}
658
659		/// Give up a para id, releasing every consideration held for it.
660		///
661		/// An id that is only reserved is released immediately. A registered para has to be
662		/// dropped on the relay chain first, so the considerations stay taken until it answers.
663		///
664		/// Can be called again on a para that is already deregistering, once `can_retry_after`
665		/// has passed. This resends the request.
666		#[pallet::call_index(6)]
667		#[pallet::weight(
668			T::WeightInfo::deregister_reserved()
669				.max(T::WeightInfo::deregister_registered())
670				.max(T::WeightInfo::deregister_retry())
671		)]
672		pub fn deregister(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
673			let mut info = Paras::<T>::get(para_id).ok_or(Error::<T>::NotReserved)?;
674			Self::ensure_root_para_or_manager(origin, para_id, &info)?;
675
676			let manager = info.manager.clone();
677			let now = T::BlockNumberProvider::current_block_number();
678			let ticket = match info.state {
679				// The relay chain never knew this id, so there is nobody to ask.
680				RegistrationState::Reserved => {
681					info.reservation.drop(&manager)?;
682					Paras::<T>::remove(para_id);
683					Self::deposit_event(Event::ReservationDropped { para_id, manager });
684					return Ok(());
685				},
686				RegistrationState::Pending { .. } => return Err(Error::<T>::NotRegistered.into()),
687				RegistrationState::Registered { ticket } => ticket,
688				RegistrationState::Deregistering {
689					ticket,
690					can_retry_after,
691					message_id: awaited,
692				} => {
693					ensure!(now >= can_retry_after, Error::<T>::CannotRetryYet);
694					Self::report_unexpected(UnexpectedKind::ResponseNeverArrived {
695						para_id,
696						message_id: awaited,
697					});
698					ticket
699				},
700			};
701
702			let message_id = Self::next_message_id();
703			info.state = RegistrationState::Deregistering {
704				ticket,
705				can_retry_after: now.saturating_add(T::PendingDeadline::get()),
706				message_id,
707			};
708			Paras::<T>::insert(para_id, info);
709
710			// A transport failure returns `Err` and unwinds the new state with it.
711			T::SendToRelay::send(MessageToRelay::V1(MessageToRelayV1::Deregister {
712				para_id,
713				message_id,
714			}))
715			.map_err(|()| Error::<T>::SendFailed)?;
716
717			Self::deposit_event(Event::DeregisterRequested { para_id, message_id, manager });
718			Ok(())
719		}
720
721		#[pallet::call_index(7)]
722		#[pallet::weight(Weight::MAX)]
723		pub fn schedule_code_upgrade(
724			origin: OriginFor<T>,
725			para_id: ParaId,
726			code_hash: H256,
727			code_len: u32,
728		) -> DispatchResult {
729			let _ = (origin, para_id, code_hash, code_len);
730			// TODO(ahm-v2): send the code upgrade authorization to the relay chain.
731			Err(Error::<T>::Unimplemented.into())
732		}
733
734		#[pallet::call_index(8)]
735		#[pallet::weight(Weight::MAX)]
736		pub fn set_current_head(
737			origin: OriginFor<T>,
738			para_id: ParaId,
739			head: Vec<u8>,
740		) -> DispatchResult {
741			let _ = (origin, para_id, head);
742			// TODO(ahm-v2): send the new head to the relay chain.
743			Err(Error::<T>::Unimplemented.into())
744		}
745
746		#[pallet::call_index(9)]
747		#[pallet::weight(Weight::MAX)]
748		pub fn force_register(
749			origin: OriginFor<T>,
750			para_id: ParaId,
751			manager: T::AccountId,
752			genesis_head: Vec<u8>,
753			code_len: u32,
754			code_hash: H256,
755		) -> DispatchResult {
756			let _ = (origin, para_id, manager, genesis_head, code_len, code_hash);
757			// TODO(ahm-v2): send a root-authorized registration to the relay chain.
758			Err(Error::<T>::Unimplemented.into())
759		}
760	}
761}
762
763impl<T: Config> Pallet<T> {
764	/// The footprint a registration is charged for: the head data plus the *declared* code length.
765	pub fn registration_footprint(head_len: u32, code_len: u32) -> Footprint {
766		Footprint::from_parts(1, head_len.saturating_add(code_len) as usize)
767	}
768
769	/// Ensure `origin` may manage `para_id`: the para itself, its manager while unlocked, or root.
770	fn ensure_root_para_or_manager(
771		origin: frame_system::pallet_prelude::OriginFor<T>,
772		para_id: ParaId,
773		info: &ParaInfoOf<T>,
774	) -> DispatchResult {
775		if let Ok(id) = T::ParachainOrigin::ensure_origin(origin.clone()) {
776			ensure!(id == para_id, Error::<T>::NotOwner);
777			return Ok(());
778		}
779		if let Ok(who) = frame_system::ensure_signed(origin.clone()) {
780			ensure!(who == info.manager, Error::<T>::NotOwner);
781			ensure!(!info.is_locked(), Error::<T>::ParaLocked);
782			return Ok(());
783		}
784		frame_system::ensure_root(origin)?;
785		Ok(())
786	}
787
788	/// Ensure `origin` is root or `para_id` itself.
789	fn ensure_root_or_para(
790		origin: frame_system::pallet_prelude::OriginFor<T>,
791		para_id: ParaId,
792	) -> DispatchResult {
793		if frame_system::ensure_root(origin.clone()).is_ok() {
794			return Ok(());
795		}
796		let id = T::ParachainOrigin::ensure_origin(origin)?;
797		ensure!(id == para_id, Error::<T>::NotOwner);
798		Ok(())
799	}
800
801	/// Report a failed defensive check: loud in the logs, and visible on chain.
802	fn report_unexpected(kind: UnexpectedKind) {
803		log::error!(target: "runtime::registrar-para", "unexpected: {kind:?}");
804		Self::deposit_event(Event::Unexpected(kind));
805	}
806
807	/// Drop `ticket` without failing: the relay chain already acted on the answer being applied.
808	fn release_or_report<C: Consideration<T::AccountId, Footprint>>(
809		ticket: C,
810		who: &T::AccountId,
811		para_id: ParaId,
812	) {
813		if let Err(error) = ticket.drop(who) {
814			Self::report_unexpected(UnexpectedKind::DepositNotReleased { para_id, error });
815		}
816	}
817
818	/// Take the id for the next message to the relay chain.
819	fn next_message_id() -> u64 {
820		NextMessageId::<T>::mutate(|next| {
821			let id = *next;
822			*next = next.wrapping_add(1);
823			id
824		})
825	}
826
827	/// Apply the relay chain's verdict on a registration.
828	///
829	/// A response about a para id we are not expecting one for is dropped rather than treated as a
830	/// dispatch error: erroring here would unwind the whole incoming message for something we can
831	/// do nothing about anyway. They are reported as [`Event::Unexpected`] instead.
832	fn on_register_response(para_id: ParaId, message_id: u64, outcome: Outcome) -> DispatchResult {
833		let Some(mut info) = Paras::<T>::get(para_id) else {
834			Self::report_unexpected(UnexpectedKind::RegisterResponseForUnknownPara {
835				para_id,
836				message_id,
837			});
838			return Ok(());
839		};
840		let RegistrationState::Pending { ticket, message_id: expected, .. } = info.state else {
841			Self::report_unexpected(UnexpectedKind::RegisterResponseNotPending {
842				para_id,
843				message_id,
844			});
845			return Ok(());
846		};
847		if message_id != expected {
848			Self::report_unexpected(UnexpectedKind::StaleResponse {
849				para_id,
850				message_id,
851				expected,
852			});
853			return Ok(());
854		}
855
856		let manager = info.manager.clone();
857		match outcome {
858			Ok(()) => {
859				info.state = RegistrationState::Registered { ticket };
860				Paras::<T>::insert(para_id, info);
861				Self::deposit_event(Event::Registered { para_id, message_id, manager });
862			},
863			Err(reason) => {
864				Self::release_or_report(ticket, &manager, para_id);
865				info.state = RegistrationState::Reserved;
866				Paras::<T>::insert(para_id, info);
867				Self::deposit_event(Event::RegistrationFailed {
868					para_id,
869					message_id,
870					manager,
871					reason,
872				});
873			},
874		}
875
876		Ok(())
877	}
878
879	/// Lock a para the relay chain has seen produce a head.
880	fn on_head_noted(para_id: ParaId) -> DispatchResult {
881		let Some(mut info) = Paras::<T>::get(para_id) else {
882			Self::report_unexpected(UnexpectedKind::HeadNotedForUnknownPara { para_id });
883			return Ok(());
884		};
885
886		if info.locked.is_some() {
887			return Ok(());
888		}
889
890		if !matches!(info.state, RegistrationState::Registered { .. }) {
891			Self::report_unexpected(UnexpectedKind::HeadNotedForUnregisteredPara { para_id });
892		}
893
894		info.locked = Some(true);
895		Paras::<T>::insert(para_id, info);
896
897		Self::deposit_event(Event::ParaLocked { para_id });
898
899		Ok(())
900	}
901
902	/// Apply the relay chain's answer to a cancellation.
903	///
904	/// `Ok(())` means the authorization is gone, so the deposit goes back. The one refusal is
905	/// [`FailureReason::AlreadyRegistered`]: the code landed after all and the earlier verdict was
906	/// simply lost, so the para is recorded as registered and the deposit stays held.
907	///
908	/// Unlike a register response, an answer for a para that is no longer pending is expected
909	/// rather than unexpected: a verdict already in flight when the cancellation was sent settles
910	/// the registration first, and this then has nothing left to do.
911	fn on_cancel_response(para_id: ParaId, message_id: u64, outcome: Outcome) -> DispatchResult {
912		let Some(mut info) = Paras::<T>::get(para_id) else {
913			Self::report_unexpected(UnexpectedKind::CancelResponseForUnknownPara {
914				para_id,
915				message_id,
916			});
917			return Ok(());
918		};
919		let RegistrationState::Pending { ticket, message_id: expected, .. } = info.state else {
920			log::debug!(
921				target: "runtime::registrar-para",
922				"cancel response for para {para_id} which is no longer pending, dropping",
923			);
924			return Ok(());
925		};
926		if message_id != expected {
927			Self::report_unexpected(UnexpectedKind::StaleResponse {
928				para_id,
929				message_id,
930				expected,
931			});
932			return Ok(());
933		}
934
935		let manager = info.manager.clone();
936		match outcome {
937			Ok(()) => {
938				Self::release_or_report(ticket, &manager, para_id);
939				info.state = RegistrationState::Reserved;
940				Paras::<T>::insert(para_id, info);
941				Self::deposit_event(Event::RegistrationCancelled { para_id, message_id, manager });
942			},
943			Err(FailureReason::AlreadyRegistered) => {
944				info.state = RegistrationState::Registered { ticket };
945				Paras::<T>::insert(para_id, info);
946				Self::deposit_event(Event::Registered { para_id, message_id, manager });
947			},
948			// Nothing else is a cancellation the relay chain refuses, so leave the registration
949			// pending: the manager can ask again once the deadline comes round.
950			Err(reason) => {
951				Self::report_unexpected(UnexpectedKind::CancelRefused {
952					para_id,
953					message_id,
954					reason,
955				});
956			},
957		}
958
959		Ok(())
960	}
961
962	/// Apply the relay chain's verdict on a deregistration.
963	///
964	/// `Ok(())` releases every consideration and the entry with them; a refusal puts the para back
965	/// to [`RegistrationState::Registered`].
966	fn on_deregister_response(
967		para_id: ParaId,
968		message_id: u64,
969		outcome: Outcome,
970	) -> DispatchResult {
971		let Some(mut info) = Paras::<T>::get(para_id) else {
972			Self::report_unexpected(UnexpectedKind::DeregisterResponseForUnknownPara {
973				para_id,
974				message_id,
975			});
976			return Ok(());
977		};
978		let RegistrationState::Deregistering { ticket, message_id: expected, .. } = info.state
979		else {
980			Self::report_unexpected(UnexpectedKind::DeregisterResponseNotDeregistering {
981				para_id,
982				message_id,
983			});
984			return Ok(());
985		};
986		if message_id != expected {
987			Self::report_unexpected(UnexpectedKind::StaleResponse {
988				para_id,
989				message_id,
990				expected,
991			});
992			return Ok(());
993		}
994
995		let manager = info.manager.clone();
996		match outcome {
997			Ok(()) => {
998				Self::release_or_report(ticket, &manager, para_id);
999				Self::release_or_report(info.reservation, &manager, para_id);
1000				Paras::<T>::remove(para_id);
1001				Self::deposit_event(Event::Deregistered { para_id, message_id, manager });
1002			},
1003			Err(reason) => {
1004				info.state = RegistrationState::Registered { ticket };
1005				Paras::<T>::insert(para_id, info);
1006				Self::deposit_event(Event::DeregistrationFailed {
1007					para_id,
1008					message_id,
1009					manager,
1010					reason,
1011				});
1012			},
1013		}
1014
1015		Ok(())
1016	}
1017
1018	fn on_code_upgrade_response(
1019		para_id: ParaId,
1020		message_id: u64,
1021		outcome: Result<u32, FailureReason>,
1022	) -> DispatchResult {
1023		let _ = (para_id, message_id, outcome);
1024		// TODO(ahm-v2): settle the pending code upgrade from the relay chain's answer.
1025		Err(Error::<T>::Unimplemented.into())
1026	}
1027
1028	fn on_code_upgrade_scheduled(para_id: ParaId, message_id: u64) -> DispatchResult {
1029		let _ = (para_id, message_id);
1030		// TODO(ahm-v2): finish the code upgrade once the relay chain has scheduled it.
1031		Err(Error::<T>::Unimplemented.into())
1032	}
1033
1034	fn on_set_head_response(para_id: ParaId, message_id: u64, outcome: Outcome) -> DispatchResult {
1035		let _ = (para_id, message_id, outcome);
1036		// TODO(ahm-v2): settle the pending head update from the relay chain's answer.
1037		Err(Error::<T>::Unimplemented.into())
1038	}
1039}