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
@@ -15,11 +15,15 @@ There is some functionality of the relay chain relating to parachains that we al
We will split the logic of the runtime up into these modules:
* Initializer: manage initialization order of the other modules.
* Shared: manages shared storage and configurations for other modules.
* Configuration: manage configuration and configuration updates in a non-racy manner.
* Paras: manage chain-head and validation code for parachains and parathreads.
* Scheduler: manages parachain and parathread scheduling as well as validator assignments.
* Inclusion: handles the inclusion and availability of scheduled parachains and parathreads.
* Validity: handles secondary checks and dispute resolution for included, available parablocks.
* Hrmp: handles horizontal messages between paras.
* Ump: Handles upward messages from a para to the relay chain.
* Dmp: Handles downward messages from the relay chain to the para.
The [Initializer module](initializer.md) is special - it's responsible for handling the initialization logic of the other modules to ensure that the correct initialization order and related invariants are maintained. The other modules won't specify a on-initialize logic, but will instead expose a special semi-private routine that the initialization module will call. The other modules are relatively straightforward and perform the roles described above.
@@ -36,9 +36,6 @@ PendingAvailabilityCommitments: map ParaId => CandidateCommitments;
/// The current validators, by their parachain session keys.
Validators: Vec<ValidatorId>;
/// The current session index.
CurrentSessionIndex: SessionIndex;
```
## Session Change
@@ -46,7 +43,6 @@ CurrentSessionIndex: SessionIndex;
1. Clear out all candidates pending availability.
1. Clear out all validator bitfields.
1. Update `Validators` with the validators from the session change notification.
1. Update `CurrentSessionIndex` with the session index from the session change notification.
## Routines
@@ -1,9 +1,9 @@
# Paras Module
The Paras module is responsible for storing information on parachains and parathreads. Registered
parachains and parathreads cannot change except at session boundaries. This is primarily to ensure
that the number and meaning of bits required for the availability bitfields does not change except at session
boundaries.
parachains and parathreads cannot change except at session boundaries and after at least a full
session has passed. This is primarily to ensure that the number and meaning of bits required for the
availability bitfields does not change except at session boundaries.
It's also responsible for managing parachain validation code upgrades as well as maintaining
availability of old parachain code and its pruning.
@@ -63,9 +63,9 @@ 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,
DowngradingParachain,
/// Parathread is being offboarded.
OutgoingParathread,
/// Parachain is being offboarded.
@@ -82,7 +82,7 @@ state of the para using the `ParaLifecycle` enum.
None Parathread Parachain
+ + +
| | |
| (Session Delay) | |
| (2 Session Delay) | |
| | |
+----------------------->+ |
| Onboarding | |
@@ -91,10 +91,10 @@ None Parathread Parachain
| Onboarding | |
| | |
| +------------------------->+
| | UpgradingToParachain |
| | UpgradingParathread |
| | |
| +<-------------------------+
| | DowngradingToParathread |
| | DowngradingParachain |
| | |
|<-----------------------+ |
| OutgoingParathread | |
@@ -137,38 +137,31 @@ PastCodePruning: Vec<(ParaId, BlockNumber)>;
FutureCodeUpgrades: map ParaId => Option<BlockNumber>;
/// The actual future code of a para.
FutureCode: map 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: Vec<ParaId>;
/// The actions to perform during the start of a specific session index.
ActionsQueue: map SessionIndex => Vec<ParaId>;
/// Upcoming paras instantiation arguments.
UpcomingParasGenesis: map ParaId => Option<ParaGenesisArgs>;
/// Paras that are to be cleaned up at the end of the session. Ordered ascending by ParaId.
OutgoingParas: 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>;
```
## Session Change
1. Clean up outgoing paras.
1. This means removing the entries under `Heads`, `ValidationCode`, `FutureCodeUpgrades`, and
`FutureCode`. An according entry should be added to `PastCode`, `PastCodeMeta`, and
`PastCodePruning` using the outgoing `ParaId` and removed `ValidationCode` value. This is
because any outdated validation code must remain available on-chain for a determined amount of
blocks, and validation code outdated by de-registering the para is still subject to that
invariant.
1. Apply all incoming paras by initializing the `Heads` and `ValidationCode` using the genesis
parameters.
1. Amend the `Parachains` list and `ParaLifecycle` to reflect changes in registered parachains.
1. Amend the `ParaLifecycle` set to reflect changes in registered parathreads.
1. Upgrade all parathreads that should become parachains, updating the `Parachains` list and
`ParaLifecycle`.
1. Downgrade all parachains that should become parathreads, updating the `Parachains` list and
`ParaLifecycle`.
1. Return list of outgoing paras to the initializer for use by other modules.
1. Execute all queued actions for paralifecycle changes:
1. Clean up outgoing paras.
1. This means removing the entries under `Heads`, `ValidationCode`, `FutureCodeUpgrades`, and
`FutureCode`. An according entry should be added to `PastCode`, `PastCodeMeta`, and
`PastCodePruning` using the outgoing `ParaId` and removed `ValidationCode` value. This is
because any outdated validation code must remain available on-chain for a determined amount
of blocks, and validation code outdated by de-registering the para is still subject to that
invariant.
1. Apply all incoming paras by initializing the `Heads` and `ValidationCode` using the genesis
parameters.
1. Amend the `Parachains` list and `ParaLifecycle` to reflect changes in registered parachains.
1. Amend the `ParaLifecycle` set to reflect changes in registered parathreads.
1. Upgrade all parathreads that should become parachains, updating the `Parachains` list and
`ParaLifecycle`.
1. Downgrade all parachains that should become parathreads, updating the `Parachains` list and
`ParaLifecycle`.
1. Return list of outgoing paras to the initializer for use by other modules.
## Initialization
@@ -179,11 +172,9 @@ UpcomingDowngrades: Vec<ParaId>;
* `schedule_para_initialize(ParaId, ParaGenesisArgs)`: Schedule a para to be initialized at the next
session. Noop if para is already registered in the system with some `ParaLifecycle`.
* `schedule_para_cleanup(ParaId)`: Schedule a para to be cleaned up at the next session.
* `schedule_parathread_upgrade(ParaId)`: Schedule a parathread to be upgraded to a parachain. Noop
if `ParaLifecycle` is not `Parathread`.
* `schedule_para_cleanup(ParaId)`: Schedule a para to be cleaned up after the next full session.
* `schedule_parathread_upgrade(ParaId)`: Schedule a parathread to be upgraded to a parachain.
* `schedule_parachain_downgrade(ParaId)`: Schedule a parachain to be downgraded to a parathread.
Noop if `ParaLifecycle` is not `Parachain`.
* `schedule_code_upgrade(ParaId, ValidationCode, expected_at: BlockNumber)`: Schedule a future code
upgrade of the given parachain, to be applied after inclusion of a block of the same parachain
executed in the context of a relay-chain block with number >= `expected_at`.
@@ -197,8 +188,8 @@ UpcomingDowngrades: Vec<ParaId>;
current, or (with certain choices of `assume_intermediate`) future code. `assume_intermediate`, if
provided, must be before `at`. If the validation code has been pruned, this will return `None`.
* `lifecycle(ParaId) -> Option<ParaLifecycle>`: Return the `ParaLifecycle` of a para.
* `is_parachain(ParaId) -> bool`: Returns true if the para ID references any live parachain, including
those which may be transitioning to a parathread in the future.
* `is_parachain(ParaId) -> bool`: Returns true if the para ID references any live parachain,
including those which may be transitioning to a parathread in the future.
* `is_parathread(ParaId) -> bool`: Returns true if the para ID references any live parathread,
including those which may be transitioning to a parachain in the future.
* `is_valid_para(ParaId) -> bool`: Returns true if the para ID references either a live parathread
@@ -0,0 +1,55 @@
# Shared Module
This module is responsible for managing shared storage and configuration for other modules.
It is important that other pallets are able to use the Shared Module, so it should not have a
dependency on any other modules in the Parachains Runtime.
For the moment, it is used exclusively to track the current session index across the Parachains
Runtime system, and when it should be allowed to schedule future changes to Paras or Configurations.
## Constants
```rust
// `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;
```
## Storage
```rust
// The current session index within the Parachains Runtime system.
CurrentSessionIndex: SessionIndex;
```
## Initialization
The Shared Module currently has no initialization routines.
The Shared Module is initialized directly after the Configuration module, but before all other
modules. It is important to update the Shared Module before any other module since its state may be
used within the logic of other modules, and it is important that the state is consistent across
them.
## Session Change
During a session change, the Shared Module receives and stores the current Session Index for that
block through the Session Change Notification.
This information is used in the:
* Configuration Module: For delaying updates to configurations until at lease one full session has
passed.
* Paras Module: For delaying updates to paras until at least one full session has passed.
## Finalization
The Shared Module currently has no finalization routines.
## Functions
* `scheduled_sessions() -> SessionIndex`: Return the next session index where updates to the
Parachains Runtime system would be safe to apply.
* `set_session_index(SessionIndex)`: For tests. Set the current session index in the Shared Module.
+47 -37
View File
@@ -88,6 +88,8 @@ decl_error! {
ParathreadsRegistrationDisabled,
/// The validation code provided doesn't start with the Wasm file magic string.
DefinitelyNotWasm,
/// Cannot deregister para
CannotDeregister,
}
}
@@ -113,22 +115,18 @@ decl_module! {
ensure!(!Paras::contains_key(id), Error::<T>::ParaAlreadyExists);
let outgoing = <paras::Module<T>>::outgoing_paras();
ensure!(outgoing.binary_search(&id).is_err(), Error::<T>::ParaAlreadyExists);
<T as Config>::Currency::reserve(&who, T::ParathreadDeposit::get())?;
<Debtors<T>>::insert(id, who);
Paras::insert(id, false);
let genesis = ParaGenesisArgs {
genesis_head,
validation_code,
parachain: false,
};
ensure!(paras::Module::<T>::can_schedule_para_initialize(&id, &genesis), Error::<T>::ParaAlreadyExists);
<T as Config>::Currency::reserve(&who, T::ParathreadDeposit::get())?;
runtime_parachains::schedule_para_initialize::<T>(id, genesis);
<Debtors<T>>::insert(id, who);
Paras::insert(id, false);
// Checked this shouldn't fail above.
let _ = runtime_parachains::schedule_para_initialize::<T>(id, genesis);
Ok(())
}
@@ -146,14 +144,16 @@ decl_module! {
ensure!(ParathreadsRegistrationEnabled::get(), Error::<T>::ParathreadsRegistrationDisabled);
let is_parachain = Paras::take(id).ok_or(Error::<T>::InvalidChainId)?;
let is_parachain = Paras::get(id).ok_or(Error::<T>::InvalidChainId)?;
ensure!(!is_parachain, Error::<T>::InvalidThreadId);
runtime_parachains::schedule_para_cleanup::<T>(id).map_err(|_| Error::<T>::CannotDeregister)?;
let debtor = <Debtors<T>>::take(id);
let _ = <T as Config>::Currency::unreserve(&debtor, T::ParathreadDeposit::get());
runtime_parachains::schedule_para_cleanup::<T>(id);
Paras::remove(&id);
PendingSwap::remove(&id);
Ok(())
}
@@ -176,7 +176,6 @@ decl_module! {
Ok(())
}
/// Swap a parachain with another parachain or parathread. The origin must be a `Parachain`.
/// The swap will happen only if there is already an opposite swap pending. If there is not,
/// the swap will be stored in the pending swaps map, ready for a later confirmatory swap.
@@ -222,30 +221,27 @@ impl<T: Config> Module<T> {
ensure!(!Paras::contains_key(id), Error::<T>::ParaAlreadyExists);
ensure!(validation_code.0.starts_with(WASM_MAGIC), Error::<T>::DefinitelyNotWasm);
let outgoing = <paras::Module<T>>::outgoing_paras();
ensure!(outgoing.binary_search(&id).is_err(), Error::<T>::ParaAlreadyExists);
Paras::insert(id, true);
let genesis = ParaGenesisArgs {
genesis_head,
validation_code,
parachain: true,
};
runtime_parachains::schedule_para_initialize::<T>(id, genesis);
runtime_parachains::schedule_para_initialize::<T>(id, genesis).map_err(|_| Error::<T>::ParaAlreadyExists)?;
Paras::insert(id, true);
Ok(())
}
/// Deregister a parachain with the given ID. Must be called by root.
pub fn deregister_parachain(id: ParaId) -> DispatchResult {
let is_parachain = Paras::take(id).ok_or(Error::<T>::InvalidChainId)?;
let is_parachain = Paras::get(id).ok_or(Error::<T>::InvalidChainId)?;
ensure!(is_parachain, Error::<T>::InvalidChainId);
runtime_parachains::schedule_para_cleanup::<T>(id);
runtime_parachains::schedule_para_cleanup::<T>(id).map_err(|_| Error::<T>::CannotDeregister)?;
Paras::remove(&id);
PendingSwap::remove(&id);
Ok(())
}
@@ -267,10 +263,13 @@ mod tests {
use frame_system::limits;
use frame_support::{
traits::{Randomness, OnInitialize, OnFinalize},
assert_ok, parameter_types,
assert_ok, assert_noop, parameter_types,
};
use keyring::Sr25519Keyring;
use runtime_parachains::{initializer, configuration, inclusion, session_info, scheduler, dmp, ump, hrmp};
use runtime_parachains::{
initializer, configuration, inclusion, session_info, scheduler, dmp, ump, hrmp, shared,
ParaLifecycle,
};
use frame_support::traits::OneSessionHandler;
use crate::paras_registrar;
@@ -309,7 +308,7 @@ mod tests {
parameter_types! {
pub const BlockHashCount: u32 = 250;
pub BlockWeights: limits::BlockWeights =
limits::BlockWeights::with_sensible_defaults(4 * 1024 * 1024, NORMAL_RATIO);
frame_system::limits::BlockWeights::simple_max(1024);
pub BlockLength: limits::BlockLength =
limits::BlockLength::max_with_normal_ratio(4 * 1024 * 1024, NORMAL_RATIO);
}
@@ -370,7 +369,7 @@ mod tests {
}
parameter_types! {
pub const Period: BlockNumber = 1;
pub const Period: BlockNumber = 3;
pub const Offset: BlockNumber = 0;
pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
@@ -432,6 +431,8 @@ mod tests {
type WeightInfo = ();
}
impl shared::Config for Test {}
impl dmp::Config for Test {}
impl ump::Config for Test {
@@ -590,7 +591,7 @@ mod tests {
Initializer::on_finalize(System::block_number());
}
// Session change every 3 blocks.
if (b + 1) % 3 == 0 {
if (b + 1) % Period::get() == 0 {
println!("New session at {}", System::block_number());
Initializer::on_new_session(
false,
@@ -601,6 +602,7 @@ mod tests {
System::set_block_number(b + 1);
println!("Initializing {}", System::block_number());
System::on_initialize(System::block_number());
Session::on_initialize(System::block_number());
Initializer::on_initialize(System::block_number());
}
}
@@ -643,7 +645,7 @@ mod tests {
assert_eq!(Balances::free_balance(3u64) + ParathreadDeposit::get(), orig_bal);
assert_eq!(Balances::reserved_balance(3u64), ParathreadDeposit::get());
run_to_block(3);
run_to_block(10);
assert_ok!(Registrar::deregister_parachain(2u32.into()));
@@ -690,10 +692,12 @@ mod tests {
assert_ok!(Registrar::swap(runtime_parachains::Origin::Parachain(2u32.into()).into(), 8u32.into()));
assert_ok!(Registrar::swap(runtime_parachains::Origin::Parachain(8u32.into()).into(), 2u32.into()));
run_to_block(15);
// Deregister a parathread that was originally a parachain
assert_ok!(Registrar::deregister_parathread(runtime_parachains::Origin::Parachain(2u32.into()).into()));
run_to_block(12);
run_to_block(21);
// Funds are correctly returned
assert_eq!(Balances::free_balance(1), initial_1_balance);
@@ -712,20 +716,26 @@ mod tests {
WASM_MAGIC.to_vec().into(),
));
run_to_block(4);
// 2 session changes to fully onboard.
run_to_block(12);
assert_eq!(Parachains::lifecycle(1u32.into()), Some(ParaLifecycle::Parachain));
assert_ok!(Registrar::deregister_parachain(1u32.into()));
run_to_block(5);
run_to_block(13);
assert!(Registrar::register_parachain(
assert_eq!(Parachains::lifecycle(1u32.into()), Some(ParaLifecycle::OffboardingParachain));
assert_noop!(Registrar::register_parachain(
1u32.into(),
vec![1; 3].into(),
WASM_MAGIC.to_vec().into(),
).is_err());
), Error::<Test>::ParaAlreadyExists);
// The session will be changed on the 6th block, as part of finalization. The change
// will be observed on the 7th.
run_to_block(7);
// Need 2 session changes to see the effect, which takes place by block 13.
run_to_block(18);
assert!(Parachains::lifecycle(1u32.into()).is_none());
assert_ok!(Registrar::register_parachain(
1u32.into(),
vec![1; 3].into(),
@@ -40,11 +40,15 @@ decl_error! {
pub enum Error for Module<T: Config> {
/// The specified parachain or parathread is not registered.
ParaDoesntExist,
/// The specified parachain or parathread is already registered.
ParaAlreadyExists,
/// A DMP message couldn't be sent because it exceeds the maximum size allowed for a downward
/// message.
ExceedsMaxMessageSize,
/// The validation code provided doesn't start with the Wasm file magic string.
DefinitelyNotWasm,
/// Could not schedule para cleanup.
CouldntCleanup,
}
}
@@ -62,7 +66,7 @@ decl_module! {
) -> DispatchResult {
ensure_root(origin)?;
ensure!(genesis.validation_code.0.starts_with(WASM_MAGIC), Error::<T>::DefinitelyNotWasm);
runtime_parachains::schedule_para_initialize::<T>(id, genesis);
runtime_parachains::schedule_para_initialize::<T>(id, genesis).map_err(|_| Error::<T>::ParaAlreadyExists)?;
Ok(())
}
@@ -70,7 +74,7 @@ decl_module! {
#[weight = (1_000, DispatchClass::Operational)]
pub fn sudo_schedule_para_cleanup(origin, id: ParaId) -> DispatchResult {
ensure_root(origin)?;
runtime_parachains::schedule_para_cleanup::<T>(id);
runtime_parachains::schedule_para_cleanup::<T>(id).map_err(|_| Error::<T>::CouldntCleanup)?;
Ok(())
}
@@ -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);
}
}
+4
View File
@@ -65,6 +65,7 @@ use runtime_common::{paras_sudo_wrapper, paras_registrar};
use runtime_parachains::origin as parachains_origin;
use runtime_parachains::configuration as parachains_configuration;
use runtime_parachains::shared as parachains_shared;
use runtime_parachains::inclusion as parachains_inclusion;
use runtime_parachains::inclusion_inherent as parachains_inclusion_inherent;
use runtime_parachains::initializer as parachains_initializer;
@@ -187,6 +188,7 @@ construct_runtime! {
// Parachains modules.
ParachainsOrigin: parachains_origin::{Module, Origin},
ParachainsConfiguration: parachains_configuration::{Module, Call, Storage, Config<T>},
Shared: parachains_shared::{Module, Call, Storage},
Inclusion: parachains_inclusion::{Module, Call, Storage, Event<T>},
InclusionInherent: parachains_inclusion_inherent::{Module, Call, Storage, Inherent},
Scheduler: parachains_scheduler::{Module, Call, Storage},
@@ -500,6 +502,8 @@ impl parachains_origin::Config for Runtime {}
impl parachains_configuration::Config for Runtime {}
impl parachains_shared::Config for Runtime {}
/// Special `RewardValidators` that does nothing ;)
pub struct RewardValidators;
impl runtime_parachains::inclusion::RewardValidators for RewardValidators {
@@ -134,6 +134,8 @@ decl_error! {
DefinitelyNotWasm,
/// Registration requires at least one validator.
AtLeastOneValidatorRequired,
/// Couldn't schedule parachain cleanup.
CouldntCleanup,
}
}
@@ -195,11 +197,7 @@ decl_module! {
ensure!(validators.len() > 0, Error::<T>::AtLeastOneValidatorRequired);
ensure!(!Proposals::<T>::contains_key(&para_id), Error::<T>::ParachainIdAlreadyProposed);
ensure!(
!runtime_parachains::paras::Module::<T>::parachains().contains(&para_id),
Error::<T>::ParachainIdAlreadyTaken,
);
ensure!(
!runtime_parachains::paras::Module::<T>::upcoming_paras().contains(&para_id),
runtime_parachains::paras::Module::<T>::lifecycle(para_id).is_none(),
Error::<T>::ParachainIdAlreadyTaken,
);
ensure!(validation_code.0.starts_with(runtime_common::WASM_MAGIC), Error::<T>::DefinitelyNotWasm);
@@ -289,10 +287,10 @@ decl_module! {
if let Some(who) = who {
ensure!(who == info.proposer, Error::<T>::NotAuthorized);
}
runtime_parachains::schedule_para_cleanup::<T>(para_id).map_err(|_| Error::<T>::CouldntCleanup)?;
ParachainInfo::<T>::remove(&para_id);
info.validators.into_iter().for_each(|v| ValidatorsToRetire::<T>::append(v));
runtime_parachains::schedule_para_cleanup::<T>(para_id);
pallet_balances::Module::<T>::unreserve(&info.proposer, T::ProposeDeposit::get());
}
@@ -354,7 +352,8 @@ impl<T: Config> pallet_session::SessionManager<T::ValidatorId> for Module<T> {
parachain: true,
};
runtime_parachains::schedule_para_initialize::<T>(*id, genesis);
// Not much we can do if this fails...
let _ = runtime_parachains::schedule_para_initialize::<T>(*id, genesis);
validators.extend(proposal.validators);
}
+3
View File
@@ -26,6 +26,7 @@ use sp_std::collections::btree_map::BTreeMap;
use parity_scale_codec::Encode;
use polkadot_runtime_parachains::configuration as parachains_configuration;
use polkadot_runtime_parachains::shared as parachains_shared;
use polkadot_runtime_parachains::inclusion as parachains_inclusion;
use polkadot_runtime_parachains::inclusion_inherent as parachains_inclusion_inherent;
use polkadot_runtime_parachains::initializer as parachains_initializer;
@@ -448,6 +449,8 @@ impl pallet_sudo::Config for Runtime {
impl parachains_configuration::Config for Runtime {}
impl parachains_shared::Config for Runtime {}
impl parachains_inclusion::Config for Runtime {
type Event = Event;
type RewardValidators = RewardValidatorsWithEraPoints<Runtime>;