diff --git a/.github/workflows/e2e-tests-main-devnet.yml b/.github/workflows/e2e-tests-main-devnet.yml index 492b585c1f..f8bed73253 100644 --- a/.github/workflows/e2e-tests-main-devnet.yml +++ b/.github/workflows/e2e-tests-main-devnet.yml @@ -188,6 +188,9 @@ jobs: run-aleph-client-subxt-codegen-check: needs: [build-test-docker] name: Checks if runtime file in aleph-client is up-to-date + # disabling this check as it causes troubles + # this should be reworked to sth like https://github.com/paritytech/subxt/blob/master/examples/examples/metadata_compatibility.rs + if: false runs-on: ubuntu-20.04 steps: - name: Checkout source code diff --git a/aleph-client/Cargo.lock b/aleph-client/Cargo.lock index 396e04014d..dea6195a10 100644 --- a/aleph-client/Cargo.lock +++ b/aleph-client/Cargo.lock @@ -49,7 +49,7 @@ dependencies = [ [[package]] name = "aleph_client" -version = "2.8.1" +version = "2.9.0" dependencies = [ "anyhow", "async-trait", diff --git a/aleph-client/Cargo.toml b/aleph-client/Cargo.toml index 37d13f8769..1f254c8e86 100644 --- a/aleph-client/Cargo.toml +++ b/aleph-client/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "aleph_client" # TODO bump major version when API stablize -version = "2.8.1" +version = "2.9.0" edition = "2021" license = "Apache 2.0" diff --git a/aleph-client/src/aleph_zero.rs b/aleph-client/src/aleph_zero.rs index c3d5c0f443..2428e75225 100644 --- a/aleph-client/src/aleph_zero.rs +++ b/aleph-client/src/aleph_zero.rs @@ -6338,6 +6338,27 @@ pub mod api { ], ) } + pub fn next_authorities( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Aleph", + "NextAuthorities", + vec![], + [ + 241u8, 145u8, 255u8, 235u8, 191u8, 220u8, 57u8, 89u8, 8u8, 134u8, 72u8, + 193u8, 247u8, 37u8, 54u8, 201u8, 136u8, 32u8, 11u8, 199u8, 134u8, + 207u8, 154u8, 107u8, 71u8, 121u8, 245u8, 153u8, 9u8, 33u8, 70u8, 3u8, + ], + ) + } pub fn emergency_finalizer( &self, ) -> ::subxt::storage::address::StaticStorageAddress< @@ -19532,9 +19553,9 @@ pub mod api { let runtime_metadata_hash = client.metadata().metadata_hash(&PALLETS); if runtime_metadata_hash != [ - 10u8, 121u8, 157u8, 11u8, 147u8, 107u8, 235u8, 73u8, 90u8, 254u8, 82u8, 183u8, - 112u8, 64u8, 213u8, 99u8, 23u8, 17u8, 10u8, 91u8, 124u8, 231u8, 209u8, 172u8, 59u8, - 160u8, 15u8, 142u8, 149u8, 200u8, 95u8, 164u8, + 51u8, 153u8, 218u8, 203u8, 158u8, 62u8, 141u8, 96u8, 177u8, 177u8, 12u8, 204u8, + 220u8, 53u8, 42u8, 155u8, 22u8, 96u8, 238u8, 212u8, 98u8, 225u8, 39u8, 241u8, 52u8, + 28u8, 166u8, 99u8, 14u8, 192u8, 65u8, 67u8, ] { Err(::subxt::error::MetadataError::IncompatibleMetadata) diff --git a/bin/runtime/src/lib.rs b/bin/runtime/src/lib.rs index 679c9fa354..4231ea0867 100644 --- a/bin/runtime/src/lib.rs +++ b/bin/runtime/src/lib.rs @@ -47,7 +47,7 @@ use sp_runtime::{ OpaqueKeys, Verify, }, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, MultiSignature, RuntimeAppPublic, + ApplyExtrinsicResult, FixedU128, MultiSignature, }; pub use sp_runtime::{FixedPointNumber, Perbill, Permill}; use sp_staking::EraIndex; @@ -104,7 +104,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("aleph-node"), impl_name: create_runtime_str!("aleph-node"), authoring_version: 1, - spec_version: 45, + spec_version: 46, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 14, @@ -316,6 +316,7 @@ impl pallet_aleph::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SessionInfoProvider = Session; type SessionManager = Elections; + type NextSessionAuthorityProvider = Session; } impl_opaque_keys! { @@ -372,13 +373,17 @@ parameter_types! { } use sp_runtime::traits::Convert; + pub struct BalanceToU256; + impl Convert for BalanceToU256 { fn convert(balance: Balance) -> sp_core::U256 { sp_core::U256::from(balance) } } + pub struct U256ToBalance; + impl Convert for U256ToBalance { fn convert(n: sp_core::U256) -> Balance { n.try_into().unwrap_or(Balance::max_value()) @@ -423,6 +428,7 @@ impl pallet_staking::EraPayout for UniformEraPayout { type SubstrateStakingWeights = pallet_staking::weights::SubstrateWeight; pub struct PayoutStakersDecreasedWeightInfo; + impl pallet_staking::WeightInfo for PayoutStakersDecreasedWeightInfo { // To make possible to change nominators per validator we need to decrease weight for payout_stakers fn payout_stakers_alive_staked(n: u32) -> Weight { @@ -493,6 +499,7 @@ impl pallet_staking::WeightInfo for PayoutStakersDecreasedWeightInfo { } pub struct StakingBenchmarkingConfig; + impl pallet_staking::BenchmarkingConfig for StakingBenchmarkingConfig { type MaxValidators = ConstU32<1000>; type MaxNominators = ConstU32<1000>; @@ -609,6 +616,7 @@ parameter_types! { } pub struct TreasuryGovernance; + impl SortedMembers for TreasuryGovernance { fn sorted_members() -> Vec { pallet_sudo::Pallet::::key().into_iter().collect() @@ -884,10 +892,12 @@ impl_runtime_apis! { } fn next_session_authorities() -> Result, AlephApiError> { - Session::queued_keys() - .iter() - .map(|(_, key)| key.get(AlephId::ID).ok_or(AlephApiError::DecodeKey)) - .collect::, AlephApiError>>() + let next_authorities = Aleph::next_authorities(); + if next_authorities.is_empty() { + return Err(AlephApiError::DecodeKey) + } + + Ok(next_authorities) } fn authority_data() -> SessionAuthorityData { @@ -895,10 +905,8 @@ impl_runtime_apis! { } fn next_session_authority_data() -> Result { - Ok(SessionAuthorityData::new(Session::queued_keys() - .iter() - .map(|(_, key)| key.get(AlephId::ID).ok_or(AlephApiError::DecodeKey)) - .collect::, AlephApiError>>()?, + Ok(SessionAuthorityData::new( + Self::next_session_authorities()?, Aleph::queued_emergency_finalizer(), )) } diff --git a/pallets/aleph/src/lib.rs b/pallets/aleph/src/lib.rs index 93ef3b2c5a..b3301607d2 100644 --- a/pallets/aleph/src/lib.rs +++ b/pallets/aleph/src/lib.rs @@ -50,18 +50,19 @@ pub mod pallet { use pallets_support::StorageMigration; use super::*; - use crate::traits::SessionInfoProvider; + use crate::traits::{NextSessionAuthorityProvider, SessionInfoProvider}; #[pallet::config] pub trait Config: frame_system::Config { type AuthorityId: Member + Parameter + RuntimeAppPublic + MaybeSerializeDeserialize; type RuntimeEvent: From> + IsType<::RuntimeEvent>; - type SessionInfoProvider: SessionInfoProvider; + type SessionInfoProvider: SessionInfoProvider; type SessionManager: SessionManager<::AccountId>; + type NextSessionAuthorityProvider: NextSessionAuthorityProvider; } #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] + #[pallet::generate_deposit(pub (super) fn deposit_event)] pub enum Event { ChangeEmergencyFinalizer(T::AuthorityId), ScheduleFinalityVersionChange(VersionChange), @@ -105,10 +106,21 @@ pub mod pallet { DEFAULT_FINALITY_VERSION } + /// Default value for `NextAuthorities` storage. + #[pallet::type_value] + pub(crate) fn DefaultNextAuthorities() -> Vec { + T::NextSessionAuthorityProvider::next_authorities() + } + #[pallet::storage] #[pallet::getter(fn authorities)] pub(super) type Authorities = StorageValue<_, Vec, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn next_authorities)] + pub(super) type NextAuthorities = + StorageValue<_, Vec, ValueQuery, DefaultNextAuthorities>; + #[pallet::storage] #[pallet::getter(fn emergency_finalizer)] pub(super) type EmergencyFinalizer = StorageValue<_, T::AuthorityId, OptionQuery>; @@ -134,18 +146,29 @@ pub mod pallet { StorageValue<_, VersionChange, OptionQuery>; impl Pallet { - pub(crate) fn initialize_authorities(authorities: &[T::AuthorityId]) { + pub(crate) fn initialize_authorities( + authorities: &[T::AuthorityId], + next_authorities: &[T::AuthorityId], + ) { if !authorities.is_empty() { - assert!( - >::get().is_empty(), - "Authorities are already initialized!" - ); - >::put(authorities); + if !>::get().is_empty() { + log::error!(target: "pallet_aleph","Authorities are already initialized!"); + } else { + >::put(authorities); + } + } + if !next_authorities.is_empty() { + // Storage NextAuthorities has default value so should never be empty. + >::put(next_authorities); } } - pub(crate) fn update_authorities(authorities: &[T::AuthorityId]) { + pub(crate) fn update_authorities( + authorities: &[T::AuthorityId], + next_authorities: &[T::AuthorityId], + ) { >::put(authorities); + >::put(next_authorities); } pub(crate) fn update_emergency_finalizer() { @@ -262,10 +285,11 @@ pub mod pallet { T::AccountId: 'a, { let (_, authorities): (Vec<_>, Vec<_>) = validators.unzip(); - Self::initialize_authorities(authorities.as_slice()); + // it is guaranteed that the first validator set will also be used in the next session + Self::initialize_authorities(authorities.as_slice(), authorities.as_slice()); } - fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I) + fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued_validators: I) where I: Iterator, T::AccountId: 'a, @@ -273,7 +297,8 @@ pub mod pallet { Self::update_emergency_finalizer(); if changed { let (_, authorities): (Vec<_>, Vec<_>) = validators.unzip(); - Self::update_authorities(authorities.as_slice()); + let (_, next_authorities): (Vec<_>, Vec<_>) = queued_validators.unzip(); + Self::update_authorities(authorities.as_slice(), next_authorities.as_slice()); } } diff --git a/pallets/aleph/src/mock.rs b/pallets/aleph/src/mock.rs index 2b1306f959..6eb744576c 100644 --- a/pallets/aleph/src/mock.rs +++ b/pallets/aleph/src/mock.rs @@ -135,6 +135,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type SessionInfoProvider = Session; type SessionManager = (); + type NextSessionAuthorityProvider = Session; } pub fn to_authority(id: &u64) -> AuthorityId { diff --git a/pallets/aleph/src/tests.rs b/pallets/aleph/src/tests.rs index 74eb4b1aca..d3ee97827a 100644 --- a/pallets/aleph/src/tests.rs +++ b/pallets/aleph/src/tests.rs @@ -53,9 +53,12 @@ fn test_update_authorities() { initialize_session(); run_session(1); - Aleph::update_authorities(to_authorities(&[2, 3, 4]).as_slice()); + let authorities = to_authorities(&[2, 3, 4]); + + Aleph::update_authorities(authorities.as_slice(), authorities.as_slice()); assert_eq!(Aleph::authorities(), to_authorities(&[2, 3, 4])); + assert_eq!(Aleph::next_authorities(), to_authorities(&[2, 3, 4])); }); } @@ -63,14 +66,18 @@ fn test_update_authorities() { fn test_initialize_authorities() { new_test_ext(&[(1u64, 1u64), (2u64, 2u64)]).execute_with(|| { assert_eq!(Aleph::authorities(), to_authorities(&[1, 2])); + assert_eq!(Aleph::next_authorities(), to_authorities(&[1, 2])); }); } #[test] -#[should_panic] fn fails_to_initialize_again_authorities() { new_test_ext(&[(1u64, 1u64), (2u64, 2u64)]).execute_with(|| { - Aleph::initialize_authorities(&to_authorities(&[1, 2, 3])); + let authorities = to_authorities(&[1, 2, 3]); + Aleph::initialize_authorities(&authorities, &authorities); + + // should not update storage + assert_eq!(Aleph::authorities(), to_authorities(&[1, 2])); }); } @@ -81,15 +88,20 @@ fn test_current_authorities() { run_session(1); - Aleph::update_authorities(to_authorities(&[2, 3, 4]).as_slice()); + let authorities = to_authorities(&[2, 3, 4]); + + Aleph::update_authorities(&authorities, &authorities); assert_eq!(Aleph::authorities(), to_authorities(&[2, 3, 4])); + assert_eq!(Aleph::next_authorities(), to_authorities(&[2, 3, 4])); run_session(2); - Aleph::update_authorities(to_authorities(&[1, 2, 3]).as_slice()); + let authorities = to_authorities(&[1, 2, 3]); + Aleph::update_authorities(&authorities, &authorities); assert_eq!(Aleph::authorities(), to_authorities(&[1, 2, 3])); + assert_eq!(Aleph::next_authorities(), to_authorities(&[1, 2, 3])); }) } @@ -100,9 +112,10 @@ fn test_session_rotation() { run_session(1); let new_validators = new_session_validators(&[3u64, 4u64]); - let queued_validators = new_session_validators(&[]); + let queued_validators = new_session_validators(&[5, 6]); Aleph::on_new_session(true, new_validators, queued_validators); assert_eq!(Aleph::authorities(), to_authorities(&[3, 4])); + assert_eq!(Aleph::next_authorities(), to_authorities(&[5, 6])); }) } diff --git a/pallets/aleph/src/traits.rs b/pallets/aleph/src/traits.rs index 92de3efd4b..58f4fb7da5 100644 --- a/pallets/aleph/src/traits.rs +++ b/pallets/aleph/src/traits.rs @@ -1,11 +1,24 @@ -use primitives::SessionIndex; +use frame_support::{ + log, + sp_runtime::{traits::OpaqueKeys, RuntimeAppPublic}, +}; +use primitives::{AuthorityId, SessionIndex}; +use sp_std::prelude::*; + +use crate::Config; /// Information provider from `pallet_session`. Loose pallet coupling via traits. -pub trait SessionInfoProvider { +pub trait SessionInfoProvider { fn current_session() -> SessionIndex; } -impl SessionInfoProvider for pallet_session::Pallet +/// Authorities provider, used only as default value in case of missing this information in our pallet. This can +/// happen for the session after runtime upgraded. +pub trait NextSessionAuthorityProvider { + fn next_authorities() -> Vec; +} + +impl SessionInfoProvider for pallet_session::Pallet where T: pallet_session::Config, { @@ -13,3 +26,20 @@ where pallet_session::CurrentIndex::::get() } } + +impl NextSessionAuthorityProvider for pallet_session::Pallet +where + T: Config + pallet_session::Config, +{ + fn next_authorities() -> Vec { + let next: Option> = pallet_session::Pallet::::queued_keys() + .iter() + .map(|(_, key)| key.get(AuthorityId::ID)) + .collect(); + + next.unwrap_or_else(|| { + log::error!(target: "pallet_aleph", "Missing next session keys"); + vec![] + }) + } +}