diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index e0a9aa1ec0..8ba8bad1f6 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -6520,6 +6520,7 @@ dependencies = [ "frame-support", "frame-system", "frame-system-rpc-runtime-api", + "hex-literal", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index dbe56c08cc..8ee46f3b9f 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -103,7 +103,14 @@ db = ["service/db"] full-node = [ "polkadot-node-core-av-store", ] -runtime-benchmarks = ["polkadot-runtime/runtime-benchmarks", "kusama-runtime/runtime-benchmarks", "westend-runtime/runtime-benchmarks"] + +runtime-benchmarks = [ + "polkadot-runtime/runtime-benchmarks", + "kusama-runtime/runtime-benchmarks", + "westend-runtime/runtime-benchmarks", + "rococo-runtime/runtime-benchmarks" +] + real-overseer = [ "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", diff --git a/polkadot/node/service/src/chain_spec.rs b/polkadot/node/service/src/chain_spec.rs index 67dece91f3..e8b01ecba3 100644 --- a/polkadot/node/service/src/chain_spec.rs +++ b/polkadot/node/service/src/chain_spec.rs @@ -856,7 +856,6 @@ fn rococo_staging_testnet_config_genesis(wasm_binary: &[u8]) -> rococo_runtime:: pallet_authority_discovery: Some(rococo_runtime::AuthorityDiscoveryConfig { keys: vec![], }), - pallet_staking: Some(Default::default()), pallet_sudo: Some(rococo_runtime::SudoConfig { key: endowed_accounts[0].clone(), }), @@ -1343,7 +1342,6 @@ pub fn rococo_testnet_genesis( pallet_authority_discovery: Some(rococo_runtime::AuthorityDiscoveryConfig { keys: vec![], }), - pallet_staking: Some(Default::default()), pallet_sudo: Some(rococo_runtime::SudoConfig { key: root_key }), parachains_configuration: Some(rococo_runtime::ParachainsConfigurationConfig { config: polkadot_runtime_parachains::configuration::HostConfiguration { diff --git a/polkadot/runtime/common/src/lib.rs b/polkadot/runtime/common/src/lib.rs index 45f1a8856b..3b5246b4bf 100644 --- a/polkadot/runtime/common/src/lib.rs +++ b/polkadot/runtime/common/src/lib.rs @@ -52,7 +52,7 @@ pub type NegativeImbalance = as Currency<; + UpcomingParas get(fn upcoming_paras): Vec; /// Upcoming paras instantiation arguments. UpcomingParasGenesis: map hasher(twox_64_concat) ParaId => Option; /// Paras that are to be cleaned up at the end of the session. diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index b5260bb626..4515fbb481 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -10,6 +10,7 @@ parity-scale-codec = { version = "1.3.6", default-features = false, features = [ serde = { version = "1.0.118", default-features = false } serde_derive = { version = "1.0.117", optional = true } smallvec = "1.6.1" +hex-literal = "0.3.1" frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -113,3 +114,16 @@ std = [ # runtime without clashing with the runtime api exported functions # in WASM. disable-runtime-api = [] +runtime-benchmarks = [ + "runtime-common/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "pallet-babe/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-grandpa/runtime-benchmarks", + "pallet-im-online/runtime-benchmarks", + "pallet-indices/runtime-benchmarks", + "pallet-staking/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", +] diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index b75a29309f..c97aa3ec69 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -32,22 +32,18 @@ use primitives::v1::{ SessionInfo as SessionInfoData, }; use runtime_common::{ - SlowAdjustingFeeUpdate, - impls::ToAuthor, - BlockHashCount, BlockWeights, BlockLength, RocksDbWeight, OffchainSolutionWeightLimit, + SlowAdjustingFeeUpdate, impls::ToAuthor, BlockHashCount, BlockWeights, BlockLength, RocksDbWeight, }; use runtime_parachains::{ self, runtime_api_impl::v1 as runtime_api_impl, }; use frame_support::{ - parameter_types, construct_runtime, debug, - traits::{KeyOwnerProofSystem, Filter}, - weights::Weight, + parameter_types, construct_runtime, debug, traits::{KeyOwnerProofSystem, Filter, EnsureOrigin}, weights::Weight, }; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - ApplyExtrinsicResult, KeyTypeId, Perbill, curve::PiecewiseLinear, + ApplyExtrinsicResult, KeyTypeId, Perbill, transaction_validity::{TransactionValidity, TransactionSource, TransactionPriority}, traits::{ BlakeTwo256, Block as BlockT, OpaqueKeys, IdentityLookup, @@ -64,7 +60,7 @@ use pallet_grandpa::{AuthorityId as GrandpaId, fg_primitives}; use sp_core::OpaqueMetadata; use sp_staking::SessionIndex; use pallet_session::historical as session_historical; -use frame_system::EnsureRoot; +use frame_system::{EnsureRoot, EnsureOneOf, EnsureSigned}; use runtime_common::{paras_sudo_wrapper, paras_registrar}; use runtime_parachains::origin as parachains_origin; @@ -78,10 +74,8 @@ use runtime_parachains::dmp as parachains_dmp; use runtime_parachains::ump as parachains_ump; use runtime_parachains::hrmp as parachains_hrmp; use runtime_parachains::scheduler as parachains_scheduler; -use runtime_parachains::reward_points::RewardValidatorsWithEraPoints; pub use pallet_balances::Call as BalancesCall; -pub use pallet_staking::StakerStatus; use polkadot_parachain::primitives::Id as ParaId; use xcm::v0::{MultiLocation, NetworkId}; @@ -91,10 +85,11 @@ use xcm_builder::{ CurrencyAdapter as XcmCurrencyAdapter, ChildParachainAsNative, SignedAccountId32AsNative, ChildSystemParachainAsSuperuser, LocationInverter, }; +use constants::{time::*, currency::*, fee::*}; /// Constant values used within the runtime. pub mod constants; -use constants::{time::*, currency::*, fee::*}; +mod propose_parachain; // Make the WASM binary available. #[cfg(feature = "std")] @@ -105,7 +100,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("rococo"), impl_name: create_runtime_str!("parity-rococo-v1"), authoring_version: 0, - spec_version: 14, + spec_version: 15, impl_version: 0, #[cfg(not(feature = "disable-runtime-api"))] apis: RUNTIME_API_VERSIONS, @@ -182,7 +177,6 @@ construct_runtime! { // Consensus support. Authorship: pallet_authorship::{Module, Call, Storage}, - Staking: pallet_staking::{Module, Call, Storage, Config, Event, ValidateUnsigned}, Offences: pallet_offences::{Module, Call, Storage, Event}, Historical: session_historical::{Module}, Session: pallet_session::{Module, Call, Storage, Event, Config}, @@ -208,6 +202,9 @@ construct_runtime! { // Sudo Sudo: pallet_sudo::{Module, Call, Storage, Event, Config}, + + // Propose parachain pallet. + ProposeParachain: propose_parachain::{Module, Call, Storage, Event}, } } @@ -304,35 +301,15 @@ impl frame_system::offchain::SigningTypes for Runtime { type Signature = Signature; } +/// Special `FullIdentificationOf` implementation that is returning for every input `Some(Default::default())`. +pub struct FullIdentificationOf; +impl sp_runtime::traits::Convert> for FullIdentificationOf { + fn convert(_: AccountId) -> Option<()> { Some(Default::default()) } +} + impl pallet_session::historical::Config for Runtime { - type FullIdentification = pallet_staking::Exposure; - type FullIdentificationOf = pallet_staking::ExposureOf; -} - -pallet_staking_reward_curve::build! { - const REWARD_CURVE: PiecewiseLinear<'static> = curve!( - min_inflation: 0_025_000, - max_inflation: 0_100_000, - ideal_stake: 0_500_000, - falloff: 0_050_000, - max_piece_count: 40, - test_precision: 0_005_000, - ); -} - -parameter_types! { - // Six sessions in an era (6 hours). - pub const SessionsPerEra: SessionIndex = 6; - // 28 eras for unbonding (7 days). - pub const BondingDuration: pallet_staking::EraIndex = 28; - // 27 eras in which slashes can be cancelled (~7 days). - pub const SlashDeferDuration: pallet_staking::EraIndex = 27; - pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; - pub const MaxNominatorRewardedPerValidator: u32 = 64; - // quarter of the last session will be for election. - pub ElectionLookahead: BlockNumber = EpochDurationInBlocks::get() / 4; - pub const MaxIterations: u32 = 10; - pub MinSolutionScoreBump: Perbill = Perbill::from_rational_approximation(5u32, 10_000); + type FullIdentification = (); + type FullIdentificationOf = FullIdentificationOf; } parameter_types! { @@ -353,32 +330,6 @@ impl pallet_im_online::Config for Runtime { type WeightInfo = (); } -impl pallet_staking::Config for Runtime { - type Currency = Balances; - type UnixTime = Timestamp; - type CurrencyToVote = frame_support::traits::U128CurrencyToVote; - type RewardRemainder = (); - type Event = Event; - type Slash = (); - type Reward = (); - type SessionsPerEra = SessionsPerEra; - type BondingDuration = BondingDuration; - type SlashDeferDuration = SlashDeferDuration; - // A majority of the council can cancel the slash. - type SlashCancelOrigin = EnsureRoot; - type SessionInterface = Self; - type RewardCurve = RewardCurve; - type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; - type NextNewSession = Session; - type ElectionLookahead = ElectionLookahead; - type Call = Call; - type UnsignedPriority = StakingUnsignedPriority; - type MaxIterations = MaxIterations; - type OffchainSolutionWeightLimit = OffchainSolutionWeightLimit; - type MinSolutionScoreBump = MinSolutionScoreBump; - type WeightInfo = (); -} - parameter_types! { pub const ExistentialDeposit: Balance = 1 * CENTS; pub const MaxLocks: u32 = 50; @@ -414,7 +365,7 @@ parameter_types! { impl pallet_offences::Config for Runtime { type Event = Event; type IdentificationTuple = pallet_session::historical::IdentificationTuple; - type OnOffenceHandler = Staking; + type OnOffenceHandler = (); type WeightSoftLimit = OffencesWeightSoftLimit; } @@ -445,13 +396,19 @@ parameter_types! { pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(17); } +/// Special `ValidatorIdOf` implementation that is just returning the input as result. +pub struct ValidatorIdOf; +impl sp_runtime::traits::Convert> for ValidatorIdOf { + fn convert(a: AccountId) -> Option { Some(a) } +} + impl pallet_session::Config for Runtime { type Event = Event; type ValidatorId = AccountId; - type ValidatorIdOf = pallet_staking::StashOf; + type ValidatorIdOf = ValidatorIdOf; type ShouldEndSession = Babe; type NextSessionRotation = Babe; - type SessionManager = pallet_session::historical::NoteHistoricalRoot; + type SessionManager = pallet_session::historical::NoteHistoricalRoot; type SessionHandler = ::KeyTypeIdProviders; type Keys = SessionKeys; type DisabledValidatorsThreshold = DisabledValidatorsThreshold; @@ -531,16 +488,23 @@ impl pallet_authorship::Config for Runtime { type FindAuthor = pallet_session::FindAccountFromAuthorIndex; type UncleGenerations = UncleGenerations; type FilterUncle = (); - type EventHandler = (Staking, ImOnline); + type EventHandler = ImOnline; } impl parachains_origin::Config for Runtime {} impl parachains_configuration::Config for Runtime {} +/// Special `RewardValidators` that does nothing ;) +pub struct RewardValidators; +impl runtime_parachains::inclusion::RewardValidators for RewardValidators { + fn reward_backing(_: impl IntoIterator) {} + fn reward_bitfields(_: impl IntoIterator) {} +} + impl parachains_inclusion::Config for Runtime { type Event = Event; - type RewardValidators = RewardValidatorsWithEraPoints; + type RewardValidators = RewardValidators; } impl parachains_paras::Config for Runtime { @@ -621,6 +585,41 @@ impl pallet_sudo::Config for Runtime { type Call = Call; } +/// Priviledged origin used by propose parachain. +pub struct PriviledgedOrigin; + +impl EnsureOrigin for PriviledgedOrigin { + type Success = (); + + fn try_origin(o: Origin) -> Result { + let allowed = [ + hex_literal::hex!("b44c58e50328768ac06ed44b842bfa69d86ea10f60bc36156c9ffc5e00867220"), + hex_literal::hex!("762a6a38ba72b139cba285a39a6766e02046fb023f695f5ecf7f48b037c0dd6b") + ]; + + let origin = o.clone(); + match EnsureSigned::try_origin(o) { + Ok(who) if allowed.iter().any(|a| a == &who.as_ref()) => Ok(()), + _ => Err(origin), + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> Origin { Origin::root() } +} + +parameter_types! { + pub const ProposeDeposit: Balance = 1000 * DOLLARS; + pub const MaxNameLength: u32 = 20; +} + +impl propose_parachain::Config for Runtime { + type Event = Event; + type MaxNameLength = MaxNameLength; + type ProposeDeposit = ProposeDeposit; + type PriviledgedOrigin = EnsureOneOf, PriviledgedOrigin>; +} + #[cfg(not(feature = "disable-runtime-api"))] sp_api::impl_runtime_apis! { impl sp_api::Core for Runtime { diff --git a/polkadot/runtime/rococo/src/propose_parachain.rs b/polkadot/runtime/rococo/src/propose_parachain.rs new file mode 100644 index 0000000000..3af1d89801 --- /dev/null +++ b/polkadot/runtime/rococo/src/propose_parachain.rs @@ -0,0 +1,380 @@ +// Copyright 2020 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 . + +//! A pallet for proposing a parachain for Rococo. +//! +//! This pallet works as registration of parachains for Rococo. The idea is to have +//! the registration of community provides parachains being handled by this pallet. +//! People will be able to propose their parachain for registration. This proposal +//! will need to be improved by some priviledged account. After approval the workflow +//! is the following: +//! +//! 1. On start of the next session the pallet announces the new relay chain validators. +//! +//! 2. The session after announcing the new relay chain validators, they will be active. At the +//! switch to this session, the parachain will be registered and is allowed to produce blocks. +//! +//! When deregistering a parachain, we basically reverse the operations. + +use frame_support::{ + decl_event, decl_error, decl_module, traits::{Get, ReservableCurrency, EnsureOrigin, Currency}, + decl_storage, ensure, IterableStorageMap, +}; +use primitives::v1::{Id as ParaId, HeadData, ValidationCode}; +use polkadot_parachain::primitives::AccountIdConversion; +use frame_system::{ensure_signed, EnsureOneOf, EnsureSigned}; +use sp_runtime::Either; +use sp_staking::SessionIndex; +use sp_std::vec::Vec; +use runtime_parachains::paras::ParaGenesisArgs; + +type EnsurePriviledgedOrSigned = EnsureOneOf< + ::AccountId, + ::PriviledgedOrigin, + EnsureSigned<::AccountId> +>; + +type Session = pallet_session::Module; + +type BalanceOf = ::Balance; + +/// Configuration for the parachain proposer. +pub trait Config: pallet_session::Config + + pallet_balances::Config + + pallet_balances::Config + + runtime_parachains::paras::Config + + runtime_parachains::dmp::Config + + runtime_parachains::ump::Config + + runtime_parachains::hrmp::Config +{ + /// The overreaching event type. + type Event: From + Into<::Event>; + + /// The maximum name length of a parachain. + type MaxNameLength: Get; + + /// The amount that should be deposited when creating a proposal. + type ProposeDeposit: Get>; + + /// Priviledged origin that can approve/cancel/deregister parachain and proposals. + type PriviledgedOrigin: EnsureOrigin<::Origin>; +} + +/// A proposal for adding a parachain to the relay chain. +#[derive(parity_scale_codec::Encode, parity_scale_codec::Decode)] +struct Proposal { + /// The account that proposed this parachain. + proposer: AccountId, + /// The validation WASM code of the parachain. + validation_code: ValidationCode, + /// The genesis head state of the parachain. + genesis_head: HeadData, + /// The validators for the relay chain provided by the parachain. + validators: Vec, + /// The name of the parachain. + name: Vec, + /// The balance that the parachain should receive. + balance: Balance, +} + +/// Information about the registered parachain. +#[derive(parity_scale_codec::Encode, parity_scale_codec::Decode)] +struct RegisteredParachainInfo { + /// The validators for the relay chain provided by the parachain. + validators: Vec, + /// The account that proposed the parachain. + proposer: AccountId, +} + +decl_event! { + pub enum Event { + /// A parachain was proposed for registration. + ParachainProposed(Vec, ParaId), + /// A parachain was approved and is scheduled for being activated. + ParachainApproved(ParaId), + /// A parachain was registered and is now running. + ParachainRegistered(ParaId), + } +} + +decl_error! { + pub enum Error for Module { + /// The name of the parachain is too long. + NameTooLong, + /// The requested parachain id is already registered. + ParachainIdAlreadyTaken, + /// The requested parachain id is already proposed for another parachain. + ParachainIdAlreadyProposed, + /// Could not find the parachain proposal. + ProposalNotFound, + /// Not authorized to do a certain operation. + NotAuthorized, + /// A validator is already registered in the active validator set. + ValidatorAlreadyRegistered, + /// No information about the registered parachain found. + ParachainInfoNotFound, + /// Parachain is already approved for registration. + ParachainAlreadyApproved, + /// Parachain is already scheduled for registration. + ParachainAlreadyScheduled, + /// The given WASM blob is definitley not valid. + DefinitelyNotWasm, + /// Registration requires at least one validator. + AtLeastOneValidatorRequired, + } +} + +decl_storage! { + trait Store for Module as ParachainProposer { + /// All the proposals. + Proposals: map hasher(twox_64_concat) ParaId => Option>>; + /// Proposals that are approved. + ApprovedProposals: Vec; + /// Proposals that are scheduled at for a fixed session to be applied. + ScheduledProposals: map hasher(twox_64_concat) SessionIndex => Vec; + /// Information about the registered parachains. + ParachainInfo: map hasher(twox_64_concat) ParaId => Option>; + /// Validators that should be retired, because their Parachain was deregistered. + ValidatorsToRetire: Vec; + } +} + +decl_module! { + pub struct Module for enum Call where origin: ::Origin { + type Error = Error; + + /// The maximum name length of a parachain. + const MaxNameLength: u32 = T::MaxNameLength::get(); + + /// The deposit that will be reserved when proposing a parachain. + const ProposeDeposit: BalanceOf = T::ProposeDeposit::get(); + + fn deposit_event() = default; + + /// Propose a new parachain + /// + /// This requires: + /// - `para_id`: The id of the parachain. + /// - `name`: The name of the parachain. + /// - `validation_function`: The wasm runtime of the parachain. + /// - `initial_head_state`: The genesis state of the parachain. + /// - `validators`: Validators that will validate for the relay chain, needs to be at least one. + /// - `balance`: The initial balance of the parachain on the relay chain. + /// + /// It will reserve a deposit from the sender account over the lifetime of the chain. + #[weight = 1_000_000] + fn propose_parachain( + origin, + para_id: ParaId, + name: Vec, + validation_code: ValidationCode, + genesis_head: HeadData, + validators: Vec, + balance: BalanceOf, + ) { + let who = ensure_signed(origin)?; + + ensure!(name.len() <= T::MaxNameLength::get() as usize, Error::::NameTooLong); + ensure!(validators.len() > 0, Error::::AtLeastOneValidatorRequired); + ensure!(!Proposals::::contains_key(¶_id), Error::::ParachainIdAlreadyProposed); + ensure!( + !runtime_parachains::paras::Module::::parachains().contains(¶_id), + Error::::ParachainIdAlreadyTaken, + ); + ensure!( + !runtime_parachains::paras::Module::::upcoming_paras().contains(¶_id), + Error::::ParachainIdAlreadyTaken, + ); + ensure!(validation_code.0.starts_with(runtime_common::WASM_MAGIC), Error::::DefinitelyNotWasm); + + let active_validators = Session::::validators(); + ensure!( + validators.iter().all(|v| !active_validators.contains(v)), + Error::::ValidatorAlreadyRegistered, + ); + Proposals::::iter().try_for_each(|(_, prop)| + if validators.iter().all(|v| !prop.validators.contains(v)) { + Ok(()) + } else { + Err(Error::::ValidatorAlreadyRegistered) + } + )?; + + pallet_balances::Module::::reserve(&who, T::ProposeDeposit::get())?; + + let proposal = Proposal { + name: name.clone(), + proposer: who, + validators: validators.into(), + genesis_head, + validation_code, + balance, + }; + + Proposals::::insert(para_id, proposal); + + Self::deposit_event(Event::ParachainProposed(name, para_id)); + } + + /// Approve a parachain proposal. + #[weight = 100_000] + fn approve_proposal( + origin, + para_id: ParaId, + ) { + T::PriviledgedOrigin::ensure_origin(origin)?; + + ensure!(Proposals::::contains_key(¶_id), Error::::ProposalNotFound); + + Self::is_approved_or_scheduled(para_id)?; + + ApprovedProposals::append(para_id); + + Self::deposit_event(Event::ParachainApproved(para_id)); + } + + /// Cancel a parachain proposal. + /// + /// This also unreserves the deposit. + #[weight = 100_000] + fn cancel_proposal(origin, para_id: ParaId) { + let who = match EnsurePriviledgedOrSigned::::ensure_origin(origin)? { + Either::Left(_) => None, + Either::Right(who) => Some(who), + }; + + Self::is_approved_or_scheduled(para_id)?; + + let proposal = Proposals::::get(¶_id).ok_or(Error::::ProposalNotFound)?; + + if let Some(who) = who { + ensure!(who == proposal.proposer, Error::::NotAuthorized); + } + + Proposals::::remove(¶_id); + + pallet_balances::Module::::unreserve(&proposal.proposer, T::ProposeDeposit::get()); + } + + /// Deregister a parachain that was already successfully registered in the relay chain. + #[weight = 100_000] + fn deregister_parachain(origin, para_id: ParaId) { + let who = match EnsurePriviledgedOrSigned::::ensure_origin(origin)? { + Either::Left(_) => None, + Either::Right(who) => Some(who), + }; + + let info = ParachainInfo::::get(¶_id).ok_or(Error::::ParachainInfoNotFound)?; + + if let Some(who) = who { + ensure!(who == info.proposer, Error::::NotAuthorized); + } + + ParachainInfo::::remove(¶_id); + info.validators.into_iter().for_each(|v| ValidatorsToRetire::::append(v)); + runtime_parachains::schedule_para_cleanup::(para_id); + + pallet_balances::Module::::unreserve(&info.proposer, T::ProposeDeposit::get()); + } + } +} + +impl Module { + /// Returns wether the given `para_id` approval is approved or already scheduled. + fn is_approved_or_scheduled(para_id: ParaId) -> frame_support::dispatch::DispatchResult { + if ApprovedProposals::get().iter().any(|p| *p == para_id) { + return Err(Error::::ParachainAlreadyApproved.into()) + } + + if ScheduledProposals::get(&Session::::current_index() + 1).iter().any(|p| *p == para_id) { + return Err(Error::::ParachainAlreadyScheduled.into()) + } + + Ok(()) + } +} + +impl pallet_session::SessionManager for Module { + fn new_session(new_index: SessionIndex) -> Option> { + if new_index <= 1 { + return None; + } + + let proposals = ApprovedProposals::take(); + + let mut validators = Session::::validators(); + + ValidatorsToRetire::::take().iter().for_each(|v| { + if let Some(pos) = validators.iter().position(|r| r == v) { + validators.swap_remove(pos); + } + }); + + // Schedule all approved proposals + for (id, proposal) in proposals.iter().filter_map(|id| Proposals::::get(&id).map(|p| (id, p))) { + ScheduledProposals::append(new_index, id); + + let genesis = ParaGenesisArgs { + genesis_head: proposal.genesis_head, + validation_code: proposal.validation_code, + parachain: true, + }; + + runtime_parachains::schedule_para_initialize::(*id, genesis); + + validators.extend(proposal.validators); + } + + Some(validators) + } + + fn end_session(_: SessionIndex) {} + + fn start_session(start_index: SessionIndex) { + let proposals = ScheduledProposals::take(&start_index); + + // Register all parachains that are allowed to start with the new session. + for (id, proposal) in proposals.iter().filter_map(|id| Proposals::::take(&id).map(|p| (id, p))) { + Self::deposit_event(Event::ParachainRegistered(*id)); + + // Add some funds to the Parachain + let _ = pallet_balances::Module::::deposit_creating(&id.into_account(), proposal.balance); + + let info = RegisteredParachainInfo { + proposer: proposal.proposer, + validators: proposal.validators, + }; + ParachainInfo::::insert(id, info); + } + } +} + +impl pallet_session::historical::SessionManager for Module { + fn new_session( + new_index: SessionIndex, + ) -> Option> { + >::new_session(new_index) + .map(|r| r.into_iter().map(|v| (v, Default::default())).collect()) + } + + fn start_session(start_index: SessionIndex) { + >::start_session(start_index) + } + + fn end_session(end_index: SessionIndex) { + >::end_session(end_index) + } +}