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.
This commit is contained in:
Sergei Shulepov
2022-02-22 14:52:27 +01:00
committed by GitHub
parent 1ba4af9f15
commit 95a78e877f
3 changed files with 90 additions and 72 deletions
+80 -66
View File
@@ -537,6 +537,8 @@ pub mod pallet {
StorageValue<_, Vec<ValidationCodeHash>, 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<T: Config> = StorageValue<_, Vec<ParaId>, ValueQuery>;
@@ -683,30 +685,14 @@ pub mod pallet {
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> 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::<T>::put(&parachains);
let mut parachains = ParachainsCache::new();
for (id, genesis_args) in &self.paras {
let code_hash = genesis_args.validation_code.hash();
<Pallet<T>>::increase_code_ref(&code_hash, &genesis_args.validation_code);
<Pallet<T> as Store>::CurrentCodeHash::insert(&id, &code_hash);
<Pallet<T> as Store>::Heads::insert(&id, &genesis_args.genesis_head);
if genesis_args.parachain {
ParaLifecycles::<T>::insert(&id, ParaLifecycle::Parachain);
} else {
ParaLifecycles::<T>::insert(&id, ParaLifecycle::Parathread);
if genesis_args.validation_code.0.is_empty() {
panic!("empty validation code is not allowed in genesis");
}
Pallet::<T>::initialize_para_now(&mut parachains, *id, genesis_args);
}
// parachains are flushed on drop
}
}
@@ -1082,7 +1068,7 @@ impl<T: Config> Pallet<T> {
// Returns the list of outgoing paras from the actions queue.
fn apply_actions_queue(session: SessionIndex) -> Vec<ParaId> {
let actions = ActionsQueue::<T>::take(session);
let mut parachains = <Self as Store>::Parachains::get();
let mut parachains = ParachainsCache::new();
let now = <frame_system::Pallet<T>>::block_number();
let mut outgoing = Vec::new();
@@ -1093,49 +1079,23 @@ impl<T: Config> Pallet<T> {
},
Some(ParaLifecycle::Onboarding) => {
if let Some(genesis_data) = <Self as Store>::UpcomingParasGenesis::take(&para) {
if genesis_data.parachain {
if let Err(i) = parachains.binary_search(&para) {
parachains.insert(i, para);
}
ParaLifecycles::<T>::insert(&para, ParaLifecycle::Parachain);
} else {
ParaLifecycles::<T>::insert(&para, 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);
<Self as Store>::CurrentCodeHash::insert(&para, code_hash);
}
<Self as Store>::Heads::insert(&para, 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(&para) {
parachains.insert(i, para);
}
parachains.add(para);
ParaLifecycles::<T>::insert(&para, ParaLifecycle::Parachain);
},
// Downgrade a parachain to a parathread
Some(ParaLifecycle::DowngradingParachain) => {
if let Ok(i) = parachains.binary_search(&para) {
parachains.remove(i);
}
parachains.remove(para);
ParaLifecycles::<T>::insert(&para, ParaLifecycle::Parathread);
},
// Offboard a parathread or parachain from the system
Some(ParaLifecycle::OffboardingParachain) |
Some(ParaLifecycle::OffboardingParathread) => {
if let Ok(i) = parachains.binary_search(&para) {
parachains.remove(i);
}
parachains.remove(para);
<Self as Store>::Heads::remove(&para);
<Self as Store>::FutureCodeUpgrades::remove(&para);
@@ -1178,8 +1138,8 @@ impl<T: Config> Pallet<T> {
});
}
// Place the new parachains set in storage.
<Self as Store>::Parachains::set(parachains);
// Persist parachains into the storage explicitly.
drop(parachains);
return outgoing
}
@@ -1994,19 +1954,73 @@ impl<T: Config> Pallet<T> {
Heads::<T>::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<T>,
id: ParaId,
genesis_data: &ParaGenesisArgs,
) {
if genesis_data.parachain {
parachains.add(id);
ParaLifecycles::<T>::insert(&id, ParaLifecycle::Parachain);
} else {
ParaLifecycles::<T>::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::<T>::insert(&id, code_hash);
}
// ensure it has become a para.
ensure!(
ParaLifecycles::<T>::get(id) == Some(ParaLifecycle::Parachain),
"Parachain not created properly"
);
Ok(())
Heads::<T>::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<T: Config> {
// `None` here means the parachains list has not been accessed yet, nevermind modified.
parachains: Option<Vec<ParaId>>,
_config: PhantomData<T>,
}
impl<T: Config> ParachainsCache<T> {
pub fn new() -> Self {
Self { parachains: None, _config: PhantomData }
}
fn ensure_initialized(&mut self) -> &mut Vec<ParaId> {
self.parachains.get_or_insert_with(|| Parachains::<T>::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<T: Config> Drop for ParachainsCache<T> {
fn drop(&mut self) {
if let Some(parachains) = self.parachains.take() {
Parachains::<T>::put(&parachains);
}
}
}
@@ -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,
},
)];