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 frame_support::{pallet_prelude::*, traits::EstimateNextSessionRotation, DefaultNoBound};
121use frame_system::pallet_prelude::*;
122use polkadot_primitives::{
123 ConsensusLog, HeadData, Id as ParaId, PvfCheckStatement, SessionIndex, UpgradeGoAhead,
124 UpgradeRestriction, ValidationCode, ValidationCodeHash, ValidatorSignature, MIN_CODE_SIZE,
125};
126use scale_info::{Type, TypeInfo};
127use sp_core::RuntimeDebug;
128use sp_runtime::{
129 traits::{AppVerify, One, Saturating},
130 DispatchResult, SaturatedConversion,
131};
132
133use serde::{Deserialize, Serialize};
134
135pub use crate::Origin as ParachainOrigin;
136
137#[cfg(feature = "runtime-benchmarks")]
138pub mod benchmarking;
139
140#[cfg(test)]
141pub(crate) mod tests;
142
143pub use pallet::*;
144
145const LOG_TARGET: &str = "runtime::paras";
146
147#[derive(Default, Encode, Decode, TypeInfo)]
149#[cfg_attr(test, derive(Debug, Clone, PartialEq))]
150pub struct ReplacementTimes<N> {
151 expected_at: N,
155 activated_at: N,
159}
160
161#[derive(Default, Encode, Decode, TypeInfo)]
164#[cfg_attr(test, derive(Debug, Clone, PartialEq))]
165pub struct ParaPastCodeMeta<N> {
166 upgrade_times: Vec<ReplacementTimes<N>>,
172 last_pruned: Option<N>,
175}
176
177#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
183pub enum ParaLifecycle {
184 Onboarding,
186 Parathread,
188 Parachain,
190 UpgradingParathread,
192 DowngradingParachain,
194 OffboardingParathread,
196 OffboardingParachain,
198}
199
200impl ParaLifecycle {
201 pub fn is_onboarding(&self) -> bool {
205 matches!(self, ParaLifecycle::Onboarding)
206 }
207
208 pub fn is_stable(&self) -> bool {
211 matches!(self, ParaLifecycle::Parathread | ParaLifecycle::Parachain)
212 }
213
214 pub fn is_parachain(&self) -> bool {
218 matches!(
219 self,
220 ParaLifecycle::Parachain |
221 ParaLifecycle::DowngradingParachain |
222 ParaLifecycle::OffboardingParachain
223 )
224 }
225
226 pub fn is_parathread(&self) -> bool {
230 matches!(
231 self,
232 ParaLifecycle::Parathread |
233 ParaLifecycle::UpgradingParathread |
234 ParaLifecycle::OffboardingParathread
235 )
236 }
237
238 pub fn is_offboarding(&self) -> bool {
240 matches!(self, ParaLifecycle::OffboardingParathread | ParaLifecycle::OffboardingParachain)
241 }
242
243 pub fn is_transitioning(&self) -> bool {
245 !Self::is_stable(self)
246 }
247}
248
249impl<N: Ord + Copy + PartialEq> ParaPastCodeMeta<N> {
250 pub(crate) fn note_replacement(&mut self, expected_at: N, activated_at: N) {
252 self.upgrade_times.push(ReplacementTimes { expected_at, activated_at })
253 }
254
255 fn is_empty(&self) -> bool {
257 self.upgrade_times.is_empty()
258 }
259
260 #[cfg(test)]
263 fn most_recent_change(&self) -> Option<N> {
264 self.upgrade_times.last().map(|x| x.expected_at)
265 }
266
267 fn prune_up_to(&'_ mut self, max: N) -> impl Iterator<Item = N> + '_ {
278 let to_prune = self.upgrade_times.iter().take_while(|t| t.activated_at <= max).count();
279 let drained = if to_prune == 0 {
280 self.upgrade_times.drain(self.upgrade_times.len()..)
282 } else {
283 self.last_pruned = Some(self.upgrade_times[to_prune - 1].activated_at);
285 self.upgrade_times.drain(..to_prune)
286 };
287
288 drained.map(|times| times.expected_at)
289 }
290}
291
292#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Serialize, Deserialize)]
294pub struct ParaGenesisArgs {
295 pub genesis_head: HeadData,
297 pub validation_code: ValidationCode,
299 #[serde(rename = "parachain")]
301 pub para_kind: ParaKind,
302}
303
304#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
306pub enum ParaKind {
307 Parathread,
308 Parachain,
309}
310
311impl Serialize for ParaKind {
312 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
313 where
314 S: serde::Serializer,
315 {
316 match self {
317 ParaKind::Parachain => serializer.serialize_bool(true),
318 ParaKind::Parathread => serializer.serialize_bool(false),
319 }
320 }
321}
322
323impl<'de> Deserialize<'de> for ParaKind {
324 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
325 where
326 D: serde::Deserializer<'de>,
327 {
328 match serde::de::Deserialize::deserialize(deserializer) {
329 Ok(true) => Ok(ParaKind::Parachain),
330 Ok(false) => Ok(ParaKind::Parathread),
331 _ => Err(serde::de::Error::custom("invalid ParaKind serde representation")),
332 }
333 }
334}
335
336impl Encode for ParaKind {
339 fn size_hint(&self) -> usize {
340 true.size_hint()
341 }
342
343 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
344 match self {
345 ParaKind::Parachain => true.using_encoded(f),
346 ParaKind::Parathread => false.using_encoded(f),
347 }
348 }
349}
350
351impl Decode for ParaKind {
352 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
353 match bool::decode(input) {
354 Ok(true) => Ok(ParaKind::Parachain),
355 Ok(false) => Ok(ParaKind::Parathread),
356 _ => Err("Invalid ParaKind representation".into()),
357 }
358 }
359}
360
361impl TypeInfo for ParaKind {
362 type Identity = bool;
363 fn type_info() -> Type {
364 bool::type_info()
365 }
366}
367
368#[derive(Debug, Encode, Decode, TypeInfo)]
371pub(crate) enum PvfCheckCause<BlockNumber> {
372 Onboarding(ParaId),
374 Upgrade {
376 id: ParaId,
379 included_at: BlockNumber,
388 upgrade_strategy: UpgradeStrategy,
393 },
394}
395
396#[derive(Debug, Copy, Clone, PartialEq, TypeInfo, Decode, Encode)]
404pub enum UpgradeStrategy {
405 SetGoAheadSignal,
410 ApplyAtExpectedBlock,
414}
415
416impl<BlockNumber> PvfCheckCause<BlockNumber> {
417 fn para_id(&self) -> ParaId {
419 match *self {
420 PvfCheckCause::Onboarding(id) => id,
421 PvfCheckCause::Upgrade { id, .. } => id,
422 }
423 }
424}
425
426#[derive(Copy, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
428enum PvfCheckOutcome {
429 Accepted,
430 Rejected,
431}
432
433#[derive(Encode, Decode, TypeInfo)]
435pub(crate) struct PvfCheckActiveVoteState<BlockNumber> {
436 votes_accept: BitVec<u8, BitOrderLsb0>,
442 votes_reject: BitVec<u8, BitOrderLsb0>,
443
444 age: SessionIndex,
447 created_at: BlockNumber,
449 causes: Vec<PvfCheckCause<BlockNumber>>,
451}
452
453impl<BlockNumber> PvfCheckActiveVoteState<BlockNumber> {
454 fn new(now: BlockNumber, n_validators: usize, cause: PvfCheckCause<BlockNumber>) -> Self {
457 let mut causes = Vec::with_capacity(1);
458 causes.push(cause);
459 Self {
460 created_at: now,
461 votes_accept: bitvec::bitvec![u8, BitOrderLsb0; 0; n_validators],
462 votes_reject: bitvec::bitvec![u8, BitOrderLsb0; 0; n_validators],
463 age: 0,
464 causes,
465 }
466 }
467
468 fn reinitialize_ballots(&mut self, n_validators: usize) {
471 let clear_and_resize = |v: &mut BitVec<_, _>| {
472 v.clear();
473 v.resize(n_validators, false);
474 };
475 clear_and_resize(&mut self.votes_accept);
476 clear_and_resize(&mut self.votes_reject);
477 }
478
479 fn has_vote(&self, validator_index: usize) -> Option<bool> {
482 let accept_vote = self.votes_accept.get(validator_index)?;
483 let reject_vote = self.votes_reject.get(validator_index)?;
484 Some(*accept_vote || *reject_vote)
485 }
486
487 fn quorum(&self, n_validators: usize) -> Option<PvfCheckOutcome> {
489 let accept_threshold = polkadot_primitives::supermajority_threshold(n_validators);
490 let reject_threshold = n_validators - accept_threshold;
492
493 if self.votes_accept.count_ones() >= accept_threshold {
494 Some(PvfCheckOutcome::Accepted)
495 } else if self.votes_reject.count_ones() > reject_threshold {
496 Some(PvfCheckOutcome::Rejected)
497 } else {
498 None
499 }
500 }
501
502 #[cfg(test)]
503 pub(crate) fn causes(&self) -> &[PvfCheckCause<BlockNumber>] {
504 self.causes.as_slice()
505 }
506}
507
508pub trait OnNewHead {
510 fn on_new_head(id: ParaId, head: &HeadData) -> Weight;
513}
514
515#[impl_trait_for_tuples::impl_for_tuples(30)]
516impl OnNewHead for Tuple {
517 fn on_new_head(id: ParaId, head: &HeadData) -> Weight {
518 let mut weight: Weight = Default::default();
519 for_tuples!( #( weight.saturating_accrue(Tuple::on_new_head(id, head)); )* );
520 weight
521 }
522}
523
524pub trait AssignCoretime {
529 fn assign_coretime(id: ParaId) -> DispatchResult;
531}
532
533impl AssignCoretime for () {
534 fn assign_coretime(_: ParaId) -> DispatchResult {
535 Ok(())
536 }
537}
538
539pub trait WeightInfo {
540 fn force_set_current_code(c: u32) -> Weight;
541 fn force_set_current_head(s: u32) -> Weight;
542 fn force_set_most_recent_context() -> Weight;
543 fn force_schedule_code_upgrade(c: u32) -> Weight;
544 fn force_note_new_head(s: u32) -> Weight;
545 fn force_queue_action() -> Weight;
546 fn add_trusted_validation_code(c: u32) -> Weight;
547 fn poke_unused_validation_code() -> Weight;
548
549 fn include_pvf_check_statement_finalize_upgrade_accept() -> Weight;
550 fn include_pvf_check_statement_finalize_upgrade_reject() -> Weight;
551 fn include_pvf_check_statement_finalize_onboarding_accept() -> Weight;
552 fn include_pvf_check_statement_finalize_onboarding_reject() -> Weight;
553 fn include_pvf_check_statement() -> Weight;
554}
555
556pub struct TestWeightInfo;
557impl WeightInfo for TestWeightInfo {
558 fn force_set_current_code(_c: u32) -> Weight {
559 Weight::MAX
560 }
561 fn force_set_current_head(_s: u32) -> Weight {
562 Weight::MAX
563 }
564 fn force_set_most_recent_context() -> Weight {
565 Weight::MAX
566 }
567 fn force_schedule_code_upgrade(_c: u32) -> Weight {
568 Weight::MAX
569 }
570 fn force_note_new_head(_s: u32) -> Weight {
571 Weight::MAX
572 }
573 fn force_queue_action() -> Weight {
574 Weight::MAX
575 }
576 fn add_trusted_validation_code(_c: u32) -> Weight {
577 Weight::zero()
579 }
580 fn poke_unused_validation_code() -> Weight {
581 Weight::MAX
582 }
583 fn include_pvf_check_statement_finalize_upgrade_accept() -> Weight {
584 Weight::MAX
585 }
586 fn include_pvf_check_statement_finalize_upgrade_reject() -> Weight {
587 Weight::MAX
588 }
589 fn include_pvf_check_statement_finalize_onboarding_accept() -> Weight {
590 Weight::MAX
591 }
592 fn include_pvf_check_statement_finalize_onboarding_reject() -> Weight {
593 Weight::MAX
594 }
595 fn include_pvf_check_statement() -> Weight {
596 Weight::MAX - Weight::from_parts(1, 1)
598 }
599}
600
601#[frame_support::pallet]
602pub mod pallet {
603 use super::*;
604 use sp_runtime::transaction_validity::{
605 InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
606 ValidTransaction,
607 };
608
609 #[pallet::pallet]
610 #[pallet::without_storage_info]
611 pub struct Pallet<T>(_);
612
613 #[pallet::config]
614 pub trait Config:
615 frame_system::Config
616 + configuration::Config
617 + shared::Config
618 + frame_system::offchain::CreateInherent<Call<Self>>
619 {
620 type RuntimeEvent: From<Event> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
621
622 #[pallet::constant]
623 type UnsignedPriority: Get<TransactionPriority>;
624
625 type NextSessionRotation: EstimateNextSessionRotation<BlockNumberFor<Self>>;
626
627 type QueueFootprinter: QueueFootprinter<Origin = UmpQueueId>;
632
633 type OnNewHead: OnNewHead;
635
636 type WeightInfo: WeightInfo;
638
639 type AssignCoretime: AssignCoretime;
645 }
646
647 #[pallet::event]
648 #[pallet::generate_deposit(pub(super) fn deposit_event)]
649 pub enum Event {
650 CurrentCodeUpdated(ParaId),
652 CurrentHeadUpdated(ParaId),
654 CodeUpgradeScheduled(ParaId),
656 NewHeadNoted(ParaId),
658 ActionQueued(ParaId, SessionIndex),
660 PvfCheckStarted(ValidationCodeHash, ParaId),
663 PvfCheckAccepted(ValidationCodeHash, ParaId),
666 PvfCheckRejected(ValidationCodeHash, ParaId),
669 }
670
671 #[pallet::error]
672 pub enum Error<T> {
673 NotRegistered,
675 CannotOnboard,
677 CannotOffboard,
679 CannotUpgrade,
681 CannotDowngrade,
683 PvfCheckStatementStale,
685 PvfCheckStatementFuture,
687 PvfCheckValidatorIndexOutOfBounds,
689 PvfCheckInvalidSignature,
691 PvfCheckDoubleVote,
693 PvfCheckSubjectInvalid,
695 CannotUpgradeCode,
697 InvalidCode,
699 }
700
701 #[pallet::storage]
706 pub(super) type PvfActiveVoteMap<T: Config> = StorageMap<
707 _,
708 Twox64Concat,
709 ValidationCodeHash,
710 PvfCheckActiveVoteState<BlockNumberFor<T>>,
711 OptionQuery,
712 >;
713
714 #[pallet::storage]
716 pub(super) type PvfActiveVoteList<T: Config> =
717 StorageValue<_, Vec<ValidationCodeHash>, ValueQuery>;
718
719 #[pallet::storage]
724 pub type Parachains<T: Config> = StorageValue<_, Vec<ParaId>, ValueQuery>;
725
726 #[pallet::storage]
728 pub(super) type ParaLifecycles<T: Config> = StorageMap<_, Twox64Concat, ParaId, ParaLifecycle>;
729
730 #[pallet::storage]
732 pub type Heads<T: Config> = StorageMap<_, Twox64Concat, ParaId, HeadData>;
733
734 #[pallet::storage]
736 pub type MostRecentContext<T: Config> = StorageMap<_, Twox64Concat, ParaId, BlockNumberFor<T>>;
737
738 #[pallet::storage]
742 pub type CurrentCodeHash<T: Config> = StorageMap<_, Twox64Concat, ParaId, ValidationCodeHash>;
743
744 #[pallet::storage]
749 pub(super) type PastCodeHash<T: Config> =
750 StorageMap<_, Twox64Concat, (ParaId, BlockNumberFor<T>), ValidationCodeHash>;
751
752 #[pallet::storage]
756 pub type PastCodeMeta<T: Config> =
757 StorageMap<_, Twox64Concat, ParaId, ParaPastCodeMeta<BlockNumberFor<T>>, ValueQuery>;
758
759 #[pallet::storage]
766 pub(super) type PastCodePruning<T: Config> =
767 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
768
769 #[pallet::storage]
774 pub type FutureCodeUpgrades<T: Config> = StorageMap<_, Twox64Concat, ParaId, BlockNumberFor<T>>;
775
776 #[pallet::storage]
785 pub(super) type FutureCodeUpgradesAt<T: Config> =
786 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
787
788 #[pallet::storage]
792 pub type FutureCodeHash<T: Config> = StorageMap<_, Twox64Concat, ParaId, ValidationCodeHash>;
793
794 #[pallet::storage]
805 pub(super) type UpgradeGoAheadSignal<T: Config> =
806 StorageMap<_, Twox64Concat, ParaId, UpgradeGoAhead>;
807
808 #[pallet::storage]
818 pub type UpgradeRestrictionSignal<T: Config> =
819 StorageMap<_, Twox64Concat, ParaId, UpgradeRestriction>;
820
821 #[pallet::storage]
825 pub(super) type UpgradeCooldowns<T: Config> =
826 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
827
828 #[pallet::storage]
835 pub(super) type UpcomingUpgrades<T: Config> =
836 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
837
838 #[pallet::storage]
840 pub type ActionsQueue<T: Config> =
841 StorageMap<_, Twox64Concat, SessionIndex, Vec<ParaId>, ValueQuery>;
842
843 #[pallet::storage]
848 pub(super) type UpcomingParasGenesis<T: Config> =
849 StorageMap<_, Twox64Concat, ParaId, ParaGenesisArgs>;
850
851 #[pallet::storage]
853 pub(super) type CodeByHashRefs<T: Config> =
854 StorageMap<_, Identity, ValidationCodeHash, u32, ValueQuery>;
855
856 #[pallet::storage]
861 pub type CodeByHash<T: Config> = StorageMap<_, Identity, ValidationCodeHash, ValidationCode>;
862
863 #[pallet::genesis_config]
864 #[derive(DefaultNoBound)]
865 pub struct GenesisConfig<T: Config> {
866 #[serde(skip)]
867 pub _config: core::marker::PhantomData<T>,
868 pub paras: Vec<(ParaId, ParaGenesisArgs)>,
869 }
870
871 #[pallet::genesis_build]
872 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
873 fn build(&self) {
874 let mut parachains = ParachainsCache::new();
875 for (id, genesis_args) in &self.paras {
876 if genesis_args.validation_code.0.is_empty() {
877 panic!("empty validation code is not allowed in genesis");
878 }
879 Pallet::<T>::initialize_para_now(&mut parachains, *id, genesis_args);
880 T::AssignCoretime::assign_coretime(*id)
881 .expect("Assigning coretime works at genesis; qed");
882 }
883 }
885 }
886
887 #[pallet::call]
888 impl<T: Config> Pallet<T> {
889 #[pallet::call_index(0)]
891 #[pallet::weight(<T as Config>::WeightInfo::force_set_current_code(new_code.0.len() as u32))]
892 pub fn force_set_current_code(
893 origin: OriginFor<T>,
894 para: ParaId,
895 new_code: ValidationCode,
896 ) -> DispatchResult {
897 ensure_root(origin)?;
898 let new_code_hash = new_code.hash();
899 Self::increase_code_ref(&new_code_hash, &new_code);
900 Self::set_current_code(para, new_code_hash, frame_system::Pallet::<T>::block_number());
901 Self::deposit_event(Event::CurrentCodeUpdated(para));
902 Ok(())
903 }
904
905 #[pallet::call_index(1)]
907 #[pallet::weight(<T as Config>::WeightInfo::force_set_current_head(new_head.0.len() as u32))]
908 pub fn force_set_current_head(
909 origin: OriginFor<T>,
910 para: ParaId,
911 new_head: HeadData,
912 ) -> DispatchResult {
913 ensure_root(origin)?;
914 Self::set_current_head(para, new_head);
915 Ok(())
916 }
917
918 #[pallet::call_index(2)]
920 #[pallet::weight(<T as Config>::WeightInfo::force_schedule_code_upgrade(new_code.0.len() as u32))]
921 pub fn force_schedule_code_upgrade(
922 origin: OriginFor<T>,
923 para: ParaId,
924 new_code: ValidationCode,
925 relay_parent_number: BlockNumberFor<T>,
926 ) -> DispatchResult {
927 ensure_root(origin)?;
928 let config = configuration::ActiveConfig::<T>::get();
929 Self::schedule_code_upgrade(
930 para,
931 new_code,
932 relay_parent_number,
933 &config,
934 UpgradeStrategy::ApplyAtExpectedBlock,
935 );
936 Self::deposit_event(Event::CodeUpgradeScheduled(para));
937 Ok(())
938 }
939
940 #[pallet::call_index(3)]
942 #[pallet::weight(<T as Config>::WeightInfo::force_note_new_head(new_head.0.len() as u32))]
943 pub fn force_note_new_head(
944 origin: OriginFor<T>,
945 para: ParaId,
946 new_head: HeadData,
947 ) -> DispatchResult {
948 ensure_root(origin)?;
949 let now = frame_system::Pallet::<T>::block_number();
950 Self::note_new_head(para, new_head, now);
951 Self::deposit_event(Event::NewHeadNoted(para));
952 Ok(())
953 }
954
955 #[pallet::call_index(4)]
959 #[pallet::weight(<T as Config>::WeightInfo::force_queue_action())]
960 pub fn force_queue_action(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
961 ensure_root(origin)?;
962 let next_session = shared::CurrentSessionIndex::<T>::get().saturating_add(One::one());
963 ActionsQueue::<T>::mutate(next_session, |v| {
964 if let Err(i) = v.binary_search(¶) {
965 v.insert(i, para);
966 }
967 });
968 Self::deposit_event(Event::ActionQueued(para, next_session));
969 Ok(())
970 }
971
972 #[pallet::call_index(5)]
987 #[pallet::weight(<T as Config>::WeightInfo::add_trusted_validation_code(validation_code.0.len() as u32))]
988 pub fn add_trusted_validation_code(
989 origin: OriginFor<T>,
990 validation_code: ValidationCode,
991 ) -> DispatchResult {
992 ensure_root(origin)?;
993 let code_hash = validation_code.hash();
994
995 if let Some(vote) = PvfActiveVoteMap::<T>::get(&code_hash) {
996 PvfActiveVoteMap::<T>::remove(&code_hash);
998 PvfActiveVoteList::<T>::mutate(|l| {
999 if let Ok(i) = l.binary_search(&code_hash) {
1000 l.remove(i);
1001 }
1002 });
1003
1004 let cfg = configuration::ActiveConfig::<T>::get();
1005 Self::enact_pvf_accepted(
1006 frame_system::Pallet::<T>::block_number(),
1007 &code_hash,
1008 &vote.causes,
1009 vote.age,
1010 &cfg,
1011 );
1012 return Ok(())
1013 }
1014
1015 if CodeByHash::<T>::contains_key(&code_hash) {
1016 return Ok(())
1018 }
1019
1020 CodeByHash::<T>::insert(code_hash, &validation_code);
1026
1027 Ok(())
1028 }
1029
1030 #[pallet::call_index(6)]
1036 #[pallet::weight(<T as Config>::WeightInfo::poke_unused_validation_code())]
1037 pub fn poke_unused_validation_code(
1038 origin: OriginFor<T>,
1039 validation_code_hash: ValidationCodeHash,
1040 ) -> DispatchResult {
1041 ensure_root(origin)?;
1042 if CodeByHashRefs::<T>::get(&validation_code_hash) == 0 {
1043 CodeByHash::<T>::remove(&validation_code_hash);
1044 }
1045 Ok(())
1046 }
1047
1048 #[pallet::call_index(7)]
1051 #[pallet::weight(
1052 <T as Config>::WeightInfo::include_pvf_check_statement_finalize_upgrade_accept()
1053 .max(<T as Config>::WeightInfo::include_pvf_check_statement_finalize_upgrade_reject())
1054 .max(<T as Config>::WeightInfo::include_pvf_check_statement_finalize_onboarding_accept()
1055 .max(<T as Config>::WeightInfo::include_pvf_check_statement_finalize_onboarding_reject())
1056 )
1057 )]
1058 pub fn include_pvf_check_statement(
1059 origin: OriginFor<T>,
1060 stmt: PvfCheckStatement,
1061 signature: ValidatorSignature,
1062 ) -> DispatchResultWithPostInfo {
1063 ensure_none(origin)?;
1064
1065 let validators = shared::ActiveValidatorKeys::<T>::get();
1066 let current_session = shared::CurrentSessionIndex::<T>::get();
1067 if stmt.session_index < current_session {
1068 return Err(Error::<T>::PvfCheckStatementStale.into())
1069 } else if stmt.session_index > current_session {
1070 return Err(Error::<T>::PvfCheckStatementFuture.into())
1071 }
1072 let validator_index = stmt.validator_index.0 as usize;
1073 let validator_public = validators
1074 .get(validator_index)
1075 .ok_or(Error::<T>::PvfCheckValidatorIndexOutOfBounds)?;
1076
1077 let signing_payload = stmt.signing_payload();
1078 ensure!(
1079 signature.verify(&signing_payload[..], &validator_public),
1080 Error::<T>::PvfCheckInvalidSignature,
1081 );
1082
1083 let mut active_vote = PvfActiveVoteMap::<T>::get(&stmt.subject)
1084 .ok_or(Error::<T>::PvfCheckSubjectInvalid)?;
1085
1086 ensure!(
1088 !active_vote
1089 .has_vote(validator_index)
1090 .ok_or(Error::<T>::PvfCheckValidatorIndexOutOfBounds)?,
1091 Error::<T>::PvfCheckDoubleVote,
1092 );
1093
1094 if stmt.accept {
1096 active_vote.votes_accept.set(validator_index, true);
1097 } else {
1098 active_vote.votes_reject.set(validator_index, true);
1099 }
1100
1101 if let Some(outcome) = active_vote.quorum(validators.len()) {
1102 PvfActiveVoteMap::<T>::remove(&stmt.subject);
1107 PvfActiveVoteList::<T>::mutate(|l| {
1108 if let Ok(i) = l.binary_search(&stmt.subject) {
1109 l.remove(i);
1110 }
1111 });
1112 match outcome {
1113 PvfCheckOutcome::Accepted => {
1114 let cfg = configuration::ActiveConfig::<T>::get();
1115 Self::enact_pvf_accepted(
1116 frame_system::Pallet::<T>::block_number(),
1117 &stmt.subject,
1118 &active_vote.causes,
1119 active_vote.age,
1120 &cfg,
1121 );
1122 },
1123 PvfCheckOutcome::Rejected => {
1124 Self::enact_pvf_rejected(&stmt.subject, active_vote.causes);
1125 },
1126 }
1127
1128 Ok(().into())
1130 } else {
1131 PvfActiveVoteMap::<T>::insert(&stmt.subject, active_vote);
1136 Ok(Some(<T as Config>::WeightInfo::include_pvf_check_statement()).into())
1137 }
1138 }
1139
1140 #[pallet::call_index(8)]
1142 #[pallet::weight(<T as Config>::WeightInfo::force_set_most_recent_context())]
1143 pub fn force_set_most_recent_context(
1144 origin: OriginFor<T>,
1145 para: ParaId,
1146 context: BlockNumberFor<T>,
1147 ) -> DispatchResult {
1148 ensure_root(origin)?;
1149 MostRecentContext::<T>::insert(¶, context);
1150 Ok(())
1151 }
1152 }
1153
1154 #[pallet::validate_unsigned]
1155 impl<T: Config> ValidateUnsigned for Pallet<T> {
1156 type Call = Call<T>;
1157
1158 fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
1159 let (stmt, signature) = match call {
1160 Call::include_pvf_check_statement { stmt, signature } => (stmt, signature),
1161 _ => return InvalidTransaction::Call.into(),
1162 };
1163
1164 let current_session = shared::CurrentSessionIndex::<T>::get();
1165 if stmt.session_index < current_session {
1166 return InvalidTransaction::Stale.into()
1167 } else if stmt.session_index > current_session {
1168 return InvalidTransaction::Future.into()
1169 }
1170
1171 let validator_index = stmt.validator_index.0 as usize;
1172 let validators = shared::ActiveValidatorKeys::<T>::get();
1173 let validator_public = match validators.get(validator_index) {
1174 Some(pk) => pk,
1175 None => return InvalidTransaction::Custom(INVALID_TX_BAD_VALIDATOR_IDX).into(),
1176 };
1177
1178 let signing_payload = stmt.signing_payload();
1179 if !signature.verify(&signing_payload[..], &validator_public) {
1180 return InvalidTransaction::BadProof.into()
1181 }
1182
1183 let active_vote = match PvfActiveVoteMap::<T>::get(&stmt.subject) {
1184 Some(v) => v,
1185 None => return InvalidTransaction::Custom(INVALID_TX_BAD_SUBJECT).into(),
1186 };
1187
1188 match active_vote.has_vote(validator_index) {
1189 Some(false) => (),
1190 Some(true) => return InvalidTransaction::Custom(INVALID_TX_DOUBLE_VOTE).into(),
1191 None => return InvalidTransaction::Custom(INVALID_TX_BAD_VALIDATOR_IDX).into(),
1192 }
1193
1194 ValidTransaction::with_tag_prefix("PvfPreCheckingVote")
1195 .priority(T::UnsignedPriority::get())
1196 .longevity(
1197 TryInto::<u64>::try_into(
1198 T::NextSessionRotation::average_session_length() / 2u32.into(),
1199 )
1200 .unwrap_or(64_u64),
1201 )
1202 .and_provides((stmt.session_index, stmt.validator_index, stmt.subject))
1203 .propagate(true)
1204 .build()
1205 }
1206
1207 fn pre_dispatch(_call: &Self::Call) -> Result<(), TransactionValidityError> {
1208 Ok(())
1216 }
1217 }
1218}
1219
1220const INVALID_TX_BAD_VALIDATOR_IDX: u8 = 1;
1222const INVALID_TX_BAD_SUBJECT: u8 = 2;
1223const INVALID_TX_DOUBLE_VOTE: u8 = 3;
1224
1225pub const MAX_PARA_HEADS: usize = 1024;
1233
1234impl<T: Config> Pallet<T> {
1235 pub(crate) fn schedule_code_upgrade_external(
1240 id: ParaId,
1241 new_code: ValidationCode,
1242 upgrade_strategy: UpgradeStrategy,
1243 ) -> DispatchResult {
1244 ensure!(Self::can_upgrade_validation_code(id), Error::<T>::CannotUpgradeCode);
1246 let config = configuration::ActiveConfig::<T>::get();
1247 ensure!(new_code.0.len() >= MIN_CODE_SIZE as usize, Error::<T>::InvalidCode);
1249 ensure!(new_code.0.len() <= config.max_code_size as usize, Error::<T>::InvalidCode);
1250
1251 let current_block = frame_system::Pallet::<T>::block_number();
1252 let upgrade_block = current_block.saturating_add(config.validation_upgrade_delay);
1254 Self::schedule_code_upgrade(id, new_code, upgrade_block, &config, upgrade_strategy);
1255 Self::deposit_event(Event::CodeUpgradeScheduled(id));
1256 Ok(())
1257 }
1258
1259 pub(crate) fn set_current_head(para: ParaId, new_head: HeadData) {
1261 Heads::<T>::insert(¶, new_head);
1262 Self::deposit_event(Event::CurrentHeadUpdated(para));
1263 }
1264
1265 pub(crate) fn initializer_initialize(now: BlockNumberFor<T>) -> Weight {
1267 Self::prune_old_code(now) +
1268 Self::process_scheduled_upgrade_changes(now) +
1269 Self::process_future_code_upgrades_at(now)
1270 }
1271
1272 pub(crate) fn initializer_finalize(now: BlockNumberFor<T>) {
1274 Self::process_scheduled_upgrade_cooldowns(now);
1275 }
1276
1277 pub(crate) fn initializer_on_new_session(
1281 notification: &SessionChangeNotification<BlockNumberFor<T>>,
1282 ) -> Vec<ParaId> {
1283 let outgoing_paras = Self::apply_actions_queue(notification.session_index);
1284 Self::groom_ongoing_pvf_votes(¬ification.new_config, notification.validators.len());
1285 outgoing_paras
1286 }
1287
1288 pub(crate) fn current_code(para_id: &ParaId) -> Option<ValidationCode> {
1290 CurrentCodeHash::<T>::get(para_id).and_then(|code_hash| {
1291 let code = CodeByHash::<T>::get(&code_hash);
1292 if code.is_none() {
1293 log::error!(
1294 "Pallet paras storage is inconsistent, code not found for hash {}",
1295 code_hash,
1296 );
1297 debug_assert!(false, "inconsistent paras storages");
1298 }
1299 code
1300 })
1301 }
1302
1303 pub fn sorted_para_heads() -> Vec<(u32, Vec<u8>)> {
1306 let mut heads: Vec<(u32, Vec<u8>)> =
1307 Heads::<T>::iter().map(|(id, head)| (id.into(), head.0)).collect();
1308 heads.sort_by_key(|(id, _)| *id);
1309 heads.truncate(MAX_PARA_HEADS);
1310 heads
1311 }
1312
1313 fn apply_actions_queue(session: SessionIndex) -> Vec<ParaId> {
1322 let actions = ActionsQueue::<T>::take(session);
1323 let mut parachains = ParachainsCache::new();
1324 let now = frame_system::Pallet::<T>::block_number();
1325 let mut outgoing = Vec::new();
1326
1327 for para in actions {
1328 let lifecycle = ParaLifecycles::<T>::get(¶);
1329 match lifecycle {
1330 None | Some(ParaLifecycle::Parathread) | Some(ParaLifecycle::Parachain) => { },
1332 Some(ParaLifecycle::Onboarding) => {
1333 if let Some(genesis_data) = UpcomingParasGenesis::<T>::take(¶) {
1334 Self::initialize_para_now(&mut parachains, para, &genesis_data);
1335 }
1336 },
1337 Some(ParaLifecycle::UpgradingParathread) => {
1339 parachains.add(para);
1340 ParaLifecycles::<T>::insert(¶, ParaLifecycle::Parachain);
1341 },
1342 Some(ParaLifecycle::DowngradingParachain) => {
1344 parachains.remove(para);
1345 ParaLifecycles::<T>::insert(¶, ParaLifecycle::Parathread);
1346 },
1347 Some(ParaLifecycle::OffboardingParachain) |
1349 Some(ParaLifecycle::OffboardingParathread) => {
1350 parachains.remove(para);
1351
1352 Heads::<T>::remove(¶);
1353 MostRecentContext::<T>::remove(¶);
1354 FutureCodeUpgrades::<T>::remove(¶);
1355 UpgradeGoAheadSignal::<T>::remove(¶);
1356 UpgradeRestrictionSignal::<T>::remove(¶);
1357 ParaLifecycles::<T>::remove(¶);
1358 let removed_future_code_hash = FutureCodeHash::<T>::take(¶);
1359 if let Some(removed_future_code_hash) = removed_future_code_hash {
1360 Self::decrease_code_ref(&removed_future_code_hash);
1361 }
1362
1363 let removed_code_hash = CurrentCodeHash::<T>::take(¶);
1364 if let Some(removed_code_hash) = removed_code_hash {
1365 Self::note_past_code(para, now, now, removed_code_hash);
1366 }
1367
1368 outgoing.push(para);
1369 },
1370 }
1371 }
1372
1373 if !outgoing.is_empty() {
1374 UpcomingUpgrades::<T>::mutate(|upcoming_upgrades| {
1381 upcoming_upgrades.retain(|(para, _)| !outgoing.contains(para));
1382 });
1383 UpgradeCooldowns::<T>::mutate(|upgrade_cooldowns| {
1384 upgrade_cooldowns.retain(|(para, _)| !outgoing.contains(para));
1385 });
1386 FutureCodeUpgradesAt::<T>::mutate(|future_upgrades| {
1387 future_upgrades.retain(|(para, _)| !outgoing.contains(para));
1388 });
1389 }
1390
1391 drop(parachains);
1393
1394 outgoing
1395 }
1396
1397 fn note_past_code(
1404 id: ParaId,
1405 at: BlockNumberFor<T>,
1406 now: BlockNumberFor<T>,
1407 old_code_hash: ValidationCodeHash,
1408 ) -> Weight {
1409 PastCodeMeta::<T>::mutate(&id, |past_meta| {
1410 past_meta.note_replacement(at, now);
1411 });
1412
1413 PastCodeHash::<T>::insert(&(id, at), old_code_hash);
1414
1415 PastCodePruning::<T>::mutate(|pruning| {
1418 let insert_idx =
1419 pruning.binary_search_by_key(&now, |&(_, b)| b).unwrap_or_else(|idx| idx);
1420 pruning.insert(insert_idx, (id, now));
1421 });
1422
1423 T::DbWeight::get().reads_writes(2, 3)
1424 }
1425
1426 fn prune_old_code(now: BlockNumberFor<T>) -> Weight {
1429 let config = configuration::ActiveConfig::<T>::get();
1430 let code_retention_period = config.code_retention_period;
1431 if now <= code_retention_period {
1432 let weight = T::DbWeight::get().reads_writes(1, 0);
1433 return weight
1434 }
1435
1436 let pruning_height = now - (code_retention_period + One::one());
1438
1439 let pruning_tasks_done =
1440 PastCodePruning::<T>::mutate(|pruning_tasks: &mut Vec<(_, BlockNumberFor<T>)>| {
1441 let (pruning_tasks_done, pruning_tasks_to_do) = {
1442 let up_to_idx =
1444 pruning_tasks.iter().take_while(|&(_, at)| at <= &pruning_height).count();
1445 (up_to_idx, pruning_tasks.drain(..up_to_idx))
1446 };
1447
1448 for (para_id, _) in pruning_tasks_to_do {
1449 let full_deactivate = PastCodeMeta::<T>::mutate(¶_id, |meta| {
1450 for pruned_repl_at in meta.prune_up_to(pruning_height) {
1451 let removed_code_hash =
1452 PastCodeHash::<T>::take(&(para_id, pruned_repl_at));
1453
1454 if let Some(removed_code_hash) = removed_code_hash {
1455 Self::decrease_code_ref(&removed_code_hash);
1456 } else {
1457 log::warn!(
1458 target: LOG_TARGET,
1459 "Missing code for removed hash {:?}",
1460 removed_code_hash,
1461 );
1462 }
1463 }
1464
1465 meta.is_empty() && Heads::<T>::get(¶_id).is_none()
1466 });
1467
1468 if full_deactivate {
1471 PastCodeMeta::<T>::remove(¶_id);
1472 }
1473 }
1474
1475 pruning_tasks_done as u64
1476 });
1477
1478 T::DbWeight::get().reads_writes(1 + pruning_tasks_done, 2 * pruning_tasks_done)
1481 }
1482
1483 fn process_future_code_upgrades_at(now: BlockNumberFor<T>) -> Weight {
1488 let mut weight = T::DbWeight::get().reads_writes(1, 1);
1490 FutureCodeUpgradesAt::<T>::mutate(
1491 |upcoming_upgrades: &mut Vec<(ParaId, BlockNumberFor<T>)>| {
1492 let num = upcoming_upgrades.iter().take_while(|&(_, at)| at <= &now).count();
1493 for (id, expected_at) in upcoming_upgrades.drain(..num) {
1494 weight += T::DbWeight::get().reads_writes(1, 1);
1495
1496 let new_code_hash = if let Some(new_code_hash) = FutureCodeHash::<T>::take(&id)
1498 {
1499 new_code_hash
1500 } else {
1501 log::error!(target: LOG_TARGET, "Missing future code hash for {:?}", &id);
1502 continue
1503 };
1504
1505 weight += Self::set_current_code(id, new_code_hash, expected_at);
1506 }
1507 num
1508 },
1509 );
1510
1511 weight
1512 }
1513
1514 fn process_scheduled_upgrade_changes(now: BlockNumberFor<T>) -> Weight {
1520 let mut weight = T::DbWeight::get().reads_writes(1, 1);
1522 let upgrades_signaled = UpcomingUpgrades::<T>::mutate(
1523 |upcoming_upgrades: &mut Vec<(ParaId, BlockNumberFor<T>)>| {
1524 let num = upcoming_upgrades.iter().take_while(|&(_, at)| at <= &now).count();
1525 for (para, _) in upcoming_upgrades.drain(..num) {
1526 UpgradeGoAheadSignal::<T>::insert(¶, UpgradeGoAhead::GoAhead);
1527 }
1528 num
1529 },
1530 );
1531 weight += T::DbWeight::get().writes(upgrades_signaled as u64);
1532
1533 weight += T::DbWeight::get().reads(1);
1535 let cooldowns_expired =
1536 UpgradeCooldowns::<T>::get().iter().take_while(|&(_, at)| at <= &now).count();
1537
1538 weight += T::DbWeight::get().reads_writes(1, 1);
1542 weight += T::DbWeight::get().reads(cooldowns_expired as u64);
1543
1544 weight
1545 }
1546
1547 fn process_scheduled_upgrade_cooldowns(now: BlockNumberFor<T>) {
1551 UpgradeCooldowns::<T>::mutate(
1552 |upgrade_cooldowns: &mut Vec<(ParaId, BlockNumberFor<T>)>| {
1553 upgrade_cooldowns.retain(|(para, at)| {
1555 if at <= &now {
1556 UpgradeRestrictionSignal::<T>::remove(¶);
1557 false
1558 } else {
1559 true
1560 }
1561 });
1562 },
1563 );
1564 }
1565
1566 fn groom_ongoing_pvf_votes(
1569 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1570 new_n_validators: usize,
1571 ) -> Weight {
1572 let mut weight = T::DbWeight::get().reads(1);
1573
1574 let potentially_active_votes = PvfActiveVoteList::<T>::get();
1575
1576 let mut actually_active_votes = Vec::with_capacity(potentially_active_votes.len());
1581
1582 for vote_subject in potentially_active_votes {
1583 let mut vote_state = match PvfActiveVoteMap::<T>::take(&vote_subject) {
1584 Some(v) => v,
1585 None => {
1586 log::warn!(
1590 target: LOG_TARGET,
1591 "The PvfActiveVoteMap is out of sync with PvfActiveVoteList!",
1592 );
1593 debug_assert!(false);
1594 continue
1595 },
1596 };
1597
1598 vote_state.age += 1;
1599 if vote_state.age < cfg.pvf_voting_ttl {
1600 weight += T::DbWeight::get().writes(1);
1601 vote_state.reinitialize_ballots(new_n_validators);
1602 PvfActiveVoteMap::<T>::insert(&vote_subject, vote_state);
1603
1604 actually_active_votes.push(vote_subject);
1606 } else {
1607 weight += Self::enact_pvf_rejected(&vote_subject, vote_state.causes);
1609 }
1610 }
1611
1612 weight += T::DbWeight::get().writes(1);
1613 PvfActiveVoteList::<T>::put(actually_active_votes);
1614
1615 weight
1616 }
1617
1618 fn enact_pvf_accepted(
1619 now: BlockNumberFor<T>,
1620 code_hash: &ValidationCodeHash,
1621 causes: &[PvfCheckCause<BlockNumberFor<T>>],
1622 sessions_observed: SessionIndex,
1623 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1624 ) -> Weight {
1625 let mut weight = Weight::zero();
1626 for cause in causes {
1627 weight += T::DbWeight::get().reads_writes(3, 2);
1628 Self::deposit_event(Event::PvfCheckAccepted(*code_hash, cause.para_id()));
1629
1630 match cause {
1631 PvfCheckCause::Onboarding(id) => {
1632 weight += Self::proceed_with_onboarding(*id, sessions_observed);
1633 },
1634 PvfCheckCause::Upgrade { id, included_at, upgrade_strategy } => {
1635 weight += Self::proceed_with_upgrade(
1636 *id,
1637 code_hash,
1638 now,
1639 *included_at,
1640 cfg,
1641 *upgrade_strategy,
1642 );
1643 },
1644 }
1645 }
1646 weight
1647 }
1648
1649 fn proceed_with_onboarding(id: ParaId, sessions_observed: SessionIndex) -> Weight {
1650 let weight = T::DbWeight::get().reads_writes(2, 1);
1651
1652 let onboard_at: SessionIndex = shared::CurrentSessionIndex::<T>::get() +
1658 cmp::max(shared::SESSION_DELAY.saturating_sub(sessions_observed), 1);
1659
1660 ActionsQueue::<T>::mutate(onboard_at, |v| {
1661 if let Err(i) = v.binary_search(&id) {
1662 v.insert(i, id);
1663 }
1664 });
1665
1666 weight
1667 }
1668
1669 fn proceed_with_upgrade(
1670 id: ParaId,
1671 code_hash: &ValidationCodeHash,
1672 now: BlockNumberFor<T>,
1673 relay_parent_number: BlockNumberFor<T>,
1674 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1675 upgrade_strategy: UpgradeStrategy,
1676 ) -> Weight {
1677 let mut weight = Weight::zero();
1678
1679 let expected_at = cmp::max(
1692 relay_parent_number + cfg.validation_upgrade_delay,
1693 now + cfg.minimum_validation_upgrade_delay,
1694 );
1695
1696 match upgrade_strategy {
1697 UpgradeStrategy::ApplyAtExpectedBlock => {
1698 FutureCodeUpgradesAt::<T>::mutate(|future_upgrades| {
1699 let insert_idx = future_upgrades
1700 .binary_search_by_key(&expected_at, |&(_, b)| b)
1701 .unwrap_or_else(|idx| idx);
1702 future_upgrades.insert(insert_idx, (id, expected_at));
1703 });
1704
1705 weight += T::DbWeight::get().reads_writes(0, 2);
1706 },
1707 UpgradeStrategy::SetGoAheadSignal => {
1708 FutureCodeUpgrades::<T>::insert(&id, expected_at);
1709
1710 UpcomingUpgrades::<T>::mutate(|upcoming_upgrades| {
1711 let insert_idx = upcoming_upgrades
1712 .binary_search_by_key(&expected_at, |&(_, b)| b)
1713 .unwrap_or_else(|idx| idx);
1714 upcoming_upgrades.insert(insert_idx, (id, expected_at));
1715 });
1716
1717 weight += T::DbWeight::get().reads_writes(1, 3);
1718 },
1719 }
1720
1721 let expected_at = expected_at.saturated_into();
1722 let log = ConsensusLog::ParaScheduleUpgradeCode(id, *code_hash, expected_at);
1723 frame_system::Pallet::<T>::deposit_log(log.into());
1724
1725 weight
1726 }
1727
1728 fn enact_pvf_rejected(
1729 code_hash: &ValidationCodeHash,
1730 causes: Vec<PvfCheckCause<BlockNumberFor<T>>>,
1731 ) -> Weight {
1732 let mut weight = Weight::zero();
1733
1734 for cause in causes {
1735 weight += Self::decrease_code_ref(code_hash);
1738
1739 weight += T::DbWeight::get().reads_writes(3, 2);
1740 Self::deposit_event(Event::PvfCheckRejected(*code_hash, cause.para_id()));
1741
1742 match cause {
1743 PvfCheckCause::Onboarding(id) => {
1744 weight += T::DbWeight::get().writes(3);
1750 UpcomingParasGenesis::<T>::remove(&id);
1751 CurrentCodeHash::<T>::remove(&id);
1752 ParaLifecycles::<T>::remove(&id);
1753 },
1754 PvfCheckCause::Upgrade { id, .. } => {
1755 weight += T::DbWeight::get().writes(2);
1756 UpgradeGoAheadSignal::<T>::insert(&id, UpgradeGoAhead::Abort);
1757 FutureCodeHash::<T>::remove(&id);
1758 },
1759 }
1760 }
1761
1762 weight
1763 }
1764
1765 pub fn can_schedule_para_initialize(id: &ParaId) -> bool {
1769 ParaLifecycles::<T>::get(id).is_none()
1770 }
1771
1772 pub(crate) fn schedule_para_initialize(
1783 id: ParaId,
1784 mut genesis_data: ParaGenesisArgs,
1785 ) -> DispatchResult {
1786 ensure!(Self::can_schedule_para_initialize(&id), Error::<T>::CannotOnboard);
1789 ensure!(!genesis_data.validation_code.0.is_empty(), Error::<T>::CannotOnboard);
1790 ParaLifecycles::<T>::insert(&id, ParaLifecycle::Onboarding);
1791
1792 let validation_code =
1826 mem::replace(&mut genesis_data.validation_code, ValidationCode(Vec::new()));
1827 UpcomingParasGenesis::<T>::insert(&id, genesis_data);
1828 let validation_code_hash = validation_code.hash();
1829 CurrentCodeHash::<T>::insert(&id, validation_code_hash);
1830
1831 let cfg = configuration::ActiveConfig::<T>::get();
1832 Self::kick_off_pvf_check(
1833 PvfCheckCause::Onboarding(id),
1834 validation_code_hash,
1835 validation_code,
1836 &cfg,
1837 );
1838
1839 Ok(())
1840 }
1841
1842 pub(crate) fn schedule_para_cleanup(id: ParaId) -> DispatchResult {
1852 if let Some(future_code_hash) = FutureCodeHash::<T>::get(&id) {
1864 let active_prechecking = PvfActiveVoteList::<T>::get();
1865 if active_prechecking.contains(&future_code_hash) {
1866 return Err(Error::<T>::CannotOffboard.into())
1867 }
1868 }
1869
1870 let lifecycle = ParaLifecycles::<T>::get(&id);
1871 match lifecycle {
1872 None => return Ok(()),
1874 Some(ParaLifecycle::Parathread) => {
1875 ParaLifecycles::<T>::insert(&id, ParaLifecycle::OffboardingParathread);
1876 },
1877 Some(ParaLifecycle::Parachain) => {
1878 ParaLifecycles::<T>::insert(&id, ParaLifecycle::OffboardingParachain);
1879 },
1880 _ => return Err(Error::<T>::CannotOffboard.into()),
1881 }
1882
1883 let scheduled_session = Self::scheduled_session();
1884 ActionsQueue::<T>::mutate(scheduled_session, |v| {
1885 if let Err(i) = v.binary_search(&id) {
1886 v.insert(i, id);
1887 }
1888 });
1889
1890 if <T as Config>::QueueFootprinter::message_count(UmpQueueId::Para(id)) != 0 {
1891 return Err(Error::<T>::CannotOffboard.into())
1892 }
1893
1894 Ok(())
1895 }
1896
1897 pub(crate) fn schedule_parathread_upgrade(id: ParaId) -> DispatchResult {
1901 let scheduled_session = Self::scheduled_session();
1902 let lifecycle = ParaLifecycles::<T>::get(&id).ok_or(Error::<T>::NotRegistered)?;
1903
1904 ensure!(lifecycle == ParaLifecycle::Parathread, Error::<T>::CannotUpgrade);
1905
1906 ParaLifecycles::<T>::insert(&id, ParaLifecycle::UpgradingParathread);
1907 ActionsQueue::<T>::mutate(scheduled_session, |v| {
1908 if let Err(i) = v.binary_search(&id) {
1909 v.insert(i, id);
1910 }
1911 });
1912
1913 Ok(())
1914 }
1915
1916 pub(crate) fn schedule_parachain_downgrade(id: ParaId) -> DispatchResult {
1920 let scheduled_session = Self::scheduled_session();
1921 let lifecycle = ParaLifecycles::<T>::get(&id).ok_or(Error::<T>::NotRegistered)?;
1922
1923 ensure!(lifecycle == ParaLifecycle::Parachain, Error::<T>::CannotDowngrade);
1924
1925 ParaLifecycles::<T>::insert(&id, ParaLifecycle::DowngradingParachain);
1926 ActionsQueue::<T>::mutate(scheduled_session, |v| {
1927 if let Err(i) = v.binary_search(&id) {
1928 v.insert(i, id);
1929 }
1930 });
1931
1932 Ok(())
1933 }
1934
1935 pub(crate) fn schedule_code_upgrade(
1954 id: ParaId,
1955 new_code: ValidationCode,
1956 inclusion_block_number: BlockNumberFor<T>,
1957 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1958 upgrade_strategy: UpgradeStrategy,
1959 ) {
1960 let new_code_len = new_code.0.len();
1962 if new_code_len < MIN_CODE_SIZE as usize || new_code_len > cfg.max_code_size as usize {
1963 log::warn!(target: LOG_TARGET, "attempted to schedule an upgrade with invalid new validation code",);
1964 return
1965 }
1966
1967 if FutureCodeHash::<T>::contains_key(&id) {
1969 log::warn!(target: LOG_TARGET, "ended up scheduling an upgrade while one is pending",);
1978 return
1979 }
1980
1981 let code_hash = new_code.hash();
1982
1983 if CurrentCodeHash::<T>::get(&id) == Some(code_hash) {
1988 log::warn!(
1991 target: LOG_TARGET,
1992 "para tried to upgrade to the same code. Abort the upgrade",
1993 );
1994 return
1995 }
1996
1997 FutureCodeHash::<T>::insert(&id, &code_hash);
1999 UpgradeRestrictionSignal::<T>::insert(&id, UpgradeRestriction::Present);
2000
2001 let next_possible_upgrade_at = inclusion_block_number + cfg.validation_upgrade_cooldown;
2002 UpgradeCooldowns::<T>::mutate(|upgrade_cooldowns| {
2003 let insert_idx = upgrade_cooldowns
2004 .binary_search_by_key(&next_possible_upgrade_at, |&(_, b)| b)
2005 .unwrap_or_else(|idx| idx);
2006 upgrade_cooldowns.insert(insert_idx, (id, next_possible_upgrade_at));
2007 });
2008
2009 Self::kick_off_pvf_check(
2010 PvfCheckCause::Upgrade { id, included_at: inclusion_block_number, upgrade_strategy },
2011 code_hash,
2012 new_code,
2013 cfg,
2014 );
2015 }
2016
2017 fn kick_off_pvf_check(
2031 cause: PvfCheckCause<BlockNumberFor<T>>,
2032 code_hash: ValidationCodeHash,
2033 code: ValidationCode,
2034 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
2035 ) -> Weight {
2036 let mut weight = Weight::zero();
2037
2038 weight += T::DbWeight::get().reads_writes(3, 2);
2039 Self::deposit_event(Event::PvfCheckStarted(code_hash, cause.para_id()));
2040
2041 weight += T::DbWeight::get().reads(1);
2042 match PvfActiveVoteMap::<T>::get(&code_hash) {
2043 None => {
2044 let known_code = CodeByHash::<T>::contains_key(&code_hash);
2047 weight += T::DbWeight::get().reads(1);
2048
2049 if known_code {
2050 weight += T::DbWeight::get().reads(1);
2053 let now = frame_system::Pallet::<T>::block_number();
2054 weight += Self::enact_pvf_accepted(now, &code_hash, &[cause], 0, cfg);
2055 } else {
2056 weight += T::DbWeight::get().reads_writes(3, 2);
2059 let now = frame_system::Pallet::<T>::block_number();
2060 let n_validators = shared::ActiveValidatorKeys::<T>::get().len();
2061 PvfActiveVoteMap::<T>::insert(
2062 &code_hash,
2063 PvfCheckActiveVoteState::new(now, n_validators, cause),
2064 );
2065 PvfActiveVoteList::<T>::mutate(|l| {
2066 if let Err(idx) = l.binary_search(&code_hash) {
2067 l.insert(idx, code_hash);
2068 }
2069 });
2070 }
2071 },
2072 Some(mut vote_state) => {
2073 weight += T::DbWeight::get().writes(1);
2076 vote_state.causes.push(cause);
2077 PvfActiveVoteMap::<T>::insert(&code_hash, vote_state);
2078 },
2079 }
2080
2081 weight += Self::increase_code_ref(&code_hash, &code);
2093
2094 weight
2095 }
2096
2097 pub(crate) fn note_new_head(
2101 id: ParaId,
2102 new_head: HeadData,
2103 execution_context: BlockNumberFor<T>,
2104 ) {
2105 Heads::<T>::insert(&id, &new_head);
2106 MostRecentContext::<T>::insert(&id, execution_context);
2107
2108 if let Some(expected_at) = FutureCodeUpgrades::<T>::get(&id) {
2109 if expected_at <= execution_context {
2110 FutureCodeUpgrades::<T>::remove(&id);
2111 UpgradeGoAheadSignal::<T>::remove(&id);
2112
2113 let new_code_hash = if let Some(new_code_hash) = FutureCodeHash::<T>::take(&id) {
2115 new_code_hash
2116 } else {
2117 log::error!(target: LOG_TARGET, "Missing future code hash for {:?}", &id);
2118 return
2119 };
2120
2121 Self::set_current_code(id, new_code_hash, expected_at);
2122 }
2123 } else {
2124 UpgradeGoAheadSignal::<T>::remove(&id);
2129 };
2130
2131 T::OnNewHead::on_new_head(id, &new_head);
2132 }
2133
2134 pub(crate) fn set_current_code(
2139 id: ParaId,
2140 new_code_hash: ValidationCodeHash,
2141 at: BlockNumberFor<T>,
2142 ) -> Weight {
2143 let maybe_prior_code_hash = CurrentCodeHash::<T>::get(&id);
2144 CurrentCodeHash::<T>::insert(&id, &new_code_hash);
2145
2146 let log = ConsensusLog::ParaUpgradeCode(id, new_code_hash);
2147 <frame_system::Pallet<T>>::deposit_log(log.into());
2148
2149 let now = <frame_system::Pallet<T>>::block_number();
2151
2152 let weight = if let Some(prior_code_hash) = maybe_prior_code_hash {
2153 Self::note_past_code(id, at, now, prior_code_hash)
2154 } else {
2155 log::error!(target: LOG_TARGET, "Missing prior code hash for para {:?}", &id);
2156 Weight::zero()
2157 };
2158
2159 weight + T::DbWeight::get().writes(1)
2160 }
2161
2162 pub(crate) fn pvfs_require_precheck() -> Vec<ValidationCodeHash> {
2165 PvfActiveVoteList::<T>::get()
2166 }
2167
2168 pub(crate) fn submit_pvf_check_statement(
2175 stmt: PvfCheckStatement,
2176 signature: ValidatorSignature,
2177 ) {
2178 use frame_system::offchain::SubmitTransaction;
2179
2180 let xt = T::create_inherent(Call::include_pvf_check_statement { stmt, signature }.into());
2181 if let Err(e) = SubmitTransaction::<T, Call<T>>::submit_transaction(xt) {
2182 log::error!(target: LOG_TARGET, "Error submitting pvf check statement: {:?}", e,);
2183 }
2184 }
2185
2186 pub fn lifecycle(id: ParaId) -> Option<ParaLifecycle> {
2188 ParaLifecycles::<T>::get(&id)
2189 }
2190
2191 pub fn is_valid_para(id: ParaId) -> bool {
2195 if let Some(state) = ParaLifecycles::<T>::get(&id) {
2196 !state.is_onboarding() && !state.is_offboarding()
2197 } else {
2198 false
2199 }
2200 }
2201
2202 pub fn is_offboarding(id: ParaId) -> bool {
2206 ParaLifecycles::<T>::get(&id).map_or(false, |state| state.is_offboarding())
2207 }
2208
2209 pub fn is_parachain(id: ParaId) -> bool {
2214 if let Some(state) = ParaLifecycles::<T>::get(&id) {
2215 state.is_parachain()
2216 } else {
2217 false
2218 }
2219 }
2220
2221 pub fn is_parathread(id: ParaId) -> bool {
2225 if let Some(state) = ParaLifecycles::<T>::get(&id) {
2226 state.is_parathread()
2227 } else {
2228 false
2229 }
2230 }
2231
2232 pub(crate) fn can_upgrade_validation_code(id: ParaId) -> bool {
2235 FutureCodeHash::<T>::get(&id).is_none() && UpgradeRestrictionSignal::<T>::get(&id).is_none()
2236 }
2237
2238 fn scheduled_session() -> SessionIndex {
2240 shared::Pallet::<T>::scheduled_session()
2241 }
2242
2243 fn increase_code_ref(code_hash: &ValidationCodeHash, code: &ValidationCode) -> Weight {
2247 let mut weight = T::DbWeight::get().reads_writes(1, 1);
2248 CodeByHashRefs::<T>::mutate(code_hash, |refs| {
2249 if *refs == 0 {
2250 weight += T::DbWeight::get().writes(1);
2251 CodeByHash::<T>::insert(code_hash, code);
2252 }
2253 *refs += 1;
2254 });
2255 weight
2256 }
2257
2258 fn decrease_code_ref(code_hash: &ValidationCodeHash) -> Weight {
2263 let mut weight = T::DbWeight::get().reads(1);
2264 let refs = CodeByHashRefs::<T>::get(code_hash);
2265 if refs == 0 {
2266 log::error!(target: LOG_TARGET, "Code refs is already zero for {:?}", code_hash);
2267 return weight
2268 }
2269 if refs <= 1 {
2270 weight += T::DbWeight::get().writes(2);
2271 CodeByHash::<T>::remove(code_hash);
2272 CodeByHashRefs::<T>::remove(code_hash);
2273 } else {
2274 weight += T::DbWeight::get().writes(1);
2275 CodeByHashRefs::<T>::insert(code_hash, refs - 1);
2276 }
2277 weight
2278 }
2279
2280 #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
2282 pub fn test_on_new_session() {
2283 Self::initializer_on_new_session(&SessionChangeNotification {
2284 session_index: shared::CurrentSessionIndex::<T>::get(),
2285 ..Default::default()
2286 });
2287 }
2288
2289 #[cfg(any(feature = "runtime-benchmarks", test))]
2290 pub fn heads_insert(para_id: &ParaId, head_data: HeadData) {
2291 Heads::<T>::insert(para_id, head_data);
2292 }
2293
2294 pub(crate) fn initialize_para_now(
2296 parachains: &mut ParachainsCache<T>,
2297 id: ParaId,
2298 genesis_data: &ParaGenesisArgs,
2299 ) {
2300 match genesis_data.para_kind {
2301 ParaKind::Parachain => {
2302 parachains.add(id);
2303 ParaLifecycles::<T>::insert(&id, ParaLifecycle::Parachain);
2304 },
2305 ParaKind::Parathread => ParaLifecycles::<T>::insert(&id, ParaLifecycle::Parathread),
2306 }
2307
2308 if !genesis_data.validation_code.0.is_empty() {
2314 let code_hash = genesis_data.validation_code.hash();
2315 Self::increase_code_ref(&code_hash, &genesis_data.validation_code);
2316 CurrentCodeHash::<T>::insert(&id, code_hash);
2317 }
2318
2319 Heads::<T>::insert(&id, &genesis_data.genesis_head);
2320 MostRecentContext::<T>::insert(&id, BlockNumberFor::<T>::from(0u32));
2321 }
2322
2323 #[cfg(test)]
2324 pub(crate) fn active_vote_state(
2325 code_hash: &ValidationCodeHash,
2326 ) -> Option<PvfCheckActiveVoteState<BlockNumberFor<T>>> {
2327 PvfActiveVoteMap::<T>::get(code_hash)
2328 }
2329}
2330
2331pub(crate) struct ParachainsCache<T: Config> {
2334 parachains: Option<BTreeSet<ParaId>>,
2336 _config: PhantomData<T>,
2337}
2338
2339impl<T: Config> ParachainsCache<T> {
2340 pub fn new() -> Self {
2341 Self { parachains: None, _config: PhantomData }
2342 }
2343
2344 fn ensure_initialized(&mut self) -> &mut BTreeSet<ParaId> {
2345 self.parachains
2346 .get_or_insert_with(|| Parachains::<T>::get().into_iter().collect())
2347 }
2348
2349 pub fn add(&mut self, id: ParaId) {
2351 let parachains = self.ensure_initialized();
2352 parachains.insert(id);
2353 }
2354
2355 pub fn remove(&mut self, id: ParaId) {
2358 let parachains = self.ensure_initialized();
2359 parachains.remove(&id);
2360 }
2361}
2362
2363impl<T: Config> Drop for ParachainsCache<T> {
2364 fn drop(&mut self) {
2365 if let Some(parachains) = self.parachains.take() {
2366 Parachains::<T>::put(parachains.into_iter().collect::<Vec<ParaId>>());
2367 }
2368 }
2369}