1#![cfg_attr(not(feature = "std"), no_std)]
57
58extern crate alloc;
59
60use alloc::vec::Vec;
61use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
62use frame_support::{traits::Get, weights::Weight};
63pub use pallet::*;
64use polkadot_primitives::{HeadData, Id};
65use polkadot_runtime_parachains::paras::OnNewHead;
66use registrar_primitives::{
67 FailureReason, MessageToPara, MessageToParaV1, MessageToRelay, MessageToRelayV1, Outcome,
68 ParaId, ParachainRegistrar,
69};
70use scale_info::TypeInfo;
71use sp_core::H256;
72pub use weights::WeightInfo;
73
74pub mod weights;
75
76#[cfg(feature = "runtime-benchmarks")]
77mod benchmarking;
78#[cfg(test)]
79mod mock;
80#[cfg(test)]
81mod tests;
82
83pub trait SendToPara {
84 #[allow(clippy::result_unit_err)]
89 fn send(message: MessageToPara) -> Result<(), ()>;
90}
91
92#[cfg(feature = "std")]
93impl SendToPara for () {
94 fn send(_message: MessageToPara) -> Result<(), ()> {
95 Ok(())
96 }
97}
98
99#[derive(
101 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo, MaxEncodedLen, Debug,
102)]
103#[scale_info(skip_type_params(MaxHeadDataSize))]
104pub struct PendingRegistration<AccountId, MaxHeadDataSize: Get<u32>> {
105 pub message_id: u64,
108 pub manager: AccountId,
110 pub genesis_head: frame_support::BoundedVec<u8, MaxHeadDataSize>,
112 pub code_hash: H256,
114 pub code_len: u32,
116}
117
118pub type PendingRegistrationOf<T> =
120 PendingRegistration<<T as frame_system::Config>::AccountId, <T as Config>::MaxHeadDataSize>;
121
122#[frame_support::pallet]
123pub mod pallet {
124 use super::*;
125 use frame_support::pallet_prelude::*;
126 use frame_system::pallet_prelude::*;
127 use sp_runtime::traits::{BlakeTwo256, Hash};
128
129 #[pallet::config]
130 pub trait Config: frame_system::Config {
131 #[allow(deprecated)]
133 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
134
135 type ParaOrigin: EnsureOrigin<Self::RuntimeOrigin>;
137
138 type SendToPara: SendToPara;
140
141 type Registrar: ParachainRegistrar<AccountId = Self::AccountId>;
143
144 #[pallet::constant]
148 type MaxHeadDataSize: Get<u32>;
149
150 #[pallet::constant]
154 type MaxCodeSize: Get<u32>;
155
156 #[pallet::constant]
160 type MaxPendingRegistrations: Get<u32>;
161
162 #[pallet::constant]
164 type UnsignedPriority: Get<TransactionPriority>;
165
166 type WeightInfo: WeightInfo;
168 }
169
170 #[pallet::pallet]
171 pub struct Pallet<T>(_);
172
173 #[pallet::storage]
177 pub type PendingRegistrations<T: Config> =
178 CountedStorageMap<_, Blake2_128Concat, ParaId, PendingRegistrationOf<T>>;
179
180 #[pallet::storage]
182 pub type AwaitingFirstHead<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, ()>;
183
184 #[pallet::event]
185 #[pallet::generate_deposit(pub(super) fn deposit_event)]
186 pub enum Event<T: Config> {
187 RegistrationPending { para_id: ParaId, message_id: u64, code_hash: H256 },
189 RegistrationRejected { para_id: ParaId, message_id: u64, reason: FailureReason },
191 Registered { para_id: ParaId, message_id: u64, manager: T::AccountId },
193 AuthorizationCancelled { para_id: ParaId, message_id: u64 },
195 CancellationRefused { para_id: ParaId, message_id: u64 },
197 ReportFailed { para_id: ParaId, message_id: u64 },
199 HeadNoted { para_id: ParaId },
201 HeadNoteFailed { para_id: ParaId },
203 }
204
205 #[pallet::error]
206 pub enum Error<T> {
207 NothingPending,
209 CodeHashMismatch,
211 CodeLenMismatch,
213 CodeTooLarge,
215 }
216
217 #[pallet::call]
218 impl<T: Config> Pallet<T> {
219 #[pallet::call_index(0)]
225 #[pallet::weight(match message {
226 MessageToRelay::V1(MessageToRelayV1::Register { genesis_head, .. }) =>
227 T::WeightInfo::receive_register(genesis_head.len() as u32),
228 MessageToRelay::V1(MessageToRelayV1::CancelRegistration { .. }) =>
229 T::WeightInfo::receive_cancel_registration(),
230 MessageToRelay::V1(MessageToRelayV1::Deregister { .. }) |
231 MessageToRelay::V1(MessageToRelayV1::AuthorizeCodeUpgrade { .. }) |
232 MessageToRelay::V1(MessageToRelayV1::SetCurrentHead { .. }) => Weight::MAX,
233 })]
234 pub fn receive(
235 origin: OriginFor<T>,
236 message: MessageToRelay<T::AccountId>,
237 ) -> DispatchResult {
238 T::ParaOrigin::ensure_origin_or_root(origin)?;
239
240 match message {
241 MessageToRelay::V1(MessageToRelayV1::Register {
242 para_id,
243 message_id,
244 manager,
245 genesis_head,
246 code_hash,
247 code_len,
248 }) => Self::on_register_request(
249 para_id,
250 message_id,
251 manager,
252 genesis_head,
253 code_hash,
254 code_len,
255 ),
256 MessageToRelay::V1(MessageToRelayV1::CancelRegistration {
257 para_id,
258 message_id,
259 }) => Self::on_cancel_request(para_id, message_id),
260 MessageToRelay::V1(MessageToRelayV1::Deregister { para_id, message_id }) => {
261 Self::on_deregister_request(para_id, message_id)
262 },
263 MessageToRelay::V1(MessageToRelayV1::AuthorizeCodeUpgrade {
264 para_id,
265 message_id,
266 code_hash,
267 code_len,
268 }) => Self::on_authorize_code_upgrade_request(
269 para_id, message_id, code_hash, code_len,
270 ),
271 MessageToRelay::V1(MessageToRelayV1::SetCurrentHead {
272 para_id,
273 message_id,
274 head,
275 }) => Self::on_set_current_head_request(para_id, message_id, head),
276 }
277
278 Ok(())
279 }
280
281 #[pallet::call_index(1)]
286 #[pallet::authorize(Self::authorize_apply_authorized_code)]
287 #[pallet::weight_of_authorize(T::WeightInfo::authorize_apply_authorized_code(validation_code.len() as u32))]
288 #[pallet::weight(T::WeightInfo::apply_authorized_code(validation_code.len() as u32))]
289 pub fn apply_authorized_code(
290 origin: OriginFor<T>,
291 para_id: ParaId,
292 validation_code: Vec<u8>,
293 ) -> DispatchResultWithPostInfo {
294 ensure_authorized(origin)?;
295
296 let pending = Self::validate_pending_code(para_id, &validation_code)?;
297
298 T::Registrar::register(
299 pending.manager.clone(),
300 para_id,
301 pending.genesis_head.into_inner(),
302 validation_code,
303 )?;
304 PendingRegistrations::<T>::remove(para_id);
305
306 let message_id = pending.message_id;
307 Self::report_registration(para_id, message_id, Ok(()));
308 Self::deposit_event(Event::Registered {
309 para_id,
310 message_id,
311 manager: pending.manager,
312 });
313 Ok(Pays::No.into())
314 }
315 }
316
317 impl<T: Config> Pallet<T> {
318 #[allow(clippy::ptr_arg)]
323 pub fn authorize_apply_authorized_code(
324 _source: TransactionSource,
325 para_id: &ParaId,
326 validation_code: &Vec<u8>,
327 ) -> TransactionValidityWithRefund {
328 let pending = Self::validate_pending_code(*para_id, validation_code)
329 .map_err(|e| InvalidTransaction::Custom(Self::err_to_code(e)))?;
330
331 let validity = ValidTransaction::with_tag_prefix("RegistrarApplyAuthorizedCode")
334 .priority(T::UnsignedPriority::get())
335 .and_provides((*para_id, pending.code_hash))
336 .propagate(true)
337 .build()?;
338
339 Ok((validity, Weight::zero()))
340 }
341
342 fn on_register_request(
344 para_id: ParaId,
345 message_id: u64,
346 manager: T::AccountId,
347 genesis_head: Vec<u8>,
348 code_hash: H256,
349 code_len: u32,
350 ) {
351 let Ok(head_len) = u32::try_from(genesis_head.len()) else {
352 return Self::reject(para_id, message_id, FailureReason::InvalidOnboardingData);
353 };
354
355 if T::Registrar::is_registered(para_id) ||
356 PendingRegistrations::<T>::contains_key(para_id)
357 {
358 return Self::reject(para_id, message_id, FailureReason::AlreadyRegistered);
359 }
360 if PendingRegistrations::<T>::count() >= T::MaxPendingRegistrations::get() {
361 return Self::reject(para_id, message_id, FailureReason::TooManyPending);
362 }
363 if code_len > T::MaxCodeSize::get() ||
364 T::Registrar::check_onboarding(head_len, code_len).is_err()
365 {
366 return Self::reject(para_id, message_id, FailureReason::InvalidOnboardingData);
367 }
368 let Ok(genesis_head) = BoundedVec::try_from(genesis_head) else {
369 return Self::reject(para_id, message_id, FailureReason::InvalidOnboardingData);
370 };
371
372 PendingRegistrations::<T>::insert(
373 para_id,
374 PendingRegistration { message_id, manager, genesis_head, code_hash, code_len },
375 );
376
377 AwaitingFirstHead::<T>::insert(para_id, ());
378
379 Self::deposit_event(Event::RegistrationPending { para_id, message_id, code_hash });
380 }
381
382 fn reject(para_id: ParaId, message_id: u64, reason: FailureReason) {
384 Self::report_registration(para_id, message_id, Err(reason.clone()));
385 Self::deposit_event(Event::RegistrationRejected { para_id, message_id, reason });
386 }
387
388 fn on_cancel_request(para_id: ParaId, message_id: u64) {
393 PendingRegistrations::<T>::remove(para_id);
394
395 if T::Registrar::is_registered(para_id) {
396 Self::report_cancellation(
397 para_id,
398 message_id,
399 Err(FailureReason::AlreadyRegistered),
400 );
401 return Self::deposit_event(Event::CancellationRefused { para_id, message_id });
402 }
403
404 AwaitingFirstHead::<T>::remove(para_id);
405
406 Self::report_cancellation(para_id, message_id, Ok(()));
407 Self::deposit_event(Event::AuthorizationCancelled { para_id, message_id });
408 }
409
410 fn validate_pending_code(
412 para_id: ParaId,
413 validation_code: &[u8],
414 ) -> Result<PendingRegistrationOf<T>, Error<T>> {
415 let code_len =
417 u32::try_from(validation_code.len()).map_err(|_| Error::<T>::CodeTooLarge)?;
418 ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
419
420 let pending =
421 PendingRegistrations::<T>::get(para_id).ok_or(Error::<T>::NothingPending)?;
422 ensure!(code_len == pending.code_len, Error::<T>::CodeLenMismatch);
423 ensure!(
424 BlakeTwo256::hash(validation_code) == pending.code_hash,
425 Error::<T>::CodeHashMismatch
426 );
427
428 Ok(pending)
429 }
430
431 pub fn err_to_code(error: Error<T>) -> u8 {
433 match error {
434 Error::<T>::NothingPending => 0,
435 Error::<T>::CodeHashMismatch => 1,
436 Error::<T>::CodeLenMismatch => 2,
437 Error::<T>::CodeTooLarge => 3,
438 }
439 }
440
441 fn report_registration(para_id: ParaId, message_id: u64, outcome: Outcome) {
443 Self::report(
444 para_id,
445 message_id,
446 MessageToParaV1::RegisterResponse { para_id, message_id, outcome },
447 );
448 }
449
450 fn report_cancellation(para_id: ParaId, message_id: u64, outcome: Outcome) {
452 Self::report(
453 para_id,
454 message_id,
455 MessageToParaV1::CancelResponse { para_id, message_id, outcome },
456 );
457 }
458
459 fn on_deregister_request(para_id: ParaId, message_id: u64) {
460 let _ = (para_id, message_id);
461 }
463
464 fn on_authorize_code_upgrade_request(
465 para_id: ParaId,
466 message_id: u64,
467 code_hash: H256,
468 code_len: u32,
469 ) {
470 let _ = (para_id, message_id, code_hash, code_len);
471 }
473
474 fn on_set_current_head_request(para_id: ParaId, message_id: u64, head: Vec<u8>) {
475 let _ = (para_id, message_id, head);
476 }
478
479 #[allow(dead_code)]
480 fn report_deregistration(para_id: ParaId, message_id: u64, outcome: Outcome) {
481 Self::report(
482 para_id,
483 message_id,
484 MessageToParaV1::DeregisterResponse { para_id, message_id, outcome },
485 );
486 }
487
488 #[allow(dead_code)]
489 fn report_code_upgrade(
490 para_id: ParaId,
491 message_id: u64,
492 outcome: Result<u32, FailureReason>,
493 ) {
494 Self::report(
495 para_id,
496 message_id,
497 MessageToParaV1::CodeUpgradeResponse { para_id, message_id, outcome },
498 );
499 }
500
501 #[allow(dead_code)]
502 fn report_code_upgrade_scheduled(para_id: ParaId, message_id: u64) {
503 Self::report(
504 para_id,
505 message_id,
506 MessageToParaV1::CodeUpgradeScheduled { para_id, message_id },
507 );
508 }
509
510 #[allow(dead_code)]
511 fn report_set_head(para_id: ParaId, message_id: u64, outcome: Outcome) {
512 Self::report(
513 para_id,
514 message_id,
515 MessageToParaV1::SetHeadResponse { para_id, message_id, outcome },
516 );
517 }
518
519 fn report(para_id: ParaId, message_id: u64, message: MessageToParaV1) {
524 if T::SendToPara::send(MessageToPara::V1(message)).is_err() {
525 log::error!(
526 target: "runtime::registrar-relay",
527 "failed to report the outcome for para {para_id} back to the parachain",
528 );
529 Self::deposit_event(Event::ReportFailed { para_id, message_id });
530 }
531 }
532 }
533}
534
535impl<T: Config> OnNewHead for Pallet<T> {
540 fn on_new_head(id: Id, _head: &HeadData) -> Weight {
541 let para_id: ParaId = id.into();
542 if !AwaitingFirstHead::<T>::contains_key(para_id) {
543 return T::WeightInfo::on_new_head_already_noted();
544 }
545
546 if T::SendToPara::send(MessageToPara::V1(MessageToParaV1::HeadNoted { para_id })).is_err() {
547 log::error!(
548 target: "runtime::registrar-relay",
549 "failed to tell the parachain about a new head for para {para_id}",
550 );
551 Self::deposit_event(Event::HeadNoteFailed { para_id });
552 } else {
553 AwaitingFirstHead::<T>::remove(para_id);
554 Self::deposit_event(Event::HeadNoted { para_id });
555 }
556
557 T::WeightInfo::on_new_head()
558 }
559}