Commit 4177af7b authored by Shawn Tabrizi's avatar Shawn Tabrizi
Browse files

explicit lifecycle tracking for paras

parent addbeb20
Pipeline #122423 failed with stages
in 6 minutes and 1 second
......@@ -44,6 +44,8 @@ mod mock;
pub use origin::{Origin, ensure_parachain};
use primitives::v1::Id as ParaId;
use parity_scale_codec::{Encode, Decode};
use sp_core::RuntimeDebug;
/// Schedule a para to be initialized at the start of the next session with the given genesis data.
pub fn schedule_para_initialize<T: paras::Config>(
......@@ -85,3 +87,22 @@ pub fn schedule_para_upgrade<T: paras::Config>(id: ParaId) {
pub fn schedule_para_downgrade<T: paras::Config>(id: ParaId) {
paras::Module::<T>::schedule_para_downgrade(id);
}
/// The possible states of a para, to take into account delayed lifecycle changes.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
pub enum ParaLifecycle {
/// Para ID is new and is onboarding as a Parathread.
OnboardingAsParathread,
/// Para ID is new and is onboarding as a Parachain.
OnboardingAsParachain,
/// Para ID is a Parathread.
Parathread,
/// Para ID is a Parachain.
Parachain,
/// Para ID is a Parathread which is upgrading to a Parachain.
UpgradingToParachain,
/// Para ID is a Parachain which is downgrading to a Parathread.
DowngradingToParathread,
/// Para ID is being offboarded.
Outgoing,
}
......@@ -37,7 +37,7 @@ use frame_support::{
weights::Weight,
};
use parity_scale_codec::{Encode, Decode};
use crate::{configuration, initializer::SessionChangeNotification};
use crate::{configuration, initializer::SessionChangeNotification, ParaLifecycle};
use sp_core::RuntimeDebug;
#[cfg(feature = "std")]
......@@ -180,8 +180,8 @@ decl_storage! {
trait Store for Module<T: Config> as Paras {
/// All parachains. Ordered ascending by ParaId. Parathreads are not included.
Parachains get(fn parachains): Vec<ParaId>;
/// All parathreads.
Parathreads: map hasher(twox_64_concat) ParaId => Option<()>;
/// The current lifecycle of a all known Para IDs.
ParaLifecycles: map hasher(twox_64_concat) ParaId => Option<ParaLifecycle>;
/// The head-data of every registered para.
Heads get(fn para_head): map hasher(twox_64_concat) ParaId => Option<HeadData>;
/// The validation code of every live para.
......@@ -285,13 +285,12 @@ impl<T: Config> Module<T> {
for outgoing_para in outgoing {
if let Ok(i) = parachains.binary_search(&outgoing_para) {
parachains.remove(i);
} else {
<Self as Store>::Parathreads::remove(&outgoing_para);
}
<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 {
......@@ -313,10 +312,11 @@ impl<T: Config> Module<T> {
if genesis_data.parachain {
if let Err(i) = parachains.binary_search(&upcoming_para) {
ParaLifecycles::insert(&upcoming_para, ParaLifecycle::Parachain);
parachains.insert(i, upcoming_para);
}
} else {
<Self as Store>::Parathreads::insert(&upcoming_para, ());
ParaLifecycles::insert(&upcoming_para, ParaLifecycle::Parathread);
}
<Self as Store>::Heads::insert(&upcoming_para, genesis_data.genesis_head);
......@@ -328,11 +328,15 @@ impl<T: Config> Module<T> {
fn apply_upgrades(parachains: &mut Vec<ParaId>) {
let upgrades = UpcomingUpgrades::take();
for para in upgrades {
if Parathreads::take(&para).is_some() {
if let Err(i) = parachains.binary_search(&para) {
parachains.insert(i, para);
ParaLifecycles::mutate(&para, |v| {
if *v == Some(ParaLifecycle::UpgradingToParachain) {
if let Err(i) = parachains.binary_search(&para) {
ParaLifecycles::insert(&para, ParaLifecycle::Parachain);
parachains.insert(i, para);
}
*v = Some(ParaLifecycle::Parachain);
}
}
});
}
}
......@@ -342,7 +346,7 @@ impl<T: Config> Module<T> {
for para in downgrades {
if let Ok(i) = parachains.binary_search(&para) {
parachains.remove(i);
Parathreads::insert(&para, ());
ParaLifecycles::insert(&para, ParaLifecycle::Parathread);
}
}
}
......@@ -441,6 +445,12 @@ impl<T: Config> Module<T> {
return weight;
}
if genesis.parachain {
ParaLifecycles::insert(&id, ParaLifecycle::OnboardingAsParachain);
} else {
ParaLifecycles::insert(&id, ParaLifecycle::OnboardingAsParathread);
}
UpcomingParasGenesis::insert(&id, &genesis);
T::DbWeight::get().reads_writes(1, 2)
......@@ -452,9 +462,10 @@ impl<T: Config> Module<T> {
match v.binary_search(&id) {
Ok(i) => {
v.remove(i);
UpcomingParasGenesis::remove(id);
UpcomingParasGenesis::remove(&id);
ParaLifecycles::remove(&id);
// If a para was only in the pending state it should not be moved to `Outgoing`
return T::DbWeight::get().reads_writes(2, 2);
return T::DbWeight::get().reads_writes(2, 3);
}
Err(_) => T::DbWeight::get().reads_writes(1, 0),
}
......@@ -465,7 +476,8 @@ impl<T: Config> Module<T> {
Ok(_) => T::DbWeight::get().reads_writes(1, 0),
Err(i) => {
v.insert(i, id);
T::DbWeight::get().reads_writes(1, 1)
ParaLifecycles::insert(&id, ParaLifecycle::Outgoing);
T::DbWeight::get().reads_writes(1, 2)
}
}
});
......@@ -474,15 +486,57 @@ impl<T: Config> Module<T> {
}
#[allow(dead_code)]
pub(crate) fn schedule_para_upgrade(para: ParaId) -> Weight {
UpcomingUpgrades::append(para);
T::DbWeight::get().writes(1)
pub(crate) fn schedule_para_upgrade(id: ParaId) -> Weight {
if ParaLifecycles::get(&id) != Some(ParaLifecycle::Parathread) {
let weight = T::DbWeight::get().reads_writes(1, 0);
return weight;
}
let dup = UpcomingUpgrades::mutate(|v| {
match v.binary_search(&id) {
Ok(_) => true,
Err(i) => {
v.insert(i, id);
false
}
}
});
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)
}
#[allow(dead_code)]
pub(crate) fn schedule_para_downgrade(para: ParaId) -> Weight {
UpcomingDowngrades::append(para);
T::DbWeight::get().writes(1)
pub(crate) fn schedule_para_downgrade(id: ParaId) -> Weight {
if ParaLifecycles::get(&id) != Some(ParaLifecycle::Parachain) {
let weight = T::DbWeight::get().reads_writes(1, 0);
return weight;
}
let dup = UpcomingDowngrades::mutate(|v| {
match v.binary_search(&id) {
Ok(_) => true,
Err(i) => {
v.insert(i, id);
false
}
}
});
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)
}
/// Schedule a future code upgrade of the given parachain, to be applied after inclusion
......@@ -584,22 +638,30 @@ impl<T: Config> Module<T> {
/// Returns whether the given ID refers to a valid para.
pub fn is_valid_para(id: ParaId) -> bool {
Self::is_parachain(id) || Self::is_parathread(id)
match ParaLifecycles::get(&id) {
Some(ParaLifecycle::Parachain) |
Some(ParaLifecycle::Parathread) |
Some(ParaLifecycle::UpgradingToParachain) |
Some(ParaLifecycle::DowngradingToParathread)
=> true,
_ => false,
}
}
/// Whether a para ID corresponds to any live parathread.
pub fn is_parachain(id: ParaId) -> bool {
Parachains::get().binary_search(&id).is_ok()
match ParaLifecycles::get(&id) {
Some(ParaLifecycle::Parachain) => true,
_ => false,
}
}
/// Whether a para ID corresponds to any live parathread.
pub fn is_parathread(id: ParaId) -> bool {
Parathreads::get(&id).is_some()
}
/// Wether a para ID is in in the onboarding queue.
pub fn is_upcoming(id: ParaId) -> bool {
UpcomingParas::get().binary_search(&id).is_ok()
match ParaLifecycles::get(&id) {
Some(ParaLifecycle::Parathread) => true,
_ => false,
}
}
/// The block number of the last scheduled upgrade of the requested para. Includes future upgrades
......@@ -1178,15 +1240,22 @@ mod tests {
);
assert_eq!(<Paras as Store>::UpcomingParas::get(), vec![c, b, a]);
assert!(<Paras as Store>::Parathreads::get(&a).is_none());
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::OnboardingAsParathread));
assert_eq!(ParaLifecycles::get(&b), Some(ParaLifecycle::OnboardingAsParachain));
assert_eq!(ParaLifecycles::get(&c), Some(ParaLifecycle::OnboardingAsParachain));
// 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]);
assert!(<Paras as Store>::Parathreads::get(&a).is_none());
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::OnboardingAsParathread));
assert_eq!(ParaLifecycles::get(&b), Some(ParaLifecycle::OnboardingAsParachain));
assert_eq!(ParaLifecycles::get(&c), Some(ParaLifecycle::OnboardingAsParachain));
run_to_block(3, Some(vec![3]));
......@@ -1194,7 +1263,10 @@ mod tests {
assert_eq!(Paras::parachains(), vec![c, b]);
assert_eq!(<Paras as Store>::UpcomingParas::get(), Vec::new());
assert!(<Paras as Store>::Parathreads::get(&a).is_some());
// 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), Some(ParaLifecycle::Parachain));
assert_eq!(Paras::current_code(&a), Some(vec![2].into()));
assert_eq!(Paras::current_code(&b), Some(vec![1].into()));
......@@ -1239,7 +1311,11 @@ mod tests {
);
assert_eq!(<Paras as Store>::UpcomingParas::get(), vec![c, b, a]);
assert!(<Paras as Store>::Parathreads::get(&a).is_none());
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::OnboardingAsParathread));
assert_eq!(ParaLifecycles::get(&b), Some(ParaLifecycle::OnboardingAsParachain));
assert_eq!(ParaLifecycles::get(&c), Some(ParaLifecycle::OnboardingAsParachain));
// run to block without session change.
......@@ -1247,7 +1323,11 @@ mod tests {
assert_eq!(Paras::parachains(), Vec::new());
assert_eq!(<Paras as Store>::UpcomingParas::get(), vec![c, b, a]);
assert!(<Paras as Store>::Parathreads::get(&a).is_none());
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::OnboardingAsParathread));
assert_eq!(ParaLifecycles::get(&b), Some(ParaLifecycle::OnboardingAsParachain));
assert_eq!(ParaLifecycles::get(&c), Some(ParaLifecycle::OnboardingAsParachain));
Paras::schedule_para_cleanup(c);
......@@ -1258,7 +1338,10 @@ mod tests {
assert_eq!(<Paras as Store>::UpcomingParas::get(), Vec::new());
assert!(<Paras as Store>::UpcomingParasGenesis::get(a).is_none());
assert!(<Paras as Store>::Parathreads::get(&a).is_some());
// 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()));
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment