1#![cfg_attr(not(feature = "std"), no_std)]
62
63extern crate alloc;
64
65use alloc::vec::Vec;
66use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
67use frame_support::{traits::Get, weights::Weight};
68pub use pallet::*;
69use polkadot_primitives::{HeadData, Id};
70use polkadot_runtime_parachains::paras::OnNewHead;
71use registrar_primitives::{
72 FailureReason, MessageToPara, MessageToParaV1, MessageToRelay, MessageToRelayV1, Outcome,
73 ParaId, ParachainRegistrar,
74};
75use scale_info::TypeInfo;
76use sp_core::H256;
77pub use weights::WeightInfo;
78
79pub mod weights;
80
81const LOG_TARGET: &str = "runtime::registrar-relay";
82
83#[cfg(feature = "runtime-benchmarks")]
84mod benchmarking;
85#[cfg(test)]
86mod mock;
87#[cfg(test)]
88mod tests;
89
90pub trait SendToPara {
91 #[allow(clippy::result_unit_err)]
96 fn send(message: MessageToPara) -> Result<(), ()>;
97}
98
99#[cfg(feature = "std")]
100impl SendToPara for () {
101 fn send(_message: MessageToPara) -> Result<(), ()> {
102 Ok(())
103 }
104}
105
106#[derive(
108 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo, MaxEncodedLen, Debug,
109)]
110#[scale_info(skip_type_params(MaxHeadDataSize))]
111pub struct PendingRegistration<AccountId, MaxHeadDataSize: Get<u32>> {
112 pub message_id: u64,
115 pub manager: AccountId,
117 pub genesis_head: frame_support::BoundedVec<u8, MaxHeadDataSize>,
119 pub code_hash: H256,
121 pub code_len: u32,
123}
124
125pub type PendingRegistrationOf<T> =
127 PendingRegistration<<T as frame_system::Config>::AccountId, <T as Config>::MaxHeadDataSize>;
128
129#[frame_support::pallet]
130pub mod pallet {
131 use super::*;
132 use frame_support::pallet_prelude::*;
133 use frame_system::pallet_prelude::*;
134 use sp_runtime::traits::{BlakeTwo256, Hash};
135
136 #[pallet::config]
137 pub trait Config: frame_system::Config {
138 #[allow(deprecated)]
140 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
141
142 type ParaOrigin: EnsureOrigin<Self::RuntimeOrigin>;
144
145 type SendToPara: SendToPara;
147
148 type Registrar: ParachainRegistrar<AccountId = Self::AccountId>;
150
151 #[pallet::constant]
155 type MaxHeadDataSize: Get<u32>;
156
157 #[pallet::constant]
161 type MaxCodeSize: Get<u32>;
162
163 #[pallet::constant]
167 type MaxPendingRegistrations: Get<u32>;
168
169 #[pallet::constant]
171 type UnsignedPriority: Get<TransactionPriority>;
172
173 type WeightInfo: WeightInfo;
175 }
176
177 #[pallet::pallet]
178 pub struct Pallet<T>(_);
179
180 #[pallet::storage]
184 pub type PendingRegistrations<T: Config> =
185 CountedStorageMap<_, Blake2_128Concat, ParaId, PendingRegistrationOf<T>>;
186
187 #[pallet::storage]
189 pub type AwaitingFirstHead<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, ()>;
190
191 #[pallet::event]
192 #[pallet::generate_deposit(pub(super) fn deposit_event)]
193 pub enum Event<T: Config> {
194 RegistrationPending { para_id: ParaId, message_id: u64, code_hash: H256 },
196 RegistrationRejected { para_id: ParaId, message_id: u64, reason: FailureReason },
198 Registered { para_id: ParaId, message_id: u64, manager: T::AccountId },
200 AuthorizationCancelled { para_id: ParaId, message_id: u64 },
202 CancellationRefused { para_id: ParaId, message_id: u64 },
204 ReportFailed { para_id: ParaId, message_id: u64 },
206 HeadNoted { para_id: ParaId },
208 HeadNoteFailed { para_id: ParaId },
210 Deregistered { para_id: ParaId, message_id: u64 },
212 DeregistrationRejected { para_id: ParaId, message_id: u64 },
214 }
215
216 #[pallet::error]
217 pub enum Error<T> {
218 NothingPending,
220 CodeHashMismatch,
222 CodeLenMismatch,
224 CodeTooLarge,
226 }
227
228 #[pallet::call]
229 impl<T: Config> Pallet<T> {
230 #[pallet::call_index(0)]
236 #[pallet::weight(match message {
237 MessageToRelay::V1(MessageToRelayV1::Register { genesis_head, .. }) =>
238 T::WeightInfo::receive_register(genesis_head.len() as u32),
239 MessageToRelay::V1(MessageToRelayV1::CancelRegistration { .. }) =>
240 T::WeightInfo::receive_cancel_registration(),
241 MessageToRelay::V1(MessageToRelayV1::Deregister { .. }) =>
242 T::WeightInfo::receive_deregister(),
243 MessageToRelay::V1(MessageToRelayV1::AuthorizeCodeUpgrade { .. }) |
244 MessageToRelay::V1(MessageToRelayV1::SetCurrentHead { .. }) => Weight::MAX,
245 })]
246 pub fn receive(
247 origin: OriginFor<T>,
248 message: MessageToRelay<T::AccountId>,
249 ) -> DispatchResult {
250 T::ParaOrigin::ensure_origin_or_root(origin)?;
251
252 match message {
253 MessageToRelay::V1(MessageToRelayV1::Register {
254 para_id,
255 message_id,
256 manager,
257 genesis_head,
258 code_hash,
259 code_len,
260 }) => Self::on_register_request(
261 para_id,
262 message_id,
263 manager,
264 genesis_head,
265 code_hash,
266 code_len,
267 ),
268 MessageToRelay::V1(MessageToRelayV1::CancelRegistration {
269 para_id,
270 message_id,
271 }) => Self::on_cancel_request(para_id, message_id),
272 MessageToRelay::V1(MessageToRelayV1::Deregister { para_id, message_id }) => {
273 Self::on_deregister_request(para_id, message_id)
274 },
275 MessageToRelay::V1(MessageToRelayV1::AuthorizeCodeUpgrade {
276 para_id,
277 message_id,
278 code_hash,
279 code_len,
280 }) => Self::on_authorize_code_upgrade_request(
281 para_id, message_id, code_hash, code_len,
282 ),
283 MessageToRelay::V1(MessageToRelayV1::SetCurrentHead {
284 para_id,
285 message_id,
286 head,
287 }) => Self::on_set_current_head_request(para_id, message_id, head),
288 }
289
290 Ok(())
291 }
292
293 #[pallet::call_index(1)]
298 #[pallet::authorize(Self::authorize_apply_authorized_code)]
299 #[pallet::weight_of_authorize(T::WeightInfo::authorize_apply_authorized_code(validation_code.len() as u32))]
300 #[pallet::weight(T::WeightInfo::apply_authorized_code(validation_code.len() as u32))]
301 pub fn apply_authorized_code(
302 origin: OriginFor<T>,
303 para_id: ParaId,
304 validation_code: Vec<u8>,
305 ) -> DispatchResultWithPostInfo {
306 ensure_authorized(origin)?;
307
308 let pending = Self::validate_pending_code(para_id, &validation_code)?;
309
310 T::Registrar::register(
311 pending.manager.clone(),
312 para_id,
313 pending.genesis_head.into_inner(),
314 validation_code,
315 )?;
316 PendingRegistrations::<T>::remove(para_id);
317
318 let message_id = pending.message_id;
319 Self::report_registration(para_id, message_id, Ok(()));
320 Self::deposit_event(Event::Registered {
321 para_id,
322 message_id,
323 manager: pending.manager,
324 });
325 Ok(Pays::No.into())
326 }
327 }
328
329 impl<T: Config> Pallet<T> {
330 #[allow(clippy::ptr_arg)]
335 pub fn authorize_apply_authorized_code(
336 _source: TransactionSource,
337 para_id: &ParaId,
338 validation_code: &Vec<u8>,
339 ) -> TransactionValidityWithRefund {
340 let pending = Self::validate_pending_code(*para_id, validation_code)
341 .map_err(|e| InvalidTransaction::Custom(Self::err_to_code(e)))?;
342
343 let validity = ValidTransaction::with_tag_prefix("RegistrarApplyAuthorizedCode")
346 .priority(T::UnsignedPriority::get())
347 .and_provides((*para_id, pending.code_hash))
348 .propagate(true)
349 .build()?;
350
351 Ok((validity, Weight::zero()))
352 }
353
354 fn on_register_request(
356 para_id: ParaId,
357 message_id: u64,
358 manager: T::AccountId,
359 genesis_head: Vec<u8>,
360 code_hash: H256,
361 code_len: u32,
362 ) {
363 let Ok(head_len) = u32::try_from(genesis_head.len()) else {
364 return Self::reject(para_id, message_id, FailureReason::InvalidOnboardingData);
365 };
366
367 if T::Registrar::is_registered(para_id) ||
368 PendingRegistrations::<T>::contains_key(para_id)
369 {
370 return Self::reject(para_id, message_id, FailureReason::AlreadyRegistered);
371 }
372 if PendingRegistrations::<T>::count() >= T::MaxPendingRegistrations::get() {
373 return Self::reject(para_id, message_id, FailureReason::TooManyPending);
374 }
375 if code_len > T::MaxCodeSize::get() ||
376 T::Registrar::check_onboarding(head_len, code_len).is_err()
377 {
378 return Self::reject(para_id, message_id, FailureReason::InvalidOnboardingData);
379 }
380 let Ok(genesis_head) = BoundedVec::try_from(genesis_head) else {
381 return Self::reject(para_id, message_id, FailureReason::InvalidOnboardingData);
382 };
383
384 PendingRegistrations::<T>::insert(
385 para_id,
386 PendingRegistration { message_id, manager, genesis_head, code_hash, code_len },
387 );
388
389 AwaitingFirstHead::<T>::insert(para_id, ());
390
391 Self::deposit_event(Event::RegistrationPending { para_id, message_id, code_hash });
392 }
393
394 fn reject(para_id: ParaId, message_id: u64, reason: FailureReason) {
396 Self::report_registration(para_id, message_id, Err(reason.clone()));
397 Self::deposit_event(Event::RegistrationRejected { para_id, message_id, reason });
398 }
399
400 fn on_cancel_request(para_id: ParaId, message_id: u64) {
405 PendingRegistrations::<T>::remove(para_id);
406
407 if T::Registrar::is_registered(para_id) {
408 Self::report_cancellation(
409 para_id,
410 message_id,
411 Err(FailureReason::AlreadyRegistered),
412 );
413 return Self::deposit_event(Event::CancellationRefused { para_id, message_id });
414 }
415
416 AwaitingFirstHead::<T>::remove(para_id);
417
418 Self::report_cancellation(para_id, message_id, Ok(()));
419 Self::deposit_event(Event::AuthorizationCancelled { para_id, message_id });
420 }
421
422 fn validate_pending_code(
424 para_id: ParaId,
425 validation_code: &[u8],
426 ) -> Result<PendingRegistrationOf<T>, Error<T>> {
427 let code_len =
429 u32::try_from(validation_code.len()).map_err(|_| Error::<T>::CodeTooLarge)?;
430 ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
431
432 let pending =
433 PendingRegistrations::<T>::get(para_id).ok_or(Error::<T>::NothingPending)?;
434 ensure!(code_len == pending.code_len, Error::<T>::CodeLenMismatch);
435 ensure!(
436 BlakeTwo256::hash(validation_code) == pending.code_hash,
437 Error::<T>::CodeHashMismatch
438 );
439
440 Ok(pending)
441 }
442
443 pub fn err_to_code(error: Error<T>) -> u8 {
445 match error {
446 Error::<T>::NothingPending => 0,
447 Error::<T>::CodeHashMismatch => 1,
448 Error::<T>::CodeLenMismatch => 2,
449 Error::<T>::CodeTooLarge => 3,
450 }
451 }
452
453 fn report_registration(para_id: ParaId, message_id: u64, outcome: Outcome) {
455 Self::report(
456 para_id,
457 message_id,
458 MessageToParaV1::RegisterResponse { para_id, message_id, outcome },
459 );
460 }
461
462 fn report_cancellation(para_id: ParaId, message_id: u64, outcome: Outcome) {
464 Self::report(
465 para_id,
466 message_id,
467 MessageToParaV1::CancelResponse { para_id, message_id, outcome },
468 );
469 }
470
471 fn on_deregister_request(para_id: ParaId, message_id: u64) {
475 if let Err(e) = T::Registrar::deregister(para_id) {
476 log::debug!(
477 target: LOG_TARGET,
478 "registry refused to drop para {para_id}: {e:?}",
479 );
480 Self::report_deregistration(
481 para_id,
482 message_id,
483 Err(FailureReason::NotDeregisterable),
484 );
485 return Self::deposit_event(Event::DeregistrationRejected { para_id, message_id });
486 }
487
488 AwaitingFirstHead::<T>::remove(para_id);
489 Self::report_deregistration(para_id, message_id, Ok(()));
490 Self::deposit_event(Event::Deregistered { para_id, message_id });
491 }
492
493 fn on_authorize_code_upgrade_request(
494 para_id: ParaId,
495 message_id: u64,
496 code_hash: H256,
497 code_len: u32,
498 ) {
499 let _ = (para_id, message_id, code_hash, code_len);
500 }
502
503 fn on_set_current_head_request(para_id: ParaId, message_id: u64, head: Vec<u8>) {
504 let _ = (para_id, message_id, head);
505 }
507
508 fn report_deregistration(para_id: ParaId, message_id: u64, outcome: Outcome) {
510 Self::report(
511 para_id,
512 message_id,
513 MessageToParaV1::DeregisterResponse { para_id, message_id, outcome },
514 );
515 }
516
517 #[allow(dead_code)]
518 fn report_code_upgrade(
519 para_id: ParaId,
520 message_id: u64,
521 outcome: Result<u32, FailureReason>,
522 ) {
523 Self::report(
524 para_id,
525 message_id,
526 MessageToParaV1::CodeUpgradeResponse { para_id, message_id, outcome },
527 );
528 }
529
530 #[allow(dead_code)]
531 fn report_code_upgrade_scheduled(para_id: ParaId, message_id: u64) {
532 Self::report(
533 para_id,
534 message_id,
535 MessageToParaV1::CodeUpgradeScheduled { para_id, message_id },
536 );
537 }
538
539 #[allow(dead_code)]
540 fn report_set_head(para_id: ParaId, message_id: u64, outcome: Outcome) {
541 Self::report(
542 para_id,
543 message_id,
544 MessageToParaV1::SetHeadResponse { para_id, message_id, outcome },
545 );
546 }
547
548 fn report(para_id: ParaId, message_id: u64, message: MessageToParaV1) {
553 if T::SendToPara::send(MessageToPara::V1(message)).is_err() {
554 log::error!(
555 target: LOG_TARGET,
556 "failed to report the outcome for para {para_id} back to the parachain",
557 );
558 Self::deposit_event(Event::ReportFailed { para_id, message_id });
559 }
560 }
561 }
562}
563
564impl<T: Config> OnNewHead for Pallet<T> {
569 fn on_new_head(id: Id, _head: &HeadData) -> Weight {
570 let para_id: ParaId = id.into();
571 if !AwaitingFirstHead::<T>::contains_key(para_id) {
572 return T::WeightInfo::on_new_head_already_noted();
573 }
574
575 if T::SendToPara::send(MessageToPara::V1(MessageToParaV1::HeadNoted { para_id })).is_err() {
576 log::error!(
577 target: LOG_TARGET,
578 "failed to tell the parachain about a new head for para {para_id}",
579 );
580 Self::deposit_event(Event::HeadNoteFailed { para_id });
581 } else {
582 AwaitingFirstHead::<T>::remove(para_id);
583 Self::deposit_event(Event::HeadNoted { para_id });
584 }
585
586 T::WeightInfo::on_new_head()
587 }
588}