Module sui_system::validator_set
- Struct ValidatorSet
- Struct ValidatorEpochInfoEvent
- Struct ValidatorEpochInfoEventV2
- Struct ValidatorJoinEvent
- Struct ValidatorLeaveEvent
- Struct VotingPowerAdmissionStartEpochKey
- Constants
- Function new
- Function request_add_validator_candidate
- Function request_remove_validator_candidate
- Function request_add_validator
- Function can_join
- Function get_voting_power_thresholds
- Function assert_no_pending_or_active_duplicates
- Function request_remove_validator
- Function request_add_stake
- Function request_withdraw_stake
- Function convert_to_fungible_staked_sui
- Function redeem_fungible_staked_sui
- Function request_set_commission_rate
- Function advance_epoch
- Function update_validator_positions_and_calculate_total_stake
- Function effectuate_staged_metadata
- Function derive_reference_gas_price
- Function total_stake
- Function validator_total_stake_amount
- Function validator_stake_amount
- Function validator_voting_power
- Function validator_staking_pool_id
- Function staking_pool_mappings
- Function validator_address_by_pool_id
- Function pool_exchange_rates
- Function validator_by_pool_id
- Function next_epoch_validator_count
- Function is_active_validator_by_sui_address
- Function is_duplicate_with_active_validator
- Function is_duplicate_validator
- Function count_duplicates_vec
- Function is_duplicate_with_pending_validator
- Function count_duplicates_tablevec
- Function get_candidate_or_active_validator_mut
- Function find_validator
- Function find_validator_from_table_vec
- Function get_validator_indices
- Function get_validator_mut
- Function get_active_or_pending_or_candidate_validator_mut
- Function get_validator_mut_with_verified_cap
- Function get_validator_mut_with_ctx
- Function get_validator_mut_with_ctx_including_candidates
- Function get_validator_ref
- Function get_active_or_pending_or_candidate_validator_ref
- Function get_active_validator_ref
- Function get_pending_validator_ref
- Function verify_cap
- Function process_pending_removals
- Function process_validator_departure
- Function clean_report_records_leaving_validator
- Function sort_removal_list
- Function process_pending_stakes_and_withdraws
- Function calculate_total_stakes
- Function adjust_stake_and_gas_price
- Function compute_reward_adjustments
- Function compute_slashed_validators
- Function compute_unadjusted_reward_distribution
- Function compute_adjusted_reward_distribution
- Function distribute_reward
- Function emit_validator_epoch_events
- Function sum_voting_power_by_addresses
- Function active_validators
- Function is_validator_candidate
- Function is_active_validator
- Function is_inactive_validator
- Function is_at_risk_validator
- Function active_validator_addresses
- Macro function mul_div
use std::address;
use std::ascii;
use std::bcs;
use std::option;
use std::string;
use std::type_name;
use std::u64;
use std::vector;
use sui::accumulator;
use sui::accumulator_metadata;
use sui::accumulator_settlement;
use sui::address;
use sui::bag;
use sui::balance;
use sui::bcs;
use sui::coin;
use sui::config;
use sui::deny_list;
use sui::dynamic_field;
use sui::dynamic_object_field;
use sui::event;
use sui::funds_accumulator;
use sui::hash;
use sui::hex;
use sui::object;
use sui::party;
use sui::priority_queue;
use sui::sui;
use sui::table;
use sui::table_vec;
use sui::transfer;
use sui::tx_context;
use sui::types;
use sui::url;
use sui::vec_map;
use sui::vec_set;
use sui::versioned;
use sui_system::staking_pool;
use sui_system::validator;
use sui_system::validator_cap;
use sui_system::validator_wrapper;
use sui_system::voting_power;
Struct ValidatorSet
public struct ValidatorSet has store
Fields
-
total_stake: u64 -
Total amount of stake from all active validators at the beginning of the epoch.
Written only once per epoch, in
function.advance_epoch -
active_validators: vector<sui_system::validator::Validator> - The current list of active validators.
-
pending_active_validators: sui::table_vec::TableVec<sui_system::validator::Validator> - List of new validator candidates added during the current epoch. They will be processed at the end of the epoch.
-
pending_removals: vector<u64> -
Removal requests from the validators. Each element is an index
pointing to
.active_validators -
staking_pool_mappings: sui::table::Table<sui::object::ID, address> - Mappings from staking pool's ID to the sui address of a validator.
-
inactive_validators: sui::table::Table<sui::object::ID, sui_system::validator_wrapper::ValidatorWrapper> -
Mapping from a staking pool ID to the inactive validator that has that pool as its staking pool.
When a validator is deactivated the validator is removed from
it is added to this table so that stakers can continue to withdraw their stake from it.active_validators -
validator_candidates: sui::table::Table<address, sui_system::validator_wrapper::ValidatorWrapper> -
Table storing preactive/candidate validators, mapping their addresses to their
Validatorstructs. When an address calls
, they get added to this table and become a preactive validator. When the candidate has met the min stake requirement, they can callrequest_add_validator_candidate
to officially add them to the active validator setrequest_add_validator
next epoch.active_validators -
at_risk_validators: sui::vec_map::VecMap<address, u64> - Table storing the number of epochs during which a validator's stake has been below the low stake threshold.
-
extra_fields: sui::bag::Bag - Any extra fields that's not defined statically.
Struct ValidatorEpochInfoEvent
Event containing staking and rewards related information of each validator, emitted during epoch advancement.
public struct ValidatorEpochInfoEvent has copy, drop
Fields
-
epoch: u64 -
validator_address: address -
reference_gas_survey_quote: u64 -
stake: u64 -
commission_rate: u64 -
pool_staking_reward: u64 -
storage_fund_staking_reward: u64 -
pool_token_exchange_rate: sui_system::staking_pool::PoolTokenExchangeRate -
tallying_rule_reporters: vector<address> -
tallying_rule_global_score: u64
Struct ValidatorEpochInfoEventV2
V2 of ValidatorEpochInfoEvent containing more information about the validator.
public struct ValidatorEpochInfoEventV2 has copy, drop
Fields
-
epoch: u64 -
validator_address: address -
reference_gas_survey_quote: u64 -
stake: u64 -
voting_power: u64 -
commission_rate: u64 -
pool_staking_reward: u64 -
storage_fund_staking_reward: u64 -
pool_token_exchange_rate: sui_system::staking_pool::PoolTokenExchangeRate -
tallying_rule_reporters: vector<address> -
tallying_rule_global_score: u64
Struct ValidatorJoinEvent
Event emitted every time a new validator joins the committee. The epoch value corresponds to the first epoch this change takes place.
public struct ValidatorJoinEvent has copy, drop
Fields
-
epoch: u64 -
validator_address: address -
staking_pool_id: sui::object::ID
Struct ValidatorLeaveEvent
Event emitted every time a validator leaves the committee. The epoch value corresponds to the first epoch this change takes place.
public struct ValidatorLeaveEvent has copy, drop
Fields
-
epoch: u64 -
validator_address: address -
staking_pool_id: sui::object::ID -
is_voluntary: bool
Struct VotingPowerAdmissionStartEpochKey
Key for the extra_fields bag to store the start epoch of allowing admission
of new validators based on a minimum voting power rather than a minimum stake.
public struct VotingPowerAdmissionStartEpochKey has copy, drop, store
Fields
Constants
const ENonValidatorInReportRecords: u64 = 0;
const EInvalidStakeAdjustmentAmount: u64 = 1;
const EDuplicateValidator: u64 = 2;
const ENoPoolFound: u64 = 3;
const ENotAValidator: u64 = 4;
const EMinJoiningStakeNotReached: u64 = 5;
const EAlreadyValidatorCandidate: u64 = 6;
const EValidatorNotCandidate: u64 = 7;
const ENotValidatorCandidate: u64 = 8;
const ENotActiveOrPendingValidator: u64 = 9;
const EStakingBelowThreshold: u64 = 10;
const EValidatorAlreadyRemoved: u64 = 11;
const ENotAPendingValidator: u64 = 12;
const EValidatorSetEmpty: u64 = 13;
const EInvalidCap: u64 = 101;
const ACTIVE_VALIDATOR_ONLY: u8 = 1;
const ACTIVE_OR_PENDING_VALIDATOR: u8 = 2;
const ANY_VALIDATOR: u8 = 3;
const BASIS_POINT_DENOMINATOR: u64 = 10000;
const MIN_STAKING_THRESHOLD: u64 = 1000000000;
const PHASE_LENGTH: u64 = 14;
Function new
public(package) fun new(init_active_validators: vector<sui_system::validator::Validator>, ctx: &mut sui::tx_context::TxContext): sui_system::validator_set::ValidatorSet
Implementation
public(package) fun new(
init_active_validators: vector<Validator>,
ctx: &mut TxContext,
): ValidatorSet {
let total_stake = calculate_total_stakes(&init_active_validators);
let mut staking_pool_mappings = table::new(ctx);
init_active_validators.do_ref!(|v| {
staking_pool_mappings.add(v.staking_pool_id(), v.sui_address());
});
let mut validators = ValidatorSet {
total_stake,
active_validators: init_active_validators,
pending_active_validators: table_vec::empty(ctx),
pending_removals: vector[],
staking_pool_mappings,
inactive_validators: table::new(ctx),
validator_candidates: table::new(ctx),
at_risk_validators: vec_map::empty(),
extra_fields: bag::new(ctx),
};
voting_power::set_voting_power(&mut validators.active_validators, total_stake);
validators
}
Function request_add_validator_candidate
Called by
sui_system to add a new validator candidate.
public(package) fun request_add_validator_candidate(self: &mut sui_system::validator_set::ValidatorSet, validator: sui_system::validator::Validator, ctx: &mut sui::tx_context::TxContext)
Implementation
public(package) fun request_add_validator_candidate(
self: &mut ValidatorSet,
validator: Validator,
ctx: &mut TxContext,
) {
// The next assertions are not critical for the protocol, but they are here to catch problematic configs earlier.
assert!(
!self.is_duplicate_with_active_validator(&validator)
&& !self.is_duplicate_with_pending_validator(&validator),
EDuplicateValidator,
);
let validator_address = validator.sui_address();
assert!(!self.validator_candidates.contains(validator_address), EAlreadyValidatorCandidate);
assert!(validator.is_preactive(), EValidatorNotCandidate);
// Add validator to the candidates mapping and the pool id mappings so that users can start
// staking with this candidate.
self.staking_pool_mappings.add(validator.staking_pool_id(), validator_address);
self.validator_candidates.add(validator.sui_address(), validator.wrap_v1(ctx));
}
Function request_remove_validator_candidate
Called by
sui_system to remove a validator candidate, and move them to inactive_validators.
public(package) fun request_remove_validator_candidate(self: &mut sui_system::validator_set::ValidatorSet, ctx: &mut sui::tx_context::TxContext)
Implementation
public(package) fun request_remove_validator_candidate(
self: &mut ValidatorSet,
ctx: &mut TxContext,
) {
let validator_address = ctx.sender();
assert!(self.validator_candidates.contains(validator_address), ENotValidatorCandidate);
let mut validator = self.validator_candidates.remove(validator_address).destroy();
assert!(validator.is_preactive(), EValidatorNotCandidate);
let staking_pool_id = validator.staking_pool_id();
// Remove the validator's staking pool from mappings.
self.staking_pool_mappings.remove(staking_pool_id);
// Deactivate the staking pool.
validator.deactivate(ctx.epoch());
// Add to the inactive tables.
self.inactive_validators.add(staking_pool_id, validator.wrap_v1(ctx));
}
Function request_add_validator
Called by
sui_system to add a new validator to pending_active_validators, which will be
processed at the end of epoch.
public(package) fun request_add_validator(self: &mut sui_system::validator_set::ValidatorSet, ctx: &sui::tx_context::TxContext)
Implementation
public(package) fun request_add_validator(self: &mut ValidatorSet, ctx: &TxContext) {
let validator_address = ctx.sender();
assert!(self.validator_candidates.contains(validator_address), ENotValidatorCandidate);
let validator = self.validator_candidates.remove(validator_address).destroy();
assert!(
!self.is_duplicate_with_active_validator(&validator)
&& !self.is_duplicate_with_pending_validator(&validator),
EDuplicateValidator,
);
assert!(validator.is_preactive(), EValidatorNotCandidate);
assert!(self.can_join(validator.total_stake(), ctx), EMinJoiningStakeNotReached);
self.pending_active_validators.push_back(validator);
}
Function can_join
Return
true if a candidate validator with stake will have sufficeint voting power to join the validator set
fun can_join(self: &sui_system::validator_set::ValidatorSet, stake: u64, ctx: &sui::tx_context::TxContext): bool
Implementation
fun can_join(self: &ValidatorSet, stake: u64, ctx: &TxContext): bool {
let (min_joining_voting_power, _, _) = self.get_voting_power_thresholds(ctx);
// if the validator will have at least min_joining_voting_power after joining, they can join.
// this formula comes from SIP-39: https://github.com/sui-foundation/sips/blob/main/sips/sip-39.md
let future_total_stake = self.total_stake + stake;
let future_validator_voting_power = voting_power::derive_raw_voting_power(
stake,
future_total_stake,
);
future_validator_voting_power >= min_joining_voting_power
}
Function get_voting_power_thresholds
return (min, low, very low voting power) thresholds
fun get_voting_power_thresholds(self: &sui_system::validator_set::ValidatorSet, ctx: &sui::tx_context::TxContext): (u64, u64, u64)
Implementation
fun get_voting_power_thresholds(self: &ValidatorSet, ctx: &TxContext): (u64, u64, u64) {
let start_epoch = {
let key = VotingPowerAdmissionStartEpochKey();
if (self.extra_fields.contains(key)) self.extra_fields[key]
else ctx.epoch() + 1 // will give us the phase 1 values
};
// these numbers come from SIP-39: https://github.com/sui-foundation/sips/blob/main/sips/sip-39.md
let curr_epoch = ctx.epoch();
if (curr_epoch < start_epoch + PHASE_LENGTH) (12, 8, 4) // phase 1
else if (curr_epoch < start_epoch + (2 * PHASE_LENGTH)) (6, 4, 2) // phase 2
else (3, 2, 1) // phase 3
}
Function assert_no_pending_or_active_duplicates
public(package) fun assert_no_pending_or_active_duplicates(self: &sui_system::validator_set::ValidatorSet, validator: &sui_system::validator::Validator)
Implementation
public(package) fun assert_no_pending_or_active_duplicates(
self: &ValidatorSet,
validator: &Validator,
) {
// Validator here must be active or pending, and thus must be identified as duplicate exactly once.
assert!(
count_duplicates_vec(&self.active_validators, validator) +
count_duplicates_tablevec(&self.pending_active_validators, validator) == 1,
EDuplicateValidator,
);
}
Function request_remove_validator
Called by
sui_system, to remove a validator.
The index of the validator is added to pending_removals and
will be processed at the end of epoch.
Only an active validator can request to be removed.
public(package) fun request_remove_validator(self: &mut sui_system::validator_set::ValidatorSet, ctx: &sui::tx_context::TxContext)
Implementation
public(package) fun request_remove_validator(self: &mut ValidatorSet, ctx: &TxContext) {
let validator_address = ctx.sender();
let validator_index = find_validator(
&self.active_validators,
validator_address,
).destroy_or!(abort ENotAValidator);
assert!(!self.pending_removals.contains(&validator_index), EValidatorAlreadyRemoved);
self.pending_removals.push_back(validator_index);
}
Function request_add_stake
Called by
sui_system, to add a new stake to the validator.
This request is added to the validator's staking pool's pending stake entries, processed at the end
of the epoch.
Aborts in case the staking amount is smaller than MIN_STAKING_THRESHOLD
public(package) fun request_add_stake(self: &mut sui_system::validator_set::ValidatorSet, validator_address: address, stake: sui::balance::Balance<sui::sui::SUI>, ctx: &mut sui::tx_context::TxContext): sui_system::staking_pool::StakedSui
Implementation
public(package) fun request_add_stake(
self: &mut ValidatorSet,
validator_address: address,
stake: Balance<SUI>,
ctx: &mut TxContext,
): StakedSui {
let sui_amount = stake.value();
assert!(sui_amount >= MIN_STAKING_THRESHOLD, EStakingBelowThreshold);
self
.get_candidate_or_active_validator_mut(validator_address)
.request_add_stake(stake, ctx.sender(), ctx)
}
Function request_withdraw_stake
Called by
sui_system, to withdraw some share of a stake from the validator. The share to withdraw
is denoted by principal_withdraw_amount. One of two things occurs in this function:
- If the
staked_suiis staked with an active validator, the request is added to the validator's staking pool's pending stake withdraw entries, processed at the end of the epoch. - If the
staked_suiwas staked with a validator that is no longer active, the stake and any rewards corresponding to it will be immediately processed.
public(package) fun request_withdraw_stake(self: &mut sui_system::validator_set::ValidatorSet, staked_sui: sui_system::staking_pool::StakedSui, ctx: &sui::tx_context::TxContext): sui::balance::Balance<sui::sui::SUI>
Implementation
public(package) fun request_withdraw_stake(
self: &mut ValidatorSet,
staked_sui: StakedSui,
ctx: &TxContext,
): Balance<SUI> {
let staking_pool_id = staked_sui.pool_id();
let validator = if (self.staking_pool_mappings.contains(staking_pool_id)) {
// This is an active validator.
let validator_address = self.staking_pool_mappings[staked_sui.pool_id()];
self.get_candidate_or_active_validator_mut(validator_address)
} else {
// This is an inactive pool.
assert!(self.inactive_validators.contains(staking_pool_id), ENoPoolFound);
self.inactive_validators[staking_pool_id].load_validator_maybe_upgrade()
};
validator.request_withdraw_stake(staked_sui, ctx)
}
Function convert_to_fungible_staked_sui
public(package) fun convert_to_fungible_staked_sui(self: &mut sui_system::validator_set::ValidatorSet, staked_sui: sui_system::staking_pool::StakedSui, ctx: &mut sui::tx_context::TxContext): sui_system::staking_pool::FungibleStakedSui
Implementation
public(package) fun convert_to_fungible_staked_sui(
self: &mut ValidatorSet,
staked_sui: StakedSui,
ctx: &mut TxContext,
): FungibleStakedSui {
let staking_pool_id = staked_sui.pool_id();
let validator = if (self.staking_pool_mappings.contains(staking_pool_id)) {
// This is an active validator.
let validator_address = self.staking_pool_mappings[staking_pool_id];
self.get_candidate_or_active_validator_mut(validator_address)
} else {
// This is an inactive pool.
assert!(self.inactive_validators.contains(staking_pool_id), ENoPoolFound);
self.inactive_validators[staking_pool_id].load_validator_maybe_upgrade()
};
validator.convert_to_fungible_staked_sui(staked_sui, ctx)
}
Function redeem_fungible_staked_sui
public(package) fun redeem_fungible_staked_sui(self: &mut sui_system::validator_set::ValidatorSet, fungible_staked_sui: sui_system::staking_pool::FungibleStakedSui, ctx: &sui::tx_context::TxContext): sui::balance::Balance<sui::sui::SUI>
Implementation
public(package) fun redeem_fungible_staked_sui(
self: &mut ValidatorSet,
fungible_staked_sui: FungibleStakedSui,
ctx: &TxContext,
): Balance<SUI> {
let staking_pool_id = fungible_staked_sui.pool_id();
let validator = if (self.staking_pool_mappings.contains(staking_pool_id)) {
// This is an active validator.
let validator_address = self.staking_pool_mappings[staking_pool_id];
self.get_candidate_or_active_validator_mut(validator_address)
} else {
// This is an inactive pool.
assert!(self.inactive_validators.contains(staking_pool_id), ENoPoolFound);
self.inactive_validators[staking_pool_id].load_validator_maybe_upgrade()
};
validator.redeem_fungible_staked_sui(fungible_staked_sui, ctx)
}
Function request_set_commission_rate
public(package) fun request_set_commission_rate(self: &mut sui_system::validator_set::ValidatorSet, new_commission_rate: u64, ctx: &sui::tx_context::TxContext)
Implementation
public(package) fun request_set_commission_rate(
self: &mut ValidatorSet,
new_commission_rate: u64,
ctx: &TxContext,
) {
let validator_address = ctx.sender();
let validator = get_validator_mut(&mut self.active_validators, validator_address);
validator.request_set_commission_rate(new_commission_rate);
}
Function advance_epoch
Update the validator set at the end of epoch. It does the following things:
- Distribute stake award.
- Process pending stake deposits and withdraws for each validator (
adjust_stake). - Process pending stake deposits, and withdraws.
- Process pending validator application and withdraws.
- At the end, we calculate the total stake for the new epoch.
public(package) fun advance_epoch(self: &mut sui_system::validator_set::ValidatorSet, computation_reward: &mut sui::balance::Balance<sui::sui::SUI>, storage_fund_reward: &mut sui::balance::Balance<sui::sui::SUI>, validator_report_records: &mut sui::vec_map::VecMap<address, sui::vec_set::VecSet<address>>, reward_slashing_rate: u64, low_stake_grace_period: u64, ctx: &mut sui::tx_context::TxContext)
Implementation
public(package) fun advance_epoch(
self: &mut ValidatorSet,
computation_reward: &mut Balance<SUI>,
storage_fund_reward: &mut Balance<SUI>,
validator_report_records: &mut VecMap<address, VecSet<address>>,
reward_slashing_rate: u64,
low_stake_grace_period: u64,
ctx: &mut TxContext,
) {
let new_epoch = ctx.epoch() + 1;
let total_voting_power = voting_power::total_voting_power();
// switch to using voting power based admission, if we are not already using it
let key = VotingPowerAdmissionStartEpochKey();
if (!self.extra_fields.contains(key)) self.extra_fields.add(key, ctx.epoch());
// Compute the reward distribution without taking into account the tallying rule slashing.
let (
unadjusted_staking_reward_amounts,
unadjusted_storage_fund_reward_amounts,
) = compute_unadjusted_reward_distribution(
&self.active_validators,
total_voting_power,
computation_reward.value(),
storage_fund_reward.value(),
);
// Use the tallying rule report records for the epoch to compute validators that will be
// punished.
let slashed_validators = self.compute_slashed_validators(*validator_report_records);
let total_slashed_validator_voting_power = sum_voting_power_by_addresses(
&self.active_validators,
&slashed_validators,
);
// Compute the reward adjustments of slashed validators, to be taken into
// account in adjusted reward computation.
let (
total_staking_reward_adjustment,
individual_staking_reward_adjustments,
total_storage_fund_reward_adjustment,
individual_storage_fund_reward_adjustments,
) = compute_reward_adjustments(
get_validator_indices(&self.active_validators, &slashed_validators),
reward_slashing_rate,
&unadjusted_staking_reward_amounts,
&unadjusted_storage_fund_reward_amounts,
);
// Compute the adjusted amounts of stake each validator should get given the tallying rule
// reward adjustments we computed before.
// compute_adjusted_reward_distribution must be called before distribute_reward and adjust_stake_and_gas_price to
// make sure we are using the current epoch's stake information to compute reward distribution.
let (
adjusted_staking_reward_amounts,
adjusted_storage_fund_reward_amounts,
) = compute_adjusted_reward_distribution(
&self.active_validators,
total_voting_power,
total_slashed_validator_voting_power,
unadjusted_staking_reward_amounts,
unadjusted_storage_fund_reward_amounts,
total_staking_reward_adjustment,
individual_staking_reward_adjustments,
total_storage_fund_reward_adjustment,
individual_storage_fund_reward_adjustments,
);
// Distribute the rewards before adjusting stake so that we immediately start compounding
// the rewards for validators and stakers.
distribute_reward(
&mut self.active_validators,
&adjusted_staking_reward_amounts,
&adjusted_storage_fund_reward_amounts,
computation_reward,
storage_fund_reward,
ctx,
);
adjust_stake_and_gas_price(&mut self.active_validators);
process_pending_stakes_and_withdraws(&mut self.active_validators, ctx);
// Emit events after we have processed all the rewards distribution and pending stakes.
emit_validator_epoch_events(
new_epoch,
&self.active_validators,
&adjusted_staking_reward_amounts,
&adjusted_storage_fund_reward_amounts,
validator_report_records,
&slashed_validators,
);
self.process_pending_removals(validator_report_records, ctx);
// kick low stake validators out.
let new_total_stake = self.update_validator_positions_and_calculate_total_stake(
low_stake_grace_period,
validator_report_records,
ctx,
);
self.total_stake = new_total_stake;
voting_power::set_voting_power(&mut self.active_validators, new_total_stake);
// At this point, self.active_validators are updated for next epoch.
// Now we process the staged validator metadata.
self.effectuate_staged_metadata();
}
Function update_validator_positions_and_calculate_total_stake
This function does the following:
- removes validators from
at_riskgroup if their voting power is above the LOW threshold - increments the number of epochs a validator has been below the LOW threshold but above the VERY LOW threshold
- removes validators from the active set if they have been below the LOW threshold for more than
low_stake_grace_periodepochs - removes validators from the active set immediately if they are below the VERY LOW threshold
- activates pending validators if they have sufficient voting power
fun update_validator_positions_and_calculate_total_stake(self: &mut sui_system::validator_set::ValidatorSet, low_stake_grace_period: u64, validator_report_records: &mut sui::vec_map::VecMap<address, sui::vec_set::VecSet<address>>, ctx: &mut sui::tx_context::TxContext): u64
Implementation
fun update_validator_positions_and_calculate_total_stake(
self: &mut ValidatorSet,
low_stake_grace_period: u64,
validator_report_records: &mut VecMap<address, VecSet<address>>,
ctx: &mut TxContext,
): u64 {
// take all pending validators out of the tablevec and put them in a local vector
let pending_active_validators = vector::tabulate!(
self.pending_active_validators.length(),
|_| self.pending_active_validators.pop_back(),
);
// Note: we count the total stake of pending validators as well!
let pending_total_stake = calculate_total_stakes(&pending_active_validators);
let initial_total_stake = calculate_total_stakes(&self.active_validators) + pending_total_stake;
let (
min_joining_voting_power_threshold,
low_voting_power_threshold,
very_low_voting_power_threshold,
) = self.get_voting_power_thresholds(ctx);
// Iterate through all the active validators, record their low stake status, and kick them out if the condition is met.
let mut total_removed_stake = 0; // amount of stake to remove due to departed_validators
let mut i = self.active_validators.length();
while (i > 0) {
i = i - 1;
let validator_ref = &self.active_validators[i];
let validator_address = validator_ref.sui_address();
let validator_stake = validator_ref.total_stake();
// calculate the voting power for this validator in the next epoch if no validators are removed
// if one of more low stake validators are removed, it's possible this validator will have higher voting power--that's ok.
let voting_power = voting_power::derive_raw_voting_power(
validator_stake,
initial_total_stake,
);
// SIP-39: a validator can remain indefinitely with a voting power ≥ LOW_VOTING_POWER_THRESHOLD
if (voting_power >= low_voting_power_threshold) {
// The validator is safe. We remove their entry from the at_risk map if there exists one.
if (self.at_risk_validators.contains(&validator_address)) {
self.at_risk_validators.remove(&validator_address);
}
// SIP-39: as soon as the validator