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(
294 PartialEq,
295 Eq,
296 Clone,
297 Encode,
298 Decode,
299 DecodeWithMemTracking,
300 RuntimeDebug,
301 TypeInfo,
302 Serialize,
303 Deserialize,
304)]
305pub struct ParaGenesisArgs {
306 pub genesis_head: HeadData,
308 pub validation_code: ValidationCode,
310 #[serde(rename = "parachain")]
312 pub para_kind: ParaKind,
313}
314
315#[derive(DecodeWithMemTracking, PartialEq, Eq, Clone, RuntimeDebug)]
317pub enum ParaKind {
318 Parathread,
319 Parachain,
320}
321
322impl Serialize for ParaKind {
323 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
324 where
325 S: serde::Serializer,
326 {
327 match self {
328 ParaKind::Parachain => serializer.serialize_bool(true),
329 ParaKind::Parathread => serializer.serialize_bool(false),
330 }
331 }
332}
333
334impl<'de> Deserialize<'de> for ParaKind {
335 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
336 where
337 D: serde::Deserializer<'de>,
338 {
339 match serde::de::Deserialize::deserialize(deserializer) {
340 Ok(true) => Ok(ParaKind::Parachain),
341 Ok(false) => Ok(ParaKind::Parathread),
342 _ => Err(serde::de::Error::custom("invalid ParaKind serde representation")),
343 }
344 }
345}
346
347impl Encode for ParaKind {
350 fn size_hint(&self) -> usize {
351 true.size_hint()
352 }
353
354 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
355 match self {
356 ParaKind::Parachain => true.using_encoded(f),
357 ParaKind::Parathread => false.using_encoded(f),
358 }
359 }
360}
361
362impl Decode for ParaKind {
363 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
364 match bool::decode(input) {
365 Ok(true) => Ok(ParaKind::Parachain),
366 Ok(false) => Ok(ParaKind::Parathread),
367 _ => Err("Invalid ParaKind representation".into()),
368 }
369 }
370}
371
372impl TypeInfo for ParaKind {
373 type Identity = bool;
374 fn type_info() -> Type {
375 bool::type_info()
376 }
377}
378
379#[derive(Debug, Encode, Decode, TypeInfo)]
382pub(crate) enum PvfCheckCause<BlockNumber> {
383 Onboarding(ParaId),
385 Upgrade {
387 id: ParaId,
390 included_at: BlockNumber,
399 upgrade_strategy: UpgradeStrategy,
404 },
405}
406
407#[derive(Debug, Copy, Clone, PartialEq, TypeInfo, Decode, Encode)]
415pub enum UpgradeStrategy {
416 SetGoAheadSignal,
421 ApplyAtExpectedBlock,
425}
426
427impl<BlockNumber> PvfCheckCause<BlockNumber> {
428 fn para_id(&self) -> ParaId {
430 match *self {
431 PvfCheckCause::Onboarding(id) => id,
432 PvfCheckCause::Upgrade { id, .. } => id,
433 }
434 }
435}
436
437#[derive(Copy, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
439enum PvfCheckOutcome {
440 Accepted,
441 Rejected,
442}
443
444#[derive(Encode, Decode, TypeInfo)]
446pub(crate) struct PvfCheckActiveVoteState<BlockNumber> {
447 votes_accept: BitVec<u8, BitOrderLsb0>,
453 votes_reject: BitVec<u8, BitOrderLsb0>,
454
455 age: SessionIndex,
458 created_at: BlockNumber,
460 causes: Vec<PvfCheckCause<BlockNumber>>,
462}
463
464impl<BlockNumber> PvfCheckActiveVoteState<BlockNumber> {
465 fn new(now: BlockNumber, n_validators: usize, cause: PvfCheckCause<BlockNumber>) -> Self {
468 let mut causes = Vec::with_capacity(1);
469 causes.push(cause);
470 Self {
471 created_at: now,
472 votes_accept: bitvec::bitvec![u8, BitOrderLsb0; 0; n_validators],
473 votes_reject: bitvec::bitvec![u8, BitOrderLsb0; 0; n_validators],
474 age: 0,
475 causes,
476 }
477 }
478
479 fn reinitialize_ballots(&mut self, n_validators: usize) {
482 let clear_and_resize = |v: &mut BitVec<_, _>| {
483 v.clear();
484 v.resize(n_validators, false);
485 };
486 clear_and_resize(&mut self.votes_accept);
487 clear_and_resize(&mut self.votes_reject);
488 }
489
490 fn has_vote(&self, validator_index: usize) -> Option<bool> {
493 let accept_vote = self.votes_accept.get(validator_index)?;
494 let reject_vote = self.votes_reject.get(validator_index)?;
495 Some(*accept_vote || *reject_vote)
496 }
497
498 fn quorum(&self, n_validators: usize) -> Option<PvfCheckOutcome> {
500 let accept_threshold = polkadot_primitives::supermajority_threshold(n_validators);
501 let reject_threshold = n_validators - accept_threshold;
503
504 if self.votes_accept.count_ones() >= accept_threshold {
505 Some(PvfCheckOutcome::Accepted)
506 } else if self.votes_reject.count_ones() > reject_threshold {
507 Some(PvfCheckOutcome::Rejected)
508 } else {
509 None
510 }
511 }
512
513 #[cfg(test)]
514 pub(crate) fn causes(&self) -> &[PvfCheckCause<BlockNumber>] {
515 self.causes.as_slice()
516 }
517}
518
519pub trait OnNewHead {
521 fn on_new_head(id: ParaId, head: &HeadData) -> Weight;
524}
525
526#[impl_trait_for_tuples::impl_for_tuples(30)]
527impl OnNewHead for Tuple {
528 fn on_new_head(id: ParaId, head: &HeadData) -> Weight {
529 let mut weight: Weight = Default::default();
530 for_tuples!( #( weight.saturating_accrue(Tuple::on_new_head(id, head)); )* );
531 weight
532 }
533}
534
535pub trait AssignCoretime {
540 fn assign_coretime(id: ParaId) -> DispatchResult;
542}
543
544impl AssignCoretime for () {
545 fn assign_coretime(_: ParaId) -> DispatchResult {
546 Ok(())
547 }
548}
549
550pub trait WeightInfo {
551 fn force_set_current_code(c: u32) -> Weight;
552 fn force_set_current_head(s: u32) -> Weight;
553 fn force_set_most_recent_context() -> Weight;
554 fn force_schedule_code_upgrade(c: u32) -> Weight;
555 fn force_note_new_head(s: u32) -> Weight;
556 fn force_queue_action() -> Weight;
557 fn add_trusted_validation_code(c: u32) -> Weight;
558 fn poke_unused_validation_code() -> Weight;
559
560 fn include_pvf_check_statement_finalize_upgrade_accept() -> Weight;
561 fn include_pvf_check_statement_finalize_upgrade_reject() -> Weight;
562 fn include_pvf_check_statement_finalize_onboarding_accept() -> Weight;
563 fn include_pvf_check_statement_finalize_onboarding_reject() -> Weight;
564 fn include_pvf_check_statement() -> Weight;
565}
566
567pub struct TestWeightInfo;
568impl WeightInfo for TestWeightInfo {
569 fn force_set_current_code(_c: u32) -> Weight {
570 Weight::MAX
571 }
572 fn force_set_current_head(_s: u32) -> Weight {
573 Weight::MAX
574 }
575 fn force_set_most_recent_context() -> Weight {
576 Weight::MAX
577 }
578 fn force_schedule_code_upgrade(_c: u32) -> Weight {
579 Weight::MAX
580 }
581 fn force_note_new_head(_s: u32) -> Weight {
582 Weight::MAX
583 }
584 fn force_queue_action() -> Weight {
585 Weight::MAX
586 }
587 fn add_trusted_validation_code(_c: u32) -> Weight {
588 Weight::zero()
590 }
591 fn poke_unused_validation_code() -> Weight {
592 Weight::MAX
593 }
594 fn include_pvf_check_statement_finalize_upgrade_accept() -> Weight {
595 Weight::MAX
596 }
597 fn include_pvf_check_statement_finalize_upgrade_reject() -> Weight {
598 Weight::MAX
599 }
600 fn include_pvf_check_statement_finalize_onboarding_accept() -> Weight {
601 Weight::MAX
602 }
603 fn include_pvf_check_statement_finalize_onboarding_reject() -> Weight {
604 Weight::MAX
605 }
606 fn include_pvf_check_statement() -> Weight {
607 Weight::MAX - Weight::from_parts(1, 1)
609 }
610}
611
612#[frame_support::pallet]
613pub mod pallet {
614 use super::*;
615 use sp_runtime::transaction_validity::{
616 InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
617 ValidTransaction,
618 };
619
620 #[pallet::pallet]
621 #[pallet::without_storage_info]
622 pub struct Pallet<T>(_);
623
624 #[pallet::config]
625 pub trait Config:
626 frame_system::Config
627 + configuration::Config
628 + shared::Config
629 + frame_system::offchain::CreateInherent<Call<Self>>
630 {
631 type RuntimeEvent: From<Event> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
632
633 #[pallet::constant]
634 type UnsignedPriority: Get<TransactionPriority>;
635
636 type NextSessionRotation: EstimateNextSessionRotation<BlockNumberFor<Self>>;
637
638 type QueueFootprinter: QueueFootprinter<Origin = UmpQueueId>;
643
644 type OnNewHead: OnNewHead;
646
647 type WeightInfo: WeightInfo;
649
650 type AssignCoretime: AssignCoretime;
656 }
657
658 #[pallet::event]
659 #[pallet::generate_deposit(pub(super) fn deposit_event)]
660 pub enum Event {
661 CurrentCodeUpdated(ParaId),
663 CurrentHeadUpdated(ParaId),
665 CodeUpgradeScheduled(ParaId),
667 NewHeadNoted(ParaId),
669 ActionQueued(ParaId, SessionIndex),
671 PvfCheckStarted(ValidationCodeHash, ParaId),
674 PvfCheckAccepted(ValidationCodeHash, ParaId),
677 PvfCheckRejected(ValidationCodeHash, ParaId),
680 }
681
682 #[pallet::error]
683 pub enum Error<T> {
684 NotRegistered,
686 CannotOnboard,
688 CannotOffboard,
690 CannotUpgrade,
692 CannotDowngrade,
694 PvfCheckStatementStale,
696 PvfCheckStatementFuture,
698 PvfCheckValidatorIndexOutOfBounds,
700 PvfCheckInvalidSignature,
702 PvfCheckDoubleVote,
704 PvfCheckSubjectInvalid,
706 CannotUpgradeCode,
708 InvalidCode,
710 }
711
712 #[pallet::storage]
717 pub(super) type PvfActiveVoteMap<T: Config> = StorageMap<
718 _,
719 Twox64Concat,
720 ValidationCodeHash,
721 PvfCheckActiveVoteState<BlockNumberFor<T>>,
722 OptionQuery,
723 >;
724
725 #[pallet::storage]
727 pub(super) type PvfActiveVoteList<T: Config> =
728 StorageValue<_, Vec<ValidationCodeHash>, ValueQuery>;
729
730 #[pallet::storage]
735 pub type Parachains<T: Config> = StorageValue<_, Vec<ParaId>, ValueQuery>;
736
737 #[pallet::storage]
739 pub(super) type ParaLifecycles<T: Config> = StorageMap<_, Twox64Concat, ParaId, ParaLifecycle>;
740
741 #[pallet::storage]
743 pub type Heads<T: Config> = StorageMap<_, Twox64Concat, ParaId, HeadData>;
744
745 #[pallet::storage]
747 pub type MostRecentContext<T: Config> = StorageMap<_, Twox64Concat, ParaId, BlockNumberFor<T>>;
748
749 #[pallet::storage]
753 pub type CurrentCodeHash<T: Config> = StorageMap<_, Twox64Concat, ParaId, ValidationCodeHash>;
754
755 #[pallet::storage]
760 pub(super) type PastCodeHash<T: Config> =
761 StorageMap<_, Twox64Concat, (ParaId, BlockNumberFor<T>), ValidationCodeHash>;
762
763 #[pallet::storage]
767 pub type PastCodeMeta<T: Config> =
768 StorageMap<_, Twox64Concat, ParaId, ParaPastCodeMeta<BlockNumberFor<T>>, ValueQuery>;
769
770 #[pallet::storage]
777 pub(super) type PastCodePruning<T: Config> =
778 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
779
780 #[pallet::storage]
785 pub type FutureCodeUpgrades<T: Config> = StorageMap<_, Twox64Concat, ParaId, BlockNumberFor<T>>;
786
787 #[pallet::storage]
796 pub(super) type FutureCodeUpgradesAt<T: Config> =
797 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
798
799 #[pallet::storage]
803 pub type FutureCodeHash<T: Config> = StorageMap<_, Twox64Concat, ParaId, ValidationCodeHash>;
804
805 #[pallet::storage]
816 pub(super) type UpgradeGoAheadSignal<T: Config> =
817 StorageMap<_, Twox64Concat, ParaId, UpgradeGoAhead>;
818
819 #[pallet::storage]
829 pub type UpgradeRestrictionSignal<T: Config> =
830 StorageMap<_, Twox64Concat, ParaId, UpgradeRestriction>;
831
832 #[pallet::storage]
836 pub(super) type UpgradeCooldowns<T: Config> =
837 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
838
839 #[pallet::storage]
846 pub(super) type UpcomingUpgrades<T: Config> =
847 StorageValue<_, Vec<(ParaId, BlockNumberFor<T>)>, ValueQuery>;
848
849 #[pallet::storage]
851 pub type ActionsQueue<T: Config> =
852 StorageMap<_, Twox64Concat, SessionIndex, Vec<ParaId>, ValueQuery>;
853
854 #[pallet::storage]
859 pub(super) type UpcomingParasGenesis<T: Config> =
860 StorageMap<_, Twox64Concat, ParaId, ParaGenesisArgs>;
861
862 #[pallet::storage]
864 pub(super) type CodeByHashRefs<T: Config> =
865 StorageMap<_, Identity, ValidationCodeHash, u32, ValueQuery>;
866
867 #[pallet::storage]
872 pub type CodeByHash<T: Config> = StorageMap<_, Identity, ValidationCodeHash, ValidationCode>;
873
874 #[pallet::genesis_config]
875 #[derive(DefaultNoBound)]
876 pub struct GenesisConfig<T: Config> {
877 #[serde(skip)]
878 pub _config: core::marker::PhantomData<T>,
879 pub paras: Vec<(ParaId, ParaGenesisArgs)>,
880 }
881
882 #[pallet::genesis_build]
883 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
884 fn build(&self) {
885 let mut parachains = ParachainsCache::new();
886 for (id, genesis_args) in &self.paras {
887 if genesis_args.validation_code.0.is_empty() {
888 panic!("empty validation code is not allowed in genesis");
889 }
890 Pallet::<T>::initialize_para_now(&mut parachains, *id, genesis_args);
891 T::AssignCoretime::assign_coretime(*id)
892 .expect("Assigning coretime works at genesis; qed");
893 }
894 }
896 }
897
898 #[pallet::call]
899 impl<T: Config> Pallet<T> {
900 #[pallet::call_index(0)]
902 #[pallet::weight(<T as Config>::WeightInfo::force_set_current_code(new_code.0.len() as u32))]
903 pub fn force_set_current_code(
904 origin: OriginFor<T>,
905 para: ParaId,
906 new_code: ValidationCode,
907 ) -> DispatchResult {
908 ensure_root(origin)?;
909 let new_code_hash = new_code.hash();
910 Self::increase_code_ref(&new_code_hash, &new_code);
911 Self::set_current_code(para, new_code_hash, frame_system::Pallet::<T>::block_number());
912 Self::deposit_event(Event::CurrentCodeUpdated(para));
913 Ok(())
914 }
915
916 #[pallet::call_index(1)]
918 #[pallet::weight(<T as Config>::WeightInfo::force_set_current_head(new_head.0.len() as u32))]
919 pub fn force_set_current_head(
920 origin: OriginFor<T>,
921 para: ParaId,
922 new_head: HeadData,
923 ) -> DispatchResult {
924 ensure_root(origin)?;
925 Self::set_current_head(para, new_head);
926 Ok(())
927 }
928
929 #[pallet::call_index(2)]
931 #[pallet::weight(<T as Config>::WeightInfo::force_schedule_code_upgrade(new_code.0.len() as u32))]
932 pub fn force_schedule_code_upgrade(
933 origin: OriginFor<T>,
934 para: ParaId,
935 new_code: ValidationCode,
936 relay_parent_number: BlockNumberFor<T>,
937 ) -> DispatchResult {
938 ensure_root(origin)?;
939 let config = configuration::ActiveConfig::<T>::get();
940 Self::schedule_code_upgrade(
941 para,
942 new_code,
943 relay_parent_number,
944 &config,
945 UpgradeStrategy::ApplyAtExpectedBlock,
946 );
947 Self::deposit_event(Event::CodeUpgradeScheduled(para));
948 Ok(())
949 }
950
951 #[pallet::call_index(3)]
953 #[pallet::weight(<T as Config>::WeightInfo::force_note_new_head(new_head.0.len() as u32))]
954 pub fn force_note_new_head(
955 origin: OriginFor<T>,
956 para: ParaId,
957 new_head: HeadData,
958 ) -> DispatchResult {
959 ensure_root(origin)?;
960 let now = frame_system::Pallet::<T>::block_number();
961 Self::note_new_head(para, new_head, now);
962 Self::deposit_event(Event::NewHeadNoted(para));
963 Ok(())
964 }
965
966 #[pallet::call_index(4)]
970 #[pallet::weight(<T as Config>::WeightInfo::force_queue_action())]
971 pub fn force_queue_action(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
972 ensure_root(origin)?;
973 let next_session = shared::CurrentSessionIndex::<T>::get().saturating_add(One::one());
974 ActionsQueue::<T>::mutate(next_session, |v| {
975 if let Err(i) = v.binary_search(¶) {
976 v.insert(i, para);
977 }
978 });
979 Self::deposit_event(Event::ActionQueued(para, next_session));
980 Ok(())
981 }
982
983 #[pallet::call_index(5)]
998 #[pallet::weight(<T as Config>::WeightInfo::add_trusted_validation_code(validation_code.0.len() as u32))]
999 pub fn add_trusted_validation_code(
1000 origin: OriginFor<T>,
1001 validation_code: ValidationCode,
1002 ) -> DispatchResult {
1003 ensure_root(origin)?;
1004 let code_hash = validation_code.hash();
1005
1006 if let Some(vote) = PvfActiveVoteMap::<T>::get(&code_hash) {
1007 PvfActiveVoteMap::<T>::remove(&code_hash);
1009 PvfActiveVoteList::<T>::mutate(|l| {
1010 if let Ok(i) = l.binary_search(&code_hash) {
1011 l.remove(i);
1012 }
1013 });
1014
1015 let cfg = configuration::ActiveConfig::<T>::get();
1016 Self::enact_pvf_accepted(
1017 frame_system::Pallet::<T>::block_number(),
1018 &code_hash,
1019 &vote.causes,
1020 vote.age,
1021 &cfg,
1022 );
1023 return Ok(())
1024 }
1025
1026 if CodeByHash::<T>::contains_key(&code_hash) {
1027 return Ok(())
1029 }
1030
1031 CodeByHash::<T>::insert(code_hash, &validation_code);
1037
1038 Ok(())
1039 }
1040
1041 #[pallet::call_index(6)]
1047 #[pallet::weight(<T as Config>::WeightInfo::poke_unused_validation_code())]
1048 pub fn poke_unused_validation_code(
1049 origin: OriginFor<T>,
1050 validation_code_hash: ValidationCodeHash,
1051 ) -> DispatchResult {
1052 ensure_root(origin)?;
1053 if CodeByHashRefs::<T>::get(&validation_code_hash) == 0 {
1054 CodeByHash::<T>::remove(&validation_code_hash);
1055 }
1056 Ok(())
1057 }
1058
1059 #[pallet::call_index(7)]
1062 #[pallet::weight(
1063 <T as Config>::WeightInfo::include_pvf_check_statement_finalize_upgrade_accept()
1064 .max(<T as Config>::WeightInfo::include_pvf_check_statement_finalize_upgrade_reject())
1065 .max(<T as Config>::WeightInfo::include_pvf_check_statement_finalize_onboarding_accept()
1066 .max(<T as Config>::WeightInfo::include_pvf_check_statement_finalize_onboarding_reject())
1067 )
1068 )]
1069 pub fn include_pvf_check_statement(
1070 origin: OriginFor<T>,
1071 stmt: PvfCheckStatement,
1072 signature: ValidatorSignature,
1073 ) -> DispatchResultWithPostInfo {
1074 ensure_none(origin)?;
1075
1076 let validators = shared::ActiveValidatorKeys::<T>::get();
1077 let current_session = shared::CurrentSessionIndex::<T>::get();
1078 if stmt.session_index < current_session {
1079 return Err(Error::<T>::PvfCheckStatementStale.into())
1080 } else if stmt.session_index > current_session {
1081 return Err(Error::<T>::PvfCheckStatementFuture.into())
1082 }
1083 let validator_index = stmt.validator_index.0 as usize;
1084 let validator_public = validators
1085 .get(validator_index)
1086 .ok_or(Error::<T>::PvfCheckValidatorIndexOutOfBounds)?;
1087
1088 let signing_payload = stmt.signing_payload();
1089 ensure!(
1090 signature.verify(&signing_payload[..], &validator_public),
1091 Error::<T>::PvfCheckInvalidSignature,
1092 );
1093
1094 let mut active_vote = PvfActiveVoteMap::<T>::get(&stmt.subject)
1095 .ok_or(Error::<T>::PvfCheckSubjectInvalid)?;
1096
1097 ensure!(
1099 !active_vote
1100 .has_vote(validator_index)
1101 .ok_or(Error::<T>::PvfCheckValidatorIndexOutOfBounds)?,
1102 Error::<T>::PvfCheckDoubleVote,
1103 );
1104
1105 if stmt.accept {
1107 active_vote.votes_accept.set(validator_index, true);
1108 } else {
1109 active_vote.votes_reject.set(validator_index, true);
1110 }
1111
1112 if let Some(outcome) = active_vote.quorum(validators.len()) {
1113 PvfActiveVoteMap::<T>::remove(&stmt.subject);
1118 PvfActiveVoteList::<T>::mutate(|l| {
1119 if let Ok(i) = l.binary_search(&stmt.subject) {
1120 l.remove(i);
1121 }
1122 });
1123 match outcome {
1124 PvfCheckOutcome::Accepted => {
1125 let cfg = configuration::ActiveConfig::<T>::get();
1126 Self::enact_pvf_accepted(
1127 frame_system::Pallet::<T>::block_number(),
1128 &stmt.subject,
1129 &active_vote.causes,
1130 active_vote.age,
1131 &cfg,
1132 );
1133 },
1134 PvfCheckOutcome::Rejected => {
1135 Self::enact_pvf_rejected(&stmt.subject, active_vote.causes);
1136 },
1137 }
1138
1139 Ok(().into())
1141 } else {
1142 PvfActiveVoteMap::<T>::insert(&stmt.subject, active_vote);
1147 Ok(Some(<T as Config>::WeightInfo::include_pvf_check_statement()).into())
1148 }
1149 }
1150
1151 #[pallet::call_index(8)]
1153 #[pallet::weight(<T as Config>::WeightInfo::force_set_most_recent_context())]
1154 pub fn force_set_most_recent_context(
1155 origin: OriginFor<T>,
1156 para: ParaId,
1157 context: BlockNumberFor<T>,
1158 ) -> DispatchResult {
1159 ensure_root(origin)?;
1160 MostRecentContext::<T>::insert(¶, context);
1161 Ok(())
1162 }
1163 }
1164
1165 #[pallet::validate_unsigned]
1166 impl<T: Config> ValidateUnsigned for Pallet<T> {
1167 type Call = Call<T>;
1168
1169 fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
1170 let (stmt, signature) = match call {
1171 Call::include_pvf_check_statement { stmt, signature } => (stmt, signature),
1172 _ => return InvalidTransaction::Call.into(),
1173 };
1174
1175 let current_session = shared::CurrentSessionIndex::<T>::get();
1176 if stmt.session_index < current_session {
1177 return InvalidTransaction::Stale.into()
1178 } else if stmt.session_index > current_session {
1179 return InvalidTransaction::Future.into()
1180 }
1181
1182 let validator_index = stmt.validator_index.0 as usize;
1183 let validators = shared::ActiveValidatorKeys::<T>::get();
1184 let validator_public = match validators.get(validator_index) {
1185 Some(pk) => pk,
1186 None => return InvalidTransaction::Custom(INVALID_TX_BAD_VALIDATOR_IDX).into(),
1187 };
1188
1189 let signing_payload = stmt.signing_payload();
1190 if !signature.verify(&signing_payload[..], &validator_public) {
1191 return InvalidTransaction::BadProof.into()
1192 }
1193
1194 let active_vote = match PvfActiveVoteMap::<T>::get(&stmt.subject) {
1195 Some(v) => v,
1196 None => return InvalidTransaction::Custom(INVALID_TX_BAD_SUBJECT).into(),
1197 };
1198
1199 match active_vote.has_vote(validator_index) {
1200 Some(false) => (),
1201 Some(true) => return InvalidTransaction::Custom(INVALID_TX_DOUBLE_VOTE).into(),
1202 None => return InvalidTransaction::Custom(INVALID_TX_BAD_VALIDATOR_IDX).into(),
1203 }
1204
1205 ValidTransaction::with_tag_prefix("PvfPreCheckingVote")
1206 .priority(T::UnsignedPriority::get())
1207 .longevity(
1208 TryInto::<u64>::try_into(
1209 T::NextSessionRotation::average_session_length() / 2u32.into(),
1210 )
1211 .unwrap_or(64_u64),
1212 )
1213 .and_provides((stmt.session_index, stmt.validator_index, stmt.subject))
1214 .propagate(true)
1215 .build()
1216 }
1217
1218 fn pre_dispatch(_call: &Self::Call) -> Result<(), TransactionValidityError> {
1219 Ok(())
1227 }
1228 }
1229}
1230
1231const INVALID_TX_BAD_VALIDATOR_IDX: u8 = 1;
1233const INVALID_TX_BAD_SUBJECT: u8 = 2;
1234const INVALID_TX_DOUBLE_VOTE: u8 = 3;
1235
1236pub const MAX_PARA_HEADS: usize = 1024;
1244
1245impl<T: Config> Pallet<T> {
1246 pub(crate) fn schedule_code_upgrade_external(
1251 id: ParaId,
1252 new_code: ValidationCode,
1253 upgrade_strategy: UpgradeStrategy,
1254 ) -> DispatchResult {
1255 ensure!(Self::can_upgrade_validation_code(id), Error::<T>::CannotUpgradeCode);
1257 let config = configuration::ActiveConfig::<T>::get();
1258 ensure!(new_code.0.len() >= MIN_CODE_SIZE as usize, Error::<T>::InvalidCode);
1260 ensure!(new_code.0.len() <= config.max_code_size as usize, Error::<T>::InvalidCode);
1261
1262 let current_block = frame_system::Pallet::<T>::block_number();
1263 let upgrade_block = current_block.saturating_add(config.validation_upgrade_delay);
1265 Self::schedule_code_upgrade(id, new_code, upgrade_block, &config, upgrade_strategy);
1266 Self::deposit_event(Event::CodeUpgradeScheduled(id));
1267 Ok(())
1268 }
1269
1270 pub(crate) fn set_current_head(para: ParaId, new_head: HeadData) {
1272 Heads::<T>::insert(¶, new_head);
1273 Self::deposit_event(Event::CurrentHeadUpdated(para));
1274 }
1275
1276 pub(crate) fn initializer_initialize(now: BlockNumberFor<T>) -> Weight {
1278 Self::prune_old_code(now) +
1279 Self::process_scheduled_upgrade_changes(now) +
1280 Self::process_future_code_upgrades_at(now)
1281 }
1282
1283 pub(crate) fn initializer_finalize(now: BlockNumberFor<T>) {
1285 Self::process_scheduled_upgrade_cooldowns(now);
1286 }
1287
1288 pub(crate) fn initializer_on_new_session(
1292 notification: &SessionChangeNotification<BlockNumberFor<T>>,
1293 ) -> Vec<ParaId> {
1294 let outgoing_paras = Self::apply_actions_queue(notification.session_index);
1295 Self::groom_ongoing_pvf_votes(¬ification.new_config, notification.validators.len());
1296 outgoing_paras
1297 }
1298
1299 pub(crate) fn current_code(para_id: &ParaId) -> Option<ValidationCode> {
1301 CurrentCodeHash::<T>::get(para_id).and_then(|code_hash| {
1302 let code = CodeByHash::<T>::get(&code_hash);
1303 if code.is_none() {
1304 log::error!(
1305 "Pallet paras storage is inconsistent, code not found for hash {}",
1306 code_hash,
1307 );
1308 debug_assert!(false, "inconsistent paras storages");
1309 }
1310 code
1311 })
1312 }
1313
1314 pub fn sorted_para_heads() -> Vec<(u32, Vec<u8>)> {
1317 let mut heads: Vec<(u32, Vec<u8>)> =
1318 Heads::<T>::iter().map(|(id, head)| (id.into(), head.0)).collect();
1319 heads.sort_by_key(|(id, _)| *id);
1320 heads.truncate(MAX_PARA_HEADS);
1321 heads
1322 }
1323
1324 fn apply_actions_queue(session: SessionIndex) -> Vec<ParaId> {
1333 let actions = ActionsQueue::<T>::take(session);
1334 let mut parachains = ParachainsCache::new();
1335 let now = frame_system::Pallet::<T>::block_number();
1336 let mut outgoing = Vec::new();
1337
1338 for para in actions {
1339 let lifecycle = ParaLifecycles::<T>::get(¶);
1340 match lifecycle {
1341 None | Some(ParaLifecycle::Parathread) | Some(ParaLifecycle::Parachain) => { },
1343 Some(ParaLifecycle::Onboarding) => {
1344 if let Some(genesis_data) = UpcomingParasGenesis::<T>::take(¶) {
1345 Self::initialize_para_now(&mut parachains, para, &genesis_data);
1346 }
1347 },
1348 Some(ParaLifecycle::UpgradingParathread) => {
1350 parachains.add(para);
1351 ParaLifecycles::<T>::insert(¶, ParaLifecycle::Parachain);
1352 },
1353 Some(ParaLifecycle::DowngradingParachain) => {
1355 parachains.remove(para);
1356 ParaLifecycles::<T>::insert(¶, ParaLifecycle::Parathread);
1357 },
1358 Some(ParaLifecycle::OffboardingParachain) |
1360 Some(ParaLifecycle::OffboardingParathread) => {
1361 parachains.remove(para);
1362
1363 Heads::<T>::remove(¶);
1364 MostRecentContext::<T>::remove(¶);
1365 FutureCodeUpgrades::<T>::remove(¶);
1366 UpgradeGoAheadSignal::<T>::remove(¶);
1367 UpgradeRestrictionSignal::<T>::remove(¶);
1368 ParaLifecycles::<T>::remove(¶);
1369 let removed_future_code_hash = FutureCodeHash::<T>::take(¶);
1370 if let Some(removed_future_code_hash) = removed_future_code_hash {
1371 Self::decrease_code_ref(&removed_future_code_hash);
1372 }
1373
1374 let removed_code_hash = CurrentCodeHash::<T>::take(¶);
1375 if let Some(removed_code_hash) = removed_code_hash {
1376 Self::note_past_code(para, now, now, removed_code_hash);
1377 }
1378
1379 outgoing.push(para);
1380 },
1381 }
1382 }
1383
1384 if !outgoing.is_empty() {
1385 UpcomingUpgrades::<T>::mutate(|upcoming_upgrades| {
1392 upcoming_upgrades.retain(|(para, _)| !outgoing.contains(para));
1393 });
1394 UpgradeCooldowns::<T>::mutate(|upgrade_cooldowns| {
1395 upgrade_cooldowns.retain(|(para, _)| !outgoing.contains(para));
1396 });
1397 FutureCodeUpgradesAt::<T>::mutate(|future_upgrades| {
1398 future_upgrades.retain(|(para, _)| !outgoing.contains(para));
1399 });
1400 }
1401
1402 drop(parachains);
1404
1405 outgoing
1406 }
1407
1408 fn note_past_code(
1415 id: ParaId,
1416 at: BlockNumberFor<T>,
1417 now: BlockNumberFor<T>,
1418 old_code_hash: ValidationCodeHash,
1419 ) -> Weight {
1420 PastCodeMeta::<T>::mutate(&id, |past_meta| {
1421 past_meta.note_replacement(at, now);
1422 });
1423
1424 PastCodeHash::<T>::insert(&(id, at), old_code_hash);
1425
1426 PastCodePruning::<T>::mutate(|pruning| {
1429 let insert_idx =
1430 pruning.binary_search_by_key(&now, |&(_, b)| b).unwrap_or_else(|idx| idx);
1431 pruning.insert(insert_idx, (id, now));
1432 });
1433
1434 T::DbWeight::get().reads_writes(2, 3)
1435 }
1436
1437 fn prune_old_code(now: BlockNumberFor<T>) -> Weight {
1440 let config = configuration::ActiveConfig::<T>::get();
1441 let code_retention_period = config.code_retention_period;
1442 if now <= code_retention_period {
1443 let weight = T::DbWeight::get().reads_writes(1, 0);
1444 return weight
1445 }
1446
1447 let pruning_height = now - (code_retention_period + One::one());
1449
1450 let pruning_tasks_done =
1451 PastCodePruning::<T>::mutate(|pruning_tasks: &mut Vec<(_, BlockNumberFor<T>)>| {
1452 let (pruning_tasks_done, pruning_tasks_to_do) = {
1453 let up_to_idx =
1455 pruning_tasks.iter().take_while(|&(_, at)| at <= &pruning_height).count();
1456 (up_to_idx, pruning_tasks.drain(..up_to_idx))
1457 };
1458
1459 for (para_id, _) in pruning_tasks_to_do {
1460 let full_deactivate = PastCodeMeta::<T>::mutate(¶_id, |meta| {
1461 for pruned_repl_at in meta.prune_up_to(pruning_height) {
1462 let removed_code_hash =
1463 PastCodeHash::<T>::take(&(para_id, pruned_repl_at));
1464
1465 if let Some(removed_code_hash) = removed_code_hash {
1466 Self::decrease_code_ref(&removed_code_hash);
1467 } else {
1468 log::warn!(
1469 target: LOG_TARGET,
1470 "Missing code for removed hash {:?}",
1471 removed_code_hash,
1472 );
1473 }
1474 }
1475
1476 meta.is_empty() && Heads::<T>::get(¶_id).is_none()
1477 });
1478
1479 if full_deactivate {
1482 PastCodeMeta::<T>::remove(¶_id);
1483 }
1484 }
1485
1486 pruning_tasks_done as u64
1487 });
1488
1489 T::DbWeight::get().reads_writes(1 + pruning_tasks_done, 2 * pruning_tasks_done)
1492 }
1493
1494 fn process_future_code_upgrades_at(now: BlockNumberFor<T>) -> Weight {
1499 let mut weight = T::DbWeight::get().reads_writes(1, 1);
1501 FutureCodeUpgradesAt::<T>::mutate(
1502 |upcoming_upgrades: &mut Vec<(ParaId, BlockNumberFor<T>)>| {
1503 let num = upcoming_upgrades.iter().take_while(|&(_, at)| at <= &now).count();
1504 for (id, expected_at) in upcoming_upgrades.drain(..num) {
1505 weight += T::DbWeight::get().reads_writes(1, 1);
1506
1507 let new_code_hash = if let Some(new_code_hash) = FutureCodeHash::<T>::take(&id)
1509 {
1510 new_code_hash
1511 } else {
1512 log::error!(target: LOG_TARGET, "Missing future code hash for {:?}", &id);
1513 continue
1514 };
1515
1516 weight += Self::set_current_code(id, new_code_hash, expected_at);
1517 }
1518 num
1519 },
1520 );
1521
1522 weight
1523 }
1524
1525 fn process_scheduled_upgrade_changes(now: BlockNumberFor<T>) -> Weight {
1531 let mut weight = T::DbWeight::get().reads_writes(1, 1);
1533 let upgrades_signaled = UpcomingUpgrades::<T>::mutate(
1534 |upcoming_upgrades: &mut Vec<(ParaId, BlockNumberFor<T>)>| {
1535 let num = upcoming_upgrades.iter().take_while(|&(_, at)| at <= &now).count();
1536 for (para, _) in upcoming_upgrades.drain(..num) {
1537 UpgradeGoAheadSignal::<T>::insert(¶, UpgradeGoAhead::GoAhead);
1538 }
1539 num
1540 },
1541 );
1542 weight += T::DbWeight::get().writes(upgrades_signaled as u64);
1543
1544 weight += T::DbWeight::get().reads(1);
1546 let cooldowns_expired =
1547 UpgradeCooldowns::<T>::get().iter().take_while(|&(_, at)| at <= &now).count();
1548
1549 weight += T::DbWeight::get().reads_writes(1, 1);
1553 weight += T::DbWeight::get().reads(cooldowns_expired as u64);
1554
1555 weight
1556 }
1557
1558 fn process_scheduled_upgrade_cooldowns(now: BlockNumberFor<T>) {
1562 UpgradeCooldowns::<T>::mutate(
1563 |upgrade_cooldowns: &mut Vec<(ParaId, BlockNumberFor<T>)>| {
1564 upgrade_cooldowns.retain(|(para, at)| {
1566 if at <= &now {
1567 UpgradeRestrictionSignal::<T>::remove(¶);
1568 false
1569 } else {
1570 true
1571 }
1572 });
1573 },
1574 );
1575 }
1576
1577 fn groom_ongoing_pvf_votes(
1580 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1581 new_n_validators: usize,
1582 ) -> Weight {
1583 let mut weight = T::DbWeight::get().reads(1);
1584
1585 let potentially_active_votes = PvfActiveVoteList::<T>::get();
1586
1587 let mut actually_active_votes = Vec::with_capacity(potentially_active_votes.len());
1592
1593 for vote_subject in potentially_active_votes {
1594 let mut vote_state = match PvfActiveVoteMap::<T>::take(&vote_subject) {
1595 Some(v) => v,
1596 None => {
1597 log::warn!(
1601 target: LOG_TARGET,
1602 "The PvfActiveVoteMap is out of sync with PvfActiveVoteList!",
1603 );
1604 debug_assert!(false);
1605 continue
1606 },
1607 };
1608
1609 vote_state.age += 1;
1610 if vote_state.age < cfg.pvf_voting_ttl {
1611 weight += T::DbWeight::get().writes(1);
1612 vote_state.reinitialize_ballots(new_n_validators);
1613 PvfActiveVoteMap::<T>::insert(&vote_subject, vote_state);
1614
1615 actually_active_votes.push(vote_subject);
1617 } else {
1618 weight += Self::enact_pvf_rejected(&vote_subject, vote_state.causes);
1620 }
1621 }
1622
1623 weight += T::DbWeight::get().writes(1);
1624 PvfActiveVoteList::<T>::put(actually_active_votes);
1625
1626 weight
1627 }
1628
1629 fn enact_pvf_accepted(
1630 now: BlockNumberFor<T>,
1631 code_hash: &ValidationCodeHash,
1632 causes: &[PvfCheckCause<BlockNumberFor<T>>],
1633 sessions_observed: SessionIndex,
1634 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1635 ) -> Weight {
1636 let mut weight = Weight::zero();
1637 for cause in causes {
1638 weight += T::DbWeight::get().reads_writes(3, 2);
1639 Self::deposit_event(Event::PvfCheckAccepted(*code_hash, cause.para_id()));
1640
1641 match cause {
1642 PvfCheckCause::Onboarding(id) => {
1643 weight += Self::proceed_with_onboarding(*id, sessions_observed);
1644 },
1645 PvfCheckCause::Upgrade { id, included_at, upgrade_strategy } => {
1646 weight += Self::proceed_with_upgrade(
1647 *id,
1648 code_hash,
1649 now,
1650 *included_at,
1651 cfg,
1652 *upgrade_strategy,
1653 );
1654 },
1655 }
1656 }
1657 weight
1658 }
1659
1660 fn proceed_with_onboarding(id: ParaId, sessions_observed: SessionIndex) -> Weight {
1661 let weight = T::DbWeight::get().reads_writes(2, 1);
1662
1663 let onboard_at: SessionIndex = shared::CurrentSessionIndex::<T>::get() +
1669 cmp::max(shared::SESSION_DELAY.saturating_sub(sessions_observed), 1);
1670
1671 ActionsQueue::<T>::mutate(onboard_at, |v| {
1672 if let Err(i) = v.binary_search(&id) {
1673 v.insert(i, id);
1674 }
1675 });
1676
1677 weight
1678 }
1679
1680 fn proceed_with_upgrade(
1681 id: ParaId,
1682 code_hash: &ValidationCodeHash,
1683 now: BlockNumberFor<T>,
1684 relay_parent_number: BlockNumberFor<T>,
1685 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1686 upgrade_strategy: UpgradeStrategy,
1687 ) -> Weight {
1688 let mut weight = Weight::zero();
1689
1690 let expected_at = cmp::max(
1703 relay_parent_number + cfg.validation_upgrade_delay,
1704 now + cfg.minimum_validation_upgrade_delay,
1705 );
1706
1707 match upgrade_strategy {
1708 UpgradeStrategy::ApplyAtExpectedBlock => {
1709 FutureCodeUpgradesAt::<T>::mutate(|future_upgrades| {
1710 let insert_idx = future_upgrades
1711 .binary_search_by_key(&expected_at, |&(_, b)| b)
1712 .unwrap_or_else(|idx| idx);
1713 future_upgrades.insert(insert_idx, (id, expected_at));
1714 });
1715
1716 weight += T::DbWeight::get().reads_writes(0, 2);
1717 },
1718 UpgradeStrategy::SetGoAheadSignal => {
1719 FutureCodeUpgrades::<T>::insert(&id, expected_at);
1720
1721 UpcomingUpgrades::<T>::mutate(|upcoming_upgrades| {
1722 let insert_idx = upcoming_upgrades
1723 .binary_search_by_key(&expected_at, |&(_, b)| b)
1724 .unwrap_or_else(|idx| idx);
1725 upcoming_upgrades.insert(insert_idx, (id, expected_at));
1726 });
1727
1728 weight += T::DbWeight::get().reads_writes(1, 3);
1729 },
1730 }
1731
1732 let expected_at = expected_at.saturated_into();
1733 let log = ConsensusLog::ParaScheduleUpgradeCode(id, *code_hash, expected_at);
1734 frame_system::Pallet::<T>::deposit_log(log.into());
1735
1736 weight
1737 }
1738
1739 fn enact_pvf_rejected(
1740 code_hash: &ValidationCodeHash,
1741 causes: Vec<PvfCheckCause<BlockNumberFor<T>>>,
1742 ) -> Weight {
1743 let mut weight = Weight::zero();
1744
1745 for cause in causes {
1746 weight += Self::decrease_code_ref(code_hash);
1749
1750 weight += T::DbWeight::get().reads_writes(3, 2);
1751 Self::deposit_event(Event::PvfCheckRejected(*code_hash, cause.para_id()));
1752
1753 match cause {
1754 PvfCheckCause::Onboarding(id) => {
1755 weight += T::DbWeight::get().writes(3);
1761 UpcomingParasGenesis::<T>::remove(&id);
1762 CurrentCodeHash::<T>::remove(&id);
1763 ParaLifecycles::<T>::remove(&id);
1764 },
1765 PvfCheckCause::Upgrade { id, .. } => {
1766 weight += T::DbWeight::get().writes(2);
1767 UpgradeGoAheadSignal::<T>::insert(&id, UpgradeGoAhead::Abort);
1768 FutureCodeHash::<T>::remove(&id);
1769 },
1770 }
1771 }
1772
1773 weight
1774 }
1775
1776 pub fn can_schedule_para_initialize(id: &ParaId) -> bool {
1780 ParaLifecycles::<T>::get(id).is_none()
1781 }
1782
1783 pub(crate) fn schedule_para_initialize(
1794 id: ParaId,
1795 mut genesis_data: ParaGenesisArgs,
1796 ) -> DispatchResult {
1797 ensure!(Self::can_schedule_para_initialize(&id), Error::<T>::CannotOnboard);
1800 ensure!(!genesis_data.validation_code.0.is_empty(), Error::<T>::CannotOnboard);
1801 ParaLifecycles::<T>::insert(&id, ParaLifecycle::Onboarding);
1802
1803 let validation_code =
1837 mem::replace(&mut genesis_data.validation_code, ValidationCode(Vec::new()));
1838 UpcomingParasGenesis::<T>::insert(&id, genesis_data);
1839 let validation_code_hash = validation_code.hash();
1840 CurrentCodeHash::<T>::insert(&id, validation_code_hash);
1841
1842 let cfg = configuration::ActiveConfig::<T>::get();
1843 Self::kick_off_pvf_check(
1844 PvfCheckCause::Onboarding(id),
1845 validation_code_hash,
1846 validation_code,
1847 &cfg,
1848 );
1849
1850 Ok(())
1851 }
1852
1853 pub(crate) fn schedule_para_cleanup(id: ParaId) -> DispatchResult {
1863 if let Some(future_code_hash) = FutureCodeHash::<T>::get(&id) {
1875 let active_prechecking = PvfActiveVoteList::<T>::get();
1876 if active_prechecking.contains(&future_code_hash) {
1877 return Err(Error::<T>::CannotOffboard.into())
1878 }
1879 }
1880
1881 let lifecycle = ParaLifecycles::<T>::get(&id);
1882 match lifecycle {
1883 None => return Ok(()),
1885 Some(ParaLifecycle::Parathread) => {
1886 ParaLifecycles::<T>::insert(&id, ParaLifecycle::OffboardingParathread);
1887 },
1888 Some(ParaLifecycle::Parachain) => {
1889 ParaLifecycles::<T>::insert(&id, ParaLifecycle::OffboardingParachain);
1890 },
1891 _ => return Err(Error::<T>::CannotOffboard.into()),
1892 }
1893
1894 let scheduled_session = Self::scheduled_session();
1895 ActionsQueue::<T>::mutate(scheduled_session, |v| {
1896 if let Err(i) = v.binary_search(&id) {
1897 v.insert(i, id);
1898 }
1899 });
1900
1901 if <T as Config>::QueueFootprinter::message_count(UmpQueueId::Para(id)) != 0 {
1902 return Err(Error::<T>::CannotOffboard.into())
1903 }
1904
1905 Ok(())
1906 }
1907
1908 pub(crate) fn schedule_parathread_upgrade(id: ParaId) -> DispatchResult {
1912 let scheduled_session = Self::scheduled_session();
1913 let lifecycle = ParaLifecycles::<T>::get(&id).ok_or(Error::<T>::NotRegistered)?;
1914
1915 ensure!(lifecycle == ParaLifecycle::Parathread, Error::<T>::CannotUpgrade);
1916
1917 ParaLifecycles::<T>::insert(&id, ParaLifecycle::UpgradingParathread);
1918 ActionsQueue::<T>::mutate(scheduled_session, |v| {
1919 if let Err(i) = v.binary_search(&id) {
1920 v.insert(i, id);
1921 }
1922 });
1923
1924 Ok(())
1925 }
1926
1927 pub(crate) fn schedule_parachain_downgrade(id: ParaId) -> DispatchResult {
1931 let scheduled_session = Self::scheduled_session();
1932 let lifecycle = ParaLifecycles::<T>::get(&id).ok_or(Error::<T>::NotRegistered)?;
1933
1934 ensure!(lifecycle == ParaLifecycle::Parachain, Error::<T>::CannotDowngrade);
1935
1936 ParaLifecycles::<T>::insert(&id, ParaLifecycle::DowngradingParachain);
1937 ActionsQueue::<T>::mutate(scheduled_session, |v| {
1938 if let Err(i) = v.binary_search(&id) {
1939 v.insert(i, id);
1940 }
1941 });
1942
1943 Ok(())
1944 }
1945
1946 pub(crate) fn schedule_code_upgrade(
1965 id: ParaId,
1966 new_code: ValidationCode,
1967 inclusion_block_number: BlockNumberFor<T>,
1968 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
1969 upgrade_strategy: UpgradeStrategy,
1970 ) {
1971 let new_code_len = new_code.0.len();
1973 if new_code_len < MIN_CODE_SIZE as usize || new_code_len > cfg.max_code_size as usize {
1974 log::warn!(target: LOG_TARGET, "attempted to schedule an upgrade with invalid new validation code",);
1975 return
1976 }
1977
1978 if FutureCodeHash::<T>::contains_key(&id) {
1980 log::warn!(target: LOG_TARGET, "ended up scheduling an upgrade while one is pending",);
1989 return
1990 }
1991
1992 let code_hash = new_code.hash();
1993
1994 if CurrentCodeHash::<T>::get(&id) == Some(code_hash) {
1999 log::warn!(
2002 target: LOG_TARGET,
2003 "para tried to upgrade to the same code. Abort the upgrade",
2004 );
2005 return
2006 }
2007
2008 FutureCodeHash::<T>::insert(&id, &code_hash);
2010 UpgradeRestrictionSignal::<T>::insert(&id, UpgradeRestriction::Present);
2011
2012 let next_possible_upgrade_at = inclusion_block_number + cfg.validation_upgrade_cooldown;
2013 UpgradeCooldowns::<T>::mutate(|upgrade_cooldowns| {
2014 let insert_idx = upgrade_cooldowns
2015 .binary_search_by_key(&next_possible_upgrade_at, |&(_, b)| b)
2016 .unwrap_or_else(|idx| idx);
2017 upgrade_cooldowns.insert(insert_idx, (id, next_possible_upgrade_at));
2018 });
2019
2020 Self::kick_off_pvf_check(
2021 PvfCheckCause::Upgrade { id, included_at: inclusion_block_number, upgrade_strategy },
2022 code_hash,
2023 new_code,
2024 cfg,
2025 );
2026 }
2027
2028 fn kick_off_pvf_check(
2042 cause: PvfCheckCause<BlockNumberFor<T>>,
2043 code_hash: ValidationCodeHash,
2044 code: ValidationCode,
2045 cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
2046 ) -> Weight {
2047 let mut weight = Weight::zero();
2048
2049 weight += T::DbWeight::get().reads_writes(3, 2);
2050 Self::deposit_event(Event::PvfCheckStarted(code_hash, cause.para_id()));
2051
2052 weight += T::DbWeight::get().reads(1);
2053 match PvfActiveVoteMap::<T>::get(&code_hash) {
2054 None => {
2055 let known_code = CodeByHash::<T>::contains_key(&code_hash);
2058 weight += T::DbWeight::get().reads(1);
2059
2060 if known_code {
2061 weight += T::DbWeight::get().reads(1);
2064 let now = frame_system::Pallet::<T>::block_number();
2065 weight += Self::enact_pvf_accepted(now, &code_hash, &[cause], 0, cfg);
2066 } else {
2067 weight += T::DbWeight::get().reads_writes(3, 2);
2070 let now = frame_system::Pallet::<T>::block_number();
2071 let n_validators = shared::ActiveValidatorKeys::<T>::get().len();
2072 PvfActiveVoteMap::<T>::insert(
2073 &code_hash,
2074 PvfCheckActiveVoteState::new(now, n_validators, cause),
2075 );
2076 PvfActiveVoteList::<T>::mutate(|l| {
2077 if let Err(idx) = l.binary_search(&code_hash) {
2078 l.insert(idx, code_hash);
2079 }
2080 });
2081 }
2082 },
2083 Some(mut vote_state) => {
2084 weight += T::DbWeight::get().writes(1);
2087 vote_state.causes.push(cause);
2088 PvfActiveVoteMap::<T>::insert(&code_hash, vote_state);
2089 },
2090 }
2091
2092 weight += Self::increase_code_ref(&code_hash, &code);
2104
2105 weight
2106 }
2107
2108 pub(crate) fn note_new_head(
2112 id: ParaId,
2113 new_head: HeadData,
2114 execution_context: BlockNumberFor<T>,
2115 ) {
2116 Heads::<T>::insert(&id, &new_head);
2117 MostRecentContext::<T>::insert(&id, execution_context);
2118
2119 if let Some(expected_at) = FutureCodeUpgrades::<T>::get(&id) {
2120 if expected_at <= execution_context {
2121 FutureCodeUpgrades::<T>::remove(&id);
2122 UpgradeGoAheadSignal::<T>::remove(&id);
2123
2124 let new_code_hash = if let Some(new_code_hash) = FutureCodeHash::<T>::take(&id) {
2126 new_code_hash
2127 } else {
2128 log::error!(target: LOG_TARGET, "Missing future code hash for {:?}", &id);
2129 return
2130 };
2131
2132 Self::set_current_code(id, new_code_hash, expected_at);
2133 }
2134 } else {
2135 UpgradeGoAheadSignal::<T>::remove(&id);
2140 };
2141
2142 T::OnNewHead::on_new_head(id, &new_head);
2143 }
2144
2145 pub(crate) fn set_current_code(
2150 id: ParaId,
2151 new_code_hash: ValidationCodeHash,
2152 at: BlockNumberFor<T>,
2153 ) -> Weight {
2154 let maybe_prior_code_hash = CurrentCodeHash::<T>::get(&id);
2155 CurrentCodeHash::<T>::insert(&id, &new_code_hash);
2156
2157 let log = ConsensusLog::ParaUpgradeCode(id, new_code_hash);
2158 <frame_system::Pallet<T>>::deposit_log(log.into());
2159
2160 let now = <frame_system::Pallet<T>>::block_number();
2162
2163 let weight = if let Some(prior_code_hash) = maybe_prior_code_hash {
2164 Self::note_past_code(id, at, now, prior_code_hash)
2165 } else {
2166 log::error!(target: LOG_TARGET, "Missing prior code hash for para {:?}", &id);
2167 Weight::zero()
2168 };
2169
2170 weight + T::DbWeight::get().writes(1)
2171 }
2172
2173 pub(crate) fn pvfs_require_precheck() -> Vec<ValidationCodeHash> {
2176 PvfActiveVoteList::<T>::get()
2177 }
2178
2179 pub(crate) fn submit_pvf_check_statement(
2186 stmt: PvfCheckStatement,
2187 signature: ValidatorSignature,
2188 ) {
2189 use frame_system::offchain::SubmitTransaction;
2190
2191 let xt = T::create_inherent(Call::include_pvf_check_statement { stmt, signature }.into());
2192 if let Err(e) = SubmitTransaction::<T, Call<T>>::submit_transaction(xt) {
2193 log::error!(target: LOG_TARGET, "Error submitting pvf check statement: {:?}", e,);
2194 }
2195 }
2196
2197 pub fn lifecycle(id: ParaId) -> Option<ParaLifecycle> {
2199 ParaLifecycles::<T>::get(&id)
2200 }
2201
2202 pub fn is_valid_para(id: ParaId) -> bool {
2206 if let Some(state) = ParaLifecycles::<T>::get(&id) {
2207 !state.is_onboarding() && !state.is_offboarding()
2208 } else {
2209 false
2210 }
2211 }
2212
2213 pub fn is_offboarding(id: ParaId) -> bool {
2217 ParaLifecycles::<T>::get(&id).map_or(false, |state| state.is_offboarding())
2218 }
2219
2220 pub fn is_parachain(id: ParaId) -> bool {
2225 if let Some(state) = ParaLifecycles::<T>::get(&id) {
2226 state.is_parachain()
2227 } else {
2228 false
2229 }
2230 }
2231
2232 pub fn is_parathread(id: ParaId) -> bool {
2236 if let Some(state) = ParaLifecycles::<T>::get(&id) {
2237 state.is_parathread()
2238 } else {
2239 false
2240 }
2241 }
2242
2243 pub(crate) fn can_upgrade_validation_code(id: ParaId) -> bool {
2246 FutureCodeHash::<T>::get(&id).is_none() && UpgradeRestrictionSignal::<T>::get(&id).is_none()
2247 }
2248
2249 fn scheduled_session() -> SessionIndex {
2251 shared::Pallet::<T>::scheduled_session()
2252 }
2253
2254 fn increase_code_ref(code_hash: &ValidationCodeHash, code: &ValidationCode) -> Weight {
2258 let mut weight = T::DbWeight::get().reads_writes(1, 1);
2259 CodeByHashRefs::<T>::mutate(code_hash, |refs| {
2260 if *refs == 0 {
2261 weight += T::DbWeight::get().writes(1);
2262 CodeByHash::<T>::insert(code_hash, code);
2263 }
2264 *refs += 1;
2265 });
2266 weight
2267 }
2268
2269 fn decrease_code_ref(code_hash: &ValidationCodeHash) -> Weight {
2274 let mut weight = T::DbWeight::get().reads(1);
2275 let refs = CodeByHashRefs::<T>::get(code_hash);
2276 if refs == 0 {
2277 log::error!(target: LOG_TARGET, "Code refs is already zero for {:?}", code_hash);
2278 return weight
2279 }
2280 if refs <= 1 {
2281 weight += T::DbWeight::get().writes(2);
2282 CodeByHash::<T>::remove(code_hash);
2283 CodeByHashRefs::<T>::remove(code_hash);
2284 } else {
2285 weight += T::DbWeight::get().writes(1);
2286 CodeByHashRefs::<T>::insert(code_hash, refs - 1);
2287 }
2288 weight
2289 }
2290
2291 #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
2293 pub fn test_on_new_session() {
2294 Self::initializer_on_new_session(&SessionChangeNotification {
2295 session_index: shared::CurrentSessionIndex::<T>::get(),
2296 ..Default::default()
2297 });
2298 }
2299
2300 #[cfg(any(feature = "runtime-benchmarks", test))]
2301 pub fn heads_insert(para_id: &ParaId, head_data: HeadData) {
2302 Heads::<T>::insert(para_id, head_data);
2303 }
2304
2305 pub(crate) fn initialize_para_now(
2307 parachains: &mut ParachainsCache<T>,
2308 id: ParaId,
2309 genesis_data: &ParaGenesisArgs,
2310 ) {
2311 match genesis_data.para_kind {
2312 ParaKind::Parachain => {
2313 parachains.add(id);
2314 ParaLifecycles::<T>::insert(&id, ParaLifecycle::Parachain);
2315 },
2316 ParaKind::Parathread => ParaLifecycles::<T>::insert(&id, ParaLifecycle::Parathread),
2317 }
2318
2319 if !genesis_data.validation_code.0.is_empty() {
2325 let code_hash = genesis_data.validation_code.hash();
2326 Self::increase_code_ref(&code_hash, &genesis_data.validation_code);
2327 CurrentCodeHash::<T>::insert(&id, code_hash);
2328 }
2329
2330 Heads::<T>::insert(&id, &genesis_data.genesis_head);
2331 MostRecentContext::<T>::insert(&id, BlockNumberFor::<T>::from(0u32));
2332 }
2333
2334 #[cfg(test)]
2335 pub(crate) fn active_vote_state(
2336 code_hash: &ValidationCodeHash,
2337 ) -> Option<PvfCheckActiveVoteState<BlockNumberFor<T>>> {
2338 PvfActiveVoteMap::<T>::get(code_hash)
2339 }
2340}
2341
2342pub(crate) struct ParachainsCache<T: Config> {
2345 parachains: Option<BTreeSet<ParaId>>,
2347 _config: PhantomData<T>,
2348}
2349
2350impl<T: Config> ParachainsCache<T> {
2351 pub fn new() -> Self {
2352 Self { parachains: None, _config: PhantomData }
2353 }
2354
2355 fn ensure_initialized(&mut self) -> &mut BTreeSet<ParaId> {
2356 self.parachains
2357 .get_or_insert_with(|| Parachains::<T>::get().into_iter().collect())
2358 }
2359
2360 pub fn add(&mut self, id: ParaId) {
2362 let parachains = self.ensure_initialized();
2363 parachains.insert(id);
2364 }
2365
2366 pub fn remove(&mut self, id: ParaId) {
2369 let parachains = self.ensure_initialized();
2370 parachains.remove(&id);
2371 }
2372}
2373
2374impl<T: Config> Drop for ParachainsCache<T> {
2375 fn drop(&mut self) {
2376 if let Some(parachains) = self.parachains.take() {
2377 Parachains::<T>::put(parachains.into_iter().collect::<Vec<ParaId>>());
2378 }
2379 }
2380}