1use crate::{
111 configuration,
112 inclusion::{QueueFootprinter, UmpQueueId},
113 initializer::SessionChangeNotification,
114 shared,
115};
116use alloc::{collections::btree_set::BTreeSet, vec::Vec};
117use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec};
118use codec::{Decode, Encode};
119use core::{cmp, mem};
120use pezframe_support::{
121 pezpallet_prelude::*,
122 traits::{EnsureOriginWithArg, EstimateNextSessionRotation},
123 DefaultNoBound,
124};
125use pezframe_system::pezpallet_prelude::*;
126use pezkuwi_primitives::{
127 ConsensusLog, HeadData, Id as ParaId, PvfCheckStatement, SessionIndex, UpgradeGoAhead,
128 UpgradeRestriction, ValidationCode, ValidationCodeHash, ValidatorSignature, MIN_CODE_SIZE,
129};
130use pezsp_core::RuntimeDebug;
131use pezsp_runtime::{
132 traits::{AppVerify, One, Saturating},
133 DispatchResult, SaturatedConversion,
134};
135use scale_info::{Type, TypeInfo};
136
137use serde::{Deserialize, Serialize};
138
139pub use crate::Origin as TeyrchainOrigin;
140
141#[cfg(feature = "runtime-benchmarks")]
142pub mod benchmarking;
143
144#[cfg(test)]
145pub(crate) mod tests;
146
147pub use pezpallet::*;
148
149const LOG_TARGET: &str = "runtime::paras";
150
151#[derive(Default, Encode, Decode, TypeInfo)]
153#[cfg_attr(test, derive(Debug, Clone, PartialEq))]
154pub struct ReplacementTimes<N> {
155 expected_at: N,
159 activated_at: N,
163}
164
165#[derive(Default, Encode, Decode, TypeInfo)]
168#[cfg_attr(test, derive(Debug, Clone, PartialEq))]
169pub struct ParaPastCodeMeta<N> {
170 upgrade_times: Vec<ReplacementTimes<N>>,
176 last_pruned: Option<N>,
179}
180
181#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
187pub enum ParaLifecycle {
188 Onboarding,
190 Parathread,
192 Teyrchain,
194 UpgradingParathread,
196 DowngradingTeyrchain,
198 OffboardingParathread,
200 OffboardingTeyrchain,
202}
203
204impl ParaLifecycle {
205 pub fn is_onboarding(&self) -> bool {
209 matches!(self, ParaLifecycle::Onboarding)
210 }
211
212 pub fn is_stable(&self) -> bool {
215 matches!(self, ParaLifecycle::Parathread | ParaLifecycle::Teyrchain)
216 }
217
218 pub fn is_teyrchain(&self) -> bool {
222 matches!(
223 self,
224 ParaLifecycle::Teyrchain
225 | ParaLifecycle::DowngradingTeyrchain
226 | ParaLifecycle::OffboardingTeyrchain
227 )
228 }
229
230 pub fn is_parathread(&self) -> bool {
234 matches!(
235 self,
236 ParaLifecycle::Parathread
237 | ParaLifecycle::UpgradingParathread
238 | ParaLifecycle::OffboardingParathread
239 )
240 }
241
242 pub fn is_offboarding(&self) -> bool {
244 matches!(self, ParaLifecycle::OffboardingParathread | ParaLifecycle::OffboardingTeyrchain)
245 }
246
247 pub fn is_transitioning(&self) -> bool {
249 !Self::is_stable(self)
250 }
251}
252
253impl<N: Ord + Copy + PartialEq> ParaPastCodeMeta<N> {
254 pub(crate) fn note_replacement(&mut self, expected_at: N, activated_at: N) {
256 self.upgrade_times.push(ReplacementTimes { expected_at, activated_at })
257 }
258
259 fn is_empty(&self) -> bool {
261 self.upgrade_times.is_empty()
262 }
263
264 #[cfg(test)]
267 fn most_recent_change(&self) -> Option<N> {
268 self.upgrade_times.last().map(|x| x.expected_at)
269 }
270
271 fn prune_up_to(&'_ mut self, max: N) -> impl Iterator<Item = N> + '_ {
282 let to_prune = self.upgrade_times.iter().take_while(|t| t.activated_at <= max).count();
283 let drained = if to_prune == 0 {
284 self.upgrade_times.drain(self.upgrade_times.len()..)
286 } else {
287 self.last_pruned = Some(self.upgrade_times[to_prune - 1].activated_at);
289 self.upgrade_times.drain(..to_prune)
290 };
291
292 drained.map(|times| times.expected_at)
293 }
294}
295
296#[derive(
298 PartialEq,
299 Eq,
300 Clone,
301 Encode,
302 Decode,
303 DecodeWithMemTracking,
304 RuntimeDebug,
305 TypeInfo,
306 Serialize,
307 Deserialize,
308)]
309pub struct ParaGenesisArgs {
310 pub genesis_head: HeadData,
312 pub validation_code: ValidationCode,
314 #[serde(rename = "teyrchain")]
316 pub para_kind: ParaKind,
317}
318
319#[derive(DecodeWithMemTracking, PartialEq, Eq, Clone, RuntimeDebug)]
321pub enum ParaKind {
322 Parathread,
323 Teyrchain,
324}
325
326impl Serialize for ParaKind {
327 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
328 where
329 S: serde::Serializer,
330 {
331 match self {
332 ParaKind::Teyrchain => serializer.serialize_bool(true),
333 ParaKind::Parathread => serializer.serialize_bool(false),
334 }
335 }
336}
337
338impl<'de> Deserialize<'de> for ParaKind {
339 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
340 where
341 D: serde::Deserializer<'de>,
342 {
343 match serde::de::Deserialize::deserialize(deserializer) {
344 Ok(true) => Ok(ParaKind::Teyrchain),
345 Ok(false) => Ok(ParaKind::Parathread),
346 _ => Err(serde::de::Error::custom("invalid ParaKind serde representation")),
347 }
348 }
349}
350
351impl Encode for ParaKind {
354 fn size_hint(&self) -> usize {
355 true.size_hint()
356 }
357
358 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
359 match self {
360 ParaKind::Teyrchain => true.using_encoded(f),
361 ParaKind::Parathread => false.using_encoded(f),
362 }
363 }
364}
365
366impl Decode for ParaKind {
367 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
368 match bool::decode(input) {
369 Ok(true) => Ok(ParaKind::Teyrchain),
370 Ok(false) => Ok(ParaKind::Parathread),
371 _ => Err("Invalid ParaKind representation".into()),
372 }
373 }
374}
375
376impl TypeInfo for ParaKind {
377 type Identity = bool;
378 fn type_info() -> Type {
379 bool::type_info()
380 }
381}
382
383#[derive(Debug, Encode, Decode, TypeInfo)]
386pub(crate) enum PvfCheckCause<BlockNumber> {
387 Onboarding(ParaId),
389 Upgrade {
391 id: ParaId,
394 included_at: BlockNumber,
403 upgrade_strategy: UpgradeStrategy,
408 },
409}
410
411#[derive(Debug, Copy, Clone, PartialEq, TypeInfo, Decode, Encode)]
419pub enum UpgradeStrategy {
420 SetGoAheadSignal,
425 ApplyAtExpectedBlock,
429}
430
431impl<BlockNumber> PvfCheckCause<BlockNumber> {
432 fn para_id(&self) -> ParaId {
434 match *self {
435 PvfCheckCause::Onboarding(id) => id,
436 PvfCheckCause::Upgrade { id, .. } => id,
437 }
438 }
439}
440
441#[derive(Copy, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
443enum PvfCheckOutcome {
444 Accepted,
445 Rejected,
446}
447
448#[derive(Encode, Decode, TypeInfo)]
450pub(crate) struct PvfCheckActiveVoteState<BlockNumber> {
451 votes_accept: BitVec<u8, BitOrderLsb0>,
457 votes_reject: BitVec<u8, BitOrderLsb0>,
458
459 age: SessionIndex,
462 created_at: BlockNumber,
464 causes: Vec<PvfCheckCause<BlockNumber>>,
466}
467
468impl<BlockNumber> PvfCheckActiveVoteState<BlockNumber> {
469 fn new(now: BlockNumber, n_validators: usize, cause: PvfCheckCause<BlockNumber>) -> Self {
472 let mut causes = Vec::with_capacity(1);
473 causes.push(cause);
474 Self {
475 created_at: now,
476 votes_accept: bitvec::bitvec![u8, BitOrderLsb0; 0; n_validators],
477 votes_reject: bitvec::bitvec![u8, BitOrderLsb0; 0; n_validators],
478 age: 0,
479 causes,
480 }
481 }
482
483 fn reinitialize_ballots(&mut self, n_validators: usize) {
486 let clear_and_resize = |v: &mut BitVec<_, _>| {
487 v.clear();
488 v.resize(n_validators, false);
489 };
490 clear_and_resize(&mut self.votes_accept);
491 clear_and_resize(&mut self.votes_reject);
492 }
493
494 fn has_vote(&self, validator_index: usize) -> Option<bool> {
497 let accept_vote = self.votes_accept.get(validator_index)?;
498 let reject_vote = self.votes_reject.get(validator_index)?;
499 Some(*accept_vote || *reject_vote)
500 }
501
502 fn quorum(&self, n_validators: usize) -> Option<PvfCheckOutcome> {
504 let accept_threshold = pezkuwi_primitives::supermajority_threshold(n_validators);
505 let reject_threshold = n_validators - accept_threshold;
507
508 if self.votes_accept.count_ones() >= accept_threshold {
509 Some(PvfCheckOutcome::Accepted)
510 } else if self.votes_reject.count_ones() > reject_threshold {
511 Some(PvfCheckOutcome::Rejected)
512 } else {
513 None
514 }
515 }
516
517 #[cfg(test)]
518 pub(crate) fn causes(&self) -> &[PvfCheckCause<BlockNumber>] {
519 self.causes.as_slice()
520 }
521}
522
523pub trait OnNewHead {
525 fn on_new_head(id: ParaId, head: &HeadData) -> Weight;
528}
529
530#[impl_trait_for_tuples::impl_for_tuples(30)]
531impl OnNewHead for Tuple {
532 fn on_new_head(id: ParaId, head: &HeadData) -> Weight {
533 let mut weight: Weight = Default::default();
534 for_tuples!( #( weight.saturating_accrue(Tuple::on_new_head(id, head)); )* );
535 weight
536 }
537}
538
539pub trait AssignCoretime {
544 fn assign_coretime(id: ParaId) -> DispatchResult;
546}
547
548impl AssignCoretime for () {
549 fn assign_coretime(_: ParaId) -> DispatchResult {
550 Ok(())
551 }
552}
553
554#[derive(Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
556#[cfg_attr(test, derive(PartialEq))]
557pub struct AuthorizedCodeHashAndExpiry<T> {
558 code_hash: ValidationCodeHash,
559 expire_at: T,
560}
561impl<T> From<(ValidationCodeHash, T)> for AuthorizedCodeHashAndExpiry<T> {
562 fn from(value: (ValidationCodeHash, T)) -> Self {
563 AuthorizedCodeHashAndExpiry { code_hash: value.0, expire_at: value.1 }
564 }
565}
566
567pub trait WeightInfo {
568 fn force_set_current_code(c: u32) -> Weight;
569 fn force_set_current_head(s: u32) -> Weight;
570 fn force_set_most_recent_context() -> Weight;
571 fn force_schedule_code_upgrade(c: u32) -> Weight;
572 fn force_note_new_head(s: u32) -> Weight;
573 fn force_queue_action() -> Weight;
574 fn add_trusted_validation_code(c: u32) -> Weight;
575 fn poke_unused_validation_code() -> Weight;
576 fn remove_upgrade_cooldown() -> Weight;
577
578 fn include_pvf_check_statement_finalize_upgrade_accept() -> Weight;
579 fn include_pvf_check_statement_finalize_upgrade_reject() -> Weight;
580 fn include_pvf_check_statement_finalize_onboarding_accept() -> Weight;
581 fn include_pvf_check_statement_finalize_onboarding_reject() -> Weight;
582 fn include_pvf_check_statement() -> Weight;
583 fn authorize_force_set_current_code_hash() -> Weight;
584 fn apply_authorized_force_set_current_code(c: u32) -> Weight;
585}
586
587pub struct TestWeightInfo;
588impl WeightInfo for TestWeightInfo {
589 fn force_set_current_code(_c: u32) -> Weight {
590 Weight::MAX
591 }
592 fn force_set_current_head(_s: u32) -> Weight {
593 Weight::MAX
594 }
595 fn force_set_most_recent_context() -> Weight {
596 Weight::MAX
597 }
598 fn force_schedule_code_upgrade(_c: u32) -> Weight {
599 Weight::MAX
600 }
601 fn force_note_new_head(_s: u32) -> Weight {
602 Weight::MAX
603 }
604 fn force_queue_action() -> Weight {
605 Weight::MAX
606 }
607 fn add_trusted_validation_code(_c: u32) -> Weight {
608 Weight::zero()
610 }
611 fn poke_unused_validation_code() -> Weight {
612 Weight::MAX
613 }
614 fn include_pvf_check_statement_finalize_upgrade_accept() -> Weight {
615 Weight::MAX
616 }
617 fn include_pvf_check_statement_finalize_upgrade_reject() -> Weight {
618 Weight::MAX
619 }
620 fn include_pvf_check_statement_finalize_onboarding_accept() -> Weight {
621 Weight::MAX
622 }
623 fn include_pvf_check_statement_finalize_onboarding_reject() -> Weight {
624 Weight::MAX
625 }
626 fn include_pvf_check_statement() -> Weight {
627 Weight::MAX - Weight::from_parts(1, 1)
629 }
630 fn remove_upgrade_cooldown() -> Weight {
631 Weight::MAX
632 }
633 fn authorize_force_set_current_code_hash() -> Weight {
634 Weight::MAX
635 }
636 fn apply_authorized_force_set_current_code(_c: u32) -> Weight {
637 Weight::MAX
638 }
639}
640
641#[pezframe_support::pezpallet]
642pub mod pezpallet {
643 use super::*;
644 use pezframe_support::traits::{
645 fungible::{Inspect, Mutate},
646 tokens::{Fortitude, Precision, Preservation},
647 };
648 use pezsp_runtime::transaction_validity::{
649 InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
650 ValidTransaction,
651 };
652
653 type BalanceOf<T> = <<T as Config>::Fungible as Inspect<AccountIdFor<T>>>::Balance;
654
655 #[pezpallet::pezpallet]
656 #[pezpallet::without_storage_info]
657 pub struct Pezpallet<T>(_);
658
659 #[pezpallet::config]
660 pub trait Config:
661 pezframe_system::Config
662 + configuration::Config
663 + shared::Config
664 + pezframe_system::offchain::CreateBare<Call<Self>>
665 {
666 #[allow(deprecated)]
667 type RuntimeEvent: From<Event<Self>>
668 + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
669
670 #[pezpallet::constant]
671 type UnsignedPriority: Get<TransactionPriority>;
672
673 type NextSessionRotation: EstimateNextSessionRotation<BlockNumberFor<Self>>;
674
675 type QueueFootprinter: QueueFootprinter<Origin = UmpQueueId>;
680
681 type OnNewHead: OnNewHead;
683
684 type WeightInfo: WeightInfo;
686
687 type AssignCoretime: AssignCoretime;
693
694 type Fungible: Mutate<Self::AccountId, Balance: From<BlockNumberFor<Self>>>;
696
697 type CooldownRemovalMultiplier: Get<BalanceOf<Self>>;
707
708 type AuthorizeCurrentCodeOrigin: EnsureOriginWithArg<Self::RuntimeOrigin, ParaId>;
714 }
715
716 #[pezpallet::event]
717 #[pezpallet::generate_deposit(pub(super) fn deposit_event)]
718 pub enum Event<T: Config> {
719 CurrentCodeUpdated(ParaId),
721 CurrentHeadUpdated(ParaId),
723 CodeUpgradeScheduled(ParaId),
725 NewHeadNoted(ParaId),
727 ActionQueued(ParaId, SessionIndex),
729 PvfCheckStarted(ValidationCodeHash, ParaId),
732 PvfCheckAccepted(ValidationCodeHash, ParaId),
735 PvfCheckRejected(ValidationCodeHash, ParaId),
738 UpgradeCooldownRemoved {
740 para_id: ParaId,
742 },
743 CodeAuthorized {
745 para_id: ParaId,
747 code_hash: ValidationCodeHash,
749 expire_at: BlockNumberFor<T>,
751 },
752 }
753
754 #[pezpallet::error]
755 pub enum Error<T> {
756 NotRegistered,
758 CannotOnboard,
760 CannotOffboard,
762 CannotUpgrade,
764 CannotDowngrade,
766 PvfCheckStatementStale,
768 PvfCheckStatementFuture,
770 PvfCheckValidatorIndexOutOfBounds,
772 PvfCheckInvalidSignature,
774 PvfCheckDoubleVote,
776 PvfCheckSubjectInvalid,
778 CannotUpgradeCode,
780 InvalidCode,
782 NothingAuthorized,
784 Unauthorized,
786 InvalidBlockNumber,
788 }
789
790 #[pezpallet::storage]
795 pub(super) type PvfActiveVoteMap<T: Config> = StorageMap<
796 _,
797 Twox64Concat,
798 ValidationCodeHash,
799 PvfCheckActiveVoteState<BlockNumberFor<T>>,
800 OptionQuery,
801 >;
802
803 #[pezpallet::storage]
805 pub(super) type PvfActiveVoteList<T: Config> =
806 StorageValue<_, Vec<ValidationCodeHash>, ValueQuery>;
807
808 #[pezpallet::storage]
813 pub type Teyrchains<T: Config> = StorageValue<_, Vec<ParaId>, ValueQuery>;
814
815 #[pezpallet::storage]
817 pub(super) type ParaLifecycles<T: Config> = StorageMap<_, Twox64Concat, ParaId, ParaLifecycle>;
818
819 #[pezpallet::storage]
821 pub type Heads<T: Config> = StorageMap<_, Twox64Concat, ParaId, HeadData>;
822
823 #[pezpallet::storage]
825 pub type MostRecentContext<T: Config> = StorageMap<_, Twox64Concat, ParaId, BlockNumberFor<T>>;
826
827 #[pezpallet::storage]
831 pub type CurrentCodeHash<T: Config> = StorageMap<_, Twox64Concat, ParaId, ValidationCodeHash>;
832
833 #[pezpallet::storage]
838 pub(super) type PastCodeHash<T: Config> =
839 StorageMap<_, Twox64Concat, (ParaId, BlockNumberFor<T>), ValidationCodeHash>;
840
841 #[pezpallet::storage]
845 pub type PastCodeMeta<T: Config> =
846 StorageMap<_, Twox64Concat, ParaId, ParaPastCodeMeta<BlockNumberFor<T>>, ValueQuery>;
847
848 #[pezpallet::storage]
855 pub(super) type PastCodePruning<T: Config> =
856 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
857
858 #[pezpallet::storage]
863 pub type FutureCodeUpgrades<T: Config> = StorageMap<_, Twox64Concat, ParaId, BlockNumberFor<T>>;
864
865 #[pezpallet::storage]
874 pub(super) type FutureCodeUpgradesAt<T: Config> =
875 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
876
877 #[pezpallet::storage]
881 pub type FutureCodeHash<T: Config> = StorageMap<_, Twox64Concat, ParaId, ValidationCodeHash>;
882
883 #[pezpallet::storage]
885 pub type AuthorizedCodeHash<T: Config> =
886 StorageMap<_, Twox64Concat, ParaId, AuthorizedCodeHashAndExpiry<BlockNumberFor<T>>>;
887
888 #[pezpallet::storage]
899 pub(super) type UpgradeGoAheadSignal<T: Config> =
900 StorageMap<_, Twox64Concat, ParaId, UpgradeGoAhead>;
901
902 #[pezpallet::storage]
912 pub type UpgradeRestrictionSignal<T: Config> =
913 StorageMap<_, Twox64Concat, ParaId, UpgradeRestriction>;
914
915 #[pezpallet::storage]
919 pub(super) type UpgradeCooldowns<T: Config> =
920 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
921
922 #[pezpallet::storage]
929 pub(super) type UpcomingUpgrades<T: Config> =
930 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
931
932 #[pezpallet::storage]
934 pub type ActionsQueue<T: Config> =
935 StorageMap<_, Twox64Concat, SessionIndex, Vec<ParaId>, ValueQuery>;
936
937 #[pezpallet::storage]
942 pub(super) type UpcomingParasGenesis<T: Config> =
943 StorageMap<_, Twox64Concat, ParaId, ParaGenesisArgs>;
944
945 #[pezpallet::storage]
947 pub(super) type CodeByHashRefs<T: Config> =
948 StorageMap<_, Identity, ValidationCodeHash, u32, ValueQuery>;
949
950 #[pezpallet::storage]
955 pub type CodeByHash<T: Config> = StorageMap<_, Identity, ValidationCodeHash, ValidationCode>;
956
957 #[pezpallet::genesis_config]
958 #[derive(DefaultNoBound)]
959 pub struct GenesisConfig<T: Config> {
960 #[serde(skip)]
961 pub _config: core::marker::PhantomData<T>,
962 pub paras: Vec<(ParaId, ParaGenesisArgs)>,
963 }
964
965 #[pezpallet::genesis_build]
966 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
967 fn build(&self) {
968 let mut teyrchains = TeyrchainsCache::new();
969 for (id, genesis_args) in &self.paras {
970 if genesis_args.validation_code.0.is_empty() {
971 panic!("empty validation code is not allowed in genesis");
972 }
973 Pezpallet::<T>::initialize_para_now(&mut teyrchains, *id, genesis_args);
974 if genesis_args.para_kind == ParaKind::Teyrchain {
975 T::AssignCoretime::assign_coretime(*id)
976 .expect("Assigning coretime works at genesis; qed");
977 }
978 }
979 }
981 }
982
983 #[pezpallet::call]
984 impl<T: Config> Pezpallet<T> {
985 #[pezpallet::call_index(0)]
987 #[pezpallet::weight(<T as Config>::WeightInfo::force_set_current_code(new_code.0.len() as u32))]
988 pub fn force_set_current_code(
989 origin: OriginFor<T>,
990 para: ParaId,
991 new_code: ValidationCode,
992 ) -> DispatchResult {
993 ensure_root(origin)?;
994 Self::do_force_set_current_code_update(para, new_code);
995 Ok(())
996 }
997
998 #[pezpallet::call_index(1)]
1000 #[pezpallet::weight(<T as Config>::WeightInfo::force_set_current_head(new_head.0.len() as u32))]
1001 pub fn force_set_current_head(
1002 origin: OriginFor<T>,
1003 para: ParaId,
1004 new_head: HeadData,
1005 ) -> DispatchResult {
1006 ensure_root(origin)?;
1007 Self::set_current_head(para, new_head);
1008 Ok(())
1009 }
1010
1011 #[pezpallet::call_index(2)]
1013 #[pezpallet::weight(<T as Config>::WeightInfo::force_schedule_code_upgrade(new_code.0.len() as u32))]
1014 pub fn force_schedule_code_upgrade(
1015 origin: OriginFor<T>,
1016 para: ParaId,
1017 new_code: ValidationCode,
1018 relay_parent_number: BlockNumberFor<T>,
1019 ) -> DispatchResult {
1020 ensure_root(origin)?;
1021 let config = configuration::ActiveConfig::<T>::get();
1022 Self::schedule_code_upgrade(
1023 para,
1024 new_code,
1025 relay_parent_number,
1026 &config,
1027 UpgradeStrategy::ApplyAtExpectedBlock,
1028 );
1029 Self::deposit_event(Event::CodeUpgradeScheduled(para));
1030 Ok(())
1031 }
1032
1033 #[pezpallet::call_index(3)]
1035 #[pezpallet::weight(<T as Config>::WeightInfo::force_note_new_head(new_head.0.len() as u32))]
1036 pub fn force_note_new_head(
1037 origin: OriginFor<T>,
1038 para: ParaId,
1039 new_head: HeadData,
1040 ) -> DispatchResult {
1041 ensure_root(origin)?;
1042 let now = pezframe_system::Pezpallet::<T>::block_number();
1043 Self::note_new_head(para, new_head, now);
1044 Self::deposit_event(Event::NewHeadNoted(para));
1045 Ok(())
1046 }
1047
1048 #[pezpallet::call_index(4)]
1052 #[pezpallet::weight(<T as Config>::WeightInfo::force_queue_action())]
1053 pub fn force_queue_action(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
1054 ensure_root(origin)?;
1055 let next_session = shared::CurrentSessionIndex::<T>::get().saturating_add(One::one());
1056 ActionsQueue::<T>::mutate(next_session, |v| {
1057 if let Err(i) = v.binary_search(¶) {
1058 v.insert(i, para);
1059 }
1060 });
1061 Self::deposit_event(Event::ActionQueued(para, next_session));
1062 Ok(())
1063 }
1064
1065 #[pezpallet::call_index(5)]
1080 #[pezpallet::weight(<T as Config>::WeightInfo::add_trusted_validation_code(validation_code.0.len() as u32))]
1081 pub fn add_trusted_validation_code(
1082 origin: OriginFor<T>,
1083 validation_code: ValidationCode,
1084 ) -> DispatchResult {
1085 ensure_root(origin)?;
1086 let code_hash = validation_code.hash();
1087
1088 if let Some(vote) = PvfActiveVoteMap::<T>::get(&code_hash) {
1089 PvfActiveVoteMap::<T>::remove(&code_hash);
1091 PvfActiveVoteList::<T>::mutate(|l| {
1092 if let Ok(i) = l.binary_search(&code_hash) {
1093 l.remove(i);
1094 }
1095 });
1096
1097 let cfg = configuration::ActiveConfig::<T>::get();
1098 Self::enact_pvf_accepted(
1099 pezframe_system::Pezpallet::<T>::block_number(),
1100 &code_hash,
1101 &vote.causes,
1102 vote.age,
1103 &cfg,
1104 );
1105 return Ok(());
1106 }
1107
1108 if CodeByHash::<T>::contains_key(&code_hash) {
1109 return Ok(());
1111 }
1112
1113 CodeByHash::<T>::insert(code_hash, &validation_code);
1119
1120 Ok(())
1121 }
1122
1123 #[pezpallet::call_index(6)]
1129 #[pezpallet::weight(<T as Config>::WeightInfo::poke_unused_validation_code())]
1130 pub fn poke_unused_validation_code(
1131 origin: OriginFor<T>,
1132 validation_code_hash: ValidationCodeHash,
1133 ) -> DispatchResult {
1134 ensure_root(origin)?;
1135 if CodeByHashRefs::<T>::get(&validation_code_hash) == 0 {
1136 CodeByHash::<T>::remove(&validation_code_hash);
1137 }
1138 Ok(())
1139 }
1140
1141 #[pezpallet::call_index(7)]
1144 #[pezpallet::weight(
1145 <T as Config>::WeightInfo::include_pvf_check_statement_finalize_upgrade_accept()
1146 .max(<T as Config>::WeightInfo::include_pvf_check_statement_finalize_upgrade_reject())
1147 .max(<T as Config>::WeightInfo::include_pvf_check_statement_finalize_onboarding_accept()
1148 .max(<T as Config>::WeightInfo::include_pvf_check_statement_finalize_onboarding_reject())
1149 )
1150 )]
1151 pub fn include_pvf_check_statement(
1152 origin: OriginFor<T>,
1153 stmt: PvfCheckStatement,
1154 signature: ValidatorSignature,
1155 ) -> DispatchResultWithPostInfo {
1156 ensure_none(origin)?;
1157
1158 let validators = shared::ActiveValidatorKeys::<T>::get();
1159 let current_session = shared::CurrentSessionIndex::<T>::get();
1160 if stmt.session_index < current_session {
1161 return Err(Error::<T>::PvfCheckStatementStale.into());
1162 } else if stmt.session_index > current_session {
1163 return Err(Error::<T>::PvfCheckStatementFuture.into());
1164 }
1165 let validator_index = stmt.validator_index.0 as usize;
1166 let validator_public = validators
1167 .get(validator_index)
1168 .ok_or(Error::<T>::PvfCheckValidatorIndexOutOfBounds)?;
1169
1170 let signing_payload = stmt.signing_payload();
1171 ensure!(
1172 signature.verify(&signing_payload[..], &validator_public),
1173 Error::<T>::PvfCheckInvalidSignature,
1174 );
1175
1176 let mut active_vote = PvfActiveVoteMap::<T>::get(&stmt.subject)
1177 .ok_or(Error::<T>::PvfCheckSubjectInvalid)?;
1178
1179 ensure!(
1181 !active_vote
1182 .has_vote(validator_index)
1183 .ok_or(Error::<T>::PvfCheckValidatorIndexOutOfBounds)?,
1184 Error::<T>::PvfCheckDoubleVote,
1185 );
1186
1187 if stmt.accept {
1189 active_vote.votes_accept.set(validator_index, true);
1190 } else {
1191 active_vote.votes_reject.set(validator_index, true);
1192 }
1193
1194 if let Some(outcome) = active_vote.quorum(validators.len()) {
1195 PvfActiveVoteMap::<T>::remove(&stmt.subject);
1200 PvfActiveVoteList::<T>::mutate(|l| {
1201 if let Ok(i) = l.binary_search(&stmt.subject) {
1202 l.remove(i);
1203 }
1204 });
1205 match outcome {
1206 PvfCheckOutcome::Accepted => {
1207 let cfg = configuration::ActiveConfig::<T>::get();
1208 Self::enact_pvf_accepted(
1209 pezframe_system::Pezpallet::<T>::block_number(),
1210 &stmt.subject,
1211 &active_vote.causes,
1212 active_vote.age,
1213 &cfg,
1214 );
1215 },
1216 PvfCheckOutcome::Rejected => {
1217 Self::enact_pvf_rejected(&stmt.subject, active_vote.causes);
1218 },
1219 }
1220
1221 Ok(().into())
1223 } else {
1224 PvfActiveVoteMap::<T>::insert(&stmt.subject, active_vote);
1229 Ok(Some(<T as Config>::WeightInfo::include_pvf_check_statement()).into())
1230 }
1231 }
1232
1233 #[pezpallet::call_index(8)]
1235 #[pezpallet::weight(<T as Config>::WeightInfo::force_set_most_recent_context())]
1236 pub fn force_set_most_recent_context(
1237 origin: OriginFor<T>,
1238 para: ParaId,
1239 context: BlockNumberFor<T>,
1240 ) -> DispatchResult {
1241 ensure_root(origin)?;
1242 MostRecentContext::<T>::insert(¶, context);
1243 Ok(())
1244 }
1245
1246 #[pezpallet::call_index(9)]
1251 #[pezpallet::weight(<T as Config>::WeightInfo::remove_upgrade_cooldown())]
1252 pub fn remove_upgrade_cooldown(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
1253 let who = ensure_signed(origin)?;
1254
1255 let removed = UpgradeCooldowns::<T>::mutate(|cooldowns| {
1256 let Some(pos) = cooldowns.iter().position(|(p, _)| p == ¶) else {
1257 return Ok::<_, DispatchError>(false);
1258 };
1259 let (_, cooldown_until) = cooldowns.remove(pos);
1260
1261 let cost = Self::calculate_remove_upgrade_cooldown_cost(cooldown_until);
1262
1263 T::Fungible::burn_from(
1265 &who,
1266 cost,
1267 Preservation::Preserve,
1268 Precision::Exact,
1269 Fortitude::Polite,
1270 )?;
1271
1272 Ok(true)
1273 })?;
1274
1275 if removed {
1276 UpgradeRestrictionSignal::<T>::remove(para);
1277
1278 Self::deposit_event(Event::UpgradeCooldownRemoved { para_id: para });
1279 }
1280
1281 Ok(())
1282 }
1283
1284 #[pezpallet::call_index(10)]
1296 #[pezpallet::weight(<T as Config>::WeightInfo::authorize_force_set_current_code_hash())]
1297 pub fn authorize_force_set_current_code_hash(
1298 origin: OriginFor<T>,
1299 para: ParaId,
1300 new_code_hash: ValidationCodeHash,
1301 valid_period: BlockNumberFor<T>,
1302 ) -> DispatchResult {
1303 T::AuthorizeCurrentCodeOrigin::ensure_origin(origin, ¶)?;
1304 ensure!(Self::is_valid_para(para), Error::<T>::NotRegistered);
1306
1307 let now = pezframe_system::Pezpallet::<T>::block_number();
1308 let expire_at = now.saturating_add(valid_period);
1309
1310 AuthorizedCodeHash::<T>::insert(
1312 ¶,
1313 AuthorizedCodeHashAndExpiry::from((new_code_hash, expire_at)),
1314 );
1315 Self::deposit_event(Event::CodeAuthorized {
1316 para_id: para,
1317 code_hash: new_code_hash,
1318 expire_at,
1319 });
1320
1321 Ok(())
1322 }
1323
1324 #[pezpallet::call_index(11)]
1327 #[pezpallet::weight(<T as Config>::WeightInfo::apply_authorized_force_set_current_code(new_code.0.len() as u32))]
1328 pub fn apply_authorized_force_set_current_code(
1329 _origin: OriginFor<T>,
1330 para: ParaId,
1331 new_code: ValidationCode,
1332 ) -> DispatchResultWithPostInfo {
1333 let _ = Self::validate_code_is_authorized(&new_code, ¶)?;
1337 AuthorizedCodeHash::<T>::remove(para);
1339
1340 Self::do_force_set_current_code_update(para, new_code);
1342
1343 Ok(Pays::No.into())
1344 }
1345 }
1346
1347 impl<T: Config> Pezpallet<T> {
1348 pub(crate) fn calculate_remove_upgrade_cooldown_cost(
1349 cooldown_until: BlockNumberFor<T>,
1350 ) -> BalanceOf<T> {
1351 let time_left =
1352 cooldown_until.saturating_sub(pezframe_system::Pezpallet::<T>::block_number());
1353
1354 BalanceOf::<T>::from(time_left).saturating_mul(T::CooldownRemovalMultiplier::get())
1355 }
1356 }
1357
1358 #[pezpallet::view_functions]
1359 impl<T: Config> Pezpallet<T> {
1360 pub fn remove_upgrade_cooldown_cost(para: ParaId) -> BalanceOf<T> {
1362 UpgradeCooldowns::<T>::get()
1363 .iter()
1364 .find(|(p, _)| p == ¶)
1365 .map(|(_, c)| Self::calculate_remove_upgrade_cooldown_cost(*c))
1366 .unwrap_or_default()
1367 }
1368 }
1369
1370 #[pezpallet::validate_unsigned]
1371 impl<T: Config> ValidateUnsigned for Pezpallet<T> {
1372 type Call = Call<T>;
1373
1374 fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
1375 match call {
1376 Call::include_pvf_check_statement { stmt, signature } => {
1377 let current_session = shared::CurrentSessionIndex::<T>::get();
1378 if stmt.session_index < current_session {
1379 return InvalidTransaction::Stale.into();
1380 } else if stmt.session_index > current_session {
1381 return InvalidTransaction::Future.into();
1382 }
1383
1384 let validator_index = stmt.validator_index.0 as usize;
1385 let validators = shared::ActiveValidatorKeys::<T>::get();
1386 let validator_public = match validators.get(validator_index) {
1387 Some(pk) => pk,
1388 None => {
1389 return InvalidTransaction::Custom(INVALID_TX_BAD_VALIDATOR_IDX).into()
1390 },
1391 };
1392
1393 let signing_payload = stmt.signing_payload();
1394 if !signature.verify(&signing_payload[..], &validator_public) {
1395 return InvalidTransaction::BadProof.into();
1396 }
1397
1398 let active_vote = match PvfActiveVoteMap::<T>::get(&stmt.subject) {
1399 Some(v) => v,
1400 None => return InvalidTransaction::Custom(INVALID_TX_BAD_SUBJECT).into(),
1401 };
1402
1403 match active_vote.has_vote(validator_index) {
1404 Some(false) => (),
1405 Some(true) => {
1406 return InvalidTransaction::Custom(INVALID_TX_DOUBLE_VOTE).into()
1407 },
1408 None => {
1409 return InvalidTransaction::Custom(INVALID_TX_BAD_VALIDATOR_IDX).into()
1410 },
1411 }
1412
1413 ValidTransaction::with_tag_prefix("PvfPreCheckingVote")
1414 .priority(T::UnsignedPriority::get())
1415 .longevity(
1416 TryInto::<u64>::try_into(
1417 T::NextSessionRotation::average_session_length() / 2u32.into(),
1418 )
1419 .unwrap_or(64_u64),
1420 )
1421 .and_provides((stmt.session_index, stmt.validator_index, stmt.subject))
1422 .propagate(true)
1423 .build()
1424 },
1425 Call::apply_authorized_force_set_current_code { para, new_code } => {
1426 match Self::validate_code_is_authorized(new_code, para) {
1427 Ok(authorized_code) => {
1428 let now = pezframe_system::Pezpallet::<T>::block_number();
1429 let longevity = authorized_code.expire_at.saturating_sub(now);
1430
1431 ValidTransaction::with_tag_prefix("ApplyAuthorizedForceSetCurrentCode")
1432 .priority(T::UnsignedPriority::get())
1433 .longevity(TryInto::<u64>::try_into(longevity).unwrap_or(64_u64))
1434 .and_provides((para, authorized_code.code_hash))
1435 .propagate(true)
1436 .build()
1437 },
1438 Err(_) => {
1439 return InvalidTransaction::Custom(INVALID_TX_UNAUTHORIZED_CODE).into()
1440 },
1441 }
1442 },
1443 _ => InvalidTransaction::Call.into(),
1444 }
1445 }
1446
1447 fn pre_dispatch(_call: &Self::Call) -> Result<(), TransactionValidityError> {
1448 Ok(())
1456 }
1457 }
1458}
1459
1460const INVALID_TX_BAD_VALIDATOR_IDX: u8 = 1;
1462const INVALID_TX_BAD_SUBJECT: u8 = 2;
1463const INVALID_TX_DOUBLE_VOTE: u8 = 3;
1464const INVALID_TX_UNAUTHORIZED_CODE: u8 = 4;
1465
1466pub const MAX_PARA_HEADS: usize = 1024;
1474
1475impl<T: Config> Pezpallet<T> {
1476 pub(crate) fn schedule_code_upgrade_external(
1481 id: ParaId,
1482 new_code: ValidationCode,
1483 upgrade_strategy: UpgradeStrategy,
1484 ) -> DispatchResult {
1485 ensure!(Self::can_upgrade_validation_code(id), Error::<T>::CannotUpgradeCode);
1487 let config = configuration::ActiveConfig::<T>::get();
1488 ensure!(new_code.0.len() >= MIN_CODE_SIZE as usize, Error::<T>::InvalidCode);
1490 ensure!(new_code.0.len() <= config.max_code_size as usize, Error::<T>::InvalidCode);
1491
1492 let current_block = pezframe_system::Pezpallet::<T>::block_number();
1493 let upgrade_block = current_block.saturating_add(config.validation_upgrade_delay);
1495 Self::schedule_code_upgrade(id, new_code, upgrade_block, &config, upgrade_strategy);
1496 Self::deposit_event(Event::CodeUpgradeScheduled(id));
1497 Ok(())
1498 }
1499
1500 pub(crate) fn set_current_head(para: ParaId, new_head: HeadData) {
1502 Heads::<T>::insert(¶, new_head);
1503 Self::deposit_event(Event::CurrentHeadUpdated(para));
1504 }
1505
1506 pub(crate) fn initializer_initialize(now: BlockNumberFor<T>) -> Weight {
1508 Self::prune_old_code(now)
1509 + Self::process_scheduled_upgrade_changes(now)
1510 + Self::process_future_code_upgrades_at(now)
1511 + Self::prune_expired_authorizations(now)
1512 }
1513
1514 pub(crate) fn initializer_finalize(now: BlockNumberFor<T>) {
1516 Self::process_scheduled_upgrade_cooldowns(now);
1517 }
1518
1519 pub(crate) fn initializer_on_new_session(
1523 notification: &SessionChangeNotification<BlockNumberFor<T>>,
1524 ) -> Vec<ParaId> {
1525 let outgoing_paras = Self::apply_actions_queue(notification.session_index);
1526 Self::groom_ongoing_pvf_votes(¬ification.new_config, notification.validators.len());
1527 outgoing_paras
1528 }
1529
1530 pub(crate) fn current_code(para_id: &ParaId) -> Option<ValidationCode> {
1532 CurrentCodeHash::<T>::get(para_id).and_then(|code_hash| {
1533 let code = CodeByHash::<T>::get(&code_hash);
1534 if code.is_none() {
1535 log::error!(
1536 "Pezpallet paras storage is inconsistent, code not found for hash {}",
1537 code_hash,
1538 );
1539 debug_assert!(false, "inconsistent paras storages");
1540 }
1541 code
1542 })
1543 }
1544
1545 pub fn sorted_para_heads() -> Vec<(u32, Vec<u8>)> {
1548 let mut heads: Vec<(u32, Vec<u8>)> =
1549 Heads::<T>::iter().map(|(id, head)| (id.into(), head.0)).collect();
1550 heads.sort_by_key(|(id, _)| *id);
1551 heads.truncate(MAX_PARA_HEADS);
1552 heads
1553 }
1554
1555 fn apply_actions_queue(session: SessionIndex) -> Vec<ParaId> {
1564 let actions = ActionsQueue::<T>::take(session);
1565 let mut teyrchains = TeyrchainsCache::new();
1566 let now = pezframe_system::Pezpallet::<T>::block_number();
1567 let mut outgoing = Vec::new();
1568
1569 for para in actions {
1570 let lifecycle = ParaLifecycles::<T>::get(¶);
1571 match lifecycle {
1572 None | Some(ParaLifecycle::Parathread) | Some(ParaLifecycle::Teyrchain) => { },
1574 Some(ParaLifecycle::Onboarding) => {
1575 if let Some(genesis_data) = UpcomingParasGenesis::<T>::take(¶) {
1576 Self::initialize_para_now(&mut teyrchains, para, &genesis_data);
1577 }
1578 },
1579 Some(ParaLifecycle::UpgradingParathread) => {
1581 teyrchains.add(para);
1582 ParaLifecycles::<T>::insert(¶, ParaLifecycle::Teyrchain);
1583 },
1584 Some(ParaLifecycle::DowngradingTeyrchain) => {
1586 teyrchains.remove(para);
1587 ParaLifecycles::<T>::insert(¶, ParaLifecycle::Parathread);
1588 },
1589 Some(ParaLifecycle::OffboardingTeyrchain)
1591 | Some(ParaLifecycle::OffboardingParathread) => {
1592 teyrchains.remove(para);
1593
1594 Heads::<T>::remove(¶);
1595 MostRecentContext::<T>::remove(¶);
1596 FutureCodeUpgrades::<T>::remove(¶);
1597 UpgradeGoAheadSignal::<T>::remove(¶);
1598 UpgradeRestrictionSignal::<T>::remove(¶);
1599 ParaLifecycles::<T>::remove(¶);
1600 AuthorizedCodeHash::<T>::remove(¶);
1601 let removed_future_code_hash = FutureCodeHash::<T>::take(¶);
1602 if let Some(removed_future_code_hash) = removed_future_code_hash {
1603 Self::decrease_code_ref(&removed_future_code_hash);
1604 }
1605
1606 let removed_code_hash = CurrentCodeHash::<T>::take(¶);
1607 if let Some(removed_code_hash) = removed_code_hash {
1608 Self::note_past_code(para, now, now, removed_code_hash);
1609 }
1610
1611 outgoing.push(para);
1612 },
1613 }
1614 }
1615
1616 if !outgoing.is_empty() {
1617 UpcomingUpgrades::<T>::mutate(|upcoming_upgrades| {
1624 upcoming_upgrades.retain(|(para, _)| !outgoing.contains(para));
1625 });
1626 UpgradeCooldowns::<T>::mutate(|upgrade_cooldowns| {
1627 upgrade_cooldowns.retain(|(para, _)| !outgoing.contains(para));
1628 });
1629 FutureCodeUpgradesAt::<T>::mutate(|future_upgrades| {
1630 future_upgrades.retain(|(para, _)| !outgoing.contains(para));
1631 });
1632 }
1633
1634 drop(teyrchains);
1636
1637 outgoing
1638 }
1639
1640 fn note_past_code(
1647 id: ParaId,
1648 at: BlockNumberFor<T>,
1649 now: BlockNumberFor<T>,
1650 old_code_hash: ValidationCodeHash,
1651 ) -> Weight {
1652 PastCodeMeta::<T>::mutate(&id, |past_meta| {
1653 past_meta.note_replacement(at, now);
1654 });
1655
1656 PastCodeHash::<T>::insert(&(id, at), old_code_hash);
1657
1658 PastCodePruning::<T>::mutate(|pruning| {
1661 let insert_idx =
1662 pruning.binary_search_by_key(&now, |&(_, b)| b).unwrap_or_else(|idx| idx);
1663 pruning.insert(insert_idx, (id, now));
1664 });
1665
1666 T::DbWeight::get().reads_writes(2, 3)
1667 }
1668
1669 fn prune_old_code(now: BlockNumberFor<T>) -> Weight {
1672 let config = configuration::ActiveConfig::<T>::get();
1673 let code_retention_period = config.code_retention_period;
1674 if now <= code_retention_period {
1675 let weight = T::DbWeight::get().reads_writes(1, 0);
1676 return weight;
1677 }
1678
1679 let pruning_height = now - (code_retention_period + One::one());
1681
1682 let pruning_tasks_done =
1683 PastCodePruning::<T>::mutate(|pruning_tasks: &mut Vec<(_, BlockNumberFor<T>)>| {
1684 let (pruning_tasks_done, pruning_tasks_to_do) = {
1685 let up_to_idx =
1687 pruning_tasks.iter().take_while(|&(_, at)| at <= &pruning_height).count();
1688 (up_to_idx, pruning_tasks.drain(..up_to_idx))
1689 };
1690
1691 for (para_id, _) in pruning_tasks_to_do {
1692 let full_deactivate = PastCodeMeta::<T>::mutate(¶_id, |meta| {
1693 for pruned_repl_at in meta.prune_up_to(pruning_height) {
1694 let removed_code_hash =
1695 PastCodeHash::<T>::take(&(para_id, pruned_repl_at));
1696
1697 if let Some(removed_code_hash) = removed_code_hash {
1698 Self::decrease_code_ref(&removed_code_hash);
1699 } else {
1700 log::warn!(
1701 target: LOG_TARGET,
1702 "Missing code for removed hash {:?}",
1703 removed_code_hash,
1704 );
1705 }
1706 }
1707
1708 meta.is_empty() && Heads::<T>::get(¶_id).is_none()
1709 });
1710
1711 if full_deactivate {
1714 PastCodeMeta::<T>::remove(¶_id);
1715 }
1716 }
1717
1718 pruning_tasks_done as u64
1719 });
1720
1721 T::DbWeight::get().reads_writes(1 + pruning_tasks_done, 2 * pruning_tasks_done)
1724 }
1725
1726 fn prune_expired_authorizations(now: BlockNumberFor<T>) -> Weight {
1729 let mut weight = T::DbWeight::get().reads(1);
1730 let to_remove = AuthorizedCodeHash::<T>::iter().filter_map(
1731 |(para, AuthorizedCodeHashAndExpiry { expire_at, .. })| {
1732 if expire_at <= now {
1733 Some(para)
1734 } else {
1735 None
1736 }
1737 },
1738 );
1739 for para in to_remove {
1740 AuthorizedCodeHash::<T>::remove(¶);
1741 weight.saturating_accrue(T::DbWeight::get().writes(1));
1742 }
1743
1744 weight
1745 }
1746
1747 fn process_future_code_upgrades_at(now: BlockNumberFor<T>) -> Weight {
1752 let mut weight = T::DbWeight::get().reads_writes(1, 1);
1754 FutureCodeUpgradesAt::<T>::mutate(
1755 |upcoming_upgrades: &mut Vec<(ParaId, BlockNumberFor<T>)>| {
1756 let num = upcoming_upgrades.iter().take_while(|&(_, at)| at <= &now).count();
1757 for (id, expected_at) in upcoming_upgrades.drain(..num) {
1758 weight += T::DbWeight::get().reads_writes(1, 1);
1759
1760 let new_code_hash = if let Some(new_code_hash) = FutureCodeHash::<T>::take(&id)
1762 {
1763 new_code_hash
1764 } else {
1765 log::error!(target: LOG_TARGET, "Missing future code hash for {:?}", &id);
1766 continue;
1767 };
1768
1769 weight += Self::set_current_code(id, new_code_hash, expected_at);
1770 }
1771 num
1772 },
1773 );
1774
1775 weight
1776 }
1777
1778 fn process_scheduled_upgrade_changes(now: BlockNumberFor<T>) -> Weight {
1784 let mut weight = T::DbWeight::get().reads_writes(1, 1);
1786 let upgrades_signaled = UpcomingUpgrades::<T>::mutate(
1787 |upcoming_upgrades: &mut Vec<(ParaId, BlockNumberFor<T>)>| {
1788 let num = upcoming_upgrades.iter().take_while(|&(_, at)| at <= &now).count();
1789 for (para, _) in upcoming_upgrades.drain(..num) {
1790 UpgradeGoAheadSignal::<T>::insert(¶, UpgradeGoAhead::GoAhead);
1791 }
1792 num
1793 },
1794 );
1795 weight += T::DbWeight::get().writes(upgrades_signaled as u64);
1796
1797 weight += T::DbWeight::get().reads(1);
1799 let cooldowns_expired =
1800 UpgradeCooldowns::<T>::get().iter().take_while(|&(_, at)| at <= &now).count();
1801
1802 weight += T::DbWeight::get().reads_writes(1, 1);
1806 weight += T::DbWeight::get().reads(cooldowns_expired as u64);
1807
1808 weight
1809 }
1810
1811 fn process_scheduled_upgrade_cooldowns(now: BlockNumberFor<T>) {
1815 UpgradeCooldowns::<T>::mutate(
1816 |upgrade_cooldowns: &mut Vec<(ParaId, BlockNumberFor<T>)>| {
1817 upgrade_cooldowns.retain(|(para, at)| {
1819 if at <= &now {
1820 UpgradeRestrictionSignal::<T>::remove(¶);
1821 false
1822 } else {
1823 true
1824 }
1825 });
1826 },
1827 );
1828 }
1829
1830 fn groom_ongoing_pvf_votes(
1833 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1834 new_n_validators: usize,
1835 ) -> Weight {
1836 let mut weight = T::DbWeight::get().reads(1);
1837
1838 let potentially_active_votes = PvfActiveVoteList::<T>::get();
1839
1840 let mut actually_active_votes = Vec::with_capacity(potentially_active_votes.len());
1845
1846 for vote_subject in potentially_active_votes {
1847 let mut vote_state = match PvfActiveVoteMap::<T>::take(&vote_subject) {
1848 Some(v) => v,
1849 None => {
1850 log::warn!(
1854 target: LOG_TARGET,
1855 "The PvfActiveVoteMap is out of sync with PvfActiveVoteList!",
1856 );
1857 debug_assert!(false);
1858 continue;
1859 },
1860 };
1861
1862 vote_state.age += 1;
1863 if vote_state.age < cfg.pvf_voting_ttl {
1864 weight += T::DbWeight::get().writes(1);
1865 vote_state.reinitialize_ballots(new_n_validators);
1866 PvfActiveVoteMap::<T>::insert(&vote_subject, vote_state);
1867
1868 actually_active_votes.push(vote_subject);
1870 } else {
1871 weight += Self::enact_pvf_rejected(&vote_subject, vote_state.causes);
1873 }
1874 }
1875
1876 weight += T::DbWeight::get().writes(1);
1877 PvfActiveVoteList::<T>::put(actually_active_votes);
1878
1879 weight
1880 }
1881
1882 fn enact_pvf_accepted(
1883 now: BlockNumberFor<T>,
1884 code_hash: &ValidationCodeHash,
1885 causes: &[PvfCheckCause<BlockNumberFor<T>>],
1886 sessions_observed: SessionIndex,
1887 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1888 ) -> Weight {
1889 let mut weight = Weight::zero();
1890 for cause in causes {
1891 weight += T::DbWeight::get().reads_writes(3, 2);
1892 Self::deposit_event(Event::PvfCheckAccepted(*code_hash, cause.para_id()));
1893
1894 match cause {
1895 PvfCheckCause::Onboarding(id) => {
1896 weight += Self::proceed_with_onboarding(*id, sessions_observed);
1897 },
1898 PvfCheckCause::Upgrade { id, included_at, upgrade_strategy } => {
1899 weight += Self::proceed_with_upgrade(
1900 *id,
1901 code_hash,
1902 now,
1903 *included_at,
1904 cfg,
1905 *upgrade_strategy,
1906 );
1907 },
1908 }
1909 }
1910 weight
1911 }
1912
1913 fn proceed_with_onboarding(id: ParaId, sessions_observed: SessionIndex) -> Weight {
1914 let weight = T::DbWeight::get().reads_writes(2, 1);
1915
1916 let onboard_at: SessionIndex = shared::CurrentSessionIndex::<T>::get()
1922 + cmp::max(shared::SESSION_DELAY.saturating_sub(sessions_observed), 1);
1923
1924 ActionsQueue::<T>::mutate(onboard_at, |v| {
1925 if let Err(i) = v.binary_search(&id) {
1926 v.insert(i, id);
1927 }
1928 });
1929
1930 weight
1931 }
1932
1933 fn proceed_with_upgrade(
1934 id: ParaId,
1935 code_hash: &ValidationCodeHash,
1936 now: BlockNumberFor<T>,
1937 relay_parent_number: BlockNumberFor<T>,
1938 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1939 upgrade_strategy: UpgradeStrategy,
1940 ) -> Weight {
1941 let mut weight = Weight::zero();
1942
1943 let expected_at = cmp::max(
1956 relay_parent_number + cfg.validation_upgrade_delay,
1957 now + cfg.minimum_validation_upgrade_delay,
1958 );
1959
1960 match upgrade_strategy {
1961 UpgradeStrategy::ApplyAtExpectedBlock => {
1962 FutureCodeUpgradesAt::<T>::mutate(|future_upgrades| {
1963 let insert_idx = future_upgrades
1964 .binary_search_by_key(&expected_at, |&(_, b)| b)
1965 .unwrap_or_else(|idx| idx);
1966 future_upgrades.insert(insert_idx, (id, expected_at));
1967 });
1968
1969 weight += T::DbWeight::get().reads_writes(0, 2);
1970 },
1971 UpgradeStrategy::SetGoAheadSignal => {
1972 FutureCodeUpgrades::<T>::insert(&id, expected_at);
1973
1974 UpcomingUpgrades::<T>::mutate(|upcoming_upgrades| {
1975 let insert_idx = upcoming_upgrades
1976 .binary_search_by_key(&expected_at, |&(_, b)| b)
1977 .unwrap_or_else(|idx| idx);
1978 upcoming_upgrades.insert(insert_idx, (id, expected_at));
1979 });
1980
1981 weight += T::DbWeight::get().reads_writes(1, 3);
1982 },
1983 }
1984
1985 let expected_at = expected_at.saturated_into();
1986 let log = ConsensusLog::ParaScheduleUpgradeCode(id, *code_hash, expected_at);
1987 pezframe_system::Pezpallet::<T>::deposit_log(log.into());
1988
1989 weight
1990 }
1991
1992 fn enact_pvf_rejected(
1993 code_hash: &ValidationCodeHash,
1994 causes: Vec<PvfCheckCause<BlockNumberFor<T>>>,
1995 ) -> Weight {
1996 let mut weight = Weight::zero();
1997
1998 for cause in causes {
1999 weight += Self::decrease_code_ref(code_hash);
2002
2003 weight += T::DbWeight::get().reads_writes(3, 2);
2004 Self::deposit_event(Event::PvfCheckRejected(*code_hash, cause.para_id()));
2005
2006 match cause {
2007 PvfCheckCause::Onboarding(id) => {
2008 weight += T::DbWeight::get().writes(3);
2014 UpcomingParasGenesis::<T>::remove(&id);
2015 CurrentCodeHash::<T>::remove(&id);
2016 ParaLifecycles::<T>::remove(&id);
2017 },
2018 PvfCheckCause::Upgrade { id, .. } => {
2019 weight += T::DbWeight::get().writes(2);
2020 UpgradeGoAheadSignal::<T>::insert(&id, UpgradeGoAhead::Abort);
2021 FutureCodeHash::<T>::remove(&id);
2022 },
2023 }
2024 }
2025
2026 weight
2027 }
2028
2029 pub fn can_schedule_para_initialize(id: &ParaId) -> bool {
2033 ParaLifecycles::<T>::get(id).is_none()
2034 }
2035
2036 pub(crate) fn schedule_para_initialize(
2047 id: ParaId,
2048 mut genesis_data: ParaGenesisArgs,
2049 ) -> DispatchResult {
2050 ensure!(Self::can_schedule_para_initialize(&id), Error::<T>::CannotOnboard);
2053 ensure!(!genesis_data.validation_code.0.is_empty(), Error::<T>::CannotOnboard);
2054 ParaLifecycles::<T>::insert(&id, ParaLifecycle::Onboarding);
2055
2056 let validation_code =
2090 mem::replace(&mut genesis_data.validation_code, ValidationCode(Vec::new()));
2091 UpcomingParasGenesis::<T>::insert(&id, genesis_data);
2092 let validation_code_hash = validation_code.hash();
2093 CurrentCodeHash::<T>::insert(&id, validation_code_hash);
2094
2095 let cfg = configuration::ActiveConfig::<T>::get();
2096 Self::kick_off_pvf_check(
2097 PvfCheckCause::Onboarding(id),
2098 validation_code_hash,
2099 validation_code,
2100 &cfg,
2101 );
2102
2103 Ok(())
2104 }
2105
2106 pub(crate) fn schedule_para_cleanup(id: ParaId) -> DispatchResult {
2116 if let Some(future_code_hash) = FutureCodeHash::<T>::get(&id) {
2128 let active_prechecking = PvfActiveVoteList::<T>::get();
2129 if active_prechecking.contains(&future_code_hash) {
2130 return Err(Error::<T>::CannotOffboard.into());
2131 }
2132 }
2133
2134 let lifecycle = ParaLifecycles::<T>::get(&id);
2135 match lifecycle {
2136 None => return Ok(()),
2138 Some(ParaLifecycle::Parathread) => {
2139 ParaLifecycles::<T>::insert(&id, ParaLifecycle::OffboardingParathread);
2140 },
2141 Some(ParaLifecycle::Teyrchain) => {
2142 ParaLifecycles::<T>::insert(&id, ParaLifecycle::OffboardingTeyrchain);
2143 },
2144 _ => return Err(Error::<T>::CannotOffboard.into()),
2145 }
2146
2147 let scheduled_session = Self::scheduled_session();
2148 ActionsQueue::<T>::mutate(scheduled_session, |v| {
2149 if let Err(i) = v.binary_search(&id) {
2150 v.insert(i, id);
2151 }
2152 });
2153
2154 if <T as Config>::QueueFootprinter::message_count(UmpQueueId::Para(id)) != 0 {
2155 return Err(Error::<T>::CannotOffboard.into());
2156 }
2157
2158 Ok(())
2159 }
2160
2161 pub(crate) fn schedule_parathread_upgrade(id: ParaId) -> DispatchResult {
2165 let scheduled_session = Self::scheduled_session();
2166 let lifecycle = ParaLifecycles::<T>::get(&id).ok_or(Error::<T>::NotRegistered)?;
2167
2168 ensure!(lifecycle == ParaLifecycle::Parathread, Error::<T>::CannotUpgrade);
2169
2170 ParaLifecycles::<T>::insert(&id, ParaLifecycle::UpgradingParathread);
2171 ActionsQueue::<T>::mutate(scheduled_session, |v| {
2172 if let Err(i) = v.binary_search(&id) {
2173 v.insert(i, id);
2174 }
2175 });
2176
2177 Ok(())
2178 }
2179
2180 pub(crate) fn schedule_teyrchain_downgrade(id: ParaId) -> DispatchResult {
2184 let scheduled_session = Self::scheduled_session();
2185 let lifecycle = ParaLifecycles::<T>::get(&id).ok_or(Error::<T>::NotRegistered)?;
2186
2187 ensure!(lifecycle == ParaLifecycle::Teyrchain, Error::<T>::CannotDowngrade);
2188
2189 ParaLifecycles::<T>::insert(&id, ParaLifecycle::DowngradingTeyrchain);
2190 ActionsQueue::<T>::mutate(scheduled_session, |v| {
2191 if let Err(i) = v.binary_search(&id) {
2192 v.insert(i, id);
2193 }
2194 });
2195
2196 Ok(())
2197 }
2198
2199 pub(crate) fn schedule_code_upgrade(
2218 id: ParaId,
2219 new_code: ValidationCode,
2220 inclusion_block_number: BlockNumberFor<T>,
2221 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
2222 upgrade_strategy: UpgradeStrategy,
2223 ) {
2224 let new_code_len = new_code.0.len();
2226 if new_code_len < MIN_CODE_SIZE as usize || new_code_len > cfg.max_code_size as usize {
2227 log::warn!(target: LOG_TARGET, "attempted to schedule an upgrade with invalid new validation code",);
2228 return;
2229 }
2230
2231 if FutureCodeHash::<T>::contains_key(&id) {
2233 log::warn!(target: LOG_TARGET, "ended up scheduling an upgrade while one is pending",);
2242 return;
2243 }
2244
2245 let code_hash = new_code.hash();
2246
2247 if CurrentCodeHash::<T>::get(&id) == Some(code_hash) {
2252 log::warn!(
2255 target: LOG_TARGET,
2256 "para tried to upgrade to the same code. Abort the upgrade",
2257 );
2258 return;
2259 }
2260
2261 FutureCodeHash::<T>::insert(&id, &code_hash);
2263 UpgradeRestrictionSignal::<T>::insert(&id, UpgradeRestriction::Present);
2264
2265 let next_possible_upgrade_at = inclusion_block_number + cfg.validation_upgrade_cooldown;
2266 UpgradeCooldowns::<T>::mutate(|upgrade_cooldowns| {
2267 let insert_idx = upgrade_cooldowns
2268 .binary_search_by_key(&next_possible_upgrade_at, |&(_, b)| b)
2269 .unwrap_or_else(|idx| idx);
2270 upgrade_cooldowns.insert(insert_idx, (id, next_possible_upgrade_at));
2271 });
2272
2273 Self::kick_off_pvf_check(
2274 PvfCheckCause::Upgrade { id, included_at: inclusion_block_number, upgrade_strategy },
2275 code_hash,
2276 new_code,
2277 cfg,
2278 );
2279 }
2280
2281 fn kick_off_pvf_check(
2295 cause: PvfCheckCause<BlockNumberFor<T>>,
2296 code_hash: ValidationCodeHash,
2297 code: ValidationCode,
2298 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
2299 ) -> Weight {
2300 let mut weight = Weight::zero();
2301
2302 weight += T::DbWeight::get().reads_writes(3, 2);
2303 Self::deposit_event(Event::PvfCheckStarted(code_hash, cause.para_id()));
2304
2305 weight += T::DbWeight::get().reads(1);
2306 match PvfActiveVoteMap::<T>::get(&code_hash) {
2307 None => {
2308 let known_code = CodeByHash::<T>::contains_key(&code_hash);
2311 weight += T::DbWeight::get().reads(1);
2312
2313 if known_code {
2314 weight += T::DbWeight::get().reads(1);
2317 let now = pezframe_system::Pezpallet::<T>::block_number();
2318 weight += Self::enact_pvf_accepted(now, &code_hash, &[cause], 0, cfg);
2319 } else {
2320 weight += T::DbWeight::get().reads_writes(3, 2);
2323 let now = pezframe_system::Pezpallet::<T>::block_number();
2324 let n_validators = shared::ActiveValidatorKeys::<T>::get().len();
2325 PvfActiveVoteMap::<T>::insert(
2326 &code_hash,
2327 PvfCheckActiveVoteState::new(now, n_validators, cause),
2328 );
2329 PvfActiveVoteList::<T>::mutate(|l| {
2330 if let Err(idx) = l.binary_search(&code_hash) {
2331 l.insert(idx, code_hash);
2332 }
2333 });
2334 }
2335 },
2336 Some(mut vote_state) => {
2337 weight += T::DbWeight::get().writes(1);
2340 vote_state.causes.push(cause);
2341 PvfActiveVoteMap::<T>::insert(&code_hash, vote_state);
2342 },
2343 }
2344
2345 weight += Self::increase_code_ref(&code_hash, &code);
2357
2358 weight
2359 }
2360
2361 pub(crate) fn note_new_head(
2365 id: ParaId,
2366 new_head: HeadData,
2367 execution_context: BlockNumberFor<T>,
2368 ) {
2369 Heads::<T>::insert(&id, &new_head);
2370 MostRecentContext::<T>::insert(&id, execution_context);
2371
2372 if let Some(expected_at) = FutureCodeUpgrades::<T>::get(&id) {
2373 if expected_at <= execution_context {
2374 FutureCodeUpgrades::<T>::remove(&id);
2375 UpgradeGoAheadSignal::<T>::remove(&id);
2376
2377 let new_code_hash = if let Some(new_code_hash) = FutureCodeHash::<T>::take(&id) {
2379 new_code_hash
2380 } else {
2381 log::error!(target: LOG_TARGET, "Missing future code hash for {:?}", &id);
2382 return;
2383 };
2384
2385 Self::set_current_code(id, new_code_hash, expected_at);
2386 }
2387 } else {
2388 UpgradeGoAheadSignal::<T>::remove(&id);
2393 };
2394
2395 T::OnNewHead::on_new_head(id, &new_head);
2396 }
2397
2398 pub(crate) fn set_current_code(
2403 id: ParaId,
2404 new_code_hash: ValidationCodeHash,
2405 at: BlockNumberFor<T>,
2406 ) -> Weight {
2407 let maybe_prior_code_hash = CurrentCodeHash::<T>::get(&id);
2408 CurrentCodeHash::<T>::insert(&id, &new_code_hash);
2409
2410 let log = ConsensusLog::ParaUpgradeCode(id, new_code_hash);
2411 <pezframe_system::Pezpallet<T>>::deposit_log(log.into());
2412
2413 let now = <pezframe_system::Pezpallet<T>>::block_number();
2415
2416 let weight = if let Some(prior_code_hash) = maybe_prior_code_hash {
2417 Self::note_past_code(id, at, now, prior_code_hash)
2418 } else {
2419 log::error!(target: LOG_TARGET, "Missing prior code hash for para {:?}", &id);
2420 Weight::zero()
2421 };
2422
2423 weight + T::DbWeight::get().writes(1)
2424 }
2425
2426 fn do_force_set_current_code_update(para: ParaId, new_code: ValidationCode) {
2428 let new_code_hash = new_code.hash();
2429 Self::increase_code_ref(&new_code_hash, &new_code);
2430 Self::set_current_code(
2431 para,
2432 new_code_hash,
2433 pezframe_system::Pezpallet::<T>::block_number(),
2434 );
2435 Self::deposit_event(Event::CurrentCodeUpdated(para));
2436 }
2437
2438 pub(crate) fn pvfs_require_precheck() -> Vec<ValidationCodeHash> {
2441 PvfActiveVoteList::<T>::get()
2442 }
2443
2444 pub(crate) fn submit_pvf_check_statement(
2451 stmt: PvfCheckStatement,
2452 signature: ValidatorSignature,
2453 ) {
2454 use pezframe_system::offchain::SubmitTransaction;
2455
2456 let xt = T::create_bare(Call::include_pvf_check_statement { stmt, signature }.into());
2457 if let Err(e) = SubmitTransaction::<T, Call<T>>::submit_transaction(xt) {
2458 log::error!(target: LOG_TARGET, "Error submitting pvf check statement: {:?}", e,);
2459 }
2460 }
2461
2462 pub fn lifecycle(id: ParaId) -> Option<ParaLifecycle> {
2464 ParaLifecycles::<T>::get(&id)
2465 }
2466
2467 pub fn is_valid_para(id: ParaId) -> bool {
2471 if let Some(state) = ParaLifecycles::<T>::get(&id) {
2472 !state.is_onboarding() && !state.is_offboarding()
2473 } else {
2474 false
2475 }
2476 }
2477
2478 pub fn is_offboarding(id: ParaId) -> bool {
2482 ParaLifecycles::<T>::get(&id).map_or(false, |state| state.is_offboarding())
2483 }
2484
2485 pub fn is_teyrchain(id: ParaId) -> bool {
2490 if let Some(state) = ParaLifecycles::<T>::get(&id) {
2491 state.is_teyrchain()
2492 } else {
2493 false
2494 }
2495 }
2496
2497 pub fn is_parathread(id: ParaId) -> bool {
2501 if let Some(state) = ParaLifecycles::<T>::get(&id) {
2502 state.is_parathread()
2503 } else {
2504 false
2505 }
2506 }
2507
2508 pub(crate) fn can_upgrade_validation_code(id: ParaId) -> bool {
2511 FutureCodeHash::<T>::get(&id).is_none() && UpgradeRestrictionSignal::<T>::get(&id).is_none()
2512 }
2513
2514 fn scheduled_session() -> SessionIndex {
2516 shared::Pezpallet::<T>::scheduled_session()
2517 }
2518
2519 fn increase_code_ref(code_hash: &ValidationCodeHash, code: &ValidationCode) -> Weight {
2523 let mut weight = T::DbWeight::get().reads_writes(1, 1);
2524 CodeByHashRefs::<T>::mutate(code_hash, |refs| {
2525 if *refs == 0 {
2526 weight += T::DbWeight::get().writes(1);
2527 CodeByHash::<T>::insert(code_hash, code);
2528 }
2529 *refs += 1;
2530 });
2531 weight
2532 }
2533
2534 fn decrease_code_ref(code_hash: &ValidationCodeHash) -> Weight {
2539 let mut weight = T::DbWeight::get().reads(1);
2540 let refs = CodeByHashRefs::<T>::get(code_hash);
2541 if refs == 0 {
2542 log::error!(target: LOG_TARGET, "Code refs is already zero for {:?}", code_hash);
2543 return weight;
2544 }
2545 if refs <= 1 {
2546 weight += T::DbWeight::get().writes(2);
2547 CodeByHash::<T>::remove(code_hash);
2548 CodeByHashRefs::<T>::remove(code_hash);
2549 } else {
2550 weight += T::DbWeight::get().writes(1);
2551 CodeByHashRefs::<T>::insert(code_hash, refs - 1);
2552 }
2553 weight
2554 }
2555
2556 #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
2558 pub fn test_on_new_session() {
2559 Self::initializer_on_new_session(&SessionChangeNotification {
2560 session_index: shared::CurrentSessionIndex::<T>::get(),
2561 ..Default::default()
2562 });
2563 }
2564
2565 #[cfg(any(feature = "runtime-benchmarks", test))]
2566 pub fn heads_insert(para_id: &ParaId, head_data: HeadData) {
2567 Heads::<T>::insert(para_id, head_data);
2568 }
2569
2570 pub(crate) fn initialize_para_now(
2572 teyrchains: &mut TeyrchainsCache<T>,
2573 id: ParaId,
2574 genesis_data: &ParaGenesisArgs,
2575 ) {
2576 match genesis_data.para_kind {
2577 ParaKind::Teyrchain => {
2578 teyrchains.add(id);
2579 ParaLifecycles::<T>::insert(&id, ParaLifecycle::Teyrchain);
2580 },
2581 ParaKind::Parathread => ParaLifecycles::<T>::insert(&id, ParaLifecycle::Parathread),
2582 }
2583
2584 if !genesis_data.validation_code.0.is_empty() {
2590 let code_hash = genesis_data.validation_code.hash();
2591 Self::increase_code_ref(&code_hash, &genesis_data.validation_code);
2592 CurrentCodeHash::<T>::insert(&id, code_hash);
2593 }
2594
2595 Heads::<T>::insert(&id, &genesis_data.genesis_head);
2596 MostRecentContext::<T>::insert(&id, BlockNumberFor::<T>::from(0u32));
2597 }
2598
2599 #[cfg(test)]
2600 pub(crate) fn active_vote_state(
2601 code_hash: &ValidationCodeHash,
2602 ) -> Option<PvfCheckActiveVoteState<BlockNumberFor<T>>> {
2603 PvfActiveVoteMap::<T>::get(code_hash)
2604 }
2605
2606 pub(crate) fn validate_code_is_authorized(
2611 code: &ValidationCode,
2612 para: &ParaId,
2613 ) -> Result<AuthorizedCodeHashAndExpiry<BlockNumberFor<T>>, Error<T>> {
2614 let authorized = AuthorizedCodeHash::<T>::get(para).ok_or(Error::<T>::NothingAuthorized)?;
2615 let now = pezframe_system::Pezpallet::<T>::block_number();
2616 ensure!(authorized.expire_at > now, Error::<T>::InvalidBlockNumber);
2617 ensure!(authorized.code_hash == code.hash(), Error::<T>::Unauthorized);
2618 Ok(authorized)
2619 }
2620}
2621
2622pub(crate) struct TeyrchainsCache<T: Config> {
2625 teyrchains: Option<BTreeSet<ParaId>>,
2627 _config: PhantomData<T>,
2628}
2629
2630impl<T: Config> TeyrchainsCache<T> {
2631 pub fn new() -> Self {
2632 Self { teyrchains: None, _config: PhantomData }
2633 }
2634
2635 fn ensure_initialized(&mut self) -> &mut BTreeSet<ParaId> {
2636 self.teyrchains
2637 .get_or_insert_with(|| Teyrchains::<T>::get().into_iter().collect())
2638 }
2639
2640 pub fn add(&mut self, id: ParaId) {
2642 let teyrchains = self.ensure_initialized();
2643 teyrchains.insert(id);
2644 }
2645
2646 pub fn remove(&mut self, id: ParaId) {
2649 let teyrchains = self.ensure_initialized();
2650 teyrchains.remove(&id);
2651 }
2652}
2653
2654impl<T: Config> Drop for TeyrchainsCache<T> {
2655 fn drop(&mut self) {
2656 if let Some(teyrchains) = self.teyrchains.take() {
2657 Teyrchains::<T>::put(teyrchains.into_iter().collect::<Vec<ParaId>>());
2658 }
2659 }
2660}