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
@@ -29,6 +29,7 @@ use frame_support::{
use parity_scale_codec::{Encode, Decode};
use frame_system::ensure_root;
use sp_runtime::traits::Zero;
use crate::shared;
/// All configuration of the runtime with respect to parachains and parathreads.
#[derive(Clone, Encode, Decode, PartialEq, sp_core::RuntimeDebug)]
@@ -231,14 +232,14 @@ impl<BlockNumber: Zero> HostConfiguration<BlockNumber> {
}
}
pub trait Config: frame_system::Config { }
pub trait Config: frame_system::Config + shared::Config { }
decl_storage! {
trait Store for Module<T: Config> as Configuration {
/// The active configuration for the current session.
ActiveConfig get(fn config) config(): HostConfiguration<T::BlockNumber>;
/// Pending configuration (if any) for the next session.
PendingConfig: Option<HostConfiguration<T::BlockNumber>>;
PendingConfig: map hasher(twox_64_concat) SessionIndex => Option<HostConfiguration<T::BlockNumber>>;
}
add_extra_genesis {
build(|config: &Self| {
@@ -646,12 +647,21 @@ impl<T: Config> Module<T> {
pub(crate) fn initializer_finalize() { }
/// Called by the initializer to note that a new session has started.
pub(crate) fn initializer_on_new_session(_validators: &[ValidatorId], _queued: &[ValidatorId]) {
if let Some(pending) = <Self as Store>::PendingConfig::take() {
pub(crate) fn initializer_on_new_session(
_validators: &[ValidatorId],
_queued: &[ValidatorId],
session_index: &SessionIndex,
) {
if let Some(pending) = <Self as Store>::PendingConfig::take(session_index) {
<Self as Store>::ActiveConfig::set(pending);
}
}
/// Return the session index that should be used for any future scheduled changes.
fn scheduled_session() -> SessionIndex {
shared::Module::<T>::scheduled_session()
}
// NOTE: Explicitly tell rustc not to inline this because otherwise heuristics note the incoming
// closure making it's attractive to inline. However, in this case, we will end up with lots of
// duplicated code (making this function to show up in the top of heaviest functions) only for
@@ -660,11 +670,12 @@ impl<T: Config> Module<T> {
fn update_config_member(
updater: impl FnOnce(&mut HostConfiguration<T::BlockNumber>) -> bool,
) {
let pending = <Self as Store>::PendingConfig::get();
let scheduled_session = Self::scheduled_session();
let pending = <Self as Store>::PendingConfig::get(scheduled_session);
let mut prev = pending.unwrap_or_else(Self::config);
if updater(&mut prev) {
<Self as Store>::PendingConfig::set(Some(prev));
<Self as Store>::PendingConfig::insert(scheduled_session, prev);
}
}
}
@@ -672,32 +683,32 @@ impl<T: Config> Module<T> {
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{new_test_ext, Initializer, Configuration, Origin};
use crate::mock::{new_test_ext, Configuration, Origin};
use frame_support::traits::{OnFinalize, OnInitialize};
use frame_support::assert_ok;
#[test]
fn config_changes_on_session_boundary() {
fn config_changes_after_2_session_boundary() {
new_test_ext(Default::default()).execute_with(|| {
let old_config = Configuration::config();
let mut config = old_config.clone();
config.validation_upgrade_delay = 100;
assert!(old_config != config);
<Configuration as Store>::PendingConfig::set(Some(config.clone()));
Initializer::on_initialize(1);
assert_ok!(Configuration::set_validation_upgrade_delay(Origin::root(), 100));
assert_eq!(Configuration::config(), old_config);
assert_eq!(<Configuration as Store>::PendingConfig::get(), Some(config.clone()));
assert_eq!(<Configuration as Store>::PendingConfig::get(1), None);
Initializer::on_finalize(1);
Configuration::initializer_on_new_session(&[], &[], &1);
Configuration::initializer_on_new_session(&[], &[]);
assert_eq!(Configuration::config(), old_config);
assert_eq!(<Configuration as Store>::PendingConfig::get(2), Some(config.clone()));
Configuration::initializer_on_new_session(&[], &[], &2);
assert_eq!(Configuration::config(), config);
assert!(<Configuration as Store>::PendingConfig::get().is_none());
assert_eq!(<Configuration as Store>::PendingConfig::get(3), None);
})
}
@@ -743,7 +754,7 @@ mod tests {
hrmp_max_message_num_per_candidate: 20,
};
assert!(<Configuration as Store>::PendingConfig::get().is_none());
assert!(<Configuration as Store>::PendingConfig::get(shared::SESSION_DELAY).is_none());
Configuration::set_validation_upgrade_frequency(
Origin::root(), new_config.validation_upgrade_frequency,
@@ -865,7 +876,7 @@ mod tests {
new_config.hrmp_max_message_num_per_candidate,
).unwrap();
assert_eq!(<Configuration as Store>::PendingConfig::get(), Some(new_config));
assert_eq!(<Configuration as Store>::PendingConfig::get(shared::SESSION_DELAY), Some(new_config));
})
}
@@ -880,7 +891,7 @@ mod tests {
fn setting_config_to_same_as_current_is_noop() {
new_test_ext(Default::default()).execute_with(|| {
Configuration::set_validation_upgrade_delay(Origin::root(), Default::default()).unwrap();
assert!(<Configuration as Store>::PendingConfig::get().is_none())
assert!(<Configuration as Store>::PendingConfig::get(shared::SESSION_DELAY).is_none())
});
}
+29 -25
View File
@@ -1111,9 +1111,9 @@ impl<T: Config> Module<T> {
mod tests {
use super::*;
use crate::mock::{
new_test_ext, Test, Configuration, Paras, Hrmp, System, MockGenesisConfig,
new_test_ext, Test, Configuration, Paras, Shared, Hrmp, System, MockGenesisConfig,
};
use frame_support::{assert_err, traits::Currency as _};
use frame_support::{assert_noop, assert_ok, traits::Currency as _};
use primitives::v1::BlockNumber;
use std::collections::{BTreeMap, HashSet};
@@ -1127,15 +1127,18 @@ mod tests {
// NOTE: this is in reverse initialization order.
Hrmp::initializer_finalize();
Paras::initializer_finalize();
Shared::initializer_finalize();
if new_session.as_ref().map_or(false, |v| v.contains(&(b + 1))) {
let notification = crate::initializer::SessionChangeNotification {
prev_config: config.clone(),
new_config: config.clone(),
session_index: Shared::session_index() + 1,
..Default::default()
};
// NOTE: this is in initialization order.
Shared::initializer_on_new_session(&notification);
let outgoing_paras = Paras::initializer_on_new_session(&notification);
Hrmp::initializer_on_new_session(&notification, &outgoing_paras);
}
@@ -1146,6 +1149,7 @@ mod tests {
System::set_block_number(b + 1);
// NOTE: this is in initialization order.
Shared::initializer_initialize(b + 1);
Paras::initializer_initialize(b + 1);
Hrmp::initializer_initialize(b + 1);
}
@@ -1217,14 +1221,14 @@ mod tests {
}
fn register_parachain_with_balance(id: ParaId, balance: Balance) {
Paras::schedule_para_initialize(
assert_ok!(Paras::schedule_para_initialize(
id,
crate::paras::ParaGenesisArgs {
parachain: true,
genesis_head: vec![1].into(),
validation_code: vec![1].into(),
},
);
));
<Test as Config>::Currency::make_free_balance_be(&id.into_account(), balance);
}
@@ -1233,7 +1237,7 @@ mod tests {
}
fn deregister_parachain(id: ParaId) {
Paras::schedule_para_cleanup(id);
assert_ok!(Paras::schedule_para_cleanup(id));
}
fn channel_exists(sender: ParaId, recipient: ParaId) -> bool {
@@ -1430,7 +1434,7 @@ mod tests {
register_parachain(para_a);
register_parachain(para_b);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
assert_storage_consistency_exhaustive();
@@ -1458,7 +1462,7 @@ mod tests {
register_parachain(para_a);
register_parachain(para_b);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
@@ -1497,7 +1501,7 @@ mod tests {
register_parachain(para_a);
register_parachain(para_b);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 20).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
@@ -1535,17 +1539,17 @@ mod tests {
register_parachain(para_a);
register_parachain(para_b);
run_to_block(1, Some(vec![1]));
run_to_block(2, Some(vec![1,2]));
Hrmp::init_open_channel(para_a, para_b, 2, 20).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
run_to_block(2, Some(vec![2]));
run_to_block(3, Some(vec![3]));
let _ = Hrmp::queue_outbound_hrmp(para_a, vec![OutboundHrmpMessage {
recipient: para_b,
data: vec![1, 2, 3],
}]);
run_to_block(3, None);
run_to_block(4, None);
let _ = Hrmp::queue_outbound_hrmp(para_a, vec![OutboundHrmpMessage {
recipient: para_b,
data: vec![4, 5, 6],
@@ -1554,7 +1558,7 @@ mod tests {
assert_eq!(
Hrmp::hrmp_mqc_heads(para_b),
vec![
(para_a, hex_literal::hex!["88dc00db8cc9d22aa62b87807705831f164387dfa49f80a8600ed1cbe1704b6b"].into()),
(para_a, hex_literal::hex!["a964fd3b4f3d3ce92a0e25e576b87590d92bb5cb7031909c7f29050e1f04a375"].into()),
],
);
});
@@ -1569,13 +1573,13 @@ mod tests {
register_parachain(para_a);
register_parachain(para_b);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
deregister_parachain(para_a);
// On Block 6: session change. The channel should not be created.
run_to_block(6, Some(vec![6]));
// On Block 7: 2x session change. The channel should not be created.
run_to_block(7, Some(vec![6, 7]));
assert!(!Paras::is_valid_para(para_a));
assert!(!channel_exists(para_a, para_b));
assert_storage_consistency_exhaustive();
@@ -1593,7 +1597,7 @@ mod tests {
register_parachain(para_b);
register_parachain(para_c);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
// Open two channels to the same receiver, b:
// a -> b, c -> b
@@ -1662,7 +1666,7 @@ mod tests {
// request and accept that, and finally wait until the next session.
register_parachain(para_a);
register_parachain(para_b);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
run_to_block(8, Some(vec![8]));
@@ -1728,9 +1732,9 @@ mod tests {
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_parachain_with_balance(para_a, 0);
register_parachain(para_b);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
assert_err!(
assert_noop!(
Hrmp::init_open_channel(para_a, para_b, 2, 8),
pallet_balances::Error::<Test, _>::InsufficientBalance
);
@@ -1739,11 +1743,11 @@ mod tests {
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_parachain(para_a);
register_parachain_with_balance(para_b, 0);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
assert_err!(
assert_noop!(
Hrmp::accept_open_channel(para_b, para_a),
pallet_balances::Error::<Test, _>::InsufficientBalance
);
@@ -1762,7 +1766,7 @@ mod tests {
// Register two parachains funded with different amounts of funds and arrange a channel.
register_parachain_with_balance(para_a, 100);
register_parachain_with_balance(para_b, 110);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
assert_eq!(
@@ -1810,7 +1814,7 @@ mod tests {
// request but do not accept it.
register_parachain_with_balance(para_a, 100);
register_parachain_with_balance(para_b, 110);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
assert_eq!(
<Test as Config>::Currency::free_balance(&para_a.into_account()),
@@ -1849,7 +1853,7 @@ mod tests {
// Register two parachains and open a channel between them.
register_parachain_with_balance(para_a, 100);
register_parachain_with_balance(para_b, 110);
run_to_block(5, Some(vec![5]));
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
assert_eq!(
@@ -1865,7 +1869,7 @@ mod tests {
// Then deregister one parachain.
deregister_parachain(para_a);
run_to_block(10, Some(vec![10]));
run_to_block(10, Some(vec![9, 10]));
// The channel should be removed.
assert!(!Paras::is_valid_para(para_a));
+16 -17
View File
@@ -33,10 +33,9 @@ use frame_support::{
};
use parity_scale_codec::{Encode, Decode};
use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec};
use sp_staking::SessionIndex;
use sp_runtime::{DispatchError, traits::{One, Saturating}};
use crate::{configuration, paras, dmp, ump, hrmp, scheduler::CoreAssignment};
use crate::{configuration, paras, dmp, ump, hrmp, shared, scheduler::CoreAssignment};
/// A bitfield signed by a validator indicating that it is keeping its piece of the erasure-coding
/// for any backed candidates referred to by a `1` bit available.
@@ -111,6 +110,7 @@ pub trait RewardValidators {
pub trait Config:
frame_system::Config
+ shared::Config
+ paras::Config
+ dmp::Config
+ ump::Config
@@ -137,9 +137,6 @@ decl_storage! {
/// The current validators, by their parachain session keys.
Validators get(fn validators) config(validators): Vec<ValidatorId>;
/// The current session index.
CurrentSessionIndex get(fn session_index): SessionIndex;
}
}
@@ -236,7 +233,6 @@ impl<T: Config> Module<T> {
for _ in <AvailabilityBitfields<T>>::drain() { }
Validators::set(notification.validators.clone()); // substrate forces us to clone, stupidly.
CurrentSessionIndex::set(notification.session_index);
}
/// Process a set of incoming bitfields. Return a vec of cores freed by candidates
@@ -246,7 +242,7 @@ impl<T: Config> Module<T> {
core_lookup: impl Fn(CoreIndex) -> Option<ParaId>,
) -> Result<Vec<CoreIndex>, DispatchError> {
let validators = Validators::get();
let session_index = CurrentSessionIndex::get();
let session_index = shared::Module::<T>::session_index();
let config = <configuration::Module<T>>::config();
let parachains = <paras::Module<T>>::parachains();
@@ -428,7 +424,7 @@ impl<T: Config> Module<T> {
let signing_context = SigningContext {
parent_hash,
session_index: CurrentSessionIndex::get(),
session_index: shared::Module::<T>::session_index(),
};
// We combine an outer loop over candidates with an inner loop over the scheduled,
@@ -917,7 +913,7 @@ mod tests {
use sc_keystore::LocalKeystore;
use crate::mock::{
new_test_ext, Configuration, Paras, System, Inclusion,
MockGenesisConfig, Test,
MockGenesisConfig, Test, Shared,
};
use crate::initializer::SessionChangeNotification;
use crate::configuration::HostConfiguration;
@@ -1045,8 +1041,10 @@ mod tests {
Inclusion::initializer_finalize();
Paras::initializer_finalize();
Shared::initializer_finalize();
if let Some(notification) = new_session(b + 1) {
Shared::initializer_on_new_session(&notification);
Paras::initializer_on_new_session(&notification);
Inclusion::initializer_on_new_session(&notification);
}
@@ -1056,6 +1054,7 @@ mod tests {
System::on_initialize(b + 1);
System::set_block_number(b + 1);
Shared::initializer_initialize(b + 1);
Paras::initializer_initialize(b + 1);
Inclusion::initializer_initialize(b + 1);
}
@@ -1218,7 +1217,7 @@ mod tests {
new_test_ext(genesis_config(paras)).execute_with(|| {
Validators::set(validator_public.clone());
CurrentSessionIndex::set(5);
shared::Module::<Test>::set_session_index(5);
let signing_context = SigningContext {
parent_hash: System::parent_hash(),
@@ -1425,7 +1424,7 @@ mod tests {
new_test_ext(genesis_config(paras)).execute_with(|| {
Validators::set(validator_public.clone());
CurrentSessionIndex::set(5);
shared::Module::<Test>::set_session_index(5);
let signing_context = SigningContext {
parent_hash: System::parent_hash(),
@@ -1589,7 +1588,7 @@ mod tests {
new_test_ext(genesis_config(paras)).execute_with(|| {
Validators::set(validator_public.clone());
CurrentSessionIndex::set(5);
shared::Module::<Test>::set_session_index(5);
run_to_block(5, |_| None);
@@ -2076,7 +2075,7 @@ mod tests {
new_test_ext(genesis_config(paras)).execute_with(|| {
Validators::set(validator_public.clone());
CurrentSessionIndex::set(5);
shared::Module::<Test>::set_session_index(5);
run_to_block(5, |_| None);
@@ -2273,7 +2272,7 @@ mod tests {
new_test_ext(genesis_config(paras)).execute_with(|| {
Validators::set(validator_public.clone());
CurrentSessionIndex::set(5);
shared::Module::<Test>::set_session_index(5);
run_to_block(5, |_| None);
@@ -2370,7 +2369,7 @@ mod tests {
new_test_ext(genesis_config(paras)).execute_with(|| {
Validators::set(validator_public.clone());
CurrentSessionIndex::set(5);
shared::Module::<Test>::set_session_index(5);
let validators_new = vec![
Sr25519Keyring::Alice,
@@ -2434,7 +2433,7 @@ mod tests {
run_to_block(11, |_| None);
assert_eq!(Validators::get(), validator_public);
assert_eq!(CurrentSessionIndex::get(), 5);
assert_eq!(shared::Module::<Test>::session_index(), 5);
assert!(<AvailabilityBitfields<Test>>::get(&0).is_some());
assert!(<AvailabilityBitfields<Test>>::get(&1).is_some());
@@ -2458,7 +2457,7 @@ mod tests {
});
assert_eq!(Validators::get(), validator_public_new);
assert_eq!(CurrentSessionIndex::get(), 6);
assert_eq!(shared::Module::<Test>::session_index(), 6);
assert!(<AvailabilityBitfields<Test>>::get(&0).is_none());
assert!(<AvailabilityBitfields<Test>>::get(&1).is_none());
+15 -10
View File
@@ -21,14 +21,14 @@
use sp_std::prelude::*;
use frame_support::weights::Weight;
use primitives::v1::ValidatorId;
use primitives::v1::{ValidatorId, SessionIndex};
use frame_support::{
decl_storage, decl_module, decl_error, traits::{OneSessionHandler, Randomness},
};
use parity_scale_codec::{Encode, Decode};
use crate::{
configuration::{self, HostConfiguration},
paras, scheduler, inclusion, session_info, dmp, ump, hrmp,
shared, paras, scheduler, inclusion, session_info, dmp, ump, hrmp,
};
/// Information about a session change that has just occurred.
@@ -45,7 +45,7 @@ pub struct SessionChangeNotification<BlockNumber> {
/// A secure random seed for the session, gathered from BABE.
pub random_seed: [u8; 32],
/// New session index.
pub session_index: sp_staking::SessionIndex,
pub session_index: SessionIndex,
}
impl<BlockNumber: Default + From<u32>> Default for SessionChangeNotification<BlockNumber> {
@@ -65,12 +65,13 @@ impl<BlockNumber: Default + From<u32>> Default for SessionChangeNotification<Blo
struct BufferedSessionChange {
validators: Vec<ValidatorId>,
queued: Vec<ValidatorId>,
session_index: sp_staking::SessionIndex,
session_index: SessionIndex,
}
pub trait Config:
frame_system::Config
+ configuration::Config
+ shared::Config
+ paras::Config
+ scheduler::Config
+ inclusion::Config
@@ -126,6 +127,7 @@ decl_module! {
// - UMP
// - HRMP
let total_weight = configuration::Module::<T>::initializer_initialize(now) +
shared::Module::<T>::initializer_initialize(now) +
paras::Module::<T>::initializer_initialize(now) +
scheduler::Module::<T>::initializer_initialize(now) +
inclusion::Module::<T>::initializer_initialize(now) +
@@ -148,6 +150,7 @@ decl_module! {
inclusion::Module::<T>::initializer_finalize();
scheduler::Module::<T>::initializer_finalize();
paras::Module::<T>::initializer_finalize();
shared::Module::<T>::initializer_finalize();
configuration::Module::<T>::initializer_finalize();
// Apply buffered session changes as the last thing. This way the runtime APIs and the
@@ -170,7 +173,7 @@ decl_module! {
impl<T: Config> Module<T> {
fn apply_new_session(
session_index: sp_staking::SessionIndex,
session_index: SessionIndex,
validators: Vec<ValidatorId>,
queued: Vec<ValidatorId>,
) {
@@ -186,7 +189,7 @@ impl<T: Config> Module<T> {
// We can't pass the new config into the thing that determines the new config,
// so we don't pass the `SessionChangeNotification` into this module.
configuration::Module::<T>::initializer_on_new_session(&validators, &queued);
configuration::Module::<T>::initializer_on_new_session(&validators, &queued, &session_index);
let new_config = <configuration::Module<T>>::config();
@@ -199,6 +202,7 @@ impl<T: Config> Module<T> {
session_index,
};
shared::Module::<T>::initializer_on_new_session(&notification);
let outgoing_paras = paras::Module::<T>::initializer_on_new_session(&notification);
scheduler::Module::<T>::initializer_on_new_session(&notification);
inclusion::Module::<T>::initializer_on_new_session(&notification);
@@ -212,7 +216,7 @@ impl<T: Config> Module<T> {
/// at the end of the block. If `queued` is `None`, the `validators` are considered queued.
fn on_new_session<'a, I: 'a>(
_changed: bool,
session_index: sp_staking::SessionIndex,
session_index: SessionIndex,
validators: I,
queued: Option<I>,
)
@@ -361,10 +365,11 @@ mod tests {
assert_ok!(Dmp::queue_downward_message(&Configuration::config(), b, vec![4, 5, 6]));
assert_ok!(Dmp::queue_downward_message(&Configuration::config(), c, vec![7, 8, 9]));
Paras::schedule_para_cleanup(a);
Paras::schedule_para_cleanup(b);
assert_ok!(Paras::schedule_para_cleanup(a));
assert_ok!(Paras::schedule_para_cleanup(b));
Initializer::apply_new_session(1, vec![], vec![]);
// Apply session 2 in the future
Initializer::apply_new_session(2, vec![], vec![]);
assert!(Dmp::dmq_contents(a).is_empty());
assert!(Dmp::dmq_contents(b).is_empty());
+5 -4
View File
@@ -23,6 +23,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
pub mod configuration;
pub mod shared;
pub mod inclusion;
pub mod inclusion_inherent;
pub mod initializer;
@@ -49,11 +50,11 @@ pub use paras::ParaLifecycle;
pub fn schedule_para_initialize<T: paras::Config>(
id: primitives::v1::Id,
genesis: paras::ParaGenesisArgs,
) {
<paras::Module<T>>::schedule_para_initialize(id, genesis);
) -> Result<(), ()> {
<paras::Module<T>>::schedule_para_initialize(id, genesis).map_err(|_| ())
}
/// Schedule a para to be cleaned up at the start of the next session.
pub fn schedule_para_cleanup<T: paras::Config>(id: primitives::v1::Id) {
<paras::Module<T>>::schedule_para_cleanup(id);
pub fn schedule_para_cleanup<T: paras::Config>(id: primitives::v1::Id) -> Result<(), ()> {
<paras::Module<T>>::schedule_para_cleanup(id).map_err(|_| ())
}
+4 -1
View File
@@ -27,7 +27,7 @@ use std::cell::RefCell;
use std::collections::HashMap;
use crate::{
inclusion, scheduler, dmp, ump, hrmp, session_info, paras, configuration,
initializer,
initializer, shared,
};
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
@@ -43,6 +43,7 @@ frame_support::construct_runtime!(
Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
Paras: paras::{Module, Origin, Call, Storage, Config<T>},
Configuration: configuration::{Module, Call, Storage, Config<T>},
Shared: shared::{Module, Call, Storage},
Inclusion: inclusion::{Module, Call, Storage, Event<T>},
Scheduler: scheduler::{Module, Call, Storage},
Initializer: initializer::{Module, Call, Storage},
@@ -112,6 +113,8 @@ impl crate::initializer::Config for Test {
impl crate::configuration::Config for Test { }
impl crate::shared::Config for Test { }
impl crate::paras::Config for Test {
type Origin = Origin;
}
+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;
@@ -28,7 +28,7 @@ use primitives::v1::{
InboundDownwardMessage, InboundHrmpMessage, Hash,
};
use frame_support::debug;
use crate::{initializer, inclusion, scheduler, configuration, paras, session_info, dmp, hrmp};
use crate::{initializer, inclusion, scheduler, configuration, paras, session_info, dmp, hrmp, shared};
/// Implementation for the `validators` function of the runtime API.
pub fn validators<T: initializer::Config>() -> Vec<ValidatorId> {
@@ -228,7 +228,7 @@ pub fn session_index_for_child<T: initializer::Config>() -> SessionIndex {
//
// Incidentally, this is also the rationale for why it is OK to query validators or
// occupied cores or etc. and expect the correct response "for child".
<inclusion::Module<T>>::session_index()
<shared::Module<T>>::session_index()
}
/// Implementation for the `validation_code` function of the runtime API.
+39 -135
View File
@@ -731,19 +731,31 @@ impl<T: Config> Module<T> {
}
}
#[cfg(test)]
mod tests {
use super::*;
use primitives::v1::{BlockNumber, ValidatorId, CollatorId};
use frame_support::traits::{OnFinalize, OnInitialize};
use primitives::v1::{BlockNumber, ValidatorId, CollatorId, SessionIndex};
use frame_support::{
assert_ok,
traits::{OnFinalize, OnInitialize},
};
use keyring::Sr25519Keyring;
use crate::mock::{new_test_ext, Configuration, Paras, System, Scheduler, MockGenesisConfig};
use crate::mock::{new_test_ext, Configuration, Paras, Shared, System, Scheduler, MockGenesisConfig};
use crate::initializer::SessionChangeNotification;
use crate::configuration::HostConfiguration;
use crate::paras::ParaGenesisArgs;
fn schedule_blank_para(id: ParaId, is_chain: bool) {
assert_ok!(Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
}));
}
fn run_to_block(
to: BlockNumber,
new_session: impl Fn(BlockNumber) -> Option<SessionChangeNotification<BlockNumber>>,
@@ -755,8 +767,13 @@ mod tests {
Paras::initializer_finalize();
if let Some(notification) = new_session(b + 1) {
Paras::initializer_on_new_session(&notification);
Scheduler::initializer_on_new_session(&notification);
let mut notification_with_session_index = notification;
// We will make every session change trigger an action queue. Normally this may require 2 or more session changes.
if notification_with_session_index.session_index == SessionIndex::default() {
notification_with_session_index.session_index = Shared::scheduled_session();
}
Paras::initializer_on_new_session(&notification_with_session_index);
Scheduler::initializer_on_new_session(&notification_with_session_index);
}
System::on_finalize(b);
@@ -816,11 +833,7 @@ mod tests {
let collator = CollatorId::from(Sr25519Keyring::Alice.public());
new_test_ext(genesis_config).execute_with(|| {
Paras::schedule_para_initialize(thread_id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: false,
});
schedule_blank_para(thread_id, false);
assert!(!Paras::is_parathread(thread_id));
@@ -895,11 +908,7 @@ mod tests {
let collator = CollatorId::from(Sr25519Keyring::Alice.public());
new_test_ext(genesis_config).execute_with(|| {
Paras::schedule_para_initialize(thread_id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: false,
});
schedule_blank_para(thread_id, false);
assert!(!Paras::is_parathread(thread_id));
@@ -935,23 +944,9 @@ mod tests {
// threads a, b, and c will be live in next session, but not d.
{
Paras::schedule_para_initialize(thread_a, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: false,
});
Paras::schedule_para_initialize(thread_b, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: false,
});
Paras::schedule_para_initialize(thread_c, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: false,
});
schedule_blank_para(thread_a, false);
schedule_blank_para(thread_b, false);
schedule_blank_para(thread_c, false);
}
// set up a queue as if n_cores was 4 and with some with many retries.
@@ -1041,16 +1036,9 @@ mod tests {
let chain_b = ParaId::from(2);
// ensure that we have 5 groups by registering 2 parachains.
Paras::schedule_para_initialize(chain_a, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: true,
});
Paras::schedule_para_initialize(chain_b, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: true,
});
schedule_blank_para(chain_a, true);
schedule_blank_para(chain_b, true);
run_to_block(1, |number| match number {
1 => Some(SessionChangeNotification {
@@ -1107,21 +1095,10 @@ mod tests {
let chain_c = ParaId::from(3);
// ensure that we have 5 groups by registering 2 parachains.
Paras::schedule_para_initialize(chain_a, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: true,
});
Paras::schedule_para_initialize(chain_b, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: true,
});
Paras::schedule_para_initialize(chain_c, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: false,
});
schedule_blank_para(chain_a, true);
schedule_blank_para(chain_b, true);
schedule_blank_para(chain_c, false);
run_to_block(1, |number| match number {
1 => Some(SessionChangeNotification {
@@ -1170,12 +1147,6 @@ mod tests {
let collator = CollatorId::from(Sr25519Keyring::Alice.public());
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
assert_eq!(default_config().parathread_cores, 3);
@@ -1285,12 +1256,6 @@ mod tests {
let collator = CollatorId::from(Sr25519Keyring::Alice.public());
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
assert_eq!(default_config().parathread_cores, 3);
@@ -1442,12 +1407,6 @@ mod tests {
let chain_b = ParaId::from(2);
let chain_c = ParaId::from(3);
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
assert_eq!(default_config().parathread_cores, 3);
@@ -1552,12 +1511,6 @@ mod tests {
let collator = CollatorId::from(Sr25519Keyring::Alice.public());
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
assert_eq!(default_config().parathread_cores, 3);
@@ -1630,12 +1583,6 @@ mod tests {
let collator = CollatorId::from(Sr25519Keyring::Alice.public());
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
assert_eq!(default_config().parathread_cores, 3);
@@ -1695,12 +1642,6 @@ mod tests {
let chain_a = ParaId::from(1);
let thread_a = ParaId::from(2);
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
schedule_blank_para(chain_a, true);
schedule_blank_para(thread_a, false);
@@ -1798,12 +1739,6 @@ mod tests {
let collator = CollatorId::from(Sr25519Keyring::Alice.public());
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
schedule_blank_para(thread_a, false);
schedule_blank_para(thread_b, false);
@@ -1879,12 +1814,6 @@ mod tests {
let collator = CollatorId::from(Sr25519Keyring::Alice.public());
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
schedule_blank_para(thread_a, false);
schedule_blank_para(thread_b, false);
@@ -1966,12 +1895,6 @@ mod tests {
let chain_a = ParaId::from(1);
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
schedule_blank_para(chain_a, true);
@@ -2029,12 +1952,6 @@ mod tests {
let chain_a = ParaId::from(1);
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
schedule_blank_para(chain_a, true);
@@ -2093,16 +2010,9 @@ mod tests {
let chain_b = ParaId::from(2);
// ensure that we have 5 groups by registering 2 parachains.
Paras::schedule_para_initialize(chain_a, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: true,
});
Paras::schedule_para_initialize(chain_b, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: true,
});
schedule_blank_para(chain_a, true);
schedule_blank_para(chain_b, true);
run_to_block(1, |number| match number {
1 => Some(SessionChangeNotification {
@@ -2127,7 +2037,7 @@ mod tests {
let groups = ValidatorGroups::get();
assert_eq!(groups.len(), 5);
Paras::schedule_para_cleanup(chain_b);
assert_ok!(Paras::schedule_para_cleanup(chain_b));
run_to_end_of_block(2, |number| match number {
2 => Some(SessionChangeNotification {
@@ -2179,12 +2089,6 @@ mod tests {
let collator = CollatorId::from(Sr25519Keyring::Alice.public());
let schedule_blank_para = |id, is_chain| Paras::schedule_para_initialize(id, ParaGenesisArgs {
genesis_head: Vec::new().into(),
validation_code: Vec::new().into(),
parachain: is_chain,
});
new_test_ext(genesis_config).execute_with(|| {
assert_eq!(default_config().parathread_cores, 3);
@@ -2210,7 +2114,7 @@ mod tests {
run_to_block(2, |_| None);
assert_eq!(Scheduler::scheduled().len(), 2);
Paras::schedule_para_cleanup(thread_a);
assert_ok!(Paras::schedule_para_cleanup(thread_a));
// start a new session to activate, 5 validators for 5 cores.
run_to_block(3, |number| match number {
+53 -30
View File
@@ -166,7 +166,7 @@ mod tests {
use super::*;
use crate::mock::{
new_test_ext, Configuration, SessionInfo, System, MockGenesisConfig,
Origin,
Origin, Shared,
};
use crate::initializer::SessionChangeNotification;
use crate::configuration::HostConfiguration;
@@ -181,10 +181,16 @@ mod tests {
let b = System::block_number();
SessionInfo::initializer_finalize();
Shared::initializer_finalize();
Configuration::initializer_finalize();
if let Some(notification) = new_session(b + 1) {
Configuration::initializer_on_new_session(&notification.validators, &notification.queued);
Configuration::initializer_on_new_session(
&notification.validators,
&notification.queued,
&notification.session_index,
);
Shared::initializer_on_new_session(&notification);
SessionInfo::initializer_on_new_session(&notification);
}
@@ -194,6 +200,7 @@ mod tests {
System::set_block_number(b + 1);
Configuration::initializer_initialize(b + 1);
Shared::initializer_initialize(b + 1);
SessionInfo::initializer_initialize(b + 1);
}
}
@@ -218,24 +225,13 @@ mod tests {
}
fn session_changes(n: BlockNumber) -> Option<SessionChangeNotification<BlockNumber>> {
match n {
100 => Some(SessionChangeNotification {
session_index: 10,
if n % 10 == 0 {
Some(SessionChangeNotification {
session_index: n / 10,
..Default::default()
}),
200 => Some(SessionChangeNotification {
session_index: 20,
..Default::default()
}),
300 => Some(SessionChangeNotification {
session_index: 30,
..Default::default()
}),
400 => Some(SessionChangeNotification {
session_index: 40,
..Default::default()
}),
_ => None,
})
} else {
None
}
}
@@ -249,29 +245,55 @@ mod tests {
#[test]
fn session_pruning_is_based_on_dispute_period() {
new_test_ext(genesis_config()).execute_with(|| {
let default_info = primitives::v1::SessionInfo::default();
Sessions::insert(9, default_info);
// Dispute period starts at 2
let config = Configuration::config();
assert_eq!(config.dispute_period, 2);
// Move to session 10
run_to_block(100, session_changes);
// but the first session change is not based on dispute_period
assert_eq!(EarliestStoredSession::get(), 10);
// and we didn't prune the last changes
// Earliest stored session is 10 - 2 = 8
assert_eq!(EarliestStoredSession::get(), 8);
// Pruning works as expected
assert!(Sessions::get(7).is_none());
assert!(Sessions::get(8).is_some());
assert!(Sessions::get(9).is_some());
// changing dispute_period works
let dispute_period = 5;
Configuration::set_dispute_period(Origin::root(), dispute_period).unwrap();
// Dispute period does not automatically change
let config = Configuration::config();
assert_eq!(config.dispute_period, 2);
// Two sessions later it will though
run_to_block(120, session_changes);
let config = Configuration::config();
assert_eq!(config.dispute_period, 5);
run_to_block(200, session_changes);
assert_eq!(EarliestStoredSession::get(), 20 - dispute_period);
// we don't have that many sessions stored
// Increase dispute period even more
let new_dispute_period = 16;
Configuration::set_dispute_period(Origin::root(), new_dispute_period).unwrap();
run_to_block(210, session_changes);
assert_eq!(EarliestStoredSession::get(), 21 - dispute_period);
// Two sessions later it kicks in
run_to_block(220, session_changes);
let config = Configuration::config();
assert_eq!(config.dispute_period, 16);
// Earliest session stays the same
assert_eq!(EarliestStoredSession::get(), 21 - dispute_period);
// We still don't have enough stored sessions to start pruning
run_to_block(300, session_changes);
assert_eq!(EarliestStoredSession::get(), 20 - dispute_period);
assert_eq!(EarliestStoredSession::get(), 21 - dispute_period);
// now we do
run_to_block(400, session_changes);
assert_eq!(EarliestStoredSession::get(), 40 - new_dispute_period);
run_to_block(420, session_changes);
assert_eq!(EarliestStoredSession::get(), 42 - new_dispute_period);
})
}
@@ -284,8 +306,9 @@ mod tests {
// change some param
Configuration::set_needed_approvals(Origin::root(), 42).unwrap();
run_to_block(2, new_session_every_block);
let session = Sessions::get(&2).unwrap();
// 2 sessions later
run_to_block(3, new_session_every_block);
let session = Sessions::get(&3).unwrap();
assert_eq!(session.needed_approvals, 42);
})
}
+79
View File
@@ -0,0 +1,79 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! A module for any shared state that other pallets may want access to.
//!
//! To avoid cyclic dependencies, it is important that this module is not
//! dependent on any of the other modules.
use primitives::v1::SessionIndex;
use frame_support::{
decl_storage, decl_module, decl_error,
weights::Weight,
};
use crate::initializer::SessionChangeNotification;
pub trait Config: frame_system::Config { }
// `SESSION_DELAY` is used to delay any changes to Paras registration or configurations.
// Wait until the session index is 2 larger then the current index to apply any changes,
// which guarantees that at least one full session has passed before any changes are applied.
pub(crate) const SESSION_DELAY: SessionIndex = 2;
decl_storage! {
trait Store for Module<T: Config> as ParasShared {
/// The current session index.
CurrentSessionIndex get(fn session_index): SessionIndex;
}
}
decl_error! {
pub enum Error for Module<T: Config> { }
}
decl_module! {
/// The session info module.
pub struct Module<T: Config> for enum Call where origin: <T as frame_system::Config>::Origin {
type Error = Error<T>;
}
}
impl<T: Config> Module<T> {
/// Called by the initializer to initialize the configuration module.
pub(crate) fn initializer_initialize(_now: T::BlockNumber) -> Weight {
0
}
/// Called by the initializer to finalize the configuration module.
pub(crate) fn initializer_finalize() { }
/// Called by the initializer to note that a new session has started.
///
/// Returns the list of outgoing paras from the actions queue.
pub(crate) fn initializer_on_new_session(notification: &SessionChangeNotification<T::BlockNumber>) {
CurrentSessionIndex::set(notification.session_index);
}
/// Return the session index that should be used for any future scheduled changes.
pub (crate) fn scheduled_session() -> SessionIndex {
Self::session_index().saturating_add(SESSION_DELAY)
}
#[cfg(test)]
pub(crate) fn set_session_index(index: SessionIndex) {
CurrentSessionIndex::set(index);
}
}