mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 05:51:02 +00:00
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:
@@ -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())
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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(¬ification);
|
||||
let outgoing_paras = Paras::initializer_on_new_session(¬ification);
|
||||
Hrmp::initializer_on_new_session(¬ification, &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(¶_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));
|
||||
|
||||
@@ -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(¬ification);
|
||||
Paras::initializer_on_new_session(¬ification);
|
||||
Inclusion::initializer_on_new_session(¬ification);
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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(¬ification);
|
||||
let outgoing_paras = paras::Module::<T>::initializer_on_new_session(¬ification);
|
||||
scheduler::Module::<T>::initializer_on_new_session(¬ification);
|
||||
inclusion::Module::<T>::initializer_on_new_session(¬ification);
|
||||
@@ -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());
|
||||
|
||||
@@ -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(|_| ())
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(¶);
|
||||
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(¶) {
|
||||
if genesis_data.parachain {
|
||||
if let Err(i) = parachains.binary_search(¶) {
|
||||
parachains.insert(i, para);
|
||||
}
|
||||
ParaLifecycles::insert(¶, ParaLifecycle::Parachain);
|
||||
} else {
|
||||
ParaLifecycles::insert(¶, 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(¶, |state| {
|
||||
if *state == Some(ParaLifecycle::UpgradingToParachain) {
|
||||
<Self as Store>::Heads::insert(¶, genesis_data.genesis_head);
|
||||
<Self as Store>::CurrentCode::insert(¶, genesis_data.validation_code);
|
||||
}
|
||||
},
|
||||
// Upgrade a parathread to a parachain
|
||||
Some(ParaLifecycle::UpgradingParathread) => {
|
||||
if let Err(i) = parachains.binary_search(¶) {
|
||||
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(¶, |state| {
|
||||
if *state == Some(ParaLifecycle::DowngradingToParathread) {
|
||||
ParaLifecycles::insert(¶, ParaLifecycle::Parachain);
|
||||
},
|
||||
// Downgrade a parachain to a parathread
|
||||
Some(ParaLifecycle::DowngradingParachain) => {
|
||||
if let Ok(i) = parachains.binary_search(¶) {
|
||||
parachains.remove(i);
|
||||
}
|
||||
*state = Some(ParaLifecycle::Parathread);
|
||||
}
|
||||
});
|
||||
ParaLifecycles::insert(¶, ParaLifecycle::Parathread);
|
||||
},
|
||||
// Offboard a parathread or parachain from the system
|
||||
Some(ParaLifecycle::OffboardingParachain) | Some(ParaLifecycle::OffboardingParathread) => {
|
||||
if let Ok(i) = parachains.binary_search(¶) {
|
||||
parachains.remove(i);
|
||||
}
|
||||
|
||||
<Self as Store>::Heads::remove(¶);
|
||||
<Self as Store>::FutureCodeUpgrades::remove(¶);
|
||||
<Self as Store>::FutureCode::remove(¶);
|
||||
ParaLifecycles::remove(¶);
|
||||
|
||||
let removed_code = <Self as Store>::CurrentCode::take(¶);
|
||||
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(¶_id).most_recent_change().is_none());
|
||||
@@ -1331,8 +1260,8 @@ mod tests {
|
||||
assert_eq!(<Paras as Store>::Heads::get(¶_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.
|
||||
|
||||
@@ -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(¬ification);
|
||||
Scheduler::initializer_on_new_session(¬ification);
|
||||
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(¬ification_with_session_index);
|
||||
Scheduler::initializer_on_new_session(¬ification_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 {
|
||||
|
||||
@@ -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(¬ification.validators, ¬ification.queued);
|
||||
Configuration::initializer_on_new_session(
|
||||
¬ification.validators,
|
||||
¬ification.queued,
|
||||
¬ification.session_index,
|
||||
);
|
||||
Shared::initializer_on_new_session(¬ification);
|
||||
SessionInfo::initializer_on_new_session(¬ification);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user