Session Delayed Para Changes / Actions Queue (#2406)

* initial implementation of lifecycles and upgrades

* clean up a bit

* fix doc comment

* more rigid lifecycle checks

* include paras which are transitioning, and lifecycle query

* format guide

* update api

* update guide

* explicit outgoing state, fix genesis

* handle outgoing with transitioning paras

* do not include transitioning paras in identifier

* Update roadmap/implementers-guide/src/runtime/paras.md

* Update roadmap/implementers-guide/src/runtime/paras.md

* Update roadmap/implementers-guide/src/runtime/paras.md

* Apply suggestions from code review

* Use matches macro

* Correct terms

* Apply suggestions from code review

* actions queue

* Revert "actions queue"

This reverts commit b2e9011ec8937d6c73e99292416c9692aeb30f73.

* collapse onboarding state

* starting actions queue

* consolidate actions queue

* schedule para initialize result

* more actions queue for upgrade/downgrade

* clean up with fully implemented actions queue

* fix tests

* fix scheduler tests

* fix hrmp tests

* fix test

* doc fixes

* fix hrmp test w/ valid para

* Update paras.md

* fix paras registrar

* Update propose_parachain.rs

* fix merge

* Introduce "shared" module

* fix rococo build

* fix up and use shared

* guide updates

* add shared config to common tests

* add shared to test-runtime

* remove println

* fix note

Co-authored-by: Gavin Wood <gavin@parity.io>
This commit is contained in:
Shawn Tabrizi
2021-02-18 23:20:18 -04:00
committed by GitHub
parent 006602eff2
commit a5defa7c7f
20 changed files with 654 additions and 698 deletions
+225 -364
View File
@@ -28,16 +28,16 @@ use sp_std::result;
#[cfg(feature = "std")]
use sp_std::marker::PhantomData;
use primitives::v1::{
Id as ParaId, ValidationCode, HeadData,
Id as ParaId, ValidationCode, HeadData, SessionIndex,
};
use sp_runtime::traits::One;
use sp_runtime::{traits::One, DispatchResult};
use frame_support::{
decl_storage, decl_module, decl_error,
decl_storage, decl_module, decl_error, ensure,
traits::Get,
weights::Weight,
};
use parity_scale_codec::{Encode, Decode};
use crate::{configuration, initializer::SessionChangeNotification};
use crate::{configuration, shared, initializer::SessionChangeNotification};
use sp_core::RuntimeDebug;
#[cfg(feature = "std")]
@@ -45,7 +45,11 @@ use serde::{Serialize, Deserialize};
pub use crate::Origin;
pub trait Config: frame_system::Config + configuration::Config {
pub trait Config:
frame_system::Config +
configuration::Config +
shared::Config
{
/// The outer origin type.
type Origin: From<Origin>
+ From<<Self as frame_system::Config>::Origin>
@@ -93,6 +97,10 @@ enum UseCodeAt<N> {
}
/// The possible states of a para, to take into account delayed lifecycle changes.
///
/// If the para is in a "transition state", it is expected that the parachain is
/// queued in the `ActionsQueue` to transition it into a stable state. Its lifecycle
/// state will be used to determine the state transition to apply to the para.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
pub enum ParaLifecycle {
/// Para is new and is onboarding as a Parathread or Parachain.
@@ -102,36 +110,57 @@ pub enum ParaLifecycle {
/// Para is a Parachain.
Parachain,
/// Para is a Parathread which is upgrading to a Parachain.
UpgradingToParachain,
UpgradingParathread,
/// Para is a Parachain which is downgrading to a Parathread.
DowngradingToParathread,
/// Parathread is being offboarded.
OutgoingParathread,
/// Parachain is being offboarded.
OutgoingParachain,
DowngradingParachain,
/// Parathread is queued to be offboarded.
OffboardingParathread,
/// Parachain is queued to be offboarded.
OffboardingParachain,
}
impl ParaLifecycle {
/// Returns true if parachain is currently onboarding. To learn if the
/// parachain is onboarding as a parachain or parathread, look at the
/// `UpcomingGenesis` storage item.
pub fn is_onboarding(&self) -> bool {
matches!(self, ParaLifecycle::Onboarding)
}
/// Returns true if para is in a stable state, i.e. it is currently
/// a parachain or parathread, and not in any transition state.
pub fn is_stable(&self) -> bool {
matches!(self, ParaLifecycle::Parathread | ParaLifecycle::Parachain)
}
/// Returns true if para is currently treated as a parachain.
/// This also includes transitioning states, so you may want to combine
/// this check with `is_stable` if you specifically want `Paralifecycle::Parachain`.
pub fn is_parachain(&self) -> bool {
matches!(self, ParaLifecycle::Parachain)
matches!(self,
ParaLifecycle::Parachain |
ParaLifecycle::DowngradingParachain |
ParaLifecycle::OffboardingParachain
)
}
/// Returns true if para is currently treated as a parathread.
/// This also includes transitioning states, so you may want to combine
/// this check with `is_stable` if you specifically want `Paralifecycle::Parathread`.
pub fn is_parathread(&self) -> bool {
matches!(self, ParaLifecycle::Parathread)
matches!(self,
ParaLifecycle::Parathread |
ParaLifecycle::UpgradingParathread |
ParaLifecycle::OffboardingParathread
)
}
pub fn is_outgoing(&self) -> bool {
matches!(self, ParaLifecycle::OutgoingParathread | ParaLifecycle::OutgoingParachain)
/// Returns true if para is currently offboarding.
pub fn is_offboarding(&self) -> bool {
matches!(self, ParaLifecycle::OffboardingParathread | ParaLifecycle::OffboardingParachain)
}
/// Returns true if para is in any transitionary state.
pub fn is_transitioning(&self) -> bool {
!Self::is_stable(self)
}
@@ -251,18 +280,10 @@ decl_storage! {
FutureCodeUpgrades get(fn future_code_upgrade_at): map hasher(twox_64_concat) ParaId => Option<T::BlockNumber>;
/// The actual future code of a para.
FutureCode: map hasher(twox_64_concat) ParaId => Option<ValidationCode>;
/// Upcoming paras (chains and threads). These are only updated on session change. Corresponds to an
/// entry in the upcoming-genesis map. Ordered ascending by ParaId.
UpcomingParas get(fn upcoming_paras): Vec<ParaId>;
/// The actions to perform during the start of a specific session index.
ActionsQueue get(fn actions_queue): map hasher(twox_64_concat) SessionIndex => Vec<ParaId>;
/// Upcoming paras instantiation arguments.
UpcomingParasGenesis: map hasher(twox_64_concat) ParaId => Option<ParaGenesisArgs>;
/// Paras that are to be cleaned up at the end of the session. Ordered ascending by ParaId.
OutgoingParas get(fn outgoing_paras): Vec<ParaId>;
/// Existing Parathreads that should upgrade to be a Parachain. Ordered ascending by ParaId.
UpcomingUpgrades: Vec<ParaId>;
/// Existing Parachains that should downgrade to be a Parathread. Ordered ascending by ParaId.
UpcomingDowngrades: Vec<ParaId>;
}
add_extra_genesis {
config(paras): Vec<(ParaId, ParaGenesisArgs)>;
@@ -297,7 +318,18 @@ fn build<T: Config>(config: &GenesisConfig<T>) {
}
decl_error! {
pub enum Error for Module<T: Config> { }
pub enum Error for Module<T: Config> {
/// Para is not registered in our system.
NotRegistered,
/// Para cannot be onboarded because it is already tracked by our system.
CannotOnboard,
/// Para cannot be offboarded at this time.
CannotOffboard,
/// Para cannot be upgraded to a parachain.
CannotUpgrade,
/// Para cannot be downgraded to a parathread.
CannotDowngrade,
}
}
decl_module! {
@@ -318,109 +350,85 @@ impl<T: Config> Module<T> {
/// Called by the initializer to note that a new session has started.
///
/// Returns the list of outgoing parachains for this session.
pub(crate) fn initializer_on_new_session(_notification: &SessionChangeNotification<T::BlockNumber>)
-> Vec<ParaId>
{
let now = <frame_system::Module<T>>::block_number();
let (mut parachains, outgoing) = Self::clean_up_outgoing(now);
Self::apply_incoming(&mut parachains);
Self::apply_upgrades(&mut parachains);
Self::apply_downgrades(&mut parachains);
<Self as Store>::Parachains::set(parachains);
outgoing
/// Returns the list of outgoing paras from the actions queue.
pub(crate) fn initializer_on_new_session(notification: &SessionChangeNotification<T::BlockNumber>) -> Vec<ParaId> {
let outgoing_paras = Self::apply_actions_queue(notification.session_index);
outgoing_paras
}
/// Cleans up all outgoing paras. Returns the new set of parachains and any outgoing parachains.
fn clean_up_outgoing(now: T::BlockNumber) -> (Vec<ParaId>, Vec<ParaId>) {
// Apply all para actions queued for the given session index.
//
// The actions to take are based on the lifecycle of of the paras.
//
// The final state of any para after the actions queue should be as a
// parachain, parathread, or not registered. (stable states)
//
// Returns the list of outgoing paras from the actions queue.
fn apply_actions_queue(session: SessionIndex) -> Vec<ParaId> {
let actions = ActionsQueue::take(session);
let mut parachains = <Self as Store>::Parachains::get();
let outgoing = <Self as Store>::OutgoingParas::take();
let now = <frame_system::Module<T>>::block_number();
let mut outgoing = Vec::new();
for outgoing_para in &outgoing {
// Warn if there is a state error... but still perform the offboarding to be defensive.
if let Some(state) = ParaLifecycles::get(&outgoing_para) {
if !state.is_outgoing() {
frame_support::debug::error!(
target: "parachains",
"Outgoing parachain has wrong lifecycle state."
)
}
};
for para in actions {
let lifecycle = ParaLifecycles::get(&para);
match lifecycle {
None | Some(ParaLifecycle::Parathread) | Some(ParaLifecycle::Parachain) => { /* Nothing to do... */ },
// Onboard a new parathread or parachain.
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::insert(&para, ParaLifecycle::Parachain);
} else {
ParaLifecycles::insert(&para, ParaLifecycle::Parathread);
}
if let Ok(i) = parachains.binary_search(&outgoing_para) {
parachains.remove(i);
}
<Self as Store>::Heads::remove(&outgoing_para);
<Self as Store>::FutureCodeUpgrades::remove(&outgoing_para);
<Self as Store>::FutureCode::remove(&outgoing_para);
ParaLifecycles::remove(&outgoing_para);
let removed_code = <Self as Store>::CurrentCode::take(&outgoing_para);
if let Some(removed_code) = removed_code {
Self::note_past_code(*outgoing_para, now, now, removed_code);
}
}
(parachains, outgoing)
}
/// Applies all incoming paras, updating the parachains list for those that are parachains.
fn apply_incoming(parachains: &mut Vec<ParaId>) {
let upcoming = <Self as Store>::UpcomingParas::take();
for upcoming_para in upcoming {
if ParaLifecycles::get(&upcoming_para) != Some(ParaLifecycle::Onboarding) {
continue;
};
let genesis_data = match <Self as Store>::UpcomingParasGenesis::take(&upcoming_para) {
None => continue,
Some(g) => g,
};
if genesis_data.parachain {
if let Err(i) = parachains.binary_search(&upcoming_para) {
parachains.insert(i, upcoming_para);
}
ParaLifecycles::insert(&upcoming_para, ParaLifecycle::Parachain);
} else {
ParaLifecycles::insert(&upcoming_para, ParaLifecycle::Parathread);
}
<Self as Store>::Heads::insert(&upcoming_para, genesis_data.genesis_head);
<Self as Store>::CurrentCode::insert(&upcoming_para, genesis_data.validation_code);
}
}
/// Take an existing parathread and upgrade it to be a parachain.
fn apply_upgrades(parachains: &mut Vec<ParaId>) {
let upgrades = UpcomingUpgrades::take();
for para in upgrades {
ParaLifecycles::mutate(&para, |state| {
if *state == Some(ParaLifecycle::UpgradingToParachain) {
<Self as Store>::Heads::insert(&para, genesis_data.genesis_head);
<Self as Store>::CurrentCode::insert(&para, genesis_data.validation_code);
}
},
// Upgrade a parathread to a parachain
Some(ParaLifecycle::UpgradingParathread) => {
if let Err(i) = parachains.binary_search(&para) {
parachains.insert(i, para);
}
*state = Some(ParaLifecycle::Parachain);
}
});
}
}
/// Take an existing parachain and downgrade it to be a parathread. Update the list of parachains.
fn apply_downgrades(parachains: &mut Vec<ParaId>) {
let downgrades = UpcomingDowngrades::take();
for para in downgrades {
ParaLifecycles::mutate(&para, |state| {
if *state == Some(ParaLifecycle::DowngradingToParathread) {
ParaLifecycles::insert(&para, ParaLifecycle::Parachain);
},
// Downgrade a parachain to a parathread
Some(ParaLifecycle::DowngradingParachain) => {
if let Ok(i) = parachains.binary_search(&para) {
parachains.remove(i);
}
*state = Some(ParaLifecycle::Parathread);
}
});
ParaLifecycles::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);
}
<Self as Store>::Heads::remove(&para);
<Self as Store>::FutureCodeUpgrades::remove(&para);
<Self as Store>::FutureCode::remove(&para);
ParaLifecycles::remove(&para);
let removed_code = <Self as Store>::CurrentCode::take(&para);
if let Some(removed_code) = removed_code {
Self::note_past_code(para, now, now, removed_code);
}
outgoing.push(para);
},
}
}
// Place the new parachains set in storage.
<Self as Store>::Parachains::set(parachains);
return outgoing
}
// note replacement of the code of para with given `id`, which occured in the
@@ -500,195 +508,98 @@ impl<T: Config> Module<T> {
T::DbWeight::get().reads_writes(1 + pruning_tasks_done, 2 * pruning_tasks_done)
}
/// Verify that `schedule_para_initialize` can be called successfully.
///
/// Returns false if para is already registered in the system.
pub fn can_schedule_para_initialize(id: &ParaId, _: &ParaGenesisArgs) -> bool {
let lifecycle = ParaLifecycles::get(id);
lifecycle.is_none()
}
/// Schedule a para to be initialized at the start of the next session.
///
/// Noop if Para ID is already registered in the system with some `ParaLifecycle`.
pub(crate) fn schedule_para_initialize(id: ParaId, genesis: ParaGenesisArgs) -> Weight {
let mut weight = T::DbWeight::get().reads_writes(0, 0);
/// Will return error if para is already registered in the system.
pub(crate) fn schedule_para_initialize(id: ParaId, genesis: ParaGenesisArgs) -> DispatchResult {
let scheduled_session = Self::scheduled_session();
// Make sure parachain isn't already in our system.
if ParaLifecycles::contains_key(&id) {
weight = weight.saturating_add(T::DbWeight::get().reads(1));
return weight;
}
ensure!(Self::can_schedule_para_initialize(&id, &genesis), Error::<T>::CannotOnboard);
let dup = UpcomingParas::mutate(|v| {
match v.binary_search(&id) {
Ok(_) => true,
Err(i) => {
v.insert(i, id);
false
}
ParaLifecycles::insert(&id, ParaLifecycle::Onboarding);
UpcomingParasGenesis::insert(&id, genesis);
ActionsQueue::mutate(scheduled_session, |v| {
if let Err(i) = v.binary_search(&id) {
v.insert(i, id);
}
});
ParaLifecycles::insert(&id, ParaLifecycle::Onboarding);
weight = weight.saturating_add(T::DbWeight::get().writes(1));
if dup {
weight = weight.saturating_add(T::DbWeight::get().reads(1));
return weight;
}
weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
UpcomingParasGenesis::insert(&id, &genesis);
weight = weight.saturating_add(T::DbWeight::get().writes(2));
weight
Ok(())
}
/// Schedule a para to be cleaned up at the start of the next session.
///
/// Noop if para is already outgoing or not known.
pub(crate) fn schedule_para_cleanup(id: ParaId) -> Weight {
match ParaLifecycles::get(&id) {
Some(ParaLifecycle::Onboarding) => {
UpcomingParas::mutate(|v| {
match v.binary_search(&id) {
Ok(i) => {
v.remove(i);
UpcomingParasGenesis::remove(&id);
ParaLifecycles::remove(&id);
// If a para was only in the pending state it should not be moved to `Outgoing`
T::DbWeight::get().reads_writes(1, 3)
}
Err(_) => T::DbWeight::get().reads_writes(1, 0),
}
})
},
Some(ParaLifecycle::Parathread) => {
ParaLifecycles::insert(&id, ParaLifecycle::OutgoingParathread);
OutgoingParas::mutate(|v| {
match v.binary_search(&id) {
Ok(_) => T::DbWeight::get().reads_writes(1, 1),
Err(i) => {
v.insert(i, id);
T::DbWeight::get().reads_writes(1, 2)
}
}
})
/// Will return error if para is not a stable parachain or parathread.
pub(crate) fn schedule_para_cleanup(id: ParaId) -> DispatchResult {
let scheduled_session = Self::scheduled_session();
let lifecycle = ParaLifecycles::get(&id).ok_or(Error::<T>::NotRegistered)?;
match lifecycle {
ParaLifecycle::Parathread => {
ParaLifecycles::insert(&id, ParaLifecycle::OffboardingParathread);
},
Some(ParaLifecycle::Parachain) => {
OutgoingParas::mutate(|v| {
match v.binary_search(&id) {
Ok(_) => T::DbWeight::get().reads_writes(1, 0),
Err(i) => {
v.insert(i, id);
ParaLifecycles::insert(&id, ParaLifecycle::OutgoingParachain);
T::DbWeight::get().reads_writes(1, 2)
}
}
})
ParaLifecycle::Parachain => {
ParaLifecycles::insert(&id, ParaLifecycle::OffboardingParachain);
},
Some(ParaLifecycle::UpgradingToParachain) => {
let upgrade_weight = UpcomingUpgrades::mutate(|v| {
match v.binary_search(&id) {
Ok(i) => {
v.remove(i);
T::DbWeight::get().reads_writes(1, 1)
},
Err(_) => T::DbWeight::get().reads(1),
}
});
let outgoing_weight = OutgoingParas::mutate(|v| {
match v.binary_search(&id) {
Ok(_) => T::DbWeight::get().reads_writes(1, 0),
Err(i) => {
v.insert(i, id);
ParaLifecycles::insert(&id, ParaLifecycle::OutgoingParathread);
T::DbWeight::get().reads_writes(1, 2)
}
}
});
upgrade_weight.saturating_add(outgoing_weight)
},
Some(ParaLifecycle::DowngradingToParathread) => {
let downgrade_weight = UpcomingDowngrades::mutate(|v| {
match v.binary_search(&id) {
Ok(i) => {
v.remove(i);
T::DbWeight::get().reads_writes(1, 1)
},
Err(_) => T::DbWeight::get().reads(1),
}
});
let outgoing_weight = OutgoingParas::mutate(|v| {
match v.binary_search(&id) {
Ok(_) => T::DbWeight::get().reads_writes(1, 0),
Err(i) => {
v.insert(i, id);
ParaLifecycles::insert(&id, ParaLifecycle::OutgoingParathread);
T::DbWeight::get().reads_writes(1, 2)
}
}
});
downgrade_weight.saturating_add(outgoing_weight)
},
None |
Some(ParaLifecycle::OutgoingParathread) |
Some(ParaLifecycle::OutgoingParachain)
=> { T::DbWeight::get().reads(1) },
_ => return Err(Error::<T>::CannotOffboard)?,
}
ActionsQueue::mutate(scheduled_session, |v| {
if let Err(i) = v.binary_search(&id) {
v.insert(i, id);
}
});
Ok(())
}
/// Schedule a parathread to be upgraded to a parachain.
///
/// Noop if `ParaLifecycle` is not `Parathread`.
/// Will return error if `ParaLifecycle` is not `Parathread`.
#[allow(unused)]
pub(crate) fn schedule_parathread_upgrade(id: ParaId) -> Weight {
if ParaLifecycles::get(&id) != Some(ParaLifecycle::Parathread) {
let weight = T::DbWeight::get().reads_writes(1, 0);
return weight;
}
pub(crate) fn schedule_parathread_upgrade(id: ParaId) -> DispatchResult {
let scheduled_session = Self::scheduled_session();
let lifecycle = ParaLifecycles::get(&id).ok_or(Error::<T>::NotRegistered)?;
let dup = UpcomingUpgrades::mutate(|v| {
match v.binary_search(&id) {
Ok(_) => true,
Err(i) => {
v.insert(i, id);
false
}
ensure!(lifecycle == ParaLifecycle::Parathread, Error::<T>::CannotUpgrade);
ParaLifecycles::insert(&id, ParaLifecycle::UpgradingParathread);
ActionsQueue::mutate(scheduled_session, |v| {
if let Err(i) = v.binary_search(&id) {
v.insert(i, id);
}
});
ParaLifecycles::insert(&id, ParaLifecycle::UpgradingToParachain);
if dup {
let weight = T::DbWeight::get().reads_writes(2, 1);
return weight;
}
T::DbWeight::get().reads_writes(2, 2)
Ok(())
}
/// Schedule a parachain to be downgraded to a parathread.
///
/// Noop if `ParaLifecycle` is not `Parachain`.
#[allow(unused)]
pub(crate) fn schedule_parachain_downgrade(id: ParaId) -> Weight {
if ParaLifecycles::get(&id) != Some(ParaLifecycle::Parachain) {
let weight = T::DbWeight::get().reads_writes(1, 0);
return weight;
}
pub(crate) fn schedule_parachain_downgrade(id: ParaId) -> DispatchResult {
let scheduled_session = Self::scheduled_session();
let lifecycle = ParaLifecycles::get(&id).ok_or(Error::<T>::NotRegistered)?;
let dup = UpcomingDowngrades::mutate(|v| {
match v.binary_search(&id) {
Ok(_) => true,
Err(i) => {
v.insert(i, id);
false
}
ensure!(lifecycle == ParaLifecycle::Parachain, Error::<T>::CannotDowngrade);
ParaLifecycles::insert(&id, ParaLifecycle::DowngradingParachain);
ActionsQueue::mutate(scheduled_session, |v| {
if let Err(i) = v.binary_search(&id) {
v.insert(i, id);
}
});
ParaLifecycles::insert(&id, ParaLifecycle::DowngradingToParathread);
if dup {
let weight = T::DbWeight::get().reads_writes(2, 1);
return weight;
}
T::DbWeight::get().reads_writes(2, 2)
Ok(())
}
/// Schedule a future code upgrade of the given parachain, to be applied after inclusion
@@ -794,9 +705,11 @@ impl<T: Config> Module<T> {
}
/// Returns whether the given ID refers to a valid para.
///
/// Paras that are onboarding or offboarding are not included.
pub fn is_valid_para(id: ParaId) -> bool {
if let Some(state) = ParaLifecycles::get(&id) {
!state.is_onboarding() && !state.is_outgoing()
!state.is_onboarding() && !state.is_offboarding()
} else {
false
}
@@ -835,29 +748,42 @@ impl<T: Config> Module<T> {
Self::past_code_meta(&id).most_recent_change()
}
/// Return the session index that should be used for any future scheduled changes.
fn scheduled_session() -> SessionIndex {
shared::Module::<T>::scheduled_session()
}
}
#[cfg(test)]
mod tests {
use super::*;
use primitives::v1::BlockNumber;
use frame_support::traits::{OnFinalize, OnInitialize};
use frame_support::{
assert_ok,
traits::{OnFinalize, OnInitialize}
};
use crate::mock::{new_test_ext, Paras, System, MockGenesisConfig};
use crate::mock::{new_test_ext, Paras, Shared, System, MockGenesisConfig};
use crate::configuration::HostConfiguration;
fn run_to_block(to: BlockNumber, new_session: Option<Vec<BlockNumber>>) {
while System::block_number() < to {
let b = System::block_number();
Paras::initializer_finalize();
Shared::initializer_finalize();
if new_session.as_ref().map_or(false, |v| v.contains(&(b + 1))) {
Paras::initializer_on_new_session(&Default::default());
let mut session_change_notification = SessionChangeNotification::default();
session_change_notification.session_index = Shared::session_index() + 1;
Shared::initializer_on_new_session(&session_change_notification);
Paras::initializer_on_new_session(&session_change_notification);
}
System::on_finalize(b);
System::on_initialize(b + 1);
System::set_block_number(b + 1);
Shared::initializer_initialize(b + 1);
Paras::initializer_initialize(b + 1);
}
}
@@ -1316,11 +1242,14 @@ mod tests {
expected_at
};
Paras::schedule_para_cleanup(para_id);
assert_ok!(Paras::schedule_para_cleanup(para_id));
// Just scheduling cleanup shouldn't change anything.
{
assert_eq!(<Paras as Store>::OutgoingParas::get(), vec![para_id]);
assert_eq!(
<Paras as Store>::ActionsQueue::get(Paras::scheduled_session()),
vec![para_id],
);
assert_eq!(Paras::parachains(), vec![para_id]);
assert!(Paras::past_code_meta(&para_id).most_recent_change().is_none());
@@ -1331,8 +1260,8 @@ mod tests {
assert_eq!(<Paras as Store>::Heads::get(&para_id), Some(Default::default()));
}
// run to block 4, with a session change at the end of the block 3.
run_to_block(4, Some(vec![4]));
// run to block #4, with a 2 session changes at the end of the block 2 & 3.
run_to_block(4, Some(vec![3,4]));
// cleaning up the parachain should place the current parachain code
// into the past code buffer & schedule cleanup.
@@ -1366,34 +1295,37 @@ mod tests {
let a = ParaId::from(999);
let c = ParaId::from(333);
Paras::schedule_para_initialize(
assert_ok!(Paras::schedule_para_initialize(
b,
ParaGenesisArgs {
parachain: true,
genesis_head: vec![1].into(),
validation_code: vec![1].into(),
},
);
));
Paras::schedule_para_initialize(
assert_ok!(Paras::schedule_para_initialize(
a,
ParaGenesisArgs {
parachain: false,
genesis_head: vec![2].into(),
validation_code: vec![2].into(),
},
);
));
Paras::schedule_para_initialize(
assert_ok!(Paras::schedule_para_initialize(
c,
ParaGenesisArgs {
parachain: true,
genesis_head: vec![3].into(),
validation_code: vec![3].into(),
},
);
));
assert_eq!(<Paras as Store>::UpcomingParas::get(), vec![c, b, a]);
assert_eq!(
<Paras as Store>::ActionsQueue::get(Paras::scheduled_session()),
vec![c, b, a],
);
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::Onboarding));
@@ -1404,7 +1336,10 @@ mod tests {
run_to_block(2, None);
assert_eq!(Paras::parachains(), Vec::new());
assert_eq!(<Paras as Store>::UpcomingParas::get(), vec![c, b, a]);
assert_eq!(
<Paras as Store>::ActionsQueue::get(Paras::scheduled_session()),
vec![c, b, a],
);
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::Onboarding));
@@ -1412,10 +1347,11 @@ mod tests {
assert_eq!(ParaLifecycles::get(&c), Some(ParaLifecycle::Onboarding));
run_to_block(3, Some(vec![3]));
// Two sessions pass, so action queue is triggered
run_to_block(4, Some(vec![3,4]));
assert_eq!(Paras::parachains(), vec![c, b]);
assert_eq!(<Paras as Store>::UpcomingParas::get(), Vec::new());
assert_eq!(<Paras as Store>::ActionsQueue::get(Paras::scheduled_session()), Vec::new());
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::Parathread));
@@ -1428,81 +1364,6 @@ mod tests {
})
}
#[test]
fn para_cleanup_removes_upcoming() {
new_test_ext(Default::default()).execute_with(|| {
run_to_block(1, None);
let b = ParaId::from(525);
let a = ParaId::from(999);
let c = ParaId::from(333);
Paras::schedule_para_initialize(
b,
ParaGenesisArgs {
parachain: true,
genesis_head: vec![1].into(),
validation_code: vec![1].into(),
},
);
Paras::schedule_para_initialize(
a,
ParaGenesisArgs {
parachain: false,
genesis_head: vec![2].into(),
validation_code: vec![2].into(),
},
);
Paras::schedule_para_initialize(
c,
ParaGenesisArgs {
parachain: true,
genesis_head: vec![3].into(),
validation_code: vec![3].into(),
},
);
assert_eq!(<Paras as Store>::UpcomingParas::get(), vec![c, b, a]);
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::Onboarding));
assert_eq!(ParaLifecycles::get(&b), Some(ParaLifecycle::Onboarding));
assert_eq!(ParaLifecycles::get(&c), Some(ParaLifecycle::Onboarding));
// run to block without session change.
run_to_block(2, None);
assert_eq!(Paras::parachains(), Vec::new());
assert_eq!(<Paras as Store>::UpcomingParas::get(), vec![c, b, a]);
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::Onboarding));
assert_eq!(ParaLifecycles::get(&b), Some(ParaLifecycle::Onboarding));
assert_eq!(ParaLifecycles::get(&c), Some(ParaLifecycle::Onboarding));
Paras::schedule_para_cleanup(c);
run_to_block(3, Some(vec![3]));
assert_eq!(Paras::parachains(), vec![b]);
assert_eq!(<Paras as Store>::OutgoingParas::get(), vec![]);
assert_eq!(<Paras as Store>::UpcomingParas::get(), Vec::new());
assert!(<Paras as Store>::UpcomingParasGenesis::get(a).is_none());
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::Parathread));
assert_eq!(ParaLifecycles::get(&b), Some(ParaLifecycle::Parachain));
assert_eq!(ParaLifecycles::get(&c), None);
assert_eq!(Paras::current_code(&a), Some(vec![2].into()));
assert_eq!(Paras::current_code(&b), Some(vec![1].into()));
assert!(Paras::current_code(&c).is_none());
});
}
#[test]
fn code_at_with_intermediate() {
let acceptance_period = 10;