1#![cfg_attr(not(feature = "std"), no_std)]
50
51extern crate alloc;
52
53use alloc::vec::Vec;
54use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
55use frame_support::traits::Get;
56use registrar_primitives::{
57 FailureReason, MessageToPara, MessageToParaV1, MessageToRelay, MessageToRelayV1, Outcome,
58 ParaId, ParachainRegistrar,
59};
60use scale_info::TypeInfo;
61use sp_core::H256;
62
63pub use pallet::*;
64pub use weights::WeightInfo;
65
66pub mod weights;
67
68#[cfg(feature = "runtime-benchmarks")]
69mod benchmarking;
70#[cfg(test)]
71mod mock;
72#[cfg(test)]
73mod tests;
74
75pub trait SendToPara {
76 #[allow(clippy::result_unit_err)]
81 fn send(message: MessageToPara) -> Result<(), ()>;
82}
83
84#[cfg(feature = "std")]
85impl SendToPara for () {
86 fn send(_message: MessageToPara) -> Result<(), ()> {
87 Ok(())
88 }
89}
90
91#[derive(
93 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo, MaxEncodedLen, Debug,
94)]
95#[scale_info(skip_type_params(MaxHeadDataSize))]
96pub struct PendingRegistration<AccountId, MaxHeadDataSize: Get<u32>> {
97 pub message_id: u64,
100 pub manager: AccountId,
102 pub genesis_head: frame_support::BoundedVec<u8, MaxHeadDataSize>,
104 pub code_hash: H256,
106 pub code_len: u32,
111}
112
113pub fn head_data_len<AccountId>(message: &MessageToRelay<AccountId>) -> u32 {
118 match message {
119 MessageToRelay::V1(MessageToRelayV1::Register { genesis_head, .. }) => {
120 genesis_head.len() as u32
121 },
122 MessageToRelay::V1(MessageToRelayV1::CancelRegistration { .. }) => 0,
123 }
124}
125
126pub type PendingRegistrationOf<T> =
128 PendingRegistration<<T as frame_system::Config>::AccountId, <T as Config>::MaxHeadDataSize>;
129
130#[frame_support::pallet]
131pub mod pallet {
132 use super::*;
133 use frame_support::pallet_prelude::*;
134 use frame_system::pallet_prelude::*;
135 use sp_runtime::traits::{BlakeTwo256, Hash};
136
137 #[pallet::config]
138 pub trait Config: frame_system::Config {
139 #[allow(deprecated)]
141 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
142
143 type ParaOrigin: EnsureOrigin<Self::RuntimeOrigin>;
145
146 type SendToPara: SendToPara;
148
149 type Registrar: ParachainRegistrar<AccountId = Self::AccountId>;
151
152 #[pallet::constant]
156 type MaxHeadDataSize: Get<u32>;
157
158 #[pallet::constant]
162 type MaxCodeSize: Get<u32>;
163
164 #[pallet::constant]
170 type MaxPendingRegistrations: Get<u32>;
171
172 #[pallet::constant]
174 type UnsignedPriority: Get<TransactionPriority>;
175
176 type WeightInfo: WeightInfo;
178 }
179
180 #[pallet::pallet]
181 pub struct Pallet<T>(_);
182
183 #[pallet::storage]
187 pub type PendingRegistrations<T: Config> =
188 CountedStorageMap<_, Blake2_128Concat, ParaId, PendingRegistrationOf<T>>;
189
190 #[pallet::event]
191 #[pallet::generate_deposit(pub(super) fn deposit_event)]
192 pub enum Event<T: Config> {
193 RegistrationPending { para_id: ParaId, message_id: u64, code_hash: H256 },
195 RegistrationRejected { para_id: ParaId, message_id: u64, reason: FailureReason },
197 Registered { para_id: ParaId, message_id: u64, manager: T::AccountId },
199 AuthorizationCancelled { para_id: ParaId, message_id: u64 },
201 CancellationRefused { para_id: ParaId, message_id: u64 },
203 ReportFailed { para_id: ParaId, message_id: u64 },
208 }
209
210 #[pallet::error]
211 pub enum Error<T> {
212 NothingPending,
214 CodeHashMismatch,
216 CodeLenMismatch,
218 CodeTooLarge,
220 UnexpectedMessage,
222 }
223
224 #[pallet::call]
225 impl<T: Config> Pallet<T> {
226 #[pallet::call_index(0)]
236 #[pallet::weight(T::WeightInfo::authorize_code(head_data_len(message)))]
237 pub fn authorize_code(
238 origin: OriginFor<T>,
239 message: MessageToRelay<T::AccountId>,
240 ) -> DispatchResult {
241 T::ParaOrigin::ensure_origin_or_root(origin)?;
242
243 match message {
244 MessageToRelay::V1(MessageToRelayV1::Register {
245 para_id,
246 message_id,
247 manager,
248 genesis_head,
249 code_hash,
250 code_len,
251 }) => {
252 Self::on_register_request(
253 para_id,
254 message_id,
255 manager,
256 genesis_head,
257 code_hash,
258 code_len,
259 );
260 Ok(())
261 },
262 _ => Err(Error::<T>::UnexpectedMessage.into()),
263 }
264 }
265
266 #[pallet::call_index(1)]
272 #[pallet::authorize(Self::authorize_apply_authorized_code)]
273 #[pallet::weight_of_authorize(T::WeightInfo::authorize_apply_authorized_code(validation_code.len() as u32))]
274 #[pallet::weight(T::WeightInfo::apply_authorized_code(validation_code.len() as u32))]
275 pub fn apply_authorized_code(
276 origin: OriginFor<T>,
277 para_id: ParaId,
278 validation_code: Vec<u8>,
279 ) -> DispatchResultWithPostInfo {
280 ensure_authorized(origin)?;
281
282 let pending = Self::validate_pending_code(para_id, &validation_code)?;
283
284 T::Registrar::register(
285 pending.manager.clone(),
286 para_id,
287 pending.genesis_head.into_inner(),
288 validation_code,
289 )?;
290 PendingRegistrations::<T>::remove(para_id);
291
292 let message_id = pending.message_id;
293 Self::report_registration(para_id, message_id, Ok(()));
294 Self::deposit_event(Event::Registered {
295 para_id,
296 message_id,
297 manager: pending.manager,
298 });
299 Ok(Pays::No.into())
300 }
301
302 #[pallet::call_index(2)]
316 #[pallet::weight(T::WeightInfo::cancel_authorization())]
317 pub fn cancel_authorization(
318 origin: OriginFor<T>,
319 message: MessageToRelay<T::AccountId>,
320 ) -> DispatchResult {
321 T::ParaOrigin::ensure_origin_or_root(origin)?;
322
323 match message {
324 MessageToRelay::V1(MessageToRelayV1::CancelRegistration {
325 para_id,
326 message_id,
327 }) => {
328 Self::on_cancel_request(para_id, message_id);
329 Ok(())
330 },
331 _ => Err(Error::<T>::UnexpectedMessage.into()),
332 }
333 }
334 }
335
336 impl<T: Config> Pallet<T> {
337 #[allow(clippy::ptr_arg)]
345 pub fn authorize_apply_authorized_code(
346 _source: TransactionSource,
347 para_id: &ParaId,
348 validation_code: &Vec<u8>,
349 ) -> TransactionValidityWithRefund {
350 let pending = Self::validate_pending_code(*para_id, validation_code)
351 .map_err(|e| InvalidTransaction::Custom(Self::err_to_code(e)))?;
352
353 let validity = ValidTransaction::with_tag_prefix("RegistrarApplyAuthorizedCode")
356 .priority(T::UnsignedPriority::get())
357 .and_provides((*para_id, pending.code_hash))
358 .propagate(true)
359 .build()?;
360
361 Ok((validity, Weight::zero()))
362 }
363
364 fn on_register_request(
366 para_id: ParaId,
367 message_id: u64,
368 manager: T::AccountId,
369 genesis_head: Vec<u8>,
370 code_hash: H256,
371 code_len: u32,
372 ) {
373 let Ok(head_len) = u32::try_from(genesis_head.len()) else {
374 return Self::reject(para_id, message_id, FailureReason::InvalidOnboardingData);
375 };
376
377 if T::Registrar::is_registered(para_id) ||
378 PendingRegistrations::<T>::contains_key(para_id)
379 {
380 return Self::reject(para_id, message_id, FailureReason::AlreadyRegistered);
381 }
382 if PendingRegistrations::<T>::count() >= T::MaxPendingRegistrations::get() {
383 return Self::reject(para_id, message_id, FailureReason::TooManyPending);
384 }
385 if code_len > T::MaxCodeSize::get() ||
386 T::Registrar::check_onboarding(head_len, code_len).is_err()
387 {
388 return Self::reject(para_id, message_id, FailureReason::InvalidOnboardingData);
389 }
390 let Ok(genesis_head) = BoundedVec::try_from(genesis_head) else {
391 return Self::reject(para_id, message_id, FailureReason::InvalidOnboardingData);
392 };
393
394 PendingRegistrations::<T>::insert(
395 para_id,
396 PendingRegistration { message_id, manager, genesis_head, code_hash, code_len },
397 );
398
399 Self::deposit_event(Event::RegistrationPending { para_id, message_id, code_hash });
400 }
401
402 fn reject(para_id: ParaId, message_id: u64, reason: FailureReason) {
404 Self::report_registration(para_id, message_id, Err(reason.clone()));
405 Self::deposit_event(Event::RegistrationRejected { para_id, message_id, reason });
406 }
407
408 fn on_cancel_request(para_id: ParaId, message_id: u64) {
416 PendingRegistrations::<T>::remove(para_id);
417
418 if T::Registrar::is_registered(para_id) {
419 Self::report_cancellation(
420 para_id,
421 message_id,
422 Err(FailureReason::AlreadyRegistered),
423 );
424 return Self::deposit_event(Event::CancellationRefused { para_id, message_id });
425 }
426
427 Self::report_cancellation(para_id, message_id, Ok(()));
428 Self::deposit_event(Event::AuthorizationCancelled { para_id, message_id });
429 }
430
431 fn validate_pending_code(
433 para_id: ParaId,
434 validation_code: &[u8],
435 ) -> Result<PendingRegistrationOf<T>, Error<T>> {
436 let code_len =
438 u32::try_from(validation_code.len()).map_err(|_| Error::<T>::CodeTooLarge)?;
439 ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
440
441 let pending =
442 PendingRegistrations::<T>::get(para_id).ok_or(Error::<T>::NothingPending)?;
443 ensure!(code_len == pending.code_len, Error::<T>::CodeLenMismatch);
444 ensure!(
445 BlakeTwo256::hash(validation_code) == pending.code_hash,
446 Error::<T>::CodeHashMismatch
447 );
448
449 Ok(pending)
450 }
451
452 pub fn err_to_code(error: Error<T>) -> u8 {
454 match error {
455 Error::<T>::NothingPending => 0,
456 Error::<T>::CodeHashMismatch => 1,
457 Error::<T>::CodeLenMismatch => 2,
458 Error::<T>::CodeTooLarge => 3,
459 Error::<T>::UnexpectedMessage => 4,
460 }
461 }
462
463 fn report_registration(para_id: ParaId, message_id: u64, outcome: Outcome) {
465 Self::report(
466 para_id,
467 message_id,
468 MessageToParaV1::RegisterResponse { para_id, message_id, outcome },
469 );
470 }
471
472 fn report_cancellation(para_id: ParaId, message_id: u64, outcome: Outcome) {
474 Self::report(
475 para_id,
476 message_id,
477 MessageToParaV1::CancelResponse { para_id, message_id, outcome },
478 );
479 }
480
481 fn report(para_id: ParaId, message_id: u64, message: MessageToParaV1) {
486 if T::SendToPara::send(MessageToPara::V1(message)).is_err() {
487 log::error!(
488 target: "runtime::registrar-relay",
489 "failed to report the outcome for para {para_id} back to the parachain",
490 );
491 Self::deposit_event(Event::ReportFailed { para_id, message_id });
492 }
493 }
494 }
495}