From 3183402a541226a4eba56e45823b226b6147eb15 Mon Sep 17 00:00:00 2001 From: Nikolaos Dymitriadis Date: Wed, 19 Feb 2025 10:25:24 +0100 Subject: [PATCH] change: include candidate type and MC pkey in committee data Signed-off-by: Nikolaos Dymitriadis --- Cargo.lock | 1 + changelog.md | 3 + node/node/src/staging.rs | 3 +- node/node/src/testnet.rs | 3 +- node/runtime/Cargo.toml | 1 + node/runtime/src/lib.rs | 60 ++++---- node/runtime/src/mock.rs | 1 + .../session-validator-management/README.md | 9 ++ .../benchmarking/src/lib.rs | 8 +- .../benchmarking/src/mock.rs | 3 +- .../session-validator-management/src/lib.rs | 70 +++++---- .../src/migrations/README.md | 51 +++++++ .../src/migrations/mod.rs | 2 + .../src/migrations/v0.rs | 52 +++++++ .../src/migrations/v1.rs | 134 ++++++++++++++++++ .../session-validator-management/src/mock.rs | 1 + .../src/create_chain_spec/mod.rs | 11 +- .../src/create_chain_spec/tests.rs | 28 ++-- .../authority-selection-inherents/Cargo.toml | 2 +- .../src/authority_selection_inputs.rs | 7 +- .../src/filter_invalid_candidates.rs | 18 +++ .../authority-selection-inherents/src/lib.rs | 49 +++++++ toolkit/primitives/session-manager/src/lib.rs | 3 +- .../src/pallet_session_support.rs | 3 +- .../session-validator-management/src/lib.rs | 17 +++ 25 files changed, 451 insertions(+), 89 deletions(-) create mode 100644 toolkit/pallets/session-validator-management/src/migrations/README.md create mode 100644 toolkit/pallets/session-validator-management/src/migrations/mod.rs create mode 100644 toolkit/pallets/session-validator-management/src/migrations/v0.rs create mode 100644 toolkit/pallets/session-validator-management/src/migrations/v1.rs diff --git a/Cargo.lock b/Cargo.lock index 486f8b124..8c15e43d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10023,6 +10023,7 @@ dependencies = [ "plutus", "pretty_assertions", "scale-info", + "serde", "serde_json", "session-manager", "sidechain-domain", diff --git a/changelog.md b/changelog.md index c1e8c05a5..5172d88b2 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,9 @@ WARNING: Benchmarking command has been removed, because `frame-benchmarking-cli` mainnet or one of the official testnets, you don't need to change anything. Otherwise, the duration can be set through `MC__SLOT_DURATION_MILLIS` environment variable. * e2e-tests: updated python to 3.12 and libs versions. +* Committee member data stored by the Session Validator Management Pallet is now fullly generic. To migrate to this version, +define your own `CommitteeMember` type and implement the trait `CommitteeMember` for it. See the `CommitteeMember` +type implemented in `node/runtime/src/lib.rs` for reference using Ariadne. ## Removed diff --git a/node/node/src/staging.rs b/node/node/src/staging.rs index 97e9c53be..cf3e93251 100644 --- a/node/node/src/staging.rs +++ b/node/node/src/staging.rs @@ -1,5 +1,6 @@ use crate::chain_spec::get_account_id_from_seed; use crate::chain_spec::*; +use authority_selection_inherents::CommitteeMember; use sc_service::ChainType; use sidechain_domain::*; use sidechain_runtime::{ @@ -143,7 +144,7 @@ pub fn staging_genesis( session_committee_management: SessionCommitteeManagementConfig { initial_authorities: initial_authorities .into_iter() - .map(|keys| (keys.cross_chain, keys.session)) + .map(|keys| CommitteeMember::permissioned(keys.cross_chain, keys.session)) .collect(), main_chain_scripts: sp_session_validator_management::MainChainScripts::read_from_env()?, }, diff --git a/node/node/src/testnet.rs b/node/node/src/testnet.rs index e81c735ed..32c79d39b 100644 --- a/node/node/src/testnet.rs +++ b/node/node/src/testnet.rs @@ -1,4 +1,5 @@ use crate::chain_spec::*; +use authority_selection_inherents::CommitteeMember; use sc_service::ChainType; use sidechain_domain::*; use sidechain_runtime::{ @@ -195,7 +196,7 @@ pub fn testnet_genesis( session_committee_management: SessionCommitteeManagementConfig { initial_authorities: initial_authorities .into_iter() - .map(|keys| (keys.cross_chain, keys.session)) + .map(|keys| CommitteeMember::permissioned(keys.cross_chain, keys.session)) .collect(), main_chain_scripts: sp_session_validator_management::MainChainScripts::read_from_env()?, }, diff --git a/node/runtime/Cargo.toml b/node/runtime/Cargo.toml index 225522fdc..10fec763a 100644 --- a/node/runtime/Cargo.toml +++ b/node/runtime/Cargo.toml @@ -15,6 +15,7 @@ log = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } derive-new = { workspace = true } +serde = { workspace = true } serde_json = { workspace = true, default-features = false, features = [ "alloc", ] } diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index dcf8ab7c5..ed6626bef 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -8,7 +8,6 @@ extern crate frame_benchmarking; extern crate alloc; -// A few exports that help ease life for downstream crates. use authority_selection_inherents::authority_selection_inputs::AuthoritySelectionInputs; use authority_selection_inherents::filter_invalid_candidates::{ validate_permissioned_candidate_data, PermissionedCandidateDataError, RegistrationDataError, @@ -18,24 +17,14 @@ use authority_selection_inherents::select_authorities::select_authorities; use frame_support::genesis_builder_helper::{build_state, get_preset}; use frame_support::weights::constants::RocksDbWeight as RuntimeDbWeight; use frame_support::BoundedVec; -pub use frame_support::{ +use frame_support::{ construct_runtime, parameter_types, - traits::{ - ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, KeyOwnerProofSystem, Randomness, - StorageInfo, - }, - weights::{ - constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_REF_TIME_PER_SECOND}, - IdentityFee, - }, - PalletId, StorageValue, + traits::{ConstBool, ConstU128, ConstU32, ConstU64, ConstU8}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, IdentityFee}, }; -pub use frame_system::Call as SystemCall; use opaque::SessionKeys; -pub use pallet_balances::Call as BalancesCall; use pallet_grandpa::AuthorityId as GrandpaId; -pub use pallet_session_validator_management; -pub use pallet_timestamp::Call as TimestampCall; +use pallet_session_validator_management; use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier}; use session_manager::ValidatorManagementSessionManager; use sidechain_domain::{ @@ -44,11 +33,7 @@ use sidechain_domain::{ }; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::hexdisplay::HexDisplay; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; -#[cfg(any(feature = "std", test))] -pub use sp_runtime::BuildStorage; -use sp_runtime::DispatchResult; +use sp_core::{crypto::KeyTypeId, hexdisplay::HexDisplay, OpaqueMetadata}; use sp_runtime::{ generic, impl_opaque_keys, traits::{ @@ -56,9 +41,9 @@ use sp_runtime::{ Verify, }, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, MultiSignature, + ApplyExtrinsicResult, DispatchResult, MultiSignature, Perbill, }; -pub use sp_runtime::{Perbill, Permill}; +use sp_session_validator_management::CommitteeMember; use sp_sidechain::SidechainStatus; use sp_std::prelude::*; #[cfg(feature = "std")] @@ -413,20 +398,19 @@ impl pallet_session_validator_management::Config for Runtime { type AuthoritySelectionInputs = AuthoritySelectionInputs; type ScEpochNumber = ScEpochNumber; type WeightInfo = pallet_session_validator_management::weights::SubstrateWeight; + type CommitteeMember = + authority_selection_inherents::CommitteeMember; fn select_authorities( input: AuthoritySelectionInputs, sidechain_epoch: ScEpochNumber, - ) -> Option> { - let committee = select_authorities::( - Sidechain::genesis_utxo(), - input, - sidechain_epoch, - )? - .into_iter() - .map(|member| (member.account_id().clone(), member.account_keys().clone())) - .collect(); - Some(BoundedVec::truncate_from(committee)) + ) -> Option> { + Some(BoundedVec::truncate_from( + select_authorities(Sidechain::genesis_utxo(), input, sidechain_epoch)? + .into_iter() + .map(|member| member.into()) + .collect(), + )) } fn current_epoch_number() -> ScEpochNumber { @@ -543,6 +527,10 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// The payload being signed in transactions. pub type SignedPayload = generic::SignedPayload; +pub type Migrations = ( + pallet_session_validator_management::migrations::v1::LegacyToV1Migration, + // More migrations can be added here +); /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -550,6 +538,7 @@ pub type Executive = frame_executive::Executive< frame_system::ChainContext, Runtime, AllPalletsWithSystem, + Migrations, >; #[cfg(feature = "runtime-benchmarks")] @@ -850,7 +839,12 @@ impl_runtime_apis! { SessionCommitteeManagement::get_next_unset_epoch_number() } fn calculate_committee(authority_selection_inputs: AuthoritySelectionInputs, sidechain_epoch: ScEpochNumber) -> Option> { - SessionCommitteeManagement::calculate_committee(authority_selection_inputs, sidechain_epoch) + Some( + SessionCommitteeManagement::calculate_committee(authority_selection_inputs, sidechain_epoch)? + .into_iter() + .map(|member| (member.authority_id(), member.authority_keys())) + .collect() + ) } fn get_main_chain_scripts() -> sp_session_validator_management::MainChainScripts { SessionCommitteeManagement::get_main_chain_scripts() diff --git a/node/runtime/src/mock.rs b/node/runtime/src/mock.rs index 3c6dee0db..4a781a38e 100644 --- a/node/runtime/src/mock.rs +++ b/node/runtime/src/mock.rs @@ -149,6 +149,7 @@ impl pallet_session_validator_management::Config for Test { type AuthorityKeys = TestSessionKeys; type AuthoritySelectionInputs = AuthoritySelectionInputs; type ScEpochNumber = ScEpochNumber; + type CommitteeMember = (Self::AuthorityId, Self::AuthorityKeys); /// Mock simply selects all valid registered candidates as validators. fn select_authorities( diff --git a/toolkit/pallets/session-validator-management/README.md b/toolkit/pallets/session-validator-management/README.md index e69de29bb..8f06c52c6 100644 --- a/toolkit/pallets/session-validator-management/README.md +++ b/toolkit/pallets/session-validator-management/README.md @@ -0,0 +1,9 @@ +# Session Validator Management Pallet + +This pallet provides a way to rotate session validators based on arbitrary +inputs and selection algorithm. + +## Migrations + +This pallet's storage has changed compared to its legacy version. See +[src/migrations/README.md] for more information. diff --git a/toolkit/pallets/session-validator-management/benchmarking/src/lib.rs b/toolkit/pallets/session-validator-management/benchmarking/src/lib.rs index eea0f8a4f..b9036fd5f 100644 --- a/toolkit/pallets/session-validator-management/benchmarking/src/lib.rs +++ b/toolkit/pallets/session-validator-management/benchmarking/src/lib.rs @@ -37,7 +37,7 @@ fn set_epoch_number(epoch: u64) { } use pallet_session_validator_management::Call; -#[benchmarks] +#[benchmarks(where ::CommitteeMember: From<(::AuthorityId, ::AuthorityKeys)>)] pub mod benchmarks { use super::*; @@ -66,15 +66,15 @@ pub mod benchmarks { }) .collect(); let validators: BoundedVec< - (::AuthorityId, T::AuthorityKeys), + ::CommitteeMember, T::MaxValidators, - > = validators.try_into().unwrap(); + > = BoundedVec::truncate_from(validators.into_iter().map(|member| member.into()).collect()); let for_epoch_number = T::current_epoch_number() + One::one(); set_epoch_number::(for_epoch_number.into()); #[extrinsic_call] - _(RawOrigin::None, validators.clone(), for_epoch_number, Default::default()); + _(RawOrigin::None, validators, for_epoch_number, Default::default()); } impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/toolkit/pallets/session-validator-management/benchmarking/src/mock.rs b/toolkit/pallets/session-validator-management/benchmarking/src/mock.rs index 6a4b99d7c..ef8ec1e78 100644 --- a/toolkit/pallets/session-validator-management/benchmarking/src/mock.rs +++ b/toolkit/pallets/session-validator-management/benchmarking/src/mock.rs @@ -106,11 +106,12 @@ impl pallet::Config for Test { type AuthorityKeys = SessionKeys; type AuthoritySelectionInputs = (); type ScEpochNumber = ScEpochNumber; + type CommitteeMember = (Self::AuthorityId, Self::AuthorityKeys); fn select_authorities( _: Self::AuthoritySelectionInputs, _: ScEpochNumber, - ) -> Option> { + ) -> Option> { todo!("not used in benchmarks") } diff --git a/toolkit/pallets/session-validator-management/src/lib.rs b/toolkit/pallets/session-validator-management/src/lib.rs index ff92e9756..a03430c9e 100644 --- a/toolkit/pallets/session-validator-management/src/lib.rs +++ b/toolkit/pallets/session-validator-management/src/lib.rs @@ -3,6 +3,8 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::type_complexity)] +pub mod migrations; + pub use pallet::*; #[cfg(any(test, feature = "mock"))] @@ -13,6 +15,7 @@ mod tests; pub mod weights; +pub use sp_session_validator_management::CommitteeMember; pub use weights::WeightInfo; #[frame_support::pallet] @@ -24,12 +27,15 @@ pub mod pallet { use sidechain_domain::byte_string::SizedByteString; use sidechain_domain::{MainchainAddress, PolicyId}; use sp_core::blake2_256; - use sp_runtime::traits::{One, Zero}; + use sp_runtime::traits::{MaybeSerializeDeserialize, One, Zero}; use sp_session_validator_management::*; use sp_std::fmt::Display; use sp_std::{ops::Add, vec, vec::Vec}; + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] @@ -57,10 +63,16 @@ pub mod pallet { + From + Into; + type CommitteeMember: Parameter + + Member + + MaybeSerializeDeserialize + + MaxEncodedLen + + CommitteeMember; + fn select_authorities( input: Self::AuthoritySelectionInputs, sidechain_epoch: Self::ScEpochNumber, - ) -> Option>; + ) -> Option>; fn current_epoch_number() -> Self::ScEpochNumber; @@ -77,21 +89,15 @@ pub mod pallet { #[derive(CloneNoBound, Encode, Decode, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(MaxValidators))] - pub struct CommitteeInfo< - ScEpochNumber: Clone, - AuthorityId: Clone, - AuthorityKeys: Clone, - MaxValidators, - > { + pub struct CommitteeInfo { pub epoch: ScEpochNumber, - pub committee: BoundedVec<(AuthorityId, AuthorityKeys), MaxValidators>, + pub committee: BoundedVec, } - impl Default - for CommitteeInfo + impl Default + for CommitteeInfo where - AuthorityId: Clone, - AuthorityKeys: Clone, + CommitteeMember: Clone, ScEpochNumber: Clone + Zero, { fn default() -> Self { @@ -102,14 +108,14 @@ pub mod pallet { #[pallet::storage] pub type CurrentCommittee = StorageValue< _, - CommitteeInfo, + CommitteeInfo, ValueQuery, >; #[pallet::storage] pub type NextCommittee = StorageValue< _, - CommitteeInfo, + CommitteeInfo, OptionQuery, >; @@ -126,7 +132,7 @@ pub mod pallet { #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { - pub initial_authorities: Vec<(T::AuthorityId, T::AuthorityKeys)>, + pub initial_authorities: Vec, pub main_chain_scripts: MainChainScripts, } @@ -255,7 +261,7 @@ pub mod pallet { ))] pub fn set( origin: OriginFor, - validators: BoundedVec<(T::AuthorityId, T::AuthorityKeys), T::MaxValidators>, + validators: BoundedVec, for_epoch_number: T::ScEpochNumber, selection_inputs_hash: SizedByteString<32>, ) -> DispatchResult { @@ -304,18 +310,17 @@ pub mod pallet { CurrentCommittee::::get() .committee .get(index) - .map(|authority| authority.0.clone()) + .map(|authority| authority.authority_id()) .clone() } pub fn current_committee_storage( - ) -> CommitteeInfo { + ) -> CommitteeInfo { CurrentCommittee::::get() } - pub fn next_committee_storage() -> Option< - CommitteeInfo, - > { + pub fn next_committee_storage( + ) -> Option> { NextCommittee::::get() } @@ -325,7 +330,7 @@ pub mod pallet { NextCommittee::::get()? .committee .into_iter() - .map(|(id, _)| id) + .map(|member| member.authority_id()) .collect::>(), )) } @@ -345,7 +350,7 @@ pub mod pallet { pub fn calculate_committee( authority_selection_inputs: T::AuthoritySelectionInputs, sidechain_epoch: T::ScEpochNumber, - ) -> Option> { + ) -> Option> { T::select_authorities(authority_selection_inputs, sidechain_epoch).map(|c| c.to_vec()) } @@ -357,7 +362,7 @@ pub mod pallet { let validators: Vec<(T::AccountId, T::AuthorityKeys)> = next_committee .committee .into_iter() - .map(|(pub_key, keys)| (pub_key.into(), keys)) + .map(|member| (member.authority_id().into(), member.authority_keys())) .collect(); let len = validators.len(); info!( @@ -369,14 +374,25 @@ pub mod pallet { pub fn get_current_committee() -> (T::ScEpochNumber, Vec) { let committee_info = CurrentCommittee::::get(); - (committee_info.epoch, committee_info.committee.into_iter().map(|(id, _)| id).collect()) + ( + committee_info.epoch, + committee_info + .committee + .into_iter() + .map(|member| member.authority_id()) + .collect(), + ) } pub fn get_next_committee() -> Option<(T::ScEpochNumber, Vec)> { let committee_info = NextCommittee::::get()?; Some(( committee_info.epoch, - committee_info.committee.into_iter().map(|(id, _)| id).collect(), + committee_info + .committee + .into_iter() + .map(|member| member.authority_id()) + .collect(), )) } diff --git a/toolkit/pallets/session-validator-management/src/migrations/README.md b/toolkit/pallets/session-validator-management/src/migrations/README.md new file mode 100644 index 000000000..2a728166b --- /dev/null +++ b/toolkit/pallets/session-validator-management/src/migrations/README.md @@ -0,0 +1,51 @@ +# Migrations Readme + +This document describes changes in the storage of different versions +of the pallet and how to migrate from legacy versions. + +**Important:** It is crucial to run the migrations when upgrading runtime +to a version containing this pallet's storage version. Failing to do so +WILL break the chain and require a wipe or a rollback. + +To schedule a migration, add it to the `Executive` definition for your +runtime like this: + +``` rust +pub type Migrations = ( + pallet_session_validator_management::migrations::v1::LegacyToV1Migration, + // ... +); +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + ChainContext, + Runtime, + AllPalletsWithSystem, + Migrations, +>; +``` + +Each migration will be run only once, for the storage version for which it is +defined, and will update the storage version number. + +## V1 (v1.6.0+) + +### Changes + +This version changes the type used to store committee member information +from a tuple `(T::AuthorityId, T::AuthorityKeys)` to a generic type +`T::CommitteeMember`. This type can be arbitrary within normal +constraints of Substrate runtime types and must implement the +`CommitteeMember` trait. If your runtime uses `authority-selection-inherents` +to select its committee, use the `CommitteeMember` type provided by this crate. +A `CommitteeMember` implementation for the legacy `(T::AuthorityId, T::AuthorityKeys)` +type is also provided and can be used. + +### Migration from Legacy + +Migration logic is provided by the `migrations::v1::LegacyToV1Migration` migration. +It assumes that the types `T::AuthorityId` and `T::AuthorityKeys` do not change as +part of the same runtime upgrade. The only requirement for the new type +`T::CommitteeMember` is to implement the trait `From<(T::AuthorityId, T::AuthorityKeys)>`. + diff --git a/toolkit/pallets/session-validator-management/src/migrations/mod.rs b/toolkit/pallets/session-validator-management/src/migrations/mod.rs new file mode 100644 index 000000000..621eac83c --- /dev/null +++ b/toolkit/pallets/session-validator-management/src/migrations/mod.rs @@ -0,0 +1,2 @@ +pub mod v0; +pub mod v1; diff --git a/toolkit/pallets/session-validator-management/src/migrations/v0.rs b/toolkit/pallets/session-validator-management/src/migrations/v0.rs new file mode 100644 index 000000000..0a690587e --- /dev/null +++ b/toolkit/pallets/session-validator-management/src/migrations/v0.rs @@ -0,0 +1,52 @@ +use frame_support::pallet_prelude::{OptionQuery, ValueQuery, Zero}; +use frame_support::{storage_alias, BoundedVec, CloneNoBound}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive(CloneNoBound, Encode, Decode, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(MaxValidators))] +pub struct LegacyCommitteeInfo< + ScEpochNumber: Clone, + AuthorityId: Clone, + AuthorityKeys: Clone, + MaxValidators, +> { + pub epoch: ScEpochNumber, + pub committee: BoundedVec<(AuthorityId, AuthorityKeys), MaxValidators>, +} + +impl Default + for LegacyCommitteeInfo +where + AuthorityId: Clone, + AuthorityKeys: Clone, + ScEpochNumber: Clone + Zero, +{ + fn default() -> Self { + Self { epoch: ScEpochNumber::zero(), committee: BoundedVec::new() } + } +} + +#[storage_alias] +pub type CurrentCommittee = StorageValue< + crate::Pallet, + LegacyCommitteeInfo< + ::ScEpochNumber, + ::AuthorityId, + ::AuthorityKeys, + ::MaxValidators, + >, + ValueQuery, +>; + +#[storage_alias] +pub type NextCommittee = StorageValue< + crate::Pallet, + LegacyCommitteeInfo< + ::ScEpochNumber, + ::AuthorityId, + ::AuthorityKeys, + ::MaxValidators, + >, + OptionQuery, +>; diff --git a/toolkit/pallets/session-validator-management/src/migrations/v1.rs b/toolkit/pallets/session-validator-management/src/migrations/v1.rs new file mode 100644 index 000000000..62bdb24d0 --- /dev/null +++ b/toolkit/pallets/session-validator-management/src/migrations/v1.rs @@ -0,0 +1,134 @@ +#[cfg(feature = "try-runtime")] +extern crate alloc; +use frame_support::traits::UncheckedOnRuntimeUpgrade; +#[cfg(feature = "try-runtime")] +use { + alloc::vec::Vec, parity_scale_codec::Encode, sp_session_validator_management::CommitteeMember, +}; + +use super::v0; + +pub struct InnerMigrateV0ToV1(core::marker::PhantomData); + +impl UncheckedOnRuntimeUpgrade for InnerMigrateV0ToV1 +where + T::CommitteeMember: From<(T::AuthorityId, T::AuthorityKeys)>, +{ + fn on_runtime_upgrade() -> sp_runtime::Weight { + use sp_core::Get; + use sp_runtime::BoundedVec; + + let current_committee_v0 = v0::CurrentCommittee::::get(); + let current_committee_v1 = crate::pallet::CommitteeInfo::< + T::ScEpochNumber, + T::CommitteeMember, + T::MaxValidators, + > { + epoch: current_committee_v0.epoch, + committee: BoundedVec::truncate_from( + current_committee_v0.committee.into_iter().map(From::from).collect(), + ), + }; + + crate::CurrentCommittee::::put(current_committee_v1); + + let Some(next_committee_v0) = v0::NextCommittee::::get() else { + return T::DbWeight::get().reads_writes(2, 1); + }; + let next_committee_v1 = crate::pallet::CommitteeInfo::< + T::ScEpochNumber, + T::CommitteeMember, + T::MaxValidators, + > { + epoch: next_committee_v0.epoch, + committee: BoundedVec::truncate_from( + next_committee_v0.committee.into_iter().map(From::from).collect(), + ), + }; + + crate::NextCommittee::::put(next_committee_v1); + + T::DbWeight::get().reads_writes(2, 2) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + let current_committee_v0 = v0::CurrentCommittee::::get(); + let next_committee_v0 = v0::NextCommittee::::get(); + Ok((current_committee_v0, next_committee_v0).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + use frame_support::ensure; + use parity_scale_codec::Decode; + use v0::LegacyCommitteeInfo; + + let (current_committee_v0, next_committee_v0): ( + LegacyCommitteeInfo< + T::ScEpochNumber, + T::AuthorityId, + T::AuthorityKeys, + T::MaxValidators, + >, + Option< + LegacyCommitteeInfo< + T::ScEpochNumber, + T::AuthorityId, + T::AuthorityKeys, + T::MaxValidators, + >, + >, + ) = Decode::decode(&mut state.as_slice()) + .expect("Previously encoded state should be decodable"); + + let current_committee_v1 = crate::CurrentCommittee::::get(); + let next_committee_v1 = crate::NextCommittee::::get(); + + ensure!( + current_committee_v0.epoch == current_committee_v1.epoch, + "current epoch should be preserved" + ); + + ensure!( + current_committee_v0.committee.to_vec() + == (current_committee_v1.committee.iter()) + .map(|member| (member.authority_id(), member.authority_keys())) + .collect::>(), + "current committee membership should be preserved" + ); + + if next_committee_v0.is_none() && next_committee_v0.is_none() { + return Ok(()); + } + + ensure!(next_committee_v0.is_some(), "V0 next committee should be Some if V1 is"); + ensure!(next_committee_v1.is_some(), "V1 next committee should be Some if V0 is"); + + let next_committee_v0 = next_committee_v0.unwrap(); + let next_committee_v1 = next_committee_v1.unwrap(); + + ensure!( + next_committee_v0.epoch == next_committee_v1.epoch, + "next epoch should be preserved" + ); + + ensure!( + next_committee_v0.committee.to_vec() + == (next_committee_v1.committee.iter()) + .map(|member| (member.authority_id(), member.authority_keys())) + .collect::>(), + "next committee membership should be preserved" + ); + + Ok(()) + } +} + +pub type LegacyToV1Migration = frame_support::migrations::VersionedMigration< + 0, // The migration will only execute when the on-chain storage version is 0 + 1, // The on-chain storage version will be set to 1 after the migration is complete + InnerMigrateV0ToV1, + crate::pallet::Pallet, + ::DbWeight, +>; diff --git a/toolkit/pallets/session-validator-management/src/mock.rs b/toolkit/pallets/session-validator-management/src/mock.rs index 6733ba271..6f0a1207f 100644 --- a/toolkit/pallets/session-validator-management/src/mock.rs +++ b/toolkit/pallets/session-validator-management/src/mock.rs @@ -91,6 +91,7 @@ impl pallet::Config for Test { type AuthoritySelectionInputs = BoundedVec<(Self::AuthorityId, Self::AuthorityKeys), Self::MaxValidators>; type ScEpochNumber = ScEpochNumber; + type CommitteeMember = (Self::AuthorityId, Self::AuthorityKeys); fn select_authorities( input: Self::AuthoritySelectionInputs, diff --git a/toolkit/partner-chains-cli/src/create_chain_spec/mod.rs b/toolkit/partner-chains-cli/src/create_chain_spec/mod.rs index 3ac076f73..4ee86405c 100644 --- a/toolkit/partner-chains-cli/src/create_chain_spec/mod.rs +++ b/toolkit/partner-chains-cli/src/create_chain_spec/mod.rs @@ -4,7 +4,7 @@ use crate::permissioned_candidates::{ParsedPermissionedCandidatesKeys, Permissio use crate::{config::config_fields, CmdRun}; use anyhow::{anyhow, Context}; use serde::de::DeserializeOwned; -use serde_json::Value as JValue; +use serde_json::{json, Value as JValue}; use sidechain_domain::UtxoId; #[cfg(test)] @@ -120,7 +120,14 @@ impl CreateChainSpecCmd { let initial_authorities = config .initial_permissioned_candidates_parsed .iter() - .map(|c| serde_json::to_value((c.sidechain, c.session_keys()))) + .map(|c| -> anyhow::Result { + Ok(json!({ + "Permissioned": { + "id": serde_json::to_value(c.sidechain)?, + "keys": c.session_keys() + } + })) + }) .collect::, _>>()?; let initial_authorities = serde_json::Value::Array(initial_authorities); Self::update_field( diff --git a/toolkit/partner-chains-cli/src/create_chain_spec/tests.rs b/toolkit/partner-chains-cli/src/create_chain_spec/tests.rs index 97c3aaa58..43a64b3c6 100644 --- a/toolkit/partner-chains-cli/src/create_chain_spec/tests.rs +++ b/toolkit/partner-chains-cli/src/create_chain_spec/tests.rs @@ -285,20 +285,24 @@ fn updated_chain_spec() -> serde_json::Value { }, "sessionCommitteeManagement": { "initialAuthorities": [ - [ - "KW39r9CJjAVzmkf9zQ4YDb2hqfAVGdRqn53eRqyruqpxAP5YL", - { - "aura": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "grandpa": "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu" + { + "Permissioned":{ + "id": "KW39r9CJjAVzmkf9zQ4YDb2hqfAVGdRqn53eRqyruqpxAP5YL", + "keys": { + "aura": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "grandpa": "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu" + } } - ], - [ - "KWByAN7WfZABWS5AoWqxriRmF5f2jnDqy3rB5pfHLGkY93ibN", - { - "aura": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", - "grandpa": "5GoNkf6WdbxCFnPdAnYYQyCjAKPJgLNxXwPjwTh6DGg6gN3E" + }, + { + "Permissioned":{ + "id": "KWByAN7WfZABWS5AoWqxriRmF5f2jnDqy3rB5pfHLGkY93ibN", + "keys": { + "aura": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + "grandpa": "5GoNkf6WdbxCFnPdAnYYQyCjAKPJgLNxXwPjwTh6DGg6gN3E" + } } - ] + } ], "main_chain_scripts": { "committee_candidate_address": "0x002244", diff --git a/toolkit/primitives/authority-selection-inherents/Cargo.toml b/toolkit/primitives/authority-selection-inherents/Cargo.toml index b135befe5..b7f91fc44 100644 --- a/toolkit/primitives/authority-selection-inherents/Cargo.toml +++ b/toolkit/primitives/authority-selection-inherents/Cargo.toml @@ -25,7 +25,7 @@ sp-inherents = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } thiserror = { workspace = true, optional = true } -serde = { workspace = true, optional = true } +serde = { workspace = true } sp-consensus-slots = { workspace = true } [dev-dependencies] diff --git a/toolkit/primitives/authority-selection-inherents/src/authority_selection_inputs.rs b/toolkit/primitives/authority-selection-inherents/src/authority_selection_inputs.rs index f71383d2c..e1115f2c6 100644 --- a/toolkit/primitives/authority-selection-inherents/src/authority_selection_inputs.rs +++ b/toolkit/primitives/authority-selection-inherents/src/authority_selection_inputs.rs @@ -13,7 +13,6 @@ pub struct AuthoritySelectionInputs { pub epoch_nonce: EpochNonce, } -// #[derive(Debug, PartialEq, Eq, Clone, Decode, thiserror::Error, Serialize, Deserialize)] #[cfg(feature = "std")] #[derive(Debug, thiserror::Error)] pub enum AuthoritySelectionInputsCreationError { @@ -30,16 +29,14 @@ pub enum AuthoritySelectionInputsCreationError { GetEpochNonceQuery(McEpochNumber, Box), } -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[derive(Debug, Clone, PartialEq, serde::Serialize)] pub struct RawPermissionedCandidateData { pub sidechain_public_key: SidechainPublicKey, pub aura_public_key: AuraPublicKey, pub grandpa_public_key: GrandpaPublicKey, } -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[derive(Debug, Clone, PartialEq, serde::Serialize)] pub struct AriadneParameters { pub d_parameter: DParameter, pub permissioned_candidates: Vec, diff --git a/toolkit/primitives/authority-selection-inherents/src/filter_invalid_candidates.rs b/toolkit/primitives/authority-selection-inherents/src/filter_invalid_candidates.rs index d063bde12..49bfd311f 100644 --- a/toolkit/primitives/authority-selection-inherents/src/filter_invalid_candidates.rs +++ b/toolkit/primitives/authority-selection-inherents/src/filter_invalid_candidates.rs @@ -1,5 +1,6 @@ //! Functionality related to filtering invalid candidates from the candidates +use crate::CommitteeMember; use frame_support::pallet_prelude::TypeInfo; use parity_scale_codec::{Decode, Encode}; use plutus::*; @@ -42,6 +43,23 @@ pub enum Candidate { Registered(CandidateWithStake), } +impl From> + for CommitteeMember +{ + fn from(candidate: Candidate) -> Self { + match candidate { + Candidate::Permissioned(member) => { + Self::Permissioned { id: member.account_id, keys: member.account_keys } + }, + Candidate::Registered(member) => Self::Registered { + id: member.account_id, + keys: member.account_keys, + stake_pool_pub_key: member.stake_pool_pub_key, + }, + } + } +} + impl Candidate { pub fn account_id(&self) -> &TAccountId { match self { diff --git a/toolkit/primitives/authority-selection-inherents/src/lib.rs b/toolkit/primitives/authority-selection-inherents/src/lib.rs index 3e5bc6c97..60a7b5776 100644 --- a/toolkit/primitives/authority-selection-inherents/src/lib.rs +++ b/toolkit/primitives/authority-selection-inherents/src/lib.rs @@ -2,6 +2,12 @@ extern crate alloc; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sidechain_domain::StakePoolPublicKey; +use sp_core::{Decode, Encode, MaxEncodedLen}; +use sp_session_validator_management::CommitteeMember as CommitteeMemberT; + pub mod ariadne_inherent_data_provider; pub mod authority_selection_inputs; pub mod filter_invalid_candidates; @@ -14,3 +20,46 @@ mod tests; #[cfg(any(test, feature = "mock"))] pub mod mock; + +#[derive( + Serialize, Deserialize, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Debug, PartialEq, Eq, +)] +pub enum CommitteeMember { + Permissioned { id: AuthorityId, keys: AuthorityKeys }, + Registered { id: AuthorityId, keys: AuthorityKeys, stake_pool_pub_key: StakePoolPublicKey }, +} + +impl From<(AuthorityId, AuthorityKeys)> + for CommitteeMember +{ + fn from((id, keys): (AuthorityId, AuthorityKeys)) -> Self { + Self::Permissioned { id, keys } + } +} + +impl CommitteeMember { + pub fn permissioned(id: AuthorityId, keys: AuthorityKeys) -> Self { + Self::Permissioned { id, keys } + } +} + +impl CommitteeMemberT + for CommitteeMember +{ + type AuthorityId = AuthorityId; + type AuthorityKeys = AuthorityKeys; + + fn authority_id(&self) -> AuthorityId { + match self { + Self::Permissioned { id, .. } => id.clone(), + Self::Registered { id, .. } => id.clone(), + } + } + + fn authority_keys(&self) -> AuthorityKeys { + match self { + Self::Permissioned { keys, .. } => keys.clone(), + Self::Registered { keys, .. } => keys.clone(), + } + } +} diff --git a/toolkit/primitives/session-manager/src/lib.rs b/toolkit/primitives/session-manager/src/lib.rs index 08a92cff5..4ea37f07c 100644 --- a/toolkit/primitives/session-manager/src/lib.rs +++ b/toolkit/primitives/session-manager/src/lib.rs @@ -4,6 +4,7 @@ use core::marker::PhantomData; use derive_new::new; use frame_system::pallet_prelude::BlockNumberFor; use log::info; +use pallet_session_validator_management::CommitteeMember; use sp_staking::SessionIndex; use sp_std::vec::Vec; @@ -27,7 +28,7 @@ impl pallet_session_validator_management::Pallet::::current_committee_storage() .committee .into_iter() - .map(|(id, keys)| (id.into(), keys)) + .map(|member| (member.authority_id().into(), member.authority_keys())) .collect::>(), ) } diff --git a/toolkit/primitives/session-manager/src/pallet_session_support.rs b/toolkit/primitives/session-manager/src/pallet_session_support.rs index 3c4d8378c..386aabf3a 100644 --- a/toolkit/primitives/session-manager/src/pallet_session_support.rs +++ b/toolkit/primitives/session-manager/src/pallet_session_support.rs @@ -2,6 +2,7 @@ use core::marker::PhantomData; use derive_new::new; use frame_system::pallet_prelude::BlockNumberFor; use log::{debug, warn}; +use pallet_session_validator_management::CommitteeMember; use sp_staking::SessionIndex; use sp_std::vec::Vec; @@ -23,7 +24,7 @@ impl pallet_session_validator_management::Pallet::::current_committee_storage() .committee .into_iter() - .map(|(id, _)| id.into()) + .map(|member| member.authority_id().into()) .collect::>(), ) } diff --git a/toolkit/primitives/session-validator-management/src/lib.rs b/toolkit/primitives/session-validator-management/src/lib.rs index 40c974ca2..439653e7c 100644 --- a/toolkit/primitives/session-validator-management/src/lib.rs +++ b/toolkit/primitives/session-validator-management/src/lib.rs @@ -47,6 +47,23 @@ impl IsFatalError for InherentError { } } +pub trait CommitteeMember { + type AuthorityId; + type AuthorityKeys; + fn authority_id(&self) -> Self::AuthorityId; + fn authority_keys(&self) -> Self::AuthorityKeys; +} +impl CommitteeMember for (AuthorityId, AuthorityKeys) { + type AuthorityId = AuthorityId; + type AuthorityKeys = AuthorityKeys; + fn authority_id(&self) -> AuthorityId { + self.0.clone() + } + fn authority_keys(&self) -> AuthorityKeys { + self.1.clone() + } +} + #[cfg(feature = "std")] impl From for sp_inherents::Error { fn from(value: InherentError) -> Self {