referrerpolicy=no-referrer-when-downgrade

registrar_primitives/
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 shared primitives
19//!
20//! Types shared by the parachain registrar pallet (`pallet-registrar-para`) and relay-chain
21//! registrar pallet (`pallet-registrar-relay`). This crate is deliberately free of any FRAME,
22//! XCM, or network-specific dependency, so a single version of the wire types serves Westend,
23//! Kusama and Polkadot, and so both pallets can depend on it without forming a dependency cycle.
24//!
25//! For the same reason the types here are plain: a para id is a `u32` (byte-compatible with the
26//! relay chain's `Id` newtype), head data and validation code are `Vec<u8>`, and a validation
27//! code hash is an [`H256`] (what `ValidationCodeHash` wraps). The same holds for
28//! [`ParachainRegistrar`], the interface the relay pallet drives the registry through: conversion
29//! to the relay chain's own types happens in the pallet implementing it.
30
31#![cfg_attr(not(feature = "std"), no_std)]
32
33extern crate alloc;
34
35use alloc::vec::Vec;
36use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
37use scale_info::TypeInfo;
38use sp_core::H256;
39
40/// A parachain id.
41///
42/// Byte-compatible with the relay chain's `Id`, which is a transparent `u32` newtype.
43pub type ParaId = u32;
44
45/// Registrar control-plane messages sent to the relay chain.
46///
47/// The variant's `#[codec(index)]` is the on-wire version tag.
48#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo)]
49pub enum MessageToRelay<AccountId> {
50	/// Version 1 of the registrar control-plane messages to the relay chain.
51	#[codec(index = 0)]
52	V1(MessageToRelayV1<AccountId>),
53}
54
55/// Version 1 payloads for [`MessageToRelay`].
56#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo)]
57pub enum MessageToRelayV1<AccountId> {
58	/// Ask the relay chain to accept a registration for `para_id`.
59	///
60	/// The deposit for this registration is already held on the parachain; the relay chain takes
61	/// nothing. The validation code itself is not included: only its hash and length are, and the
62	/// blob is uploaded to the relay chain separately.
63	#[codec(index = 0)]
64	Register {
65		/// The para id being registered. Already reserved on the parachain.
66		para_id: ParaId,
67		/// The parachain's id for this message, echoed back in the response.
68		message_id: u64,
69		/// The account that manages this registration and holds the deposit on the parachain.
70		manager: AccountId,
71		/// The genesis head data of the new parachain.
72		genesis_head: Vec<u8>,
73		/// Blake2-256 hash of the validation code that will be uploaded.
74		code_hash: H256,
75		/// Length of the validation code that will be uploaded, in bytes.
76		///
77		/// The deposit on the parachain was computed from this, so the relay chain must reject any
78		/// blob whose length differs.
79		code_len: u32,
80	},
81	/// Ask the relay chain to drop the authorization it is holding for `para_id`.
82	///
83	/// Sent when the manager gives up on a registration whose validation code never arrived. The
84	/// relay chain never abandons an authorization by itself, so this is what ends a registration
85	/// that is going nowhere, and the manager pays for it. Answered with
86	/// [`MessageToParaV1::CancelResponse`].
87	#[codec(index = 1)]
88	CancelRegistration {
89		/// The para id whose authorization should be dropped.
90		para_id: ParaId,
91		/// The parachain's id for this message, echoed back in the response.
92		message_id: u64,
93	},
94	/// Ask the relay chain to drop `para_id` from the registry.
95	///
96	/// The parachain has already checked that it holds neither a lease nor a region, but only the
97	/// relay chain knows whether the para is still live, locked, or has messages left in flight,
98	/// so the deposits stay held until it answers with [`MessageToParaV1::DeregisterResponse`].
99	///
100	/// Idempotent, so this is also the retry when an answer never arrives: a para already gone or
101	/// on its way out is answered `Ok(())` rather than refused, which is what lets the parachain
102	/// tell a lost verdict from a refusal without a second kind of message.
103	#[codec(index = 2)]
104	Deregister {
105		/// The para id to drop.
106		para_id: ParaId,
107		/// The parachain's id for this message, echoed back in the response.
108		message_id: u64,
109	},
110	/// Ask the relay chain to authorize a validation code upgrade for `para_id`.
111	///
112	/// As with a registration, the blob itself is not sent: the relay chain is told which bytes to
113	/// accept and they are uploaded to it separately. Answered with
114	/// [`MessageToParaV1::CodeUpgradeResponse`].
115	#[codec(index = 3)]
116	AuthorizeCodeUpgrade {
117		/// The para id being upgraded.
118		para_id: ParaId,
119		/// The parachain's id for this message, echoed back in the response.
120		message_id: u64,
121		/// Blake2-256 hash of the validation code that will be uploaded.
122		code_hash: H256,
123		/// Length of the validation code that will be uploaded, in bytes.
124		code_len: u32,
125	},
126	/// Ask the relay chain to set the current head data of `para_id`.
127	///
128	/// The head data is small enough to travel with the request, so unlike a code upgrade this is
129	/// a single round trip. Answered with [`MessageToParaV1::SetHeadResponse`].
130	#[codec(index = 4)]
131	SetCurrentHead {
132		/// The para id whose head is being set.
133		para_id: ParaId,
134		/// The parachain's id for this message, echoed back in the response.
135		message_id: u64,
136		/// The new head data.
137		head: Vec<u8>,
138	},
139}
140
141/// Registrar report messages sent back to the parachain.
142///
143/// The variant's `#[codec(index)]` is the on-wire version tag.
144#[derive(
145	Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
146)]
147pub enum MessageToPara {
148	/// Version 1 of the registrar report messages to the parachain.
149	#[codec(index = 0)]
150	V1(MessageToParaV1),
151}
152
153/// Version 1 payloads for [`MessageToPara`].
154#[derive(
155	Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
156)]
157pub enum MessageToParaV1 {
158	/// Report how a registration requested with [`MessageToRelayV1::Register`] ended.
159	///
160	/// `para_id` correlates the response with its request: a parachain only sends
161	/// [`MessageToRelayV1::Register`] for a para id that is reserved and otherwise idle, so at
162	/// most one request per para id is ever in flight. `message_id` echoes the request's id on
163	/// top, tying the two together across chains and in events.
164	#[codec(index = 0)]
165	RegisterResponse {
166		/// The para id the report is about.
167		para_id: ParaId,
168		/// The id of the [`MessageToRelayV1::Register`] this answers, echoed back.
169		message_id: u64,
170		/// Whether the registration was applied on the relay chain.
171		outcome: Outcome,
172	},
173	/// Answer a [`MessageToRelayV1::CancelRegistration`].
174	///
175	/// `Ok(())` means the relay chain is no longer holding an authorization for this para id, so
176	/// the deposit can be released. The only refusal is
177	/// [`FailureReason::AlreadyRegistered`]: the code did land after all and the para is
178	/// registered, so the deposit stays where it is.
179	#[codec(index = 1)]
180	CancelResponse {
181		/// The para id the answer is about.
182		para_id: ParaId,
183		/// The id of the [`MessageToRelayV1::CancelRegistration`] this answers, echoed back.
184		message_id: u64,
185		/// Whether the authorization was dropped.
186		outcome: Outcome,
187	},
188	/// Answer a [`MessageToRelayV1::Deregister`].
189	///
190	/// `Ok(())` means the para is gone from the relay chain's registry, so every deposit the
191	/// parachain is holding for it can be released.
192	#[codec(index = 2)]
193	DeregisterResponse {
194		/// The para id the answer is about.
195		para_id: ParaId,
196		/// The id of the [`MessageToRelayV1::Deregister`] this answers, echoed back.
197		message_id: u64,
198		/// Whether the para was dropped.
199		outcome: Outcome,
200	},
201	/// Answer a [`MessageToRelayV1::AuthorizeCodeUpgrade`].
202	///
203	/// `Ok(expire_at)` means the relay chain is holding the authorization and will accept the blob
204	/// until that relay-chain block. The upgrade is not scheduled yet: that is reported separately
205	/// with [`MessageToParaV1::CodeUpgradeScheduled`] once the code lands.
206	#[codec(index = 3)]
207	CodeUpgradeResponse {
208		/// The para id the answer is about.
209		para_id: ParaId,
210		/// The id of the [`MessageToRelayV1::AuthorizeCodeUpgrade`] this answers, echoed back.
211		message_id: u64,
212		/// The relay-chain block the authorization lapses at, or why it was refused.
213		outcome: Result<u32, FailureReason>,
214	},
215	/// Report that an authorized code upgrade has been scheduled on the relay chain.
216	///
217	/// Sent when the blob is uploaded, which may be many blocks after the authorization and is not
218	/// something the parachain asked for, so this carries no outcome: a failure to upload simply
219	/// leaves the authorization to lapse.
220	#[codec(index = 4)]
221	CodeUpgradeScheduled {
222		/// The para id the report is about.
223		para_id: ParaId,
224		/// The id of the [`MessageToRelayV1::AuthorizeCodeUpgrade`] this concludes, echoed back.
225		message_id: u64,
226	},
227	/// Answer a [`MessageToRelayV1::SetCurrentHead`].
228	///
229	/// The parachain checks the head against its own mirror of the relay chain's limits first, so
230	/// a refusal here means the two have drifted apart.
231	#[codec(index = 5)]
232	SetHeadResponse {
233		/// The para id the answer is about.
234		para_id: ParaId,
235		/// The id of the [`MessageToRelayV1::SetCurrentHead`] this answers, echoed back.
236		message_id: u64,
237		/// Whether the head was set.
238		outcome: Outcome,
239	},
240	/// Note that `para_id` has produced a head on the relay chain.
241	///
242	/// Correlates with no request, so it carries no `message_id` and is not answered.
243	#[codec(index = 6)]
244	HeadNoted {
245		/// The para id that produced a head.
246		para_id: ParaId,
247	},
248}
249
250/// How a request ended.
251///
252/// `Ok(())` means the relay chain applied it, `Err(reason)` that it did not. Shared by every
253/// response in this protocol rather than one outcome type per request, the same way a pallet has
254/// one `Error` enum instead of one per extrinsic. Encodes as `0x00` for success and `0x01` plus
255/// the reason for failure.
256pub type Outcome = Result<(), FailureReason>;
257
258/// Why a request was rejected by the relay chain.
259#[derive(
260	Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
261)]
262pub enum FailureReason {
263	/// The relay chain already knows this para id.
264	///
265	/// Also the answer to a [`MessageToRelayV1::CancelRegistration`] that came too late, because
266	/// the validation code landed first.
267	#[codec(index = 0)]
268	AlreadyRegistered,
269	/// The head data or the declared code length is not acceptable to the relay chain.
270	#[codec(index = 1)]
271	InvalidOnboardingData,
272	// Index 2 is reserved for `NotRegistered`, which lands with the deregister flow.
273	/// The relay chain is already holding as many pending registrations as it will accept.
274	#[codec(index = 3)]
275	TooManyPending,
276	/// The relay chain will not drop this para: it is not an on-demand parathread, or its
277	/// offboarding could not be scheduled (e.g. a PVF pre-check is still in progress).
278	#[codec(index = 4)]
279	NotDeregisterable,
280}
281
282/// The parachain registry, as `pallet-registrar-relay` needs to see it.
283///
284/// Implemented by whichever pallet owns parachain registration, typically `paras_registrar` on the
285/// relay chain. Lives here so neither side of the protocol has to depend on the other.
286pub trait ParachainRegistrar {
287	/// The account id used to identify a registration's manager.
288	type AccountId;
289
290	/// Whether head data and code of these sizes could be onboarded right now.
291	///
292	/// Checked against the relay chain's live configuration so a doomed request can be rejected
293	/// before the user goes and uploads megabytes of code.
294	#[allow(clippy::result_unit_err)]
295	fn check_onboarding(head_len: u32, code_len: u32) -> Result<(), ()>;
296
297	/// Whether the relay chain already knows this para id.
298	fn is_registered(para_id: ParaId) -> bool;
299
300	/// Onboard `para_id` under `manager`.
301	///
302	/// No deposit is taken: the manager's funds are held on the chain running
303	/// `pallet-registrar-para`.
304	fn register(
305		manager: Self::AccountId,
306		para_id: ParaId,
307		genesis_head: Vec<u8>,
308		validation_code: Vec<u8>,
309	) -> sp_runtime::DispatchResult;
310
311	/// Drop `para_id` from the registry.
312	///
313	/// Idempotent: a para already gone, or still being cleaned up, answers `Ok(())` rather than
314	/// refusing, so a second [`MessageToRelayV1::Deregister`] settles instead of telling the
315	/// parachain the para is still there. Nothing here retries on its own: the parachain waits,
316	/// and if no answer turns up the manager sends the request again. That second request is how
317	/// the parachain learns the first answer never came, and it reports it as an unexpected event.
318	fn deregister(para_id: ParaId) -> sp_runtime::DispatchResult;
319
320	/// Leave `para_id` where [`Self::deregister`] does the most work and still succeeds.
321	#[cfg(feature = "runtime-benchmarks")]
322	fn setup_worst_case_deregister(para_id: ParaId) -> sp_runtime::DispatchResult;
323
324	/// Whether head data of this size is acceptable right now.
325	#[allow(clippy::result_unit_err)]
326	fn check_head_data(head_len: u32) -> Result<(), ()>;
327
328	/// Set the current head of `para_id`.
329	fn set_current_head(para_id: ParaId, head: Vec<u8>);
330
331	/// Whether `para_id` could take a code upgrade of this size right now.
332	#[allow(clippy::result_unit_err)]
333	fn check_code_upgrade(para_id: ParaId, code_len: u32) -> Result<(), ()>;
334
335	/// Schedule a validation code upgrade for `para_id`.
336	fn schedule_code_upgrade(
337		para_id: ParaId,
338		validation_code: Vec<u8>,
339	) -> sp_runtime::DispatchResult;
340}
341
342/// State of migrated para at the source chain.
343#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo)]
344pub enum MigratedParaState {
345	/// The para id is held by its manager, but nothing is registered on the relay chain.
346	Reserved,
347	/// The relay chain has onboarded this para.
348	Registered {
349		/// Length of the para's current head data, so the destination prices the registration
350		/// the way it prices a fresh one.
351		head_len: u32,
352	},
353}
354
355/// One para, as it arrives from source chain that used to own the registry.
356///
357/// Carries no deposit. [`ReceiveMigratedParas::receive_para`] takes the reservation deposit,
358/// and for a registered para the registration deposit, from `manager` at the destination's own
359/// prices.
360///
361/// Note: We recreate even if there is not enough fund to pay for the deposit so no RC para
362/// is dropped.
363#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo)]
364pub struct MigratedPara<AccountId> {
365	/// The para id.
366	pub para_id: ParaId,
367	/// The account that reserved the para id and controls it.
368	pub manager: AccountId,
369	/// Where this para id sits in the registration flow.
370	pub state: MigratedParaState,
371	/// Whether the manager is locked out of controlling this para. `None` until the lock is set
372	/// for the first time, and read as unlocked.
373	pub locked: Option<bool>,
374}
375
376/// Takes migrated para ids into the pallet that owns registration on the destination.
377pub trait ReceiveMigratedParas<AccountId> {
378	/// Take one para, charging its deposits at this chain's prices.
379	///
380	/// Note: We recreate even if there is not enough fund to pay for the deposit so no RC para
381	/// gets dropped.
382	fn receive_para(para: MigratedPara<AccountId>) -> sp_runtime::DispatchResult;
383
384	/// Adopt the next free para id from the source chain.
385	fn receive_next_free_para_id(para_id: ParaId);
386}
387
388impl<AccountId> ReceiveMigratedParas<AccountId> for () {
389	fn receive_para(_: MigratedPara<AccountId>) -> sp_runtime::DispatchResult {
390		Err(sp_runtime::DispatchError::Unavailable)
391	}
392
393	fn receive_next_free_para_id(_: ParaId) {}
394}