From 95a78e877fee1423b4999189f5173326ae7e7240 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Tue, 22 Feb 2022 14:52:27 +0100 Subject: [PATCH] paras: `initialize_para_now` and `ParachainsCache` (#4934) This commit adds a new primitive called `ParachainsCache` to manipulate the `Parachains` storage entry in a more convenient way. Then, on top of that, this commit changes the logic of `initialize_para_now` so that it is identical to what is used for initialization of onboarding. --- .../parachains/src/hrmp/benchmarking.rs | 10 +- polkadot/runtime/parachains/src/paras/mod.rs | 146 ++++++++++-------- .../runtime/parachains/src/paras/tests.rs | 6 +- 3 files changed, 90 insertions(+), 72 deletions(-) diff --git a/polkadot/runtime/parachains/src/hrmp/benchmarking.rs b/polkadot/runtime/parachains/src/hrmp/benchmarking.rs index a9ba7031fe..ddce64601f 100644 --- a/polkadot/runtime/parachains/src/hrmp/benchmarking.rs +++ b/polkadot/runtime/parachains/src/hrmp/benchmarking.rs @@ -17,7 +17,7 @@ use crate::{ configuration::Pallet as Configuration, hrmp::{Pallet as Hrmp, *}, - paras::Pallet as Paras, + paras::{Pallet as Paras, ParachainsCache}, shared::Pallet as Shared, }; use frame_support::{assert_ok, traits::Currency}; @@ -26,14 +26,16 @@ type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; fn register_parachain_with_balance(id: ParaId, balance: BalanceOf) { - assert_ok!(Paras::::initialize_para_now( + let mut parachains = ParachainsCache::new(); + Paras::::initialize_para_now( + &mut parachains, id, - crate::paras::ParaGenesisArgs { + &crate::paras::ParaGenesisArgs { parachain: true, genesis_head: vec![1].into(), validation_code: vec![1].into(), }, - )); + ); T::Currency::make_free_balance_be(&id.into_account(), balance); } diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 75fa5f8e01..26b70b42bd 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -537,6 +537,8 @@ pub mod pallet { StorageValue<_, Vec, ValueQuery>; /// All parachains. Ordered ascending by `ParaId`. Parathreads are not included. + /// + /// Consider using the [`ParachainsCache`] type of modifying. #[pallet::storage] #[pallet::getter(fn parachains)] pub(crate) type Parachains = StorageValue<_, Vec, ValueQuery>; @@ -683,30 +685,14 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - let mut parachains: Vec<_> = self - .paras - .iter() - .filter(|(_, args)| args.parachain) - .map(|&(ref id, _)| id) - .cloned() - .collect(); - - parachains.sort(); - parachains.dedup(); - - Parachains::::put(¶chains); - + let mut parachains = ParachainsCache::new(); for (id, genesis_args) in &self.paras { - let code_hash = genesis_args.validation_code.hash(); - >::increase_code_ref(&code_hash, &genesis_args.validation_code); - as Store>::CurrentCodeHash::insert(&id, &code_hash); - as Store>::Heads::insert(&id, &genesis_args.genesis_head); - if genesis_args.parachain { - ParaLifecycles::::insert(&id, ParaLifecycle::Parachain); - } else { - ParaLifecycles::::insert(&id, ParaLifecycle::Parathread); + if genesis_args.validation_code.0.is_empty() { + panic!("empty validation code is not allowed in genesis"); } + Pallet::::initialize_para_now(&mut parachains, *id, genesis_args); } + // parachains are flushed on drop } } @@ -1082,7 +1068,7 @@ impl Pallet { // Returns the list of outgoing paras from the actions queue. fn apply_actions_queue(session: SessionIndex) -> Vec { let actions = ActionsQueue::::take(session); - let mut parachains = ::Parachains::get(); + let mut parachains = ParachainsCache::new(); let now = >::block_number(); let mut outgoing = Vec::new(); @@ -1093,49 +1079,23 @@ impl Pallet { }, Some(ParaLifecycle::Onboarding) => { if let Some(genesis_data) = ::UpcomingParasGenesis::take(¶) { - if genesis_data.parachain { - if let Err(i) = parachains.binary_search(¶) { - parachains.insert(i, para); - } - ParaLifecycles::::insert(¶, ParaLifecycle::Parachain); - } else { - ParaLifecycles::::insert(¶, ParaLifecycle::Parathread); - } - - // HACK: see the notice in `schedule_para_initialize`. - // - // Apparently, this is left over from a prior version of the runtime. - // To handle this we just insert the code and link the current code hash - // to it. - if !genesis_data.validation_code.0.is_empty() { - let code_hash = genesis_data.validation_code.hash(); - Self::increase_code_ref(&code_hash, &genesis_data.validation_code); - ::CurrentCodeHash::insert(¶, code_hash); - } - - ::Heads::insert(¶, genesis_data.genesis_head); + Self::initialize_para_now(&mut parachains, para, &genesis_data); } }, // Upgrade a parathread to a parachain Some(ParaLifecycle::UpgradingParathread) => { - if let Err(i) = parachains.binary_search(¶) { - parachains.insert(i, para); - } + parachains.add(para); ParaLifecycles::::insert(¶, ParaLifecycle::Parachain); }, // Downgrade a parachain to a parathread Some(ParaLifecycle::DowngradingParachain) => { - if let Ok(i) = parachains.binary_search(¶) { - parachains.remove(i); - } + parachains.remove(para); ParaLifecycles::::insert(¶, ParaLifecycle::Parathread); }, // Offboard a parathread or parachain from the system Some(ParaLifecycle::OffboardingParachain) | Some(ParaLifecycle::OffboardingParathread) => { - if let Ok(i) = parachains.binary_search(¶) { - parachains.remove(i); - } + parachains.remove(para); ::Heads::remove(¶); ::FutureCodeUpgrades::remove(¶); @@ -1178,8 +1138,8 @@ impl Pallet { }); } - // Place the new parachains set in storage. - ::Parachains::set(parachains); + // Persist parachains into the storage explicitly. + drop(parachains); return outgoing } @@ -1994,19 +1954,73 @@ impl Pallet { Heads::::insert(para_id, head_data); } - #[cfg(feature = "runtime-benchmarks")] - pub(crate) fn initialize_para_now(id: ParaId, genesis: ParaGenesisArgs) -> DispatchResult { - // first queue this para actions.. - let _ = Self::schedule_para_initialize(id, genesis)?; + /// A low-level function to eagerly initialize a given para. + pub(crate) fn initialize_para_now( + parachains: &mut ParachainsCache, + id: ParaId, + genesis_data: &ParaGenesisArgs, + ) { + if genesis_data.parachain { + parachains.add(id); + ParaLifecycles::::insert(&id, ParaLifecycle::Parachain); + } else { + ParaLifecycles::::insert(&id, ParaLifecycle::Parathread); + } - // .. and immediately apply them. - Self::apply_actions_queue(Self::scheduled_session()); + // HACK: see the notice in `schedule_para_initialize`. + // + // Apparently, this is left over from a prior version of the runtime. + // To handle this we just insert the code and link the current code hash + // to it. + if !genesis_data.validation_code.0.is_empty() { + let code_hash = genesis_data.validation_code.hash(); + Self::increase_code_ref(&code_hash, &genesis_data.validation_code); + CurrentCodeHash::::insert(&id, code_hash); + } - // ensure it has become a para. - ensure!( - ParaLifecycles::::get(id) == Some(ParaLifecycle::Parachain), - "Parachain not created properly" - ); - Ok(()) + Heads::::insert(&id, &genesis_data.genesis_head); + } +} + +/// An overlay over the `Parachains` storage entry that provides a convenient interface for adding +/// or removing parachains in bulk. +pub(crate) struct ParachainsCache { + // `None` here means the parachains list has not been accessed yet, nevermind modified. + parachains: Option>, + _config: PhantomData, +} + +impl ParachainsCache { + pub fn new() -> Self { + Self { parachains: None, _config: PhantomData } + } + + fn ensure_initialized(&mut self) -> &mut Vec { + self.parachains.get_or_insert_with(|| Parachains::::get()) + } + + /// Adds the given para id to the list. + pub fn add(&mut self, id: ParaId) { + let parachains = self.ensure_initialized(); + if let Err(i) = parachains.binary_search(&id) { + parachains.insert(i, id); + } + } + + /// Removes the given para id from the list of parachains. Does nothing if the id is not in the + /// list. + pub fn remove(&mut self, id: ParaId) { + let parachains = self.ensure_initialized(); + if let Ok(i) = parachains.binary_search(&id) { + parachains.remove(i); + } + } +} + +impl Drop for ParachainsCache { + fn drop(&mut self) { + if let Some(parachains) = self.parachains.take() { + Parachains::::put(¶chains); + } } } diff --git a/polkadot/runtime/parachains/src/paras/tests.rs b/polkadot/runtime/parachains/src/paras/tests.rs index eb0bd60083..7f4ac54711 100644 --- a/polkadot/runtime/parachains/src/paras/tests.rs +++ b/polkadot/runtime/parachains/src/paras/tests.rs @@ -1020,6 +1020,7 @@ fn pvf_check_coalescing_onboarding_and_upgrade() { let a = ParaId::from(111); let b = ParaId::from(222); + let existing_code: ValidationCode = vec![1, 2, 3].into(); let validation_code: ValidationCode = vec![3, 2, 1].into(); let paras = vec![( @@ -1027,7 +1028,7 @@ fn pvf_check_coalescing_onboarding_and_upgrade() { ParaGenesisArgs { parachain: true, genesis_head: Default::default(), - validation_code: ValidationCode(vec![]), // valid since in genesis + validation_code: existing_code, }, )]; @@ -1159,6 +1160,7 @@ fn pvf_check_onboarding_reject_on_expiry() { #[test] fn pvf_check_upgrade_reject() { let a = ParaId::from(111); + let old_code: ValidationCode = vec![1, 2, 3].into(); let new_code: ValidationCode = vec![3, 2, 1].into(); let paras = vec![( @@ -1166,7 +1168,7 @@ fn pvf_check_upgrade_reject() { ParaGenesisArgs { parachain: false, genesis_head: Default::default(), - validation_code: ValidationCode(vec![]), // valid since in genesis + validation_code: old_code, }, )];