Unverified Commit afb6daa7 authored by Shawn Tabrizi's avatar Shawn Tabrizi Committed by GitHub
Browse files

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 b2e9011e

.

* 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: default avatarGavin Wood <gavin@parity.io>
parent 10b0d793
Pipeline #124707 passed with stages
in 33 minutes and 10 seconds
...@@ -15,11 +15,15 @@ There is some functionality of the relay chain relating to parachains that we al ...@@ -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: We will split the logic of the runtime up into these modules:
* Initializer: manage initialization order of the other 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. * Configuration: manage configuration and configuration updates in a non-racy manner.
* Paras: manage chain-head and validation code for parachains and parathreads. * Paras: manage chain-head and validation code for parachains and parathreads.
* Scheduler: manages parachain and parathread scheduling as well as validator assignments. * Scheduler: manages parachain and parathread scheduling as well as validator assignments.
* Inclusion: handles the inclusion and availability of scheduled parachains and parathreads. * Inclusion: handles the inclusion and availability of scheduled parachains and parathreads.
* Validity: handles secondary checks and dispute resolution for included, available parablocks. * 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. 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; ...@@ -36,9 +36,6 @@ PendingAvailabilityCommitments: map ParaId => CandidateCommitments;
/// The current validators, by their parachain session keys. /// The current validators, by their parachain session keys.
Validators: Vec<ValidatorId>; Validators: Vec<ValidatorId>;
/// The current session index.
CurrentSessionIndex: SessionIndex;
``` ```
## Session Change ## Session Change
...@@ -46,7 +43,6 @@ CurrentSessionIndex: SessionIndex; ...@@ -46,7 +43,6 @@ CurrentSessionIndex: SessionIndex;
1. Clear out all candidates pending availability. 1. Clear out all candidates pending availability.
1. Clear out all validator bitfields. 1. Clear out all validator bitfields.
1. Update `Validators` with the validators from the session change notification. 1. Update `Validators` with the validators from the session change notification.
1. Update `CurrentSessionIndex` with the session index from the session change notification.
## Routines ## Routines
......
# Paras Module # Paras Module
The Paras module is responsible for storing information on parachains and parathreads. Registered 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 parachains and parathreads cannot change except at session boundaries and after at least a full
that the number and meaning of bits required for the availability bitfields does not change except at session session has passed. This is primarily to ensure that the number and meaning of bits required for the
boundaries. availability bitfields does not change except at session boundaries.
It's also responsible for managing parachain validation code upgrades as well as maintaining It's also responsible for managing parachain validation code upgrades as well as maintaining
availability of old parachain code and its pruning. availability of old parachain code and its pruning.
...@@ -63,9 +63,9 @@ pub enum ParaLifecycle { ...@@ -63,9 +63,9 @@ pub enum ParaLifecycle {
/// Para is a Parachain. /// Para is a Parachain.
Parachain, Parachain,
/// Para is a Parathread which is upgrading to a Parachain. /// Para is a Parathread which is upgrading to a Parachain.
UpgradingToParachain, UpgradingParathread,
/// Para is a Parachain which is downgrading to a Parathread. /// Para is a Parachain which is downgrading to a Parathread.
DowngradingToParathread, DowngradingParachain,
/// Parathread is being offboarded. /// Parathread is being offboarded.
OutgoingParathread, OutgoingParathread,
/// Parachain is being offboarded. /// Parachain is being offboarded.
...@@ -82,7 +82,7 @@ state of the para using the `ParaLifecycle` enum. ...@@ -82,7 +82,7 @@ state of the para using the `ParaLifecycle` enum.
None Parathread Parachain None Parathread Parachain
+ + + + + +
| | | | | |
| (Session Delay) | | | (2 Session Delay) | |
| | | | | |
+----------------------->+ | +----------------------->+ |
| Onboarding | | | Onboarding | |
...@@ -91,10 +91,10 @@ None Parathread Parachain ...@@ -91,10 +91,10 @@ None Parathread Parachain
| Onboarding | | | Onboarding | |
| | | | | |
| +------------------------->+ | +------------------------->+
| | UpgradingToParachain | | | UpgradingParathread |
| | | | | |
| +<-------------------------+ | +<-------------------------+
| | DowngradingToParathread | | | DowngradingParachain |
| | | | | |
|<-----------------------+ | |<-----------------------+ |
| OutgoingParathread | | | OutgoingParathread | |
...@@ -137,38 +137,31 @@ PastCodePruning: Vec<(ParaId, BlockNumber)>; ...@@ -137,38 +137,31 @@ PastCodePruning: Vec<(ParaId, BlockNumber)>;
FutureCodeUpgrades: map ParaId => Option<BlockNumber>; FutureCodeUpgrades: map ParaId => Option<BlockNumber>;
/// The actual future code of a para. /// The actual future code of a para.
FutureCode: map ParaId => Option<ValidationCode>; FutureCode: map ParaId => Option<ValidationCode>;
/// The actions to perform during the start of a specific session index.
/// Upcoming paras (chains and threads). These are only updated on session change. Corresponds to an ActionsQueue: map SessionIndex => Vec<ParaId>;
/// entry in the upcoming-genesis map. Ordered ascending by ParaId.
UpcomingParas: Vec<ParaId>;
/// Upcoming paras instantiation arguments. /// Upcoming paras instantiation arguments.
UpcomingParasGenesis: map ParaId => Option<ParaGenesisArgs>; 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 ## Session Change
1. Clean up outgoing paras. 1. Execute all queued actions for paralifecycle changes:
1. This means removing the entries under `Heads`, `ValidationCode`, `FutureCodeUpgrades`, and 1. Clean up outgoing paras.
`FutureCode`. An according entry should be added to `PastCode`, `PastCodeMeta`, and 1. This means removing the entries under `Heads`, `ValidationCode`, `FutureCodeUpgrades`, and
`PastCodePruning` using the outgoing `ParaId` and removed `ValidationCode` value. This is `FutureCode`. An according entry should be added to `PastCode`, `PastCodeMeta`, and
because any outdated validation code must remain available on-chain for a determined amount of `PastCodePruning` using the outgoing `ParaId` and removed `ValidationCode` value. This is
blocks, and validation code outdated by de-registering the para is still subject to that because any outdated validation code must remain available on-chain for a determined amount
invariant. of blocks, and validation code outdated by de-registering the para is still subject to that
1. Apply all incoming paras by initializing the `Heads` and `ValidationCode` using the genesis invariant.
parameters. 1. Apply all incoming paras by initializing the `Heads` and `ValidationCode` using the genesis
1. Amend the `Parachains` list and `ParaLifecycle` to reflect changes in registered parachains. parameters.
1. Amend the `ParaLifecycle` set to reflect changes in registered parathreads. 1. Amend the `Parachains` list and `ParaLifecycle` to reflect changes in registered parachains.
1. Upgrade all parathreads that should become parachains, updating the `Parachains` list and 1. Amend the `ParaLifecycle` set to reflect changes in registered parathreads.
`ParaLifecycle`. 1. Upgrade all parathreads that should become parachains, updating the `Parachains` list and
1. Downgrade all parachains that should become parathreads, updating the `Parachains` list and `ParaLifecycle`.
`ParaLifecycle`. 1. Downgrade all parachains that should become parathreads, updating the `Parachains` list and
1. Return list of outgoing paras to the initializer for use by other modules. `ParaLifecycle`.
1. Return list of outgoing paras to the initializer for use by other modules.
## Initialization ## Initialization
...@@ -179,11 +172,9 @@ UpcomingDowngrades: Vec<ParaId>; ...@@ -179,11 +172,9 @@ UpcomingDowngrades: Vec<ParaId>;
* `schedule_para_initialize(ParaId, ParaGenesisArgs)`: Schedule a para to be initialized at the next * `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`. 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_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. Noop * `schedule_parathread_upgrade(ParaId)`: Schedule a parathread to be upgraded to a parachain.
if `ParaLifecycle` is not `Parathread`.
* `schedule_parachain_downgrade(ParaId)`: Schedule a parachain to be downgraded to a parathread. * `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 * `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 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`. executed in the context of a relay-chain block with number >= `expected_at`.
...@@ -197,8 +188,8 @@ UpcomingDowngrades: Vec<ParaId>; ...@@ -197,8 +188,8 @@ UpcomingDowngrades: Vec<ParaId>;
current, or (with certain choices of `assume_intermediate`) future code. `assume_intermediate`, if 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`. 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. * `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 * `is_parachain(ParaId) -> bool`: Returns true if the para ID references any live parachain,
those which may be transitioning to a parathread in the future. 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, * `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. 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 * `is_valid_para(ParaId) -> bool`: Returns true if the para ID references either a live parathread
......
# 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.
...@@ -88,6 +88,8 @@ decl_error! { ...@@ -88,6 +88,8 @@ decl_error! {
ParathreadsRegistrationDisabled, ParathreadsRegistrationDisabled,
/// The validation code provided doesn't start with the Wasm file magic string. /// The validation code provided doesn't start with the Wasm file magic string.
DefinitelyNotWasm, DefinitelyNotWasm,
/// Cannot deregister para
CannotDeregister,
} }
} }
...@@ -113,22 +115,18 @@ decl_module! { ...@@ -113,22 +115,18 @@ decl_module! {
ensure!(!Paras::contains_key(id), Error::<T>::ParaAlreadyExists); 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 { let genesis = ParaGenesisArgs {
genesis_head, genesis_head,
validation_code, validation_code,
parachain: false, 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(()) Ok(())
} }
...@@ -146,14 +144,16 @@ decl_module! { ...@@ -146,14 +144,16 @@ decl_module! {
ensure!(ParathreadsRegistrationEnabled::get(), Error::<T>::ParathreadsRegistrationDisabled); 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); 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 debtor = <Debtors<T>>::take(id);
let _ = <T as Config>::Currency::unreserve(&debtor, T::ParathreadDeposit::get()); let _ = <T as Config>::Currency::unreserve(&debtor, T::ParathreadDeposit::get());
Paras::remove(&id);
runtime_parachains::schedule_para_cleanup::<T>(id); PendingSwap::remove(&id);
Ok(()) Ok(())
} }
...@@ -176,7 +176,6 @@ decl_module! { ...@@ -176,7 +176,6 @@ decl_module! {
Ok(()) Ok(())
} }
/// Swap a parachain with another parachain or parathread. The origin must be a `Parachain`. /// 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 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. /// 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> { ...@@ -222,30 +221,27 @@ impl<T: Config> Module<T> {
ensure!(!Paras::contains_key(id), Error::<T>::ParaAlreadyExists); ensure!(!Paras::contains_key(id), Error::<T>::ParaAlreadyExists);
ensure!(validation_code.0.starts_with(WASM_MAGIC), Error::<T>::DefinitelyNotWasm); 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 { let genesis = ParaGenesisArgs {
genesis_head, genesis_head,
validation_code, validation_code,
parachain: true, 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(()) Ok(())
} }
/// Deregister a parachain with the given ID. Must be called by root. /// Deregister a parachain with the given ID. Must be called by root.
pub fn deregister_parachain(id: ParaId) -> DispatchResult { 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); 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(()) Ok(())
} }
...@@ -267,10 +263,13 @@ mod tests { ...@@ -267,10 +263,13 @@ mod tests {
use frame_system::limits; use frame_system::limits;
use frame_support::{ use frame_support::{
traits::{Randomness, OnInitialize, OnFinalize}, traits::{Randomness, OnInitialize, OnFinalize},
assert_ok, parameter_types, assert_ok, assert_noop, parameter_types,
}; };
use keyring::Sr25519Keyring; 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 frame_support::traits::OneSessionHandler;
use crate::paras_registrar; use crate::paras_registrar;
...@@ -309,7 +308,7 @@ mod tests { ...@@ -309,7 +308,7 @@ mod tests {
parameter_types! { parameter_types! {
pub const BlockHashCount: u32 = 250; pub const BlockHashCount: u32 = 250;
pub BlockWeights: limits::BlockWeights = 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 = pub BlockLength: limits::BlockLength =
limits::BlockLength::max_with_normal_ratio(4 * 1024 * 1024, NORMAL_RATIO); limits::BlockLength::max_with_normal_ratio(4 * 1024 * 1024, NORMAL_RATIO);
} }
...@@ -370,7 +369,7 @@ mod tests { ...@@ -370,7 +369,7 @@ mod tests {
} }
parameter_types! { parameter_types! {
pub const Period: BlockNumber = 1; pub const Period: BlockNumber = 3;
pub const Offset: BlockNumber = 0; pub const Offset: BlockNumber = 0;
pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(17); pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
...@@ -432,6 +431,8 @@ mod tests { ...@@ -432,6 +431,8 @@ mod tests {
type WeightInfo = (); type WeightInfo = ();
} }
impl shared::Config for Test {}
impl dmp::Config for Test {} impl dmp::Config for Test {}
impl ump::Config for Test { impl ump::Config for Test {
...@@ -590,7 +591,7 @@ mod tests { ...@@ -590,7 +591,7 @@ mod tests {
Initializer::on_finalize(System::block_number()); Initializer::on_finalize(System::block_number());
} }
// Session change every 3 blocks. // Session change every 3 blocks.
if (b + 1) % 3 == 0 { if (b + 1) % Period::get() == 0 {
println!("New session at {}", System::block_number()); println!("New session at {}", System::block_number());
Initializer::on_new_session( Initializer::on_new_session(
false, false,
...@@ -601,6 +602,7 @@ mod tests { ...@@ -601,6 +602,7 @@ mod tests {
System::set_block_number(b + 1); System::set_block_number(b + 1);
println!("Initializing {}", System::block_number()); println!("Initializing {}", System::block_number());
System::on_initialize(System::block_number()); System::on_initialize(System::block_number());
Session::on_initialize(System::block_number());
Initializer::on_initialize(System::block_number()); Initializer::on_initialize(System::block_number());
} }
} }
...@@ -643,7 +645,7 @@ mod tests { ...@@ -643,7 +645,7 @@ mod tests {
assert_eq!(Balances::free_balance(3u64) + ParathreadDeposit::get(), orig_bal); assert_eq!(Balances::free_balance(3u64) + ParathreadDeposit::get(), orig_bal);
assert_eq!(Balances::reserved_balance(3u64), ParathreadDeposit::get()); assert_eq!(Balances::reserved_balance(3u64), ParathreadDeposit::get());
run_to_block(3); run_to_block(10);
assert_ok!(Registrar::deregister_parachain(2u32.into())); assert_ok!(Registrar::deregister_parachain(2u32.into()));
...@@ -690,10 +692,12 @@ mod tests { ...@@ -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(2u32.into()).into(), 8u32.into()));
assert_ok!(Registrar::swap(runtime_parachains::Origin::Parachain(8u32.into()).into(), 2u32.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 // Deregister a parathread that was originally a parachain
assert_ok!(Registrar::deregister_parathread(runtime_parachains::Origin::Parachain(2u32.into()).into())); assert_ok!(Registrar::deregister_parathread(runtime_parachains::Origin::Parachain(2u32.into()).into()));
run_to_block(12); run_to_block(21);
// Funds are correctly returned // Funds are correctly returned
assert_eq!(Balances::free_balance(1), initial_1_balance); assert_eq!(Balances::free_balance(1), initial_1_balance);
...@@ -712,20 +716,26 @@ mod tests { ...@@ -712,20 +716,26 @@ mod tests {
WASM_MAGIC.to_vec().into(), 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())); assert_ok!(Registrar::deregister_parachain(1u32.into()));
run_to_block(5); run_to_block(13);
assert_eq!(Parachains::lifecycle(1u32.into()), Some(ParaLifecycle::OffboardingParachain));
assert!(Registrar::register_parachain( assert_noop!(Registrar::register_parachain(
1u32.into(), 1u32.into(),
vec![1; 3].into(), vec![1; 3].into(),
WASM_MAGIC.to_vec().into(), WASM_MAGIC.to_vec().into(),
).is_err()); ), Error::<Test>::ParaAlreadyExists);
// Need 2 session changes to see the effect, which takes place by block 13.
run_to_block(18);
// The session will be changed on the 6th block, as part of finalization. The change assert!(Parachains::lifecycle(1u32.into()).is_none());
// will be observed on the 7th.
run_to_block(7);
assert_ok!(Registrar::register_parachain( assert_ok!(Registrar::register_parachain(
1u32.into(), 1u32.into(),
vec![1; 3].into(), vec![1; 3].into(),
......
...@@ -40,11 +40,15 @@ decl_error! { ...@@ -40,11 +40,15 @@ decl_error! {
pub enum Error for Module<T: Config> { pub enum Error for Module<T: Config> {
/// The specified parachain or parathread is not registered. /// The specified parachain or parathread is not registered.
ParaDoesntExist, 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 /// A DMP message couldn't be sent because it exceeds the maximum size allowed for a downward
/// message. /// message.
ExceedsMaxMessageSize, ExceedsMaxMessageSize,
/// The validation code provided doesn't start with the Wasm file magic string. /// The validation code provided doesn't start with the Wasm file magic string.
DefinitelyNotWasm,