Skip to content

Commit

Permalink
L1-294: Safeguards for the inflation parameters setter (#1822)
Browse files Browse the repository at this point in the history
# Description

We introduce safeguards that make it impossible to modify the values of
the inflation parameters in a way that the new value differs too much
from the old one. The rule is that the new value should lie between half
and twice the old value. With regard to the AZERO cap, we look at the
remaining issuance instead of the actual cap value, as this parameter
influences the payouts directly.

## Type of change

- Breaking change (fix or feature that would cause existing
functionality to not work as expected)
  • Loading branch information
lesniak43 authored Oct 8, 2024
1 parent 5a47d23 commit 5bfb995
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 6 deletions.
11 changes: 10 additions & 1 deletion bin/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ use primitives::{
staking::MAX_NOMINATORS_REWARDED_PER_VALIDATOR, wrap_methods, Address,
AlephNodeSessionKeys as SessionKeys, ApiError as AlephApiError, AuraId, AuthorityId as AlephId,
BlockNumber as AlephBlockNumber, Header as AlephHeader, SessionAuthorityData, SessionCommittee,
SessionIndex, SessionInfoProvider, SessionValidatorError, Version as FinalityVersion,
SessionIndex, SessionInfoProvider, SessionValidatorError,
TotalIssuanceProvider as TotalIssuanceProviderT, Version as FinalityVersion,
ADDRESSES_ENCODING, DEFAULT_BAN_REASON_LENGTH, DEFAULT_MAX_WINNERS, DEFAULT_SESSIONS_PER_ERA,
DEFAULT_SESSION_PERIOD, MAX_BLOCK_SIZE, MILLISECS_PER_BLOCK, TOKEN,
};
Expand Down Expand Up @@ -322,6 +323,13 @@ impl SessionInfoProvider<AlephBlockNumber> for SessionInfoImpl {
}
}

pub struct TotalIssuanceProvider;
impl TotalIssuanceProviderT for TotalIssuanceProvider {
fn get() -> Balance {
pallet_balances::Pallet::<Runtime>::total_issuance()
}
}

impl pallet_aleph::Config for Runtime {
type AuthorityId = AlephId;
type RuntimeEvent = RuntimeEvent;
Expand All @@ -333,6 +341,7 @@ impl pallet_aleph::Config for Runtime {
Runtime,
>;
type NextSessionAuthorityProvider = Session;
type TotalIssuanceProvider = TotalIssuanceProvider;
}

parameter_types! {
Expand Down
79 changes: 75 additions & 4 deletions pallets/aleph/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub mod pallet {
pallet_prelude::{BlockNumberFor, OriginFor},
};
use pallet_session::SessionManager;
use primitives::SessionInfoProvider;
use primitives::{SessionInfoProvider, TotalIssuanceProvider};
use sp_std::collections::btree_map::BTreeMap;
#[cfg(feature = "std")]
use sp_std::marker::PhantomData;
Expand All @@ -48,6 +48,7 @@ pub mod pallet {
type SessionInfoProvider: SessionInfoProvider<BlockNumberFor<Self>>;
type SessionManager: SessionManager<<Self as frame_system::Config>::AccountId>;
type NextSessionAuthorityProvider: NextSessionAuthorityProvider<Self>;
type TotalIssuanceProvider: TotalIssuanceProvider;
}

#[pallet::event]
Expand Down Expand Up @@ -228,6 +229,64 @@ pub mod pallet {

Self::finality_version()
}

pub fn check_horizon_upper_bound(
new_horizon: u64,
current_horizon: u64,
) -> Result<(), &'static str> {
match new_horizon > current_horizon.saturating_mul(2).saturating_add(1) {
true => {
Err("Horizon too large, should be at most twice the current value plus one!")
}
false => Ok(()),
}
}

pub fn check_horizon_lower_bound(
new_horizon: u64,
current_horizon: u64,
) -> Result<(), &'static str> {
match new_horizon < current_horizon / 2 {
true => Err("Horizon too small, should be at least half the current value!"),
false => Ok(()),
}
}

pub fn check_azero_cap_upper_bound(
new_cap: Balance,
current_cap: Balance,
total_issuance: Balance,
) -> Result<(), &'static str> {
let current_gap = current_cap.saturating_sub(total_issuance);
let new_gap = match new_cap.checked_sub(total_issuance) {
Some(new_gap) => new_gap,
None => return Err("AZERO Cap cannot be lower than the current total issuance!"),
};
match (new_gap > current_gap.saturating_mul(2).saturating_add(1))
&& (new_gap > total_issuance / 128)
{
true => Err("Future issuance too large, should be at most the current total issuance divided by 128, or at most twice the current value plus one!"),
false => Ok(()),
}
}

pub fn check_azero_cap_lower_bound(
new_cap: Balance,
current_cap: Balance,
total_issuance: Balance,
) -> Result<(), &'static str> {
let current_gap = current_cap.saturating_sub(total_issuance);
let new_gap = match new_cap.checked_sub(total_issuance) {
Some(new_gap) => new_gap,
None => return Err("AZERO Cap cannot be lower than the current total issuance!"),
};
match new_gap < current_gap / 2 {
true => {
Err("Future issuance too small, should be at least half the current value!")
}
false => Ok(()),
}
}
}

#[pallet::call]
Expand Down Expand Up @@ -284,9 +343,21 @@ pub mod pallet {
) -> DispatchResult {
ensure_root(origin)?;

let azero_cap = azero_cap.unwrap_or_else(AzeroCap::<T>::get);
let horizon_millisecs =
horizon_millisecs.unwrap_or_else(ExponentialInflationHorizon::<T>::get);
let current_azero_cap = AzeroCap::<T>::get();
let current_horizon_millisecs = ExponentialInflationHorizon::<T>::get();
let total_issuance = T::TotalIssuanceProvider::get();

let azero_cap = azero_cap.unwrap_or(current_azero_cap);
let horizon_millisecs = horizon_millisecs.unwrap_or(current_horizon_millisecs);

Self::check_horizon_lower_bound(horizon_millisecs, current_horizon_millisecs)
.map_err(DispatchError::Other)?;
Self::check_horizon_upper_bound(horizon_millisecs, current_horizon_millisecs)
.map_err(DispatchError::Other)?;
Self::check_azero_cap_upper_bound(azero_cap, current_azero_cap, total_issuance)
.map_err(DispatchError::Other)?;
Self::check_azero_cap_lower_bound(azero_cap, current_azero_cap, total_issuance)
.map_err(DispatchError::Other)?;

AzeroCap::<T>::put(azero_cap);
ExponentialInflationHorizon::<T>::put(horizon_millisecs);
Expand Down
12 changes: 11 additions & 1 deletion pallets/aleph/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use frame_support::{
weights::{RuntimeDbWeight, Weight},
};
use frame_system::pallet_prelude::BlockNumberFor;
use primitives::{AuthorityId, SessionInfoProvider};
use primitives::{
AuthorityId, SessionInfoProvider, TotalIssuanceProvider as TotalIssuanceProviderT,
};
use sp_core::H256;
use sp_runtime::{
impl_opaque_keys,
Expand Down Expand Up @@ -147,12 +149,20 @@ impl pallet_timestamp::Config for Test {
type WeightInfo = ();
}

pub struct TotalIssuanceProvider;
impl TotalIssuanceProviderT for TotalIssuanceProvider {
fn get() -> Balance {
pallet_balances::Pallet::<Test>::total_issuance()
}
}

impl Config for Test {
type AuthorityId = AuthorityId;
type RuntimeEvent = RuntimeEvent;
type SessionInfoProvider = SessionInfoImpl;
type SessionManager = ();
type NextSessionAuthorityProvider = Session;
type TotalIssuanceProvider = TotalIssuanceProvider;
}

pub fn to_authority(id: &u64) -> AuthorityId {
Expand Down
6 changes: 6 additions & 0 deletions primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,12 @@ pub trait EraManager {
fn on_new_era(era: EraIndex);
}

/// Provides the current total issuance.
pub trait TotalIssuanceProvider {
/// Get the current total issuance.
fn get() -> Balance;
}

pub mod staking {
use crate::TOKEN;

Expand Down

0 comments on commit 5bfb995

Please sign in to comment.