Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A0-1845: Authorities api #870

Merged
merged 25 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/e2e-tests-main-devnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion aleph-client/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion aleph-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"

Expand Down
27 changes: 24 additions & 3 deletions aleph-client/src/aleph_zero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6338,6 +6338,27 @@ pub mod api {
],
)
}
pub fn next_authorities(
&self,
) -> ::subxt::storage::address::StaticStorageAddress<
::subxt::metadata::DecodeStaticType<
::std::vec::Vec<runtime_types::primitives::app::Public>,
>,
::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<
Expand Down Expand Up @@ -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)
Expand Down
28 changes: 18 additions & 10 deletions bin/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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! {
Expand Down Expand Up @@ -372,13 +373,17 @@ parameter_types! {
}

use sp_runtime::traits::Convert;

pub struct BalanceToU256;

impl Convert<Balance, sp_core::U256> for BalanceToU256 {
fn convert(balance: Balance) -> sp_core::U256 {
sp_core::U256::from(balance)
}
}

pub struct U256ToBalance;

impl Convert<sp_core::U256, Balance> for U256ToBalance {
fn convert(n: sp_core::U256) -> Balance {
n.try_into().unwrap_or(Balance::max_value())
Expand Down Expand Up @@ -423,6 +428,7 @@ impl pallet_staking::EraPayout<Balance> for UniformEraPayout {
type SubstrateStakingWeights = pallet_staking::weights::SubstrateWeight<Runtime>;

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 {
Expand Down Expand Up @@ -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>;
Expand Down Expand Up @@ -609,6 +616,7 @@ parameter_types! {
}

pub struct TreasuryGovernance;

impl SortedMembers<AccountId> for TreasuryGovernance {
fn sorted_members() -> Vec<AccountId> {
pallet_sudo::Pallet::<Runtime>::key().into_iter().collect()
Expand Down Expand Up @@ -884,21 +892,21 @@ impl_runtime_apis! {
}

fn next_session_authorities() -> Result<Vec<AlephId>, AlephApiError> {
Session::queued_keys()
.iter()
.map(|(_, key)| key.get(AlephId::ID).ok_or(AlephApiError::DecodeKey))
.collect::<Result<Vec<AlephId>, AlephApiError>>()
let next_authorities = Aleph::next_authorities();
if next_authorities.is_empty() {
return Err(AlephApiError::DecodeKey)
}

Ok(next_authorities)
}

fn authority_data() -> SessionAuthorityData {
SessionAuthorityData::new(Aleph::authorities(), Aleph::emergency_finalizer())
}

fn next_session_authority_data() -> Result<SessionAuthorityData, AlephApiError> {
Ok(SessionAuthorityData::new(Session::queued_keys()
.iter()
.map(|(_, key)| key.get(AlephId::ID).ok_or(AlephApiError::DecodeKey))
.collect::<Result<Vec<AlephId>, AlephApiError>>()?,
Ok(SessionAuthorityData::new(
Self::next_session_authorities()?,
Aleph::queued_emergency_finalizer(),
))
}
Expand Down
51 changes: 38 additions & 13 deletions pallets/aleph/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type SessionInfoProvider: SessionInfoProvider<Self>;
type SessionInfoProvider: SessionInfoProvider;
type SessionManager: SessionManager<<Self as frame_system::Config>::AccountId>;
type NextSessionAuthorityProvider: NextSessionAuthorityProvider<Self>;
}

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
#[pallet::generate_deposit(pub (super) fn deposit_event)]
pub enum Event<T: Config> {
ChangeEmergencyFinalizer(T::AuthorityId),
ScheduleFinalityVersionChange(VersionChange),
Expand Down Expand Up @@ -105,10 +106,21 @@ pub mod pallet {
DEFAULT_FINALITY_VERSION
}

/// Default value for `NextAuthorities` storage.
#[pallet::type_value]
pub(crate) fn DefaultNextAuthorities<T: Config>() -> Vec<T::AuthorityId> {
T::NextSessionAuthorityProvider::next_authorities()
}

#[pallet::storage]
#[pallet::getter(fn authorities)]
pub(super) type Authorities<T: Config> = StorageValue<_, Vec<T::AuthorityId>, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn next_authorities)]
pub(super) type NextAuthorities<T: Config> =
StorageValue<_, Vec<T::AuthorityId>, ValueQuery, DefaultNextAuthorities<T>>;

#[pallet::storage]
#[pallet::getter(fn emergency_finalizer)]
pub(super) type EmergencyFinalizer<T: Config> = StorageValue<_, T::AuthorityId, OptionQuery>;
Expand All @@ -134,18 +146,29 @@ pub mod pallet {
StorageValue<_, VersionChange, OptionQuery>;

impl<T: Config> Pallet<T> {
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!(
<Authorities<T>>::get().is_empty(),
"Authorities are already initialized!"
);
<Authorities<T>>::put(authorities);
if !<Authorities<T>>::get().is_empty() {
log::error!(target: "pallet_aleph","Authorities are already initialized!");
} else {
<Authorities<T>>::put(authorities);
}
}
if !next_authorities.is_empty() {
// Storage NextAuthorities has default value so should never be empty.
<NextAuthorities<T>>::put(next_authorities);
}
}

pub(crate) fn update_authorities(authorities: &[T::AuthorityId]) {
pub(crate) fn update_authorities(
authorities: &[T::AuthorityId],
next_authorities: &[T::AuthorityId],
) {
<Authorities<T>>::put(authorities);
<NextAuthorities<T>>::put(next_authorities);
}

pub(crate) fn update_emergency_finalizer() {
Expand Down Expand Up @@ -262,18 +285,20 @@ 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<Item = (&'a T::AccountId, T::AuthorityId)>,
T::AccountId: 'a,
{
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());
}
}

Expand Down
1 change: 1 addition & 0 deletions pallets/aleph/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
25 changes: 19 additions & 6 deletions pallets/aleph/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,31 @@ 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]));
});
}

#[test]
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]));
});
}

Expand All @@ -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]));
})
}

Expand All @@ -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]));
})
}

Expand Down
36 changes: 33 additions & 3 deletions pallets/aleph/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,45 @@
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<T: frame_system::Config> {
pub trait SessionInfoProvider {
fn current_session() -> SessionIndex;
}

impl<T> SessionInfoProvider<T> for pallet_session::Pallet<T>
/// 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<T: Config> {
fn next_authorities() -> Vec<T::AuthorityId>;
}

impl<T> SessionInfoProvider for pallet_session::Pallet<T>
where
T: pallet_session::Config,
{
fn current_session() -> SessionIndex {
pallet_session::CurrentIndex::<T>::get()
}
}

impl<T> NextSessionAuthorityProvider<T> for pallet_session::Pallet<T>
where
T: Config + pallet_session::Config,
{
fn next_authorities() -> Vec<T::AuthorityId> {
let next: Option<Vec<_>> = pallet_session::Pallet::<T>::queued_keys()
.iter()
.map(|(_, key)| key.get(AuthorityId::ID))
.collect();

next.unwrap_or_else(|| {
log::error!(target: "pallet_aleph", "Missing next session keys");
vec![]
})
}
}