Module 0x3::validator_set
- Struct
ValidatorSetV1
- Struct
ValidatorSetV2
- Struct
ValidatorEpochInfoEventV1
- Struct
ValidatorJoinEvent
- Struct
ValidatorLeaveEvent
- Struct
CommitteeValidatorJoinEvent
- Struct
CommitteeValidatorLeaveEvent
- Constants
- Function
new_v1
- Function
v1_to_v2
- Function
request_add_validator_candidate
- Function
request_remove_validator_candidate
- Function
request_add_validator
- Function
assert_no_pending_or_active_duplicates
- Function
request_remove_validator
- Function
request_add_stake
- Function
request_withdraw_stake
- Function
request_set_commission_rate
- Function
advance_epoch
- Function
update_and_process_low_stake_departures
- 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
total_stake_inner
- Function
validator_total_stake_amount_inner
- Function
validator_stake_amount_inner
- Function
validator_voting_power_inner
- Function
validator_staking_pool_id_inner
- Function
staking_pool_mappings_inner
- Function
validator_address_by_pool_id_inner
- Function
pool_exchange_rates
- Function
next_epoch_validator_count
- Function
is_active_validator_by_iota_address
- Function
is_committee_validator_by_iota_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_set
- 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
get_active_validator_ref_inner
- Function
get_committee_validator_ref_inner
- Function
get_pending_validator_ref_inner
- Function
verify_cap
- Function
process_pending_removals
- Function
process_validator_departure
- Function
clean_report_records_leaving_validator
- Function
process_pending_validators
- Function
sort_removal_list
- Function
process_pending_stakes_and_withdraws
- Function
calculate_total_active_stakes
- Function
calculate_total_committee_stakes
- Function
adjust_next_epoch_commission_rate
- 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
sum_committee_voting_power_by_addresses
- Function
active_validators
- Function
is_validator_candidate
- Function
is_inactive_validator
- Function
active_validators_inner
- Function
is_validator_candidate_inner
- Function
is_inactive_validator_inner
- Function
active_validator_addresses
- Function
committee_validator_addresses
- Function
select_committee_members_top_n_stakers
- Function
process_new_committee
use 0x1::option;
use 0x1::vector;
use 0x2::bag;
use 0x2::balance;
use 0x2::event;
use 0x2::iota;
use 0x2::object;
use 0x2::priority_queue;
use 0x2::table;
use 0x2::table_vec;
use 0x2::transfer;
use 0x2::tx_context;
use 0x2::vec_map;
use 0x2::vec_set;
use 0x3::staking_pool;
use 0x3::validator;
use 0x3::validator_cap;
use 0x3::validator_wrapper;
use 0x3::voting_power;
Struct ValidatorSetV1
struct ValidatorSetV1 has store
Fields
total_stake: u64
Total amount of stake from all active validators at the beginning of the epoch.
active_validators: vector<validator::ValidatorV1>
The current list of active validators.
pending_active_validators: table_vec::TableVec<validator::ValidatorV1>
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: table::Table<object::ID, address>
Mappings from staking pool's ID to the iota address of a validator.
inactive_validators: table::Table<object::ID, validator_wrapper::Validator>
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
active_validators
it is added to this table so that stakers can continue to withdraw their stake from it.validator_candidates: table::Table<address, validator_wrapper::Validator>
Table storing preactive/candidate validators, mapping their addresses to their
ValidatorV1
structs. When an address callsrequest_add_validator_candidate
, 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
to officially add them to the active validator setactive_validators
next epoch.at_risk_validators: 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: bag::Bag
Any extra fields that's not defined statically.
Struct ValidatorSetV2
The second version of the struct storing information about validator set.
This version is an extension on the first one, that supports a new approach to committee selection,
where committee members taking part in consensus are selected from a set of active_validators
before an epoch begins. committee_members
is a vector of indices of validators stored in active_validators
,
that have been selected to take part in consensus during the current epoch.
struct ValidatorSetV2 has store
Fields
total_stake: u64
Total amount of stake from all committee validators at the beginning of the epoch.
active_validators: vector<validator::ValidatorV1>
The current list of active validators.
committee_members: vector<u64>
Subset of validators responsible for consensus. Each element is an index pointing to
active_validators
.pending_active_validators: table_vec::TableVec<validator::ValidatorV1>
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: table::Table<object::ID, address>
Mappings from staking pool's ID to the iota address of a validator.
inactive_validators: table::Table<object::ID, validator_wrapper::Validator>
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
active_validators
it is added to this table so that stakers can continue to withdraw their stake from it.validator_candidates: table::Table<address, validator_wrapper::Validator>
Table storing preactive/candidate validators, mapping their addresses to their
ValidatorV1
structs. When an address callsrequest_add_validator_candidate
, 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
to officially add them to the active validator setactive_validators
next epoch.at_risk_validators: 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: bag::Bag
Any extra fields that's not defined statically.
Struct ValidatorEpochInfoEventV1
Event containing staking and rewards related information of each validator, emitted during epoch advancement.
struct ValidatorEpochInfoEventV1 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
pool_token_exchange_rate: staking_pool::PoolTokenExchangeRate
tallying_rule_reporters: vector<address>
tallying_rule_global_score: u64
Struct ValidatorJoinEvent
Event emitted every time a new validator becomes active. The epoch value corresponds to the first epoch this change takes place.
struct ValidatorJoinEvent has copy, drop
Fields
epoch: u64
validator_address: address
staking_pool_id: object::ID
Struct ValidatorLeaveEvent
Event emitted every time a validator leaves the active validator set. The epoch value corresponds to the first epoch this change takes place.
struct ValidatorLeaveEvent has copy, drop
Fields
epoch: u64
validator_address: address
staking_pool_id: object::ID
is_voluntary: bool
Struct CommitteeValidatorJoinEvent
Event emitted every time a new validator becomes part of the committee. The epoch value corresponds to the first epoch this change takes place.
struct CommitteeValidatorJoinEvent has copy, drop
Fields
epoch: u64
validator_address: address
staking_pool_id: object::ID
Struct CommitteeValidatorLeaveEvent
Event emitted every time a validator leaves the committee at the end of the epoch. The epoch value corresponds to the first epoch this change takes place.
struct CommitteeValidatorLeaveEvent has copy, drop
Fields
epoch: u64
validator_address: address
staking_pool_id: object::ID
Constants
const MIN_STAKING_THRESHOLD: u64 = 1000000000;
const EInvalidCap: u64 = 101;
const ENotValidatorCandidate: u64 = 8;
const ACTIVE_OR_PENDING_VALIDATOR: u8 = 2;
const ANY_VALIDATOR: u8 = 3;
const BASIS_POINT_DENOMINATOR: u128 = 10000;
const COMMITTEE_VALIDATOR_ONLY: u8 = 1;
const EAlreadyValidatorCandidate: u64 = 6;
const EDuplicateValidator: u64 = 2;
const EInvalidStakeAdjustmentAmount: u64 = 1;
const EMinJoiningStakeNotReached: u64 = 5;
const ENoPoolFound: u64 = 3;
const ENonValidatorInReportRecords: u64 = 0;
const ENotACommitteeValidator: u64 = 14;
const ENotAPendingValidator: u64 = 12;
const ENotAValidator: u64 = 4;
const ENotActiveOrPendingValidator: u64 = 9;
const EStakingBelowThreshold: u64 = 10;
const EValidatorAlreadyRemoved: u64 = 11;
const EValidatorNotCandidate: u64 = 7;
const EValidatorSetEmpty: u64 = 13;
Function new_v1
public(friend) fun new_v1(init_active_validators: vector<validator::ValidatorV1>, ctx: &mut tx_context::TxContext): validator_set::ValidatorSetV1
Implementation
public(package) fun new_v1(init_active_validators: vector<ValidatorV1>, ctx: &mut TxContext): ValidatorSetV1 {
let total_stake = calculate_total_active_stakes(&init_active_validators);
let mut staking_pool_mappings = table::new(ctx);
let num_validators = init_active_validators.length();
let mut i = 0;
while (i < num_validators) {
let validator = &init_active_validators[i];
staking_pool_mappings.add(staking_pool_id(validator), iota_address(validator));
i = i + 1;
};
let mut validators = ValidatorSetV1 {
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),
};
let validators_num = validators.active_validators.length();
let committee_of_all_validators = vector::tabulate!(validators_num, |i| i);
voting_power::set_voting_power(&committee_of_all_validators, &mut validators.active_validators);
validators
}
Function v1_to_v2
public(friend) fun v1_to_v2(self: validator_set::ValidatorSetV1): validator_set::ValidatorSetV2
Implementation
public(package) fun v1_to_v2(self: ValidatorSetV1): ValidatorSetV2 {
let ValidatorSetV1 {
total_stake,
active_validators,
pending_active_validators,
pending_removals,
staking_pool_mappings,
inactive_validators,
validator_candidates,
at_risk_validators,
extra_fields,
} = self;
let committee_members = vector::tabulate!(active_validators.length(), |i| i);
let validators = ValidatorSetV2 {
total_stake,
active_validators,
committee_members,
pending_active_validators,
pending_removals,
staking_pool_mappings,
inactive_validators,
validator_candidates,
at_risk_validators,
extra_fields,
};
validators
}
Function request_add_validator_candidate
Called by iota_system
to add a new validator candidate.
public(friend) fun request_add_validator_candidate(self: &mut validator_set::ValidatorSetV2, validator: validator::ValidatorV1, ctx: &mut tx_context::TxContext)
Implementation
public(package) fun request_add_validator_candidate(
self: &mut ValidatorSetV2,
validator: ValidatorV1,
ctx: &mut TxContext,
) {
// The next assertions are not critical for the protocol, but they are here to catch problematic configs earlier.
assert!(
!is_duplicate_with_active_validator(self, &validator)
&& !is_duplicate_with_pending_validator(self, &validator),
EDuplicateValidator
);
let validator_address = iota_address(&validator);
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(staking_pool_id(&validator), validator_address);
self.validator_candidates.add(
iota_address(&validator),
validator_wrapper::create_v1(validator, ctx),
);
}
Function request_remove_validator_candidate
Called by iota_system
to remove a validator candidate, and move them to inactive_validators
.
public(friend) fun request_remove_validator_candidate(self: &mut validator_set::ValidatorSetV2, ctx: &mut tx_context::TxContext)
Implementation
public(package) fun request_remove_validator_candidate(self: &mut ValidatorSetV2, ctx: &mut TxContext) {
let validator_address = ctx.sender();
assert!(
self.validator_candidates.contains(validator_address),
ENotValidatorCandidate
);
let wrapper = self.validator_candidates.remove(validator_address);
let mut validator = wrapper.destroy();
assert!(validator.is_preactive(), EValidatorNotCandidate);
let staking_pool_id = staking_pool_id(&validator);
// 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_wrapper::create_v1(validator, ctx),
);
}
Function request_add_validator
Called by iota_system
to add a new validator to pending_active_validators
, which will be
processed at the end of epoch.
public(friend) fun request_add_validator(self: &mut validator_set::ValidatorSetV2, min_joining_stake_amount: u64, ctx: &tx_context::TxContext)
Implementation
public(package) fun request_add_validator(self: &mut ValidatorSetV2, min_joining_stake_amount: u64, ctx: &TxContext) {
let validator_address = ctx.sender();
assert!(
self.validator_candidates.contains(validator_address),
ENotValidatorCandidate
);
let wrapper = self.validator_candidates.remove(validator_address);
let validator = wrapper.destroy();
assert!(
!is_duplicate_with_active_validator(self, &validator)
&& !is_duplicate_with_pending_validator(self, &validator),
EDuplicateValidator
);
assert!(validator.is_preactive(), EValidatorNotCandidate);
assert!(validator.total_stake_amount() >= min_joining_stake_amount, EMinJoiningStakeNotReached);
self.pending_active_validators.push_back(validator);
}
Function assert_no_pending_or_active_duplicates
public(friend) fun assert_no_pending_or_active_duplicates(self: &validator_set::ValidatorSetV2, validator: &validator::ValidatorV1)
Implementation
public(package) fun assert_no_pending_or_active_duplicates(self: &ValidatorSetV2, validator: &ValidatorV1) {
// 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 iota_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(friend) fun request_remove_validator(self: &mut validator_set::ValidatorSetV2, ctx: &tx_context::TxContext)
Implementation
public(package) fun request_remove_validator(
self: &mut ValidatorSetV2,
ctx: &TxContext,
) {
let validator_address = ctx.sender();
let mut validator_index_opt = find_validator(&self.active_validators, validator_address);
assert!(validator_index_opt.is_some(), ENotAValidator);
let validator_index = validator_index_opt.extract();
assert!(
!self.pending_removals.contains(&validator_index),
EValidatorAlreadyRemoved
);
self.pending_removals.push_back(validator_index);
}
Function request_add_stake
Called by iota_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(friend) fun request_add_stake(self: &mut validator_set::ValidatorSetV2, validator_address: address, stake: balance::Balance<iota::IOTA>, ctx: &mut tx_context::TxContext): staking_pool::StakedIota
Implementation
public(package) fun request_add_stake(
self: &mut ValidatorSetV2,
validator_address: address,
stake: Balance<IOTA>,
ctx: &mut TxContext,
) : StakedIota {
let iota_amount = stake.value();
assert!(iota_amount >= MIN_STAKING_THRESHOLD, EStakingBelowThreshold);
let validator = get_candidate_or_active_validator_mut(self, validator_address);
validator.request_add_stake(stake, ctx.sender(), ctx)
}
Function request_withdraw_stake
Called by iota_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_iota
is 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_iota
was staked with a validator that is no longer active, the stake and any rewards corresponding to it will be immediately processed.
public(friend) fun request_withdraw_stake(self: &mut validator_set::ValidatorSetV2, staked_iota: staking_pool::StakedIota, ctx: &tx_context::TxContext): balance::Balance<iota::IOTA>
Implementation
public(package) fun request_withdraw_stake(
self: &mut ValidatorSetV2,
staked_iota: StakedIota,
ctx: &TxContext,
) : Balance<IOTA> {
let staking_pool_id = pool_id(&staked_iota);
let validator =
if (self.staking_pool_mappings.contains(staking_pool_id)) { // This is an active validator.
let validator_address = self.staking_pool_mappings[pool_id(&staked_iota)];
get_candidate_or_active_validator_mut(self, validator_address)
} else { // This is an inactive pool.
assert!(self.inactive_validators.contains(staking_pool_id), ENoPoolFound);
let wrapper = &mut self.inactive_validators[staking_pool_id];
wrapper.load_validator_maybe_upgrade()
};
validator.request_withdraw_stake(staked_iota, ctx)
}
Function request_set_commission_rate
public(friend) fun request_set_commission_rate(self: &mut validator_set::ValidatorSetV2, new_commission_rate: u64, ctx: &tx_context::TxContext)
Implementation
public(package) fun request_set_commission_rate(
self: &mut ValidatorSetV2,
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(friend) fun advance_epoch(self: &mut validator_set::ValidatorSetV2, total_validator_rewards: &mut balance::Balance<iota::IOTA>, validator_report_records: &mut vec_map::VecMap<address, vec_set::VecSet<address>>, reward_slashing_rate: u64, low_stake_threshold: u64, very_low_stake_threshold: u64, low_stake_grace_period: u64, committee_size: u64, ctx: &mut tx_context::TxContext)
Implementation
public(package) fun advance_epoch(
self: &mut ValidatorSetV2,
total_validator_rewards: &mut Balance<IOTA>,
validator_report_records: &mut VecMap<address, VecSet<address>>,
reward_slashing_rate: u64,
low_stake_threshold: u64,
very_low_stake_threshold: u64,
low_stake_grace_period: u64,
committee_size: u64,
ctx: &mut TxContext,
) {
let new_epoch = ctx.epoch() + 1;
let total_voting_power = voting_power::total_voting_power();
// Compute the reward distribution without taking into account the tallying rule slashing.
let unadjusted_staking_reward_amounts = compute_unadjusted_reward_distribution(
&self.active_validators,
&self.committee_members,
total_voting_power,
total_validator_rewards.value(),
);
// Use the tallying rule report records for the epoch to compute validators that will be
// punished.
let slashed_validators = compute_slashed_validators(self, *validator_report_records);
// Compute the adjusted amounts of stake each committee validator should get according to the tallying rule.
// compute_adjusted_reward_distribution
must be called before distribute_reward
and adjust_next_epoch_commission_rate
to
// make sure we are using the current epoch's stake information to compute reward distribution.
let adjusted_staking_reward_amounts = compute_adjusted_reward_distribution(
&self.committee_members,
unadjusted_staking_reward_amounts,
get_validator_indices_set(&self.active_validators, &slashed_validators),
reward_slashing_rate,
);
// Distribute the rewards before adjusting stake so that we immediately start compounding
// the rewards for validators and stakers.
distribute_reward(
&mut self.active_validators,
&self.committee_members,
&adjusted_staking_reward_amounts,
total_validator_rewards,
ctx
);
adjust_next_epoch_commission_rate(&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, &self.committee_members,
&adjusted_staking_reward_amounts, validator_report_records, &slashed_validators);
// Collect committee validator addresses before modifying the active_validators
.
// Getting this later would result in incorrect addresses, because committee_members
values
// would be pointing to incorrect validators in active_validators
.
let prev_committee_validator_addresses = self.committee_validator_addresses();
// Note that all their staged next epoch metadata will be effectuated below.
process_pending_validators(self, new_epoch);
process_pending_removals(self, prev_committee_validator_addresses, validator_report_records, ctx);
// kick low stake validators out.
update_and_process_low_stake_departures(
self,
low_stake_threshold,
very_low_stake_threshold,
low_stake_grace_period,
validator_report_records,
prev_committee_validator_addresses,
ctx
);
self.process_new_committee(committee_size, prev_committee_validator_addresses, ctx);
self.total_stake = calculate_total_committee_stakes(&self.active_validators, &self.committee_members);
voting_power::set_voting_power(&self.committee_members, &mut self.active_validators);
// At this point, self.active_validators and the self.committee_members are updated for next epoch.
// Now we process the staged validator metadata.
effectuate_staged_metadata(self);
}
Function update_and_process_low_stake_departures
fun update_and_process_low_stake_departures(self: &mut validator_set::ValidatorSetV2, low_stake_threshold: u64, very_low_stake_threshold: u64, low_stake_grace_period: u64, validator_report_records: &mut vec_map::VecMap<address, vec_set::VecSet<address>>, committee_addresses: vector<address>, ctx: &mut tx_context::TxContext)
Implementation
fun update_and_process_low_stake_departures(
self: &mut ValidatorSetV2,
low_stake_threshold: u64,
very_low_stake_threshold: u64,
low_stake_grace_period: u64,
validator_report_records: &mut VecMap<address, VecSet<address>>,
committee_addresses: vector<address>,
ctx: &mut TxContext
) {
// Iterate through all the active validators, record their low stake status, and kick them out if the condition is met.
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.iota_address();
let stake = validator_ref.total_stake_amount();
if (stake >= low_stake_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);
}
} else if (stake >= very_low_stake_threshold) {
// The stake is a bit below the threshold so we increment the entry of the validator in the map.
let new_low_stake_period =
if (self.at_risk_validators.contains(&validator_address)) {
let num_epochs = &mut self.at_risk_validators[&validator_address];
*num_epochs = *num_epochs + 1;
*num_epochs
} else {
self.at_risk_validators.insert(validator_address, 1);
1
};
// If the grace period has passed, the validator has to leave us.
if (new_low_stake_period > low_stake_grace_period) {
let validator = self.active_validators.remove(i);
let is_committee = committee_addresses.contains(&validator.iota_address());
process_validator_departure(self, validator, validator_report_records, false /* the validator is kicked out involuntarily /, is_committee, ctx);
}
} else {
// The validator's stake is lower than the very low threshold so we kick them out immediately.
let validator = self.active_validators.remove(i);
let is_committee = committee_addresses.contains(&validator.iota_address());
process_validator_departure(self, validator, validator_report_records, false / the validator is kicked out involuntarily */, is_committee, ctx);
}
}
}
Function effectuate_staged_metadata
Effectutate pending next epoch metadata if they are staged.
fun effectuate_staged_metadata(self: &mut validator_set::ValidatorSetV2)
Implementation
fun effectuate_staged_metadata(
self: &mut ValidatorSetV2,
) {
self.active_validators.do_mut!(|v| v.effectuate_staged_metadata());
}
Function derive_reference_gas_price
Called by iota_system
to derive reference gas price for the new epoch for ValidatorSetV1.
Derive the reference gas price based on the gas price quote submitted by each validator.
The returned gas price should be greater than or equal to 2/3 of the validators submitted
gas price, weighted by stake.
public fun derive_reference_gas_price(self: &validator_set::ValidatorSetV1): u64
Implementation
public fun derive_reference_gas_price(self: &ValidatorSetV1): u64 {
let vs = &self.active_validators;
let num_validators = vs.length();
let mut entries = vector[];
let mut i = 0;
while (i < num_validators) {
let v = &vs[i];
entries.push_back(
pq::new_entry(v.gas_price(), v.voting_power())
);
i = i + 1;
};
// Build a priority queue that will pop entries with gas price from the highest to the lowest.
let mut pq = pq::new(entries);
let mut sum = 0;
let threshold = voting_power::total_voting_power() - voting_power::quorum_threshold();
let mut result = 0;
while (sum < threshold) {
let (gas_price, voting_power) = pq.pop_max();
result = gas_price;
sum = sum + voting_power;
};
result
}
Function total_stake
public fun total_stake(self: &validator_set::ValidatorSetV1): u64
Implementation
public fun total_stake(self: &ValidatorSetV1): u64 {
self.total_stake
}
Function validator_total_stake_amount
public fun validator_total_stake_amount(self: &validator_set::ValidatorSetV1, validator_address: address): u64
Implementation
public fun validator_total_stake_amount(self: &ValidatorSetV1, validator_address: address): u64 {
let validator = get_validator_ref(&self.active_validators, validator_address);
validator.total_stake_amount()
}
Function validator_stake_amount
public fun validator_stake_amount(self: &validator_set::ValidatorSetV1, validator_address: address): u64
Implementation
public fun validator_stake_amount(self: &ValidatorSetV1, validator_address: address): u64 {
let validator = get_validator_ref(&self.active_validators, validator_address);
validator.stake_amount()
}
Function validator_voting_power
public fun validator_voting_power(self: &validator_set::ValidatorSetV1, validator_address: address): u64
Implementation
public fun validator_voting_power(self: &ValidatorSetV1, validator_address: address): u64 {
let validator = get_validator_ref(&self.active_validators, validator_address);
validator.voting_power()
}
Function validator_staking_pool_id
public fun validator_staking_pool_id(self: &validator_set::ValidatorSetV1, validator_address: address): object::ID
Implementation
public fun validator_staking_pool_id(self: &ValidatorSetV1, validator_address: address): ID {
let validator = get_validator_ref(&self.active_validators, validator_address);
validator.staking_pool_id()
}
Function staking_pool_mappings
public fun staking_pool_mappings(self: &validator_set::ValidatorSetV1): &table::Table<object::ID, address>
Implementation
public fun staking_pool_mappings(self: &ValidatorSetV1): &Table<ID, address> {
&self.staking_pool_mappings
}
Function total_stake_inner
public(friend) fun total_stake_inner(self: &validator_set::ValidatorSetV2): u64
Implementation
public(package) fun total_stake_inner(self: &ValidatorSetV2): u64 {
self.total_stake
}
Function validator_total_stake_amount_inner
public(friend) fun validator_total_stake_amount_inner(self: &validator_set::ValidatorSetV2, validator_address: address): u64
Implementation
public(package) fun validator_total_stake_amount_inner(self: &ValidatorSetV2, validator_address: address): u64 {
let validator = get_validator_ref(&self.active_validators, validator_address);
validator.total_stake_amount()
}
Function validator_stake_amount_inner
public(friend) fun validator_stake_amount_inner(self: &validator_set::ValidatorSetV2, validator_address: address): u64
Implementation
public(package) fun validator_stake_amount_inner(self: &ValidatorSetV2, validator_address: address): u64 {
let validator = get_validator_ref(&self.active_validators, validator_address);
validator.stake_amount()
}
Function validator_voting_power_inner
public(friend) fun validator_voting_power_inner(self: &validator_set::ValidatorSetV2, validator_address: address): u64
Implementation
public(package) fun validator_voting_power_inner(self: &ValidatorSetV2, validator_address: address): u64 {
let validator = get_validator_ref(&self.active_validators, validator_address);
validator.voting_power()
}
Function validator_staking_pool_id_inner
public(friend) fun validator_staking_pool_id_inner(self: &validator_set::ValidatorSetV2, validator_address: address): object::ID
Implementation
public(package) fun validator_staking_pool_id_inner(self: &ValidatorSetV2, validator_address: address): ID {
let validator = get_validator_ref(&self.active_validators, validator_address);
validator.staking_pool_id()
}
Function staking_pool_mappings_inner
public(friend) fun staking_pool_mappings_inner(self: &validator_set::ValidatorSetV2): &table::Table<object::ID, address>
Implementation
public(package) fun staking_pool_mappings_inner(self: &ValidatorSetV2): &Table<ID, address> {
&self.staking_pool_mappings
}
Function validator_address_by_pool_id_inner
public(friend) fun validator_address_by_pool_id_inner(self: &mut validator_set::ValidatorSetV2, pool_id: &object::ID): address
Implementation
public(package) fun validator_address_by_pool_id_inner(self: &mut ValidatorSetV2, pool_id: &ID): address {
// If the pool id is recorded in the mapping, then it must be either candidate or active.
if (self.staking_pool_mappings.contains(*pool_id)) {
self.staking_pool_mappings[*pool_id]
} else { // otherwise it's inactive
let wrapper = &mut self.inactive_validators[*pool_id];
let validator = wrapper.load_validator_maybe_upgrade();
validator.iota_address()
}
}
Function pool_exchange_rates
public(friend) fun pool_exchange_rates(self: &mut validator_set::ValidatorSetV2, pool_id: &object::ID): &table::Table<u64, staking_pool::PoolTokenExchangeRate>
Implementation
public(package) fun pool_exchange_rates(
self: &mut ValidatorSetV2, pool_id: &ID
) : &Table<u64, PoolTokenExchangeRate> {
let validator =
// If the pool id is recorded in the mapping, then it must be either candidate or active.
if (self.staking_pool_mappings.contains(*pool_id)) {
let validator_address = self.staking_pool_mappings[*pool_id];
get_active_or_pending_or_candidate_validator_ref(self, validator_address, ANY_VALIDATOR)
} else { // otherwise it's inactive
let wrapper = &mut self.inactive_validators[*pool_id];
wrapper.load_validator_maybe_upgrade()
};
validator.get_staking_pool_ref().exchange_rates()
}
Function next_epoch_validator_count
Get the total number of validators in the next epoch.
public(friend) fun next_epoch_validator_count(self: &validator_set::ValidatorSetV2): u64
Implementation
public(package) fun next_epoch_validator_count(self: &ValidatorSetV2): u64 {
self.active_validators.length() - self.pending_removals.length() + self.pending_active_validators.length()
}
Function is_active_validator_by_iota_address
Returns true iff the address exists in active validators.
public(friend) fun is_active_validator_by_iota_address(self: &validator_set::ValidatorSetV2, validator_address: address): bool
Implementation
public(package) fun is_active_validator_by_iota_address(
self: &ValidatorSetV2,
validator_address: address,
): bool {
find_validator(&self.active_validators, validator_address).is_some()
}
Function is_committee_validator_by_iota_address
Returns true iff the address exists in committee validators.
public(friend) fun is_committee_validator_by_iota_address(self: &validator_set::ValidatorSetV2, validator_address: address): bool
Implementation
public(package) fun is_committee_validator_by_iota_address(
self: &ValidatorSetV2,
validator_address: address,
): bool {
let validator_index_opt = find_validator(&self.active_validators, validator_address);
// Validator is part of the committee if it belongs to the set of active validators
// and it's index is part of the committee members set.
validator_index_opt.is_some() && self.committee_members.contains(validator_index_opt.borrow())
}
Function is_duplicate_with_active_validator
Checks whether new_validator
is duplicate with any currently active validators.
It differs from is_active_validator_by_iota_address
in that the former checks
only the iota address but this function looks at more metadata.
fun is_duplicate_with_active_validator(self: &validator_set::ValidatorSetV2, new_validator: &validator::ValidatorV1): bool
Implementation
fun is_duplicate_with_active_validator(self: &ValidatorSetV2, new_validator: &ValidatorV1): bool {
is_duplicate_validator(&self.active_validators, new_validator)
}
Function is_duplicate_validator
public(friend) fun is_duplicate_validator(validators: &vector<validator::ValidatorV1>, new_validator: &validator::ValidatorV1): bool
Implementation
public(package) fun is_duplicate_validator(validators: &vector<ValidatorV1>, new_validator: &ValidatorV1): bool {
count_duplicates_vec(validators, new_validator) > 0
}
Function count_duplicates_vec
fun count_duplicates_vec(validators: &vector<validator::ValidatorV1>, validator: &validator::ValidatorV1): u64
Implementation
fun count_duplicates_vec(validators: &vector<ValidatorV1>, validator: &ValidatorV1): u64 {
let mut result = 0;
validators.do_ref!(|v| {
if (v.is_duplicate(validator)) {
result = result + 1;
};
});
result
}
Function is_duplicate_with_pending_validator
Checks whether new_validator
is duplicate with any currently pending validators.
fun is_duplicate_with_pending_validator(self: &validator_set::ValidatorSetV2, new_validator: &validator::ValidatorV1): bool
Implementation
fun is_duplicate_with_pending_validator(self: &ValidatorSetV2, new_validator: &ValidatorV1): bool {
count_duplicates_tablevec(&self.pending_active_validators, new_validator) > 0
}
Function count_duplicates_tablevec
fun count_duplicates_tablevec(validators: &table_vec::TableVec<validator::ValidatorV1>, validator: &validator::ValidatorV1): u64
Implementation
fun count_duplicates_tablevec(validators: &TableVec<ValidatorV1>, validator: &ValidatorV1): u64 {
let len = validators.length();
let mut i = 0;
let mut result = 0;
while (i < len) {
let v = &validators[i];
if (v.is_duplicate(validator)) {
result = result + 1;
};
i = i + 1;
};
result
}
Function get_candidate_or_active_validator_mut
Get mutable reference to either a candidate or an active validator by address.
fun get_candidate_or_active_validator_mut(self: &mut validator_set::ValidatorSetV2, validator_address: address): &mut validator::ValidatorV1
Implementation
fun get_candidate_or_active_validator_mut(self: &mut ValidatorSetV2, validator_address: address): &mut ValidatorV1 {
if (self.validator_candidates.contains(validator_address)) {
let wrapper = &mut self.validator_candidates[validator_address];
return wrapper.load_validator_maybe_upgrade()
};
get_validator_mut(&mut self.active_validators, validator_address)
}
Function find_validator
Find validator by validator_address
, in validators
.
Returns (true, index) if the validator is found, and the index is its index in the list.
If not found, returns (false, 0).
fun find_validator(validators: &vector<validator::ValidatorV1>, validator_address: address): option::Option<u64>
Implementation
fun find_validator(validators: &vector<ValidatorV1>, validator_address: address): Option<u64> {
validators.find_index!(|v| v.iota_address() == validator_address)
}
Function find_validator_from_table_vec
Find validator by validator_address
, in validators
.
Returns (true, index) if the validator is found, and the index is its index in the list.
If not found, returns (false, 0).
fun find_validator_from_table_vec(validators: &table_vec::TableVec<validator::ValidatorV1>, validator_address: address): option::Option<u64>
Implementation
fun find_validator_from_table_vec(validators: &TableVec<ValidatorV1>, validator_address: address): Option<u64> {
let length = validators.length();
let mut i = 0;
while (i < length) {
let v = &validators[i];
if (v.iota_address() == validator_address) {
return option::some(i)
};
i = i + 1;
};
option::none()
}
Function get_validator_indices_set
Given a vector of validator addresses, return a set of all indices of the validators. Aborts if any address isn't in the given validator set.
fun get_validator_indices_set(validators: &vector<validator::ValidatorV1>, validator_addresses: &vector<address>): vec_set::VecSet<u64>
Implementation
fun get_validator_indices_set(validators: &vector<ValidatorV1>, validator_addresses: &vector<address>): VecSet<u64> {
let mut res = vec_set::empty();
validator_addresses.do_ref!(|addr| {
let index_opt = find_validator(validators, *addr);
assert!(index_opt.is_some(), ENotAValidator);
res.insert(index_opt.destroy_some());
});
res
}
Function get_validator_mut
public(friend) fun get_validator_mut(validators: &mut vector<validator::ValidatorV1>, validator_address: address): &mut validator::ValidatorV1
Implementation
public(package) fun get_validator_mut(
validators: &mut vector<ValidatorV1>,
validator_address: address,
): &mut ValidatorV1 {
let mut validator_index_opt = find_validator(validators, validator_address);
assert!(validator_index_opt.is_some(), ENotAValidator);
let validator_index = validator_index_opt.extract();
&mut validators[validator_index]
}
Function get_active_or_pending_or_candidate_validator_mut
Get mutable reference to an active or (if active does not exist) pending or (if pending and
active do not exist) or candidate validator by address.
Note: this function should be called carefully, only after verifying the transaction
sender has the ability to modify the ValidatorV1
.
fun get_active_or_pending_or_candidate_validator_mut(self: &mut validator_set::ValidatorSetV2, validator_address: address, include_candidate: bool): &mut validator::ValidatorV1
Implementation
fun get_active_or_pending_or_candidate_validator_mut(
self: &mut ValidatorSetV2,
validator_address: address,
include_candidate: bool,
): &mut ValidatorV1 {
let mut validator_index_opt = find_validator(&self.active_validators, validator_address);
if (validator_index_opt.is_some()) {
let validator_index = validator_index_opt.extract();
return &mut self.active_validators[validator_index]
};
let mut validator_index_opt = find_validator_from_table_vec(&self.pending_active_validators, validator_address);
// consider both pending validators and the candidate ones
if (validator_index_opt.is_some()) {
let validator_index = validator_index_opt.extract();
return &mut self.pending_active_validators[validator_index]
};
assert!(include_candidate, ENotActiveOrPendingValidator);
let wrapper = &mut self.validator_candidates[validator_address];
wrapper.load_validator_maybe_upgrade()
}
Function get_validator_mut_with_verified_cap
public(friend) fun get_validator_mut_with_verified_cap(self: &mut validator_set::ValidatorSetV2, verified_cap: &validator_cap::ValidatorOperationCap, include_candidate: bool): &mut validator::ValidatorV1
Implementation
public(package) fun get_validator_mut_with_verified_cap(
self: &mut ValidatorSetV2,
verified_cap: &ValidatorOperationCap,
include_candidate: bool,
): &mut ValidatorV1 {
get_active_or_pending_or_candidate_validator_mut(self, *verified_cap.verified_operation_cap_address(), include_candidate)
}
Function get_validator_mut_with_ctx
public(friend) fun get_validator_mut_with_ctx(self: &mut validator_set::ValidatorSetV2, ctx: &tx_context::TxContext): &mut validator::ValidatorV1
Implementation
public(package) fun get_validator_mut_with_ctx(
self: &mut ValidatorSetV2,
ctx: &TxContext,
): &mut ValidatorV1 {
let validator_address = ctx.sender();
get_active_or_pending_or_candidate_validator_mut(self, validator_address, false)
}
Function get_validator_mut_with_ctx_including_candidates
public(friend) fun get_validator_mut_with_ctx_including_candidates(self: &mut validator_set::ValidatorSetV2, ctx: &tx_context::TxContext): &mut validator::ValidatorV1
Implementation
public(package) fun get_validator_mut_with_ctx_including_candidates(
self: &mut ValidatorSetV2,
ctx: &TxContext,
): &mut ValidatorV1 {
let validator_address = ctx.sender();
get_active_or_pending_or_candidate_validator_mut(self, validator_address, true)
}
Function get_validator_ref
fun get_validator_ref(validators: &vector<validator::ValidatorV1>, validator_address: address): &validator::ValidatorV1
Implementation
fun get_validator_ref(
validators: &vector<ValidatorV1>,
validator_address: address,
): &ValidatorV1 {
let mut validator_index_opt = find_validator(validators, validator_address);
assert!(validator_index_opt.is_some(), ENotAValidator);
let validator_index = validator_index_opt.extract();
&validators[validator_index]
}
Function get_active_or_pending_or_candidate_validator_ref
public(friend) fun get_active_or_pending_or_candidate_validator_ref(self: &mut validator_set::ValidatorSetV2, validator_address: address, which_validator: u8): &validator::ValidatorV1
Implementation
public(package) fun get_active_or_pending_or_candidate_validator_ref(
self: &mut ValidatorSetV2,
validator_address: address,
which_validator: u8,
): &ValidatorV1 {
let mut validator_index_opt = find_validator(&self.active_validators, validator_address);
if (validator_index_opt.is_some() || which_validator == COMMITTEE_VALIDATOR_ONLY) {
let validator_index = validator_index_opt.extract();
return &self.active_validators[validator_index]
};
let mut validator_index_opt = find_validator_from_table_vec(&self.pending_active_validators, validator_address);
if (validator_index_opt.is_some() || which_validator == ACTIVE_OR_PENDING_VALIDATOR) {
let validator_index = validator_index_opt.extract();
return &self.pending_active_validators[validator_index]
};
self.validator_candidates[validator_address].load_validator_maybe_upgrade()
}
Function get_active_validator_ref
public fun get_active_validator_ref(self: &validator_set::ValidatorSetV1, validator_address: address): &validator::ValidatorV1
Implementation
public fun get_active_validator_ref(
self: &ValidatorSetV1,
validator_address: address,
): &ValidatorV1 {
let mut validator_index_opt = find_validator(&self.active_validators, validator_address);
assert!(validator_index_opt.is_some(), ENotAValidator);
let validator_index = validator_index_opt.extract();
&self.active_validators[validator_index]
}
Function get_pending_validator_ref
public fun get_pending_validator_ref(self: &validator_set::ValidatorSetV1, validator_address: address): &validator::ValidatorV1
Implementation
public fun get_pending_validator_ref(
self: &ValidatorSetV1,
validator_address: address,
): &ValidatorV1 {
let mut validator_index_opt = find_validator_from_table_vec(&self.pending_active_validators, validator_address);
assert!(validator_index_opt.is_some(), ENotAPendingValidator);
let validator_index = validator_index_opt.extract();
&self.pending_active_validators[validator_index]
}
Function get_active_validator_ref_inner
public(friend) fun get_active_validator_ref_inner(self: &validator_set::ValidatorSetV2, validator_address: address): &validator::ValidatorV1
Implementation
public(package) fun get_active_validator_ref_inner(
self: &ValidatorSetV2,
validator_address: address,
): &ValidatorV1 {
let mut validator_index_opt = find_validator(&self.active_validators, validator_address);
assert!(validator_index_opt.is_some(), ENotAValidator);
let validator_index = validator_index_opt.extract();
&self.active_validators[validator_index]
}
Function get_committee_validator_ref_inner
public(friend) fun get_committee_validator_ref_inner(self: &validator_set::ValidatorSetV2, validator_address: address): &validator::ValidatorV1
Implementation
public(package) fun get_committee_validator_ref_inner(
self: &ValidatorSetV2,
validator_address: address,
): &ValidatorV1 {
let mut validator_index_opt = find_validator(&self.active_validators, validator_address);
assert!(validator_index_opt.is_some(), ENotAValidator);
assert!(self.committee_members.contains(validator_index_opt.borrow()), ENotACommitteeValidator);
let validator_index = validator_index_opt.extract();
&self.active_validators[validator_index]
}
Function get_pending_validator_ref_inner
public(friend) fun get_pending_validator_ref_inner(self: &validator_set::ValidatorSetV2, validator_address: address): &validator::ValidatorV1
Implementation
public(package) fun get_pending_validator_ref_inner(
self: &ValidatorSetV2,
validator_address: address,
): &ValidatorV1 {
let mut validator_index_opt = find_validator_from_table_vec(&self.pending_active_validators, validator_address);
assert!(validator_index_opt.is_some(), ENotAPendingValidator);
let validator_index = validator_index_opt.extract();
&self.pending_active_validators[validator_index]
}
Function verify_cap
Verify the capability is valid for a Validator.
If which_validator == COMMITTEE_VALIDATOR_ONLY
is true, only verify the Cap for an committee validator.
Otherwise, verify the Cap for an either active or pending validator.
public(friend) fun verify_cap(self: &mut validator_set::ValidatorSetV2, cap: &validator_cap::UnverifiedValidatorOperationCap, which_validator: u8): validator_cap::ValidatorOperationCap
Implementation
public(package) fun verify_cap(
self: &mut ValidatorSetV2,
cap: &UnverifiedValidatorOperationCap,
which_validator: u8,
): ValidatorOperationCap {
let cap_address = *cap.unverified_operation_cap_address();
let validator =
if (which_validator == COMMITTEE_VALIDATOR_ONLY)
get_committee_validator_ref_inner(self, cap_address)
else
get_active_or_pending_or_candidate_validator_ref(self, cap_address, which_validator);
assert!(validator.operation_cap_id() == &object::id(cap), EInvalidCap);
validator_cap::new_from_unverified(cap)
}
Function process_pending_removals
Process the pending withdraw requests. For each pending request, the validator
is removed from validators
and its staking pool is put into the inactive_validators
table.
fun process_pending_removals(self: &mut validator_set::ValidatorSetV2, committee_addresses: vector<address>, validator_report_records: &mut vec_map::VecMap<address, vec_set::VecSet<address>>, ctx: &mut tx_context::TxContext)
Implementation
fun process_pending_removals(
self: &mut ValidatorSetV2,
committee_addresses: vector<address>,
validator_report_records: &mut VecMap<address, VecSet<address>>,
ctx: &mut TxContext,
) {
sort_removal_list(&mut self.pending_removals);
while (!self.pending_removals.is_empty()) {
let index = self.pending_removals.pop_back();
let validator = self.active_validators.remove(index);
let is_committee = committee_addresses.contains(&validator.iota_address());
process_validator_departure(self, validator, validator_report_records, true /* the validator removes itself voluntarily */, is_committee, ctx);
}
}
Function process_validator_departure
fun process_validator_departure(self: &mut validator_set::ValidatorSetV2, validator: validator::ValidatorV1, validator_report_records: &mut vec_map::VecMap<address, vec_set::VecSet<address>>, is_voluntary: bool, is_committee: bool, ctx: &mut tx_context::TxContext)
Implementation
fun process_validator_departure(
self: &mut ValidatorSetV2,
mut validator: ValidatorV1,
validator_report_records: &mut VecMap<address, VecSet<address>>,
is_voluntary: bool,
is_committee: bool,
ctx: &mut TxContext,
) {
let new_epoch = ctx.epoch() + 1;
let validator_address = validator.iota_address();
let validator_pool_id = staking_pool_id(&validator);
// Remove the validator from our tables.
self.staking_pool_mappings.remove(validator_pool_id);
if (self.at_risk_validators.contains(&validator_address)) {
self.at_risk_validators.remove(&validator_address);
};
if (is_committee) {
self.total_stake = self.total_stake - validator.total_stake_amount();
event::emit(
CommitteeValidatorLeaveEvent {
epoch: new_epoch,
validator_address,
staking_pool_id: staking_pool_id(&validator),
}
);
};
clean_report_records_leaving_validator(validator_report_records, validator_address);
event::emit(
ValidatorLeaveEvent {
epoch: new_epoch,
validator_address,
staking_pool_id: staking_pool_id(&validator),
is_voluntary,
}
);
// Deactivate the validator and its staking pool
validator.deactivate(new_epoch);
self.inactive_validators.add(
validator_pool_id,
validator_wrapper::create_v1(validator, ctx),
);
}