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::{
68 ensure,
69 traits::{Consideration, EnsureOrigin, Footprint},
70};
71use registrar_primitives::{
72 FailureReason, MessageToPara, MessageToParaV1, MessageToRelay, MessageToRelayV1, Outcome,
73 ParaId,
74};
75use scale_info::TypeInfo;
76use sp_core::H256;
77use sp_runtime::{
78 traits::{BlockNumberProvider, Saturating},
79 DispatchResult,
80};
81
82pub use pallet::*;
83pub use weights::WeightInfo;
84
85pub mod weights;
86
87#[cfg(feature = "runtime-benchmarks")]
88mod benchmarking;
89#[cfg(test)]
90mod mock;
91#[cfg(test)]
92mod tests;
93
94pub type ProvidedBlockNumberOf<T> =
100 <<T as Config>::BlockNumberProvider as BlockNumberProvider>::BlockNumber;
101
102pub trait SendToRelay {
104 type AccountId;
106
107 #[allow(clippy::result_unit_err)]
112 fn send(message: MessageToRelay<Self::AccountId>) -> Result<(), ()>;
113}
114
115#[cfg(feature = "std")]
116impl SendToRelay for () {
117 type AccountId = sp_runtime::AccountId32;
118
119 fn send(_message: MessageToRelay<Self::AccountId>) -> Result<(), ()> {
120 Ok(())
121 }
122}
123
124#[derive(
126 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
127)]
128pub enum RegistrationState<Ticket, BlockNumber> {
129 Reserved,
131 Pending {
133 ticket: Ticket,
135 cancellable_at: BlockNumber,
142 message_id: u64,
144 },
145 Registered {
147 ticket: Ticket,
149 },
150 Deregistering {
158 ticket: Ticket,
160 can_retry_after: BlockNumber,
164 message_id: u64,
166 },
167}
168
169#[derive(
171 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo, MaxEncodedLen, Debug,
172)]
173pub struct ParaInfo<AccountId, ReservationTicket, RegistrationTicket, BlockNumber> {
174 pub manager: AccountId,
176 pub reservation: ReservationTicket,
178 pub state: RegistrationState<RegistrationTicket, BlockNumber>,
180 pub locked: Option<bool>,
183}
184
185impl<AccountId, ReservationTicket, RegistrationTicket, BlockNumber>
186 ParaInfo<AccountId, ReservationTicket, RegistrationTicket, BlockNumber>
187{
188 pub fn is_locked(&self) -> bool {
190 self.locked.unwrap_or(false)
191 }
192}
193
194pub type ParaInfoOf<T> = ParaInfo<
196 <T as frame_system::Config>::AccountId,
197 <T as Config>::ReservationConsideration,
198 <T as Config>::RegistrationConsideration,
199 ProvidedBlockNumberOf<T>,
200>;
201
202#[frame_support::pallet]
203pub mod pallet {
204 use super::*;
205 use frame_support::pallet_prelude::{DispatchResult, *};
206 use frame_system::pallet_prelude::*;
207
208 #[pallet::config]
209 pub trait Config: frame_system::Config {
210 type ReservationConsideration: Consideration<Self::AccountId, Footprint>;
213
214 type RegistrationConsideration: Consideration<Self::AccountId, Footprint>;
217
218 type SendToRelay: SendToRelay<AccountId = Self::AccountId>;
220
221 type RelayOrigin: EnsureOrigin<Self::RuntimeOrigin>;
223
224 type ParachainOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = ParaId>;
226
227 #[pallet::constant]
232 type FirstPublicParaId: Get<ParaId>;
233
234 #[pallet::constant]
239 type MinCodeSize: Get<u32>;
240
241 #[pallet::constant]
245 type MaxCodeSize: Get<u32>;
246
247 #[pallet::constant]
251 type MaxHeadDataSize: Get<u32>;
252
253 #[pallet::constant]
258 type PendingDeadline: Get<ProvidedBlockNumberOf<Self>>;
259
260 type BlockNumberProvider: BlockNumberProvider;
266
267 type WeightInfo: WeightInfo;
269 }
270
271 #[pallet::pallet]
272 pub struct Pallet<T>(_);
273
274 #[pallet::composite_enum]
276 pub enum HoldReason {
277 #[codec(index = 0)]
279 ParaIdReservation,
280 #[codec(index = 1)]
282 Registration,
283 }
284
285 #[pallet::storage]
287 pub type NextFreeParaId<T: Config> = StorageValue<_, ParaId, ValueQuery>;
288
289 #[pallet::storage]
294 pub type NextMessageId<T: Config> = StorageValue<_, u64, ValueQuery>;
295
296 #[pallet::storage]
298 pub type Paras<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, ParaInfoOf<T>>;
299
300 #[pallet::event]
301 #[pallet::generate_deposit(pub(super) fn deposit_event)]
302 pub enum Event<T: Config> {
303 Reserved { para_id: ParaId, who: T::AccountId },
305 RegisterRequested { para_id: ParaId, message_id: u64, manager: T::AccountId },
307 Registered { para_id: ParaId, message_id: u64, manager: T::AccountId },
309 RegistrationFailed {
311 para_id: ParaId,
312 message_id: u64,
313 manager: T::AccountId,
314 reason: FailureReason,
315 },
316 CancelRequested { para_id: ParaId, message_id: u64, manager: T::AccountId },
319 RegistrationCancelled { para_id: ParaId, message_id: u64, manager: T::AccountId },
321 ParaLocked { para_id: ParaId },
323 ParaUnlocked { para_id: ParaId },
325 Unexpected(UnexpectedKind),
327 }
328
329 #[derive(
332 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
333 )]
334 pub enum UnexpectedKind {
335 RegisterResponseForUnknownPara { para_id: ParaId, message_id: u64 },
337 RegisterResponseNotPending { para_id: ParaId, message_id: u64 },
339 HeadNotedForUnknownPara { para_id: ParaId },
341 CancelResponseForUnknownPara { para_id: ParaId, message_id: u64 },
343 CancelRefused { para_id: ParaId, message_id: u64, reason: FailureReason },
346 HeadNotedForUnregisteredPara { para_id: ParaId },
348 StaleResponse { para_id: ParaId, message_id: u64, expected: u64 },
350 ResponseNeverArrived { para_id: ParaId, message_id: u64 },
352 }
353
354 #[pallet::error]
355 pub enum Error<T> {
356 NotReserved,
358 NotOwner,
360 AlreadyRegistered,
362 NotPending,
364 CannotCancelYet,
366 HeadDataTooLarge,
368 CodeTooLarge,
370 CodeTooSmall,
372 SendFailed,
374 NoFreeParaId,
376 ParaLocked,
378 AlreadyLocked,
380 NotLocked,
382 NotRegistered,
384 Unimplemented,
386 }
387
388 #[pallet::hooks]
389 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
390 fn integrity_test() {
391 assert!(
393 T::MinCodeSize::get() <= T::MaxCodeSize::get(),
394 "MinCodeSize ({}) must not exceed MaxCodeSize ({})",
395 T::MinCodeSize::get(),
396 T::MaxCodeSize::get(),
397 );
398 }
399 }
400
401 #[pallet::call]
402 impl<T: Config> Pallet<T> {
403 #[pallet::call_index(0)]
407 #[pallet::weight(T::WeightInfo::receive())]
408 pub fn receive(origin: OriginFor<T>, message: MessageToPara) -> DispatchResult {
409 T::RelayOrigin::ensure_origin_or_root(origin)?;
410
411 match message {
412 MessageToPara::V1(MessageToParaV1::RegisterResponse {
413 para_id,
414 message_id,
415 outcome,
416 }) => Self::on_register_response(para_id, message_id, outcome),
417 MessageToPara::V1(MessageToParaV1::CancelResponse {
418 para_id,
419 message_id,
420 outcome,
421 }) => Self::on_cancel_response(para_id, message_id, outcome),
422 MessageToPara::V1(MessageToParaV1::DeregisterResponse {
423 para_id,
424 message_id,
425 outcome,
426 }) => Self::on_deregister_response(para_id, message_id, outcome),
427 MessageToPara::V1(MessageToParaV1::CodeUpgradeResponse {
428 para_id,
429 message_id,
430 outcome,
431 }) => Self::on_code_upgrade_response(para_id, message_id, outcome),
432 MessageToPara::V1(MessageToParaV1::CodeUpgradeScheduled {
433 para_id,
434 message_id,
435 }) => Self::on_code_upgrade_scheduled(para_id, message_id),
436 MessageToPara::V1(MessageToParaV1::SetHeadResponse {
437 para_id,
438 message_id,
439 outcome,
440 }) => Self::on_set_head_response(para_id, message_id, outcome),
441 MessageToPara::V1(MessageToParaV1::HeadNoted { para_id }) => {
442 Self::on_head_noted(para_id)
443 },
444 }
445 }
446
447 #[pallet::call_index(1)]
452 #[pallet::weight(T::WeightInfo::reserve())]
453 pub fn reserve(origin: OriginFor<T>) -> DispatchResult {
454 let who = ensure_signed(origin)?;
455
456 let para_id = NextFreeParaId::<T>::get().max(T::FirstPublicParaId::get());
457 let next = para_id.checked_add(1).ok_or(Error::<T>::NoFreeParaId)?;
458 ensure!(!Paras::<T>::contains_key(para_id), Error::<T>::AlreadyRegistered);
459
460 let reservation = T::ReservationConsideration::new(&who, Footprint::from_parts(1, 0))?;
461
462 Paras::<T>::insert(
463 para_id,
464 ParaInfo {
465 manager: who.clone(),
466 reservation,
467 state: RegistrationState::Reserved,
468 locked: None,
469 },
470 );
471 NextFreeParaId::<T>::put(next);
472
473 Self::deposit_event(Event::Reserved { para_id, who });
474 Ok(())
475 }
476
477 #[pallet::call_index(2)]
489 #[pallet::weight(T::WeightInfo::register(genesis_head.len() as u32))]
490 pub fn register(
491 origin: OriginFor<T>,
492 para_id: ParaId,
493 genesis_head: Vec<u8>,
494 code_len: u32,
495 code_hash: H256,
496 ) -> DispatchResult {
497 let who = ensure_signed(origin)?;
498
499 let mut info = Paras::<T>::get(para_id).ok_or(Error::<T>::NotReserved)?;
500 ensure!(info.manager == who, Error::<T>::NotOwner);
501 ensure!(
502 matches!(info.state, RegistrationState::Reserved),
503 Error::<T>::AlreadyRegistered
504 );
505
506 let head_len = genesis_head.len() as u32;
507 ensure!(head_len <= T::MaxHeadDataSize::get(), Error::<T>::HeadDataTooLarge);
508 ensure!(code_len >= T::MinCodeSize::get(), Error::<T>::CodeTooSmall);
509 ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
510
511 let ticket = T::RegistrationConsideration::new(
512 &who,
513 Self::registration_footprint(head_len, code_len),
514 )?;
515
516 let cancellable_at = T::BlockNumberProvider::current_block_number()
517 .saturating_add(T::PendingDeadline::get());
518 let message_id = Self::next_message_id();
519 info.state = RegistrationState::Pending { ticket, cancellable_at, message_id };
520 Paras::<T>::insert(para_id, info);
521
522 T::SendToRelay::send(MessageToRelay::V1(MessageToRelayV1::Register {
524 para_id,
525 message_id,
526 manager: who.clone(),
527 genesis_head,
528 code_hash,
529 code_len,
530 }))
531 .map_err(|()| Error::<T>::SendFailed)?;
532
533 Self::deposit_event(Event::RegisterRequested { para_id, message_id, manager: who });
534 Ok(())
535 }
536
537 #[pallet::call_index(3)]
552 #[pallet::weight(T::WeightInfo::cancel_registration())]
553 pub fn cancel_registration(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
554 let who = ensure_signed(origin)?;
555
556 let mut info = Paras::<T>::get(para_id).ok_or(Error::<T>::NotReserved)?;
557 ensure!(info.manager == who, Error::<T>::NotOwner);
558 let RegistrationState::Pending { ticket, cancellable_at, message_id: awaited } =
559 info.state
560 else {
561 return Err(Error::<T>::NotPending.into());
562 };
563 let now = T::BlockNumberProvider::current_block_number();
564 ensure!(now >= cancellable_at, Error::<T>::CannotCancelYet);
565
566 Self::report_unexpected(UnexpectedKind::ResponseNeverArrived {
569 para_id,
570 message_id: awaited,
571 });
572
573 let message_id = Self::next_message_id();
576 info.state = RegistrationState::Pending {
577 ticket,
578 cancellable_at: now.saturating_add(T::PendingDeadline::get()),
579 message_id,
580 };
581 Paras::<T>::insert(para_id, info);
582
583 T::SendToRelay::send(MessageToRelay::V1(MessageToRelayV1::CancelRegistration {
585 para_id,
586 message_id,
587 }))
588 .map_err(|()| Error::<T>::SendFailed)?;
589
590 Self::deposit_event(Event::CancelRequested { para_id, message_id, manager: who });
591 Ok(())
592 }
593
594 #[pallet::call_index(4)]
596 #[pallet::weight(T::WeightInfo::add_lock())]
597 pub fn add_lock(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
598 let mut info = Paras::<T>::get(para_id).ok_or(Error::<T>::NotReserved)?;
599 Self::ensure_root_para_or_manager(origin, para_id, &info)?;
600 ensure!(!info.is_locked(), Error::<T>::AlreadyLocked);
601 ensure!(
602 matches!(info.state, RegistrationState::Registered { .. }),
603 Error::<T>::NotRegistered
604 );
605
606 info.locked = Some(true);
607 Paras::<T>::insert(para_id, info);
608
609 Self::deposit_event(Event::ParaLocked { para_id });
610 Ok(())
611 }
612
613 #[pallet::call_index(5)]
617 #[pallet::weight(T::WeightInfo::remove_lock())]
618 pub fn remove_lock(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
619 Self::ensure_root_or_para(origin, para_id)?;
620 let mut info = Paras::<T>::get(para_id).ok_or(Error::<T>::NotReserved)?;
621 ensure!(info.is_locked(), Error::<T>::NotLocked);
622
623 info.locked = Some(false);
624 Paras::<T>::insert(para_id, info);
625
626 Self::deposit_event(Event::ParaUnlocked { para_id });
627 Ok(())
628 }
629
630 #[pallet::call_index(6)]
631 #[pallet::weight(Weight::MAX)]
632 pub fn deregister(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
633 let _ = (origin, para_id);
634 Err(Error::<T>::Unimplemented.into())
637 }
638
639 #[pallet::call_index(7)]
640 #[pallet::weight(Weight::MAX)]
641 pub fn schedule_code_upgrade(
642 origin: OriginFor<T>,
643 para_id: ParaId,
644 code_hash: H256,
645 code_len: u32,
646 ) -> DispatchResult {
647 let _ = (origin, para_id, code_hash, code_len);
648 Err(Error::<T>::Unimplemented.into())
650 }
651
652 #[pallet::call_index(8)]
653 #[pallet::weight(Weight::MAX)]
654 pub fn set_current_head(
655 origin: OriginFor<T>,
656 para_id: ParaId,
657 head: Vec<u8>,
658 ) -> DispatchResult {
659 let _ = (origin, para_id, head);
660 Err(Error::<T>::Unimplemented.into())
662 }
663
664 #[pallet::call_index(9)]
665 #[pallet::weight(Weight::MAX)]
666 pub fn force_register(
667 origin: OriginFor<T>,
668 para_id: ParaId,
669 manager: T::AccountId,
670 genesis_head: Vec<u8>,
671 code_len: u32,
672 code_hash: H256,
673 ) -> DispatchResult {
674 let _ = (origin, para_id, manager, genesis_head, code_len, code_hash);
675 Err(Error::<T>::Unimplemented.into())
677 }
678 }
679}
680
681impl<T: Config> Pallet<T> {
682 pub fn registration_footprint(head_len: u32, code_len: u32) -> Footprint {
684 Footprint::from_parts(1, head_len.saturating_add(code_len) as usize)
685 }
686
687 fn ensure_root_para_or_manager(
689 origin: frame_system::pallet_prelude::OriginFor<T>,
690 para_id: ParaId,
691 info: &ParaInfoOf<T>,
692 ) -> DispatchResult {
693 if let Ok(id) = T::ParachainOrigin::ensure_origin(origin.clone()) {
694 ensure!(id == para_id, Error::<T>::NotOwner);
695 return Ok(());
696 }
697 if let Ok(who) = frame_system::ensure_signed(origin.clone()) {
698 ensure!(who == info.manager, Error::<T>::NotOwner);
699 ensure!(!info.is_locked(), Error::<T>::ParaLocked);
700 return Ok(());
701 }
702 frame_system::ensure_root(origin)?;
703 Ok(())
704 }
705
706 fn ensure_root_or_para(
708 origin: frame_system::pallet_prelude::OriginFor<T>,
709 para_id: ParaId,
710 ) -> DispatchResult {
711 if frame_system::ensure_root(origin.clone()).is_ok() {
712 return Ok(());
713 }
714 let id = T::ParachainOrigin::ensure_origin(origin)?;
715 ensure!(id == para_id, Error::<T>::NotOwner);
716 Ok(())
717 }
718
719 fn report_unexpected(kind: UnexpectedKind) {
721 log::error!(target: "runtime::registrar-para", "unexpected: {kind:?}");
722 Self::deposit_event(Event::Unexpected(kind));
723 }
724
725 fn next_message_id() -> u64 {
727 NextMessageId::<T>::mutate(|next| {
728 let id = *next;
729 *next = next.wrapping_add(1);
730 id
731 })
732 }
733
734 fn on_register_response(para_id: ParaId, message_id: u64, outcome: Outcome) -> DispatchResult {
740 let Some(mut info) = Paras::<T>::get(para_id) else {
741 Self::report_unexpected(UnexpectedKind::RegisterResponseForUnknownPara {
742 para_id,
743 message_id,
744 });
745 return Ok(());
746 };
747 let RegistrationState::Pending { ticket, message_id: expected, .. } = info.state else {
748 Self::report_unexpected(UnexpectedKind::RegisterResponseNotPending {
749 para_id,
750 message_id,
751 });
752 return Ok(());
753 };
754 if message_id != expected {
755 Self::report_unexpected(UnexpectedKind::StaleResponse {
756 para_id,
757 message_id,
758 expected,
759 });
760 return Ok(());
761 }
762
763 let manager = info.manager.clone();
764 match outcome {
765 Ok(()) => {
766 info.state = RegistrationState::Registered { ticket };
767 Paras::<T>::insert(para_id, info);
768 Self::deposit_event(Event::Registered { para_id, message_id, manager });
769 },
770 Err(reason) => {
771 ticket.drop(&info.manager)?;
772 info.state = RegistrationState::Reserved;
773 Paras::<T>::insert(para_id, info);
774 Self::deposit_event(Event::RegistrationFailed {
775 para_id,
776 message_id,
777 manager,
778 reason,
779 });
780 },
781 }
782
783 Ok(())
784 }
785
786 fn on_head_noted(para_id: ParaId) -> DispatchResult {
788 let Some(mut info) = Paras::<T>::get(para_id) else {
789 Self::report_unexpected(UnexpectedKind::HeadNotedForUnknownPara { para_id });
790 return Ok(());
791 };
792
793 if info.locked.is_some() {
794 return Ok(());
795 }
796
797 if !matches!(info.state, RegistrationState::Registered { .. }) {
798 Self::report_unexpected(UnexpectedKind::HeadNotedForUnregisteredPara { para_id });
799 }
800
801 info.locked = Some(true);
802 Paras::<T>::insert(para_id, info);
803
804 Self::deposit_event(Event::ParaLocked { para_id });
805
806 Ok(())
807 }
808
809 fn on_cancel_response(para_id: ParaId, message_id: u64, outcome: Outcome) -> DispatchResult {
819 let Some(mut info) = Paras::<T>::get(para_id) else {
820 Self::report_unexpected(UnexpectedKind::CancelResponseForUnknownPara {
821 para_id,
822 message_id,
823 });
824 return Ok(());
825 };
826 let RegistrationState::Pending { ticket, message_id: expected, .. } = info.state else {
827 log::debug!(
828 target: "runtime::registrar-para",
829 "cancel response for para {para_id} which is no longer pending, dropping",
830 );
831 return Ok(());
832 };
833 if message_id != expected {
834 Self::report_unexpected(UnexpectedKind::StaleResponse {
835 para_id,
836 message_id,
837 expected,
838 });
839 return Ok(());
840 }
841
842 let manager = info.manager.clone();
843 match outcome {
844 Ok(()) => {
845 ticket.drop(&info.manager)?;
846 info.state = RegistrationState::Reserved;
847 Paras::<T>::insert(para_id, info);
848 Self::deposit_event(Event::RegistrationCancelled { para_id, message_id, manager });
849 },
850 Err(FailureReason::AlreadyRegistered) => {
851 info.state = RegistrationState::Registered { ticket };
852 Paras::<T>::insert(para_id, info);
853 Self::deposit_event(Event::Registered { para_id, message_id, manager });
854 },
855 Err(reason) => {
858 Self::report_unexpected(UnexpectedKind::CancelRefused {
859 para_id,
860 message_id,
861 reason,
862 });
863 },
864 }
865
866 Ok(())
867 }
868
869 fn on_deregister_response(
870 para_id: ParaId,
871 message_id: u64,
872 outcome: Outcome,
873 ) -> DispatchResult {
874 let _ = (para_id, message_id, outcome);
875 Err(Error::<T>::Unimplemented.into())
877 }
878
879 fn on_code_upgrade_response(
880 para_id: ParaId,
881 message_id: u64,
882 outcome: Result<u32, FailureReason>,
883 ) -> DispatchResult {
884 let _ = (para_id, message_id, outcome);
885 Err(Error::<T>::Unimplemented.into())
887 }
888
889 fn on_code_upgrade_scheduled(para_id: ParaId, message_id: u64) -> DispatchResult {
890 let _ = (para_id, message_id);
891 Err(Error::<T>::Unimplemented.into())
893 }
894
895 fn on_set_head_response(para_id: ParaId, message_id: u64, outcome: Outcome) -> DispatchResult {
896 let _ = (para_id, message_id, outcome);
897 Err(Error::<T>::Unimplemented.into())
899 }
900}