diff --git a/polkadot/bridges/modules/call-dispatch/src/lib.rs b/polkadot/bridges/modules/call-dispatch/src/lib.rs index 426b3180a1..a7ea999aa7 100644 --- a/polkadot/bridges/modules/call-dispatch/src/lib.rs +++ b/polkadot/bridges/modules/call-dispatch/src/lib.rs @@ -56,7 +56,7 @@ pub enum CallOrigin { - /// An account that belongs to Root (priviledged origin). + /// An account that belongs to Root (privileged origin). Root, - /// A non-priviledged account. + /// A non-privileged account. /// /// The embedded account ID may or may not have a private key depending on the "owner" of the /// account (private key, pallet, proxy, etc.). diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index f7fb6946e9..84a760cb72 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -40,12 +40,12 @@ use runtime_parachains::{ }; use frame_support::{ construct_runtime, parameter_types, - traits::{EnsureOrigin, Filter, KeyOwnerProofSystem, Randomness}, + traits::{Filter, KeyOwnerProofSystem, Randomness}, weights::Weight, }; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - ApplyExtrinsicResult, KeyTypeId, Perbill, + ApplyExtrinsicResult, KeyTypeId, Perbill, ModuleId, transaction_validity::{TransactionValidity, TransactionSource, TransactionPriority}, traits::{ BlakeTwo256, Block as BlockT, OpaqueKeys, AccountIdLookup, @@ -62,8 +62,8 @@ 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, EnsureOneOf, EnsureSigned}; -use runtime_common::{paras_sudo_wrapper, paras_registrar, xcm_sender}; +use frame_system::EnsureRoot; +use runtime_common::{paras_sudo_wrapper, paras_registrar, xcm_sender, auctions, crowdloan, slots}; use runtime_parachains::origin as parachains_origin; use runtime_parachains::configuration as parachains_configuration; @@ -92,7 +92,7 @@ use constants::{time::*, currency::*, fee::*}; /// Constant values used within the runtime. pub mod constants; -mod propose_parachain; +mod validator_manager; // Make the WASM binary available. #[cfg(feature = "std")] @@ -208,14 +208,18 @@ construct_runtime! { Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event}, SessionInfo: parachains_session_info::{Pallet, Call, Storage}, + // Parachain Onboarding Pallets Registrar: paras_registrar::{Pallet, Call, Storage, Event}, + Auctions: auctions::{Pallet, Call, Storage, Event}, + Crowdloan: crowdloan::{Pallet, Call, Storage, Event}, + Slots: slots::{Pallet, Call, Storage, Event}, ParasSudoWrapper: paras_sudo_wrapper::{Pallet, Call}, // Sudo Sudo: pallet_sudo::{Pallet, Call, Storage, Event, Config}, - // Propose parachain pallet. - ProposeParachain: propose_parachain::{Pallet, Call, Storage, Event}, + // Validator Manager pallet. + ValidatorManager: validator_manager::{Pallet, Call, Storage, Event}, } } @@ -418,7 +422,7 @@ impl pallet_session::Config for Runtime { 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; @@ -600,7 +604,7 @@ impl paras_registrar::Config for Runtime { type Event = Event; type Origin = Origin; type Currency = Balances; - type OnSwap = (); + type OnSwap = (Crowdloan, Slots); type ParaDeposit = ParaDeposit; type DataDepositPerByte = DataDepositPerByte; type MaxCodeSize = MaxCodeSize; @@ -608,44 +612,60 @@ impl paras_registrar::Config for Runtime { type WeightInfo = paras_registrar::TestWeightInfo; } +parameter_types! { + pub const EndingPeriod: BlockNumber = 15 * MINUTES; +} + +impl auctions::Config for Runtime { + type Event = Event; + type Leaser = Slots; + type EndingPeriod = EndingPeriod; + type Randomness = pallet_babe::RandomnessFromOneEpochAgo; + type InitiateOrigin = EnsureRoot; + type WeightInfo = auctions::TestWeightInfo; +} + +parameter_types! { + pub const LeasePeriod: BlockNumber = 1 * DAYS; +} + +impl slots::Config for Runtime { + type Event = Event; + type Currency = Balances; + type Registrar = Registrar; + type LeasePeriod = LeasePeriod; + type WeightInfo = slots::TestWeightInfo; +} + +parameter_types! { + pub const CrowdloanId: ModuleId = ModuleId(*b"py/cfund"); + pub const SubmissionDeposit: Balance = 100 * DOLLARS; + pub const MinContribution: Balance = 1 * DOLLARS; + pub const RetirementPeriod: BlockNumber = 6 * HOURS; + pub const RemoveKeysLimit: u32 = 500; +} + +impl crowdloan::Config for Runtime { + type Event = Event; + type ModuleId = CrowdloanId; + type SubmissionDeposit = SubmissionDeposit; + type MinContribution = MinContribution; + type RetirementPeriod = RetirementPeriod; + type OrphanedFunds = (); + type RemoveKeysLimit = RemoveKeysLimit; + type Registrar = Registrar; + type Auctioneer = Auctions; + type WeightInfo = crowdloan::TestWeightInfo; +} + impl pallet_sudo::Config for Runtime { type Event = Event; 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 { +impl validator_manager::Config for Runtime { type Event = Event; - type MaxNameLength = MaxNameLength; - type ProposeDeposit = ProposeDeposit; - type PriviledgedOrigin = EnsureOneOf, PriviledgedOrigin>; + type PrivilegedOrigin = EnsureRoot; } #[cfg(not(feature = "disable-runtime-api"))] diff --git a/polkadot/runtime/rococo/src/propose_parachain.rs b/polkadot/runtime/rococo/src/propose_parachain.rs deleted file mode 100644 index 7c50a26a13..0000000000 --- a/polkadot/runtime/rococo/src/propose_parachain.rs +++ /dev/null @@ -1,421 +0,0 @@ -// 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 - + runtime_parachains::paras::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 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 where ValidatorId = ::ValidatorId { - /// 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), - /// New validators were added to the set. - ValidatorsRegistered(Vec), - /// Validators were removed from the set. - ValidatorsDeregistered(Vec), - } -} - -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, - /// Couldn't schedule parachain cleanup. - CouldntCleanup, - } -} - -decl_storage! { - trait Store for Module as ParachainProposer { - /// All the proposals. - Proposals: map hasher(twox_64_concat) ParaId => Option>>; - /// The validation WASM code of the parachain. - ParachainValidationCode: 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; - /// Validators that should be added. - ValidatorsToAdd: 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::::lifecycle(para_id).is_none(), - Error::::ParachainIdAlreadyTaken, - ); - ensure!(validation_code.0.starts_with(runtime_common::WASM_MAGIC), Error::::DefinitelyNotWasm); - - let active_validators = Session::::validators(); - let validators_to_retire = ValidatorsToRetire::::get(); - ensure!( - validators.iter().all(|v| !active_validators.contains(v) || validators_to_retire.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::Pallet::::reserve(&who, T::ProposeDeposit::get())?; - - let proposal = Proposal { - name: name.clone(), - proposer: who, - validators: validators.into(), - genesis_head, - balance, - }; - - Proposals::::insert(para_id, proposal); - ParachainValidationCode::insert(para_id, validation_code); - - Self::deposit_event(RawEvent::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(RawEvent::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); - ParachainValidationCode::remove(¶_id); - - pallet_balances::Pallet::::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); - } - runtime_parachains::schedule_para_cleanup::(para_id).map_err(|_| Error::::CouldntCleanup)?; - - ParachainInfo::::remove(¶_id); - info.validators.into_iter().for_each(|v| ValidatorsToRetire::::append(v)); - - pallet_balances::Pallet::::unreserve(&info.proposer, T::ProposeDeposit::get()); - } - - /// Add new validators to the set. - /// - /// The new validators will be active from current session + 2. - #[weight = 100_000] - fn register_validators( - origin, - validators: Vec, - ) { - T::PriviledgedOrigin::ensure_origin(origin)?; - - validators.clone().into_iter().for_each(|v| ValidatorsToAdd::::append(v)); - - Self::deposit_event(RawEvent::ValidatorsRegistered(validators)); - } - - /// Remove validators from the set. - /// - /// The removed validators will be deactivated from current session + 2. - #[weight = 100_000] - fn deregister_validators( - origin, - validators: Vec, - ) { - T::PriviledgedOrigin::ensure_origin(origin)?; - - validators.clone().into_iter().for_each(|v| ValidatorsToRetire::::append(v)); - - Self::deposit_event(RawEvent::ValidatorsDeregistered(validators)); - } - } -} - -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 validation_code = ParachainValidationCode::get(&id)?; - let genesis = ParaGenesisArgs { - genesis_head: proposal.genesis_head, - validation_code, - parachain: true, - }; - - // Not much we can do if this fails... - let _ = runtime_parachains::schedule_para_initialize::(*id, genesis); - - validators.extend(proposal.validators); - } - - ValidatorsToAdd::::take().into_iter().for_each(|v| { - if !validators.contains(&v) { - validators.push(v); - } - }); - - 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(RawEvent::ParachainRegistered(*id)); - - // Add some funds to the Parachain - let _ = pallet_balances::Pallet::::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) - } -} diff --git a/polkadot/runtime/rococo/src/validator_manager.rs b/polkadot/runtime/rococo/src/validator_manager.rs new file mode 100644 index 0000000000..194dcbe358 --- /dev/null +++ b/polkadot/runtime/rococo/src/validator_manager.rs @@ -0,0 +1,142 @@ +// 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 managing validators on Rococo. + +use frame_support::{ + decl_event, decl_error, decl_module, decl_storage, + traits::EnsureOrigin, +}; +use sp_staking::SessionIndex; +use sp_std::vec::Vec; + +type Session = pallet_session::Module; + +/// Configuration for the parachain proposer. +pub trait Config: pallet_session::Config { + /// The overreaching event type. + type Event: From> + Into<::Event>; + + /// Privileged origin that can add or remove validators. + type PrivilegedOrigin: EnsureOrigin<::Origin>; +} + +decl_event! { + pub enum Event where ValidatorId = ::ValidatorId { + /// New validators were added to the set. + ValidatorsRegistered(Vec), + /// Validators were removed from the set. + ValidatorsDeregistered(Vec), + } +} + +decl_error! { + pub enum Error for Module {} +} + +decl_storage! { + trait Store for Module as ParachainProposer { + /// Validators that should be retired, because their Parachain was deregistered. + ValidatorsToRetire: Vec; + /// Validators that should be added. + ValidatorsToAdd: Vec; + } +} + +decl_module! { + pub struct Module for enum Call where origin: ::Origin { + type Error = Error; + + fn deposit_event() = default; + + /// Add new validators to the set. + /// + /// The new validators will be active from current session + 2. + #[weight = 100_000] + fn register_validators( + origin, + validators: Vec, + ) { + T::PrivilegedOrigin::ensure_origin(origin)?; + + validators.clone().into_iter().for_each(|v| ValidatorsToAdd::::append(v)); + + Self::deposit_event(RawEvent::ValidatorsRegistered(validators)); + } + + /// Remove validators from the set. + /// + /// The removed validators will be deactivated from current session + 2. + #[weight = 100_000] + fn deregister_validators( + origin, + validators: Vec, + ) { + T::PrivilegedOrigin::ensure_origin(origin)?; + + validators.clone().into_iter().for_each(|v| ValidatorsToRetire::::append(v)); + + Self::deposit_event(RawEvent::ValidatorsDeregistered(validators)); + } + } +} + +impl Module {} + +impl pallet_session::SessionManager for Module { + fn new_session(new_index: SessionIndex) -> Option> { + if new_index <= 1 { + return None; + } + + 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); + } + }); + + ValidatorsToAdd::::take().into_iter().for_each(|v| { + if !validators.contains(&v) { + validators.push(v); + } + }); + + Some(validators) + } + + fn end_session(_: SessionIndex) {} + + fn start_session(_start_index: SessionIndex) {} +} + +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) + } +}