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

e2e-tests reward points #504

Merged
Show file tree
Hide file tree
Changes from 2 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
62 changes: 55 additions & 7 deletions aleph-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{thread::sleep, time::Duration};
use std::{collections::BTreeMap, thread::sleep, time::Duration};

use ac_primitives::SubstrateDefaultSignedExtra;
use codec::Encode;
use codec::{Decode, Encode};
use log::{info, warn};
use sp_core::{sr25519, storage::StorageKey, Pair, H256};
use sp_runtime::{generic::Header as GenericHeader, traits::BlakeTwo256};
Expand All @@ -21,15 +21,17 @@ pub use multisig::{
pub use rpc::{rotate_keys, rotate_keys_raw_result, state_query_storage_at};
pub use session::{
change_next_era_reserved_validators, change_validators, get_current as get_current_session,
set_keys, wait_for as wait_for_session, Keys as SessionKeys,
get_session, get_session_period, set_keys, wait_for as wait_for_session,
wait_for_at_least as wait_for_at_least_session, Keys as SessionKeys,
};
pub use staking::{
batch_bond as staking_batch_bond, batch_nominate as staking_batch_nominate,
bond as staking_bond, bonded as staking_bonded, force_new_era as staking_force_new_era,
get_current_era, get_payout_for_era, ledger as staking_ledger,
multi_bond as staking_multi_bond, nominate as staking_nominate, payout_stakers,
payout_stakers_and_assert_locked_balance, set_staking_limits as staking_set_staking_limits,
validate as staking_validate, wait_for_full_era_completion, wait_for_next_era, StakingLedger,
get_current_era, get_era, get_exposure, get_payout_for_era, get_sessions_per_era,
ledger as staking_ledger, multi_bond as staking_multi_bond, nominate as staking_nominate,
payout_stakers, payout_stakers_and_assert_locked_balance,
set_staking_limits as staking_set_staking_limits, validate as staking_validate,
wait_for_full_era_completion, wait_for_next_era, StakingLedger,
};
pub use system::set_code;
pub use transfer::{
Expand Down Expand Up @@ -301,3 +303,49 @@ pub fn get_storage_key(pallet: &str, call: &str) -> String {
let storage_key = StorageKey(bytes.into());
hex::encode(storage_key.0)
}

pub type RewardPoint = u32;

/// Helper to decode reward points for an era without the need to fill in a generic parameter.
/// Reward points of an era. Used to split era total payout between validators.
///
/// This points will be used to reward validators and their respective nominators.
#[derive(Clone, Decode, Default)]
pub struct EraRewardPoints {
Marcin-Radecki marked this conversation as resolved.
Show resolved Hide resolved
/// Total number of points. Equals the sum of reward points for each validator.
pub total: RewardPoint,
/// The reward points earned by a given validator.
pub individual: BTreeMap<AccountId, RewardPoint>,
}

pub fn get_era_reward_points<C: AnyConnection>(
Marcin-Radecki marked this conversation as resolved.
Show resolved Hide resolved
connection: &C,
era: u32,
block_hash: Option<H256>,
) -> EraRewardPoints {
connection
.as_connection()
.get_storage_map("Staking", "ErasRewardPoints", era, block_hash)
.expect("Failed to obtain ErasRewardPoints.")
.unwrap_or_else(|| panic!("Failed to obtain EraRewardPoints for era {}.", era))
}

pub fn get_era_reward_points_result<C: AnyConnection>(
connection: &C,
era: u32,
block_hash: Option<H256>,
) -> ApiResult<Option<EraRewardPoints>> {
connection
.as_connection()
.get_storage_map("Staking", "ErasRewardPoints", era, block_hash)
}

pub fn get_block_hash<C: AnyConnection>(connection: &C, block_number: u32) -> H256 {
connection
.as_connection()
.get_block_hash(Some(block_number))
.expect("API call should have succeeded.")
.unwrap_or_else(|| {
panic!("Failed to obtain block hash for block {}.", block_number);
})
}
41 changes: 33 additions & 8 deletions aleph-client/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
};
use codec::{Decode, Encode};
use log::info;
use sp_core::Pair;
use sp_core::{Pair, H256};
use substrate_api_client::{
compose_call, compose_extrinsic, AccountId, ExtrinsicParams, FromHexString, XtStatus,
};
Expand Down Expand Up @@ -86,31 +86,56 @@ pub fn set_keys(connection: &SignedConnection, new_keys: Keys, status: XtStatus)

/// Get the number of the current session.
pub fn get_current<C: AnyConnection>(connection: &C) -> u32 {
pmikolajczyk41 marked this conversation as resolved.
Show resolved Hide resolved
get_session(connection, None)
}

pub fn get_session<C: AnyConnection>(connection: &C, block_hash: Option<H256>) -> u32 {
pmikolajczyk41 marked this conversation as resolved.
Show resolved Hide resolved
connection
.as_connection()
.get_storage_value("Session", "CurrentIndex", None)
.get_storage_value("Session", "CurrentIndex", block_hash)
.unwrap()
.unwrap_or(0)
}

pub fn wait_for<C: AnyConnection>(
pub fn wait_for_predicate<C: AnyConnection, P: Fn(u32) -> bool>(
connection: &C,
session_index: u32,
session_predicate: P,
) -> anyhow::Result<BlockNumber> {
info!(target: "aleph-client", "Waiting for session {}", session_index);
info!(target: "aleph-client", "Waiting for session");

#[derive(Debug, Decode, Clone)]
struct NewSessionEvent {
session_index: u32,
}
wait_for_event(
let result = wait_for_event(
connection,
("Session", "NewSession"),
|e: NewSessionEvent| {
info!(target: "aleph-client", "New session {}", e.session_index);

e.session_index == session_index
session_predicate(e.session_index)
},
)?;
Ok(session_index)
Ok(result.session_index)
}

pub fn wait_for<C: AnyConnection>(
connection: &C,
session_index: u32,
) -> anyhow::Result<BlockNumber> {
wait_for_predicate(connection, |session_ix| session_ix == session_index)
}

pub fn wait_for_at_least<C: AnyConnection>(
connection: &C,
session_index: u32,
) -> anyhow::Result<BlockNumber> {
wait_for_predicate(connection, |session_ix| session_ix >= session_index)
}

pub fn get_session_period<C: AnyConnection>(connection: &C) -> u32 {
connection
.as_connection()
.get_constant("Elections", "SessionPeriod")
.expect("Failed to decode SessionPeriod extrinsic!")
}
52 changes: 38 additions & 14 deletions aleph-client/src/staking.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use codec::{Compact, Decode, Encode};
use frame_support::BoundedVec;
use log::info;
use pallet_staking::{MaxUnlockingChunks, RewardDestination, UnlockChunk, ValidatorPrefs};
use pallet_staking::{
Exposure, MaxUnlockingChunks, RewardDestination, UnlockChunk, ValidatorPrefs,
};
use rayon::prelude::*;
use sp_core::Pair;
use sp_core::{Pair, H256};
use sp_runtime::Perbill;
use substrate_api_client::{
compose_call, compose_extrinsic, AccountId, Balance, ExtrinsicParams, GenericAddress, XtStatus,
Expand Down Expand Up @@ -98,32 +100,24 @@ pub fn force_new_era(connection: &RootConnection, status: XtStatus) {
}

pub fn get_current_era<C: AnyConnection>(connection: &C) -> u32 {
pmikolajczyk41 marked this conversation as resolved.
Show resolved Hide resolved
let current_era = connection
.as_connection()
.get_storage_value("Staking", "ActiveEra", None)
.expect("Failed to decode ActiveEra extrinsic!")
.expect("ActiveEra is empty in the storage!");
info!(target: "aleph-client", "Current era is {}", current_era);
current_era
get_era(connection, None)
}

pub fn wait_for_full_era_completion<C: AnyConnection>(
connection: &C,
) -> anyhow::Result<BlockNumber> {
pub fn wait_for_full_era_completion<C: AnyConnection>(connection: &C) -> anyhow::Result<u32> {
pmikolajczyk41 marked this conversation as resolved.
Show resolved Hide resolved
// staking works in such a way, that when we request a controller to be a validator in era N,
// then the changes are applied in the era N+1 (so the new validator is receiving points in N+1),
// so that we need N+1 to finish in order to claim the reward in era N+2 for the N+1 era
wait_for_era_completion(connection, get_current_era(connection) + 2)
}

pub fn wait_for_next_era<C: AnyConnection>(connection: &C) -> anyhow::Result<BlockNumber> {
pub fn wait_for_next_era<C: AnyConnection>(connection: &C) -> anyhow::Result<u32> {
wait_for_era_completion(connection, get_current_era(connection) + 1)
}

fn wait_for_era_completion<C: AnyConnection>(
connection: &C,
next_era_index: u32,
) -> anyhow::Result<BlockNumber> {
) -> anyhow::Result<u32> {
let sessions_per_era: u32 = connection
.as_connection()
.get_constant("Staking", "SessionsPerEra")
Expand All @@ -133,6 +127,23 @@ fn wait_for_era_completion<C: AnyConnection>(
Ok(next_era_index)
}

pub fn get_sessions_per_era<C: AnyConnection>(connection: &C) -> u32 {
connection
.as_connection()
.get_constant("Staking", "SessionsPerEra")
.expect("Failed to decode SessionsPerEra extrinsic!")
}

pub fn get_era<C: AnyConnection>(connection: &C, block: Option<H256>) -> u32 {
let current_era = connection
pmikolajczyk41 marked this conversation as resolved.
Show resolved Hide resolved
.as_connection()
.get_storage_value("Staking", "ActiveEra", block)
.expect("Failed to decode ActiveEra extrinsic!")
.expect("ActiveEra is empty in the storage!");
info!(target: "aleph-client", "Current era is {}", current_era);
current_era
}

pub fn payout_stakers(
stash_connection: &SignedConnection,
stash_account: &AccountId,
Expand Down Expand Up @@ -301,3 +312,16 @@ pub fn get_payout_for_era<C: AnyConnection>(connection: &C, era: u32) -> u128 {
.expect("Failed to decode ErasValidatorReward")
.expect("ErasValidatoReward is empty in the storage")
}

pub fn get_exposure<C: AnyConnection>(
connection: &C,
era: u32,
pmikolajczyk41 marked this conversation as resolved.
Show resolved Hide resolved
account_id: &AccountId,
block_hash: Option<H256>,
) -> Exposure<AccountId, u128> {
connection
.as_connection()
.get_storage_double_map("Staking", "ErasStakers", era, account_id, block_hash)
.expect("Failed to decode ErasStakers extrinsic!")
.unwrap_or_else(|| panic!("Failed to obtain ErasStakers for era {}.", era))
}
37 changes: 36 additions & 1 deletion e2e-tests/src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ use aleph_client::{keypair_from_string, KeyPair};

use crate::config::Config;

fn get_validator_seed(seed: u32) -> String {
format!("//{}", seed)
}

// this should be extracted to common code
pub fn get_validators_seeds(config: &Config) -> Vec<String> {
match config.validators_seeds {
Some(ref seeds) => seeds.clone(),
None => (0..config.validators_count)
.map(|seed| format!("//{}", seed))
.map(get_validator_seed)
.collect(),
}
}
Expand All @@ -27,3 +31,34 @@ pub fn accounts_seeds_to_keys(seeds: &[String]) -> Vec<KeyPair> {
pub fn get_sudo_key(config: &Config) -> KeyPair {
keypair_from_string(&config.sudo_seed)
}

pub struct NodeKeys {
pub validator_key: KeyPair,
pub controller_key: KeyPair,
pub stash_key: KeyPair,
}

impl From<u32> for NodeKeys {
pmikolajczyk41 marked this conversation as resolved.
Show resolved Hide resolved
fn from(seed: u32) -> Self {
let validator_seed = get_validator_seed(seed);
NodeKeys::from(validator_seed)
}
}

impl From<String> for NodeKeys {
fn from(seed: String) -> Self {
Self {
validator_key: keypair_from_string(&seed[..]),
controller_key: keypair_from_string(&get_validators_controller_seed(seed.clone())[..]),
stash_key: keypair_from_string(&get_validators_stash_seed(seed.clone())[..]),
}
}
}

fn get_validators_controller_seed(seed: String) -> String {
format!("{}//Controller", seed)
}

fn get_validators_stash_seed(seed: String) -> String {
format!("{}//stash", seed)
maciejzelaszczyk marked this conversation as resolved.
Show resolved Hide resolved
}
6 changes: 4 additions & 2 deletions e2e-tests/src/cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use crate::{
era_payouts_calculated_correctly as test_era_payout, era_validators as test_era_validators,
fee_calculation as test_fee_calculation, finalization as test_finalization,
staking_era_payouts as test_staking_era_payouts,
staking_new_validator as test_staking_new_validator, token_transfer as test_token_transfer,
treasury_access as test_treasury_access, validators_rotate as test_validators_rotate,
staking_new_validator as test_staking_new_validator, test_disable_node,
maciejzelaszczyk marked this conversation as resolved.
Show resolved Hide resolved
token_transfer as test_token_transfer, treasury_access as test_treasury_access,
validators_rotate as test_validators_rotate,
},
};

Expand All @@ -20,6 +21,7 @@ pub type PossibleTestCases = Vec<(&'static str, TestCase)>;
/// This comes up in local tests.
pub fn possible_test_cases() -> PossibleTestCases {
vec![
("reward_points_disable_node", test_disable_node as TestCase),
pmikolajczyk41 marked this conversation as resolved.
Show resolved Hide resolved
("finalization", test_finalization as TestCase),
("token_transfer", test_token_transfer as TestCase),
(
Expand Down
18 changes: 18 additions & 0 deletions e2e-tests/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use aleph_client::RootConnection;
use clap::Parser;

use crate::accounts::{get_sudo_key, get_validators_seeds, NodeKeys};

#[derive(Debug, Parser, Clone)]
#[clap(version = "1.0")]
pub struct Config {
Expand All @@ -24,3 +27,18 @@ pub struct Config {
#[clap(long, default_value = "//Alice")]
pub sudo_seed: String,
}

impl Config {
pub fn node_keys(self: &Self) -> NodeKeys {
let validator_seed = get_validators_seeds(self)
.into_iter()
.next()
.expect("we should have a seed for at least one validator");
NodeKeys::from(validator_seed)
}

pub fn create_root_connection(self: &Self) -> RootConnection {
let sudo_keypair = get_sudo_key(self);
RootConnection::new(&self.node, sudo_keypair)
}
}
2 changes: 2 additions & 0 deletions e2e-tests/src/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub use era_payout::era_payouts_calculated_correctly;
pub use era_validators::era_validators;
pub use fee::fee_calculation;
pub use finalization::finalization;
pub use rewards::test_disable_node;
pub use staking::{staking_era_payouts, staking_new_validator};
pub use transfer::token_transfer;
pub use treasury::{channeling_fee_and_tip, treasury_access};
Expand All @@ -13,6 +14,7 @@ mod era_payout;
mod era_validators;
mod fee;
mod finalization;
mod rewards;
mod staking;
mod transfer;
mod treasury;
Expand Down
Loading