Skip to content

Commit

Permalink
feat: dynamic min authority count (#4224)
Browse files Browse the repository at this point in the history
  • Loading branch information
msgmaxim authored Nov 10, 2023
1 parent cabb2f2 commit db83d0d
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 9 deletions.
9 changes: 5 additions & 4 deletions state-chain/cf-integration-tests/src/funding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,8 @@ fn can_calculate_account_apy() {
#[test]
fn apy_can_be_above_100_percent() {
const EPOCH_BLOCKS: u32 = 1_000;
const MAX_AUTHORITIES: u32 = 1;
const NUM_BACKUPS: u32 = 1;
const MAX_AUTHORITIES: u32 = 2;
const NUM_BACKUPS: u32 = 2;
super::genesis::default()
.blocks_per_epoch(EPOCH_BLOCKS)
.max_authorities(MAX_AUTHORITIES)
Expand All @@ -266,10 +266,11 @@ fn apy_can_be_above_100_percent() {

// APY rate of > 100% can be calculated correctly.
let total = Flip::balance(&validator);
let reward = Emissions::current_authority_emission_per_block() * YEAR as u128;
let reward = Emissions::current_authority_emission_per_block() * YEAR as u128 /
MAX_AUTHORITIES as u128;
let apy_basis_point =
FixedU64::from_rational(reward, total).checked_mul_int(10_000u32).unwrap();
assert_eq!(apy_basis_point, 242_543_802u32);
assert_eq!(apy_basis_point, 241_377_726u32);
assert_eq!(calculate_account_apy(&validator), Some(apy_basis_point));
});
}
6 changes: 5 additions & 1 deletion state-chain/cf-integration-tests/src/mock_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ use crate::{
threshold_signing::{EthKeyComponents, KeyUtils},
GENESIS_KEY_SEED,
};
use cf_primitives::{AccountRole, AuthorityCount, BlockNumber, FlipBalance, GENESIS_EPOCH};
use cf_primitives::{
AccountRole, AuthorityCount, BlockNumber, FlipBalance, DEFAULT_MAX_AUTHORITY_SET_CONTRACTION,
GENESIS_EPOCH,
};

pub struct ExtBuilder {
pub genesis_accounts: Vec<(AccountId, AccountRole, FlipBalance)>,
Expand Down Expand Up @@ -163,6 +166,7 @@ impl ExtBuilder {
min_size: self.min_authorities,
max_size: self.max_authorities,
max_expansion: self.max_authorities,
max_authority_set_contraction_percentage: DEFAULT_MAX_AUTHORITY_SET_CONTRACTION,
},
ethereum_vault: EthereumVaultConfig {
vault_key: Some(ethereum_vault_key),
Expand Down
9 changes: 8 additions & 1 deletion state-chain/node/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use cf_chains::{
dot::{PolkadotAccountId, PolkadotHash},
ChainState,
};
use cf_primitives::{chains::assets, AccountRole, AssetAmount, AuthorityCount, NetworkEnvironment};
use cf_primitives::{
chains::assets, AccountRole, AssetAmount, AuthorityCount, NetworkEnvironment,
DEFAULT_MAX_AUTHORITY_SET_CONTRACTION,
};

use cf_chains::{
btc::{BitcoinFeeInfo, BitcoinTrackedData},
Expand Down Expand Up @@ -231,6 +234,7 @@ pub fn inner_cf_development_config(
testnet::SNOW_WHITE_SR25519.into(),
1,
devnet::MAX_AUTHORITIES,
DEFAULT_MAX_AUTHORITY_SET_CONTRACTION,
EnvironmentConfig {
flip_token_address: flip_token_address.into(),
eth_usdc_address: eth_usdc_address.into(),
Expand Down Expand Up @@ -358,6 +362,7 @@ macro_rules! network_spec {
SNOW_WHITE_SR25519.into(),
MIN_AUTHORITIES,
MAX_AUTHORITIES,
DEFAULT_MAX_AUTHORITY_SET_CONTRACTION,
EnvironmentConfig {
flip_token_address: flip_token_address.into(),
eth_usdc_address: eth_usdc_address.into(),
Expand Down Expand Up @@ -428,6 +433,7 @@ fn testnet_genesis(
root_key: AccountId,
min_authorities: AuthorityCount,
max_authorities: AuthorityCount,
max_authority_set_contraction_percentage: Percent,
config_set: EnvironmentConfig,
eth_init_agg_key: [u8; 33],
ethereum_deployment_block: u64,
Expand Down Expand Up @@ -556,6 +562,7 @@ fn testnet_genesis(
min_size: min_authorities,
max_size: max_authorities,
max_expansion: max_authorities,
max_authority_set_contraction_percentage,
},
session: SessionConfig {
keys: initial_authorities
Expand Down
28 changes: 27 additions & 1 deletion state-chain/pallets/cf-validator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ mod migrations;
mod rotation_state;
pub use auction_resolver::*;

use cf_primitives::{AuthorityCount, EpochIndex, NodeCFEVersions, SemVer, FLIPPERINOS_PER_FLIP};
use cf_primitives::{
AuthorityCount, EpochIndex, NodeCFEVersions, SemVer, DEFAULT_MAX_AUTHORITY_SET_CONTRACTION,
FLIPPERINOS_PER_FLIP,
};

use cf_traits::{
impl_pallet_safe_mode, offence_reporting::OffenceReporter, AsyncResult, AuthoritiesCfeVersions,
Expand Down Expand Up @@ -62,6 +65,7 @@ pub enum PalletConfigUpdate {
AuthoritySetMinSize { min_size: AuthorityCount },
AuctionParameters { parameters: SetSizeParameters },
MinimumReportedCfeVersion { version: SemVer },
MaxAuthoritySetContractionPercentage { percentage: Percent },
}

type RuntimeRotationState<T> =
Expand Down Expand Up @@ -286,6 +290,13 @@ pub mod pallet {
#[pallet::getter(fn minimum_reported_cfe_version)]
pub(super) type MinimumReportedCfeVersion<T: Config> = StorageValue<_, SemVer, ValueQuery>;

/// Determines the maximum allowed reduction of authority set size in percents between two
/// consecutive epochs.
#[pallet::storage]
#[pallet::getter(fn max_authority_set_contraction_percentage)]
pub(super) type MaxAuthoritySetContractionPercentage<T: Config> =
StorageValue<_, Percent, ValueQuery>;

#[pallet::event]
#[pallet::generate_deposit(pub (super) fn deposit_event)]
pub enum Event<T: Config> {
Expand Down Expand Up @@ -533,6 +544,9 @@ pub mod pallet {
PalletConfigUpdate::MinimumReportedCfeVersion { version } => {
MinimumReportedCfeVersion::<T>::put(version);
},
PalletConfigUpdate::MaxAuthoritySetContractionPercentage { percentage } => {
MaxAuthoritySetContractionPercentage::<T>::put(percentage);
},
}

Self::deposit_event(Event::PalletConfigUpdated { update });
Expand Down Expand Up @@ -780,6 +794,7 @@ pub mod pallet {
pub min_size: AuthorityCount,
pub max_size: AuthorityCount,
pub max_expansion: AuthorityCount,
pub max_authority_set_contraction_percentage: Percent,
}

impl<T: Config> Default for GenesisConfig<T> {
Expand All @@ -796,6 +811,7 @@ pub mod pallet {
min_size: 3,
max_size: 15,
max_expansion: 5,
max_authority_set_contraction_percentage: DEFAULT_MAX_AUTHORITY_SET_CONTRACTION,
}
}
}
Expand All @@ -811,6 +827,9 @@ pub mod pallet {
BackupRewardNodePercentage::<T>::set(self.backup_reward_node_percentage);
AuthoritySetMinSize::<T>::set(self.authority_set_min_size);
VanityNames::<T>::put(&self.genesis_vanity_names);
MaxAuthoritySetContractionPercentage::<T>::set(
self.max_authority_set_contraction_percentage,
);

CurrentEpoch::<T>::set(GENESIS_EPOCH);

Expand Down Expand Up @@ -1091,6 +1110,13 @@ impl<T: Config> Pallet<T> {
fn try_start_keygen(rotation_state: RuntimeRotationState<T>) {
let candidates = rotation_state.authority_candidates();
let SetSizeParameters { min_size, .. } = AuctionParameters::<T>::get();

let min_size = sp_std::cmp::max(
min_size,
(Percent::one().saturating_sub(MaxAuthoritySetContractionPercentage::<T>::get())) *
Self::current_authority_count(),
);

if (candidates.len() as u32) < min_size {
log::warn!(
target: "cf-validator",
Expand Down
1 change: 1 addition & 0 deletions state-chain/pallets/cf-validator/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ cf_test_utilities::impl_test_helpers! {
min_size: MIN_AUTHORITY_SIZE,
max_size: MAX_AUTHORITY_SIZE,
max_expansion: MAX_AUTHORITY_SET_EXPANSION,
max_authority_set_contraction_percentage: DEFAULT_MAX_AUTHORITY_SET_CONTRACTION,
},
},
||{
Expand Down
30 changes: 29 additions & 1 deletion state-chain/pallets/cf-validator/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,12 @@ const AUTHORITIES: Range<u64> = 0..10;

lazy_static::lazy_static! {
/// How many candidates can fail without preventing us from re-trying keygen
static ref MAX_ALLOWED_KEYGEN_OFFENDERS: usize = CANDIDATES.count().checked_sub(MIN_AUTHORITY_SIZE as usize).unwrap();
static ref MAX_ALLOWED_KEYGEN_OFFENDERS: usize = {

let min_size = std::cmp::max(MIN_AUTHORITY_SIZE, (Percent::one() - DEFAULT_MAX_AUTHORITY_SET_CONTRACTION) * AUTHORITIES.count() as u32);

CANDIDATES.count().checked_sub(min_size as usize).unwrap()
};

/// How many current authorities can fail to leave enough healthy ones to handover the key
static ref MAX_ALLOWED_SHARING_OFFENDERS: usize = {
Expand Down Expand Up @@ -788,6 +793,29 @@ mod keygen {
assert_rotation_aborted();
});
}

#[test]
fn rotation_aborts_if_candidates_below_min_percentage() {
new_test_ext().execute_with(|| {
// Ban half of the candidates:
let failing_count = CANDIDATES.count() / 2;
let remaining_count = CANDIDATES.count() - failing_count;

// We still have enough candidates according to auction resolver parameters:
assert!(remaining_count > MIN_AUTHORITY_SIZE as usize);

// But the rotation should be aborted since authority count would drop too much
// compared to the previous set:
assert!(
remaining_count <
(Percent::one() - DEFAULT_MAX_AUTHORITY_SET_CONTRACTION) *
AUTHORITIES.count()
);

failed_keygen_with_offenders(CANDIDATES.take(failing_count));
assert_rotation_aborted();
});
}
}

#[cfg(test)]
Expand Down
6 changes: 5 additions & 1 deletion state-chain/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::sp_runtime::{
traits::{IdentifyAccount, Verify},
MultiSignature, RuntimeDebug,
MultiSignature, Percent, RuntimeDebug,
};
use scale_info::TypeInfo;
use semver::{Error, Version};
Expand Down Expand Up @@ -75,6 +75,10 @@ pub const SECONDS_PER_BLOCK: u64 = MILLISECONDS_PER_BLOCK / 1000;

pub const STABLE_ASSET: Asset = Asset::Usdc;

/// Determines the default (genesis) maximum allowed reduction of authority set size in
/// between two consecutive epochs.
pub const DEFAULT_MAX_AUTHORITY_SET_CONTRACTION: Percent = Percent::from_percent(30);

// Polkadot extrinsics are uniquely identified by <block number>-<extrinsic index>
// https://wiki.polkadot.network/docs/build-protocol-info
#[derive(Clone, Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq)]
Expand Down

0 comments on commit db83d0d

Please sign in to comment.