spec: update reward processing to v0.6.1 + bugfix
Two bugs fixed by this commit: * Reward proposers rather than attesters in `get_proposer_deltas` * Prevent double-counting of validator balances towards the total when computing validator statuses
This commit is contained in:
parent
1ad0024045
commit
caff553af9
@ -1,3 +1,7 @@
|
|||||||
|
use super::common::{
|
||||||
|
get_attesting_balance, get_matching_head_attestations, get_matching_target_attestations,
|
||||||
|
get_total_active_balance, get_unslashed_attesting_indices,
|
||||||
|
};
|
||||||
use super::validator_statuses::{TotalBalances, ValidatorStatus, ValidatorStatuses};
|
use super::validator_statuses::{TotalBalances, ValidatorStatus, ValidatorStatuses};
|
||||||
use super::{Error, WinningRootHashSet};
|
use super::{Error, WinningRootHashSet};
|
||||||
use integer_sqrt::IntegerSquareRoot;
|
use integer_sqrt::IntegerSquareRoot;
|
||||||
@ -32,57 +36,52 @@ impl std::ops::AddAssign for Delta {
|
|||||||
|
|
||||||
/// Apply attester and proposer rewards.
|
/// Apply attester and proposer rewards.
|
||||||
///
|
///
|
||||||
/// Spec v0.5.1
|
/// Spec v0.6.1
|
||||||
pub fn apply_rewards(
|
pub fn process_rewards_and_penalties(
|
||||||
state: &mut BeaconState,
|
state: &mut BeaconState,
|
||||||
validator_statuses: &mut ValidatorStatuses,
|
validator_statuses: &mut ValidatorStatuses,
|
||||||
winning_root_for_shards: &WinningRootHashSet,
|
winning_root_for_shards: &WinningRootHashSet,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
if state.current_epoch(spec) == spec.genesis_epoch {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
// Guard against an out-of-bounds during the validator balance update.
|
// Guard against an out-of-bounds during the validator balance update.
|
||||||
if validator_statuses.statuses.len() != state.validator_balances.len() {
|
if validator_statuses.statuses.len() != state.balances.len()
|
||||||
return Err(Error::ValidatorStatusesInconsistent);
|
|| validator_statuses.statuses.len() != state.validator_registry.len()
|
||||||
}
|
{
|
||||||
// Guard against an out-of-bounds during the attester inclusion balance update.
|
|
||||||
if validator_statuses.statuses.len() != state.validator_registry.len() {
|
|
||||||
return Err(Error::ValidatorStatusesInconsistent);
|
return Err(Error::ValidatorStatusesInconsistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut deltas = vec![Delta::default(); state.validator_balances.len()];
|
let mut deltas = vec![Delta::default(); state.balances.len()];
|
||||||
|
|
||||||
get_justification_and_finalization_deltas(&mut deltas, state, &validator_statuses, spec)?;
|
get_attestation_deltas(&mut deltas, state, &validator_statuses, spec)?;
|
||||||
get_crosslink_deltas(&mut deltas, state, &validator_statuses, spec)?;
|
get_crosslink_deltas(&mut deltas, state, &validator_statuses, spec)?;
|
||||||
|
|
||||||
// Apply the proposer deltas if we are finalizing normally.
|
get_proposer_deltas(
|
||||||
//
|
&mut deltas,
|
||||||
// This is executed slightly differently to the spec because of the way our functions are
|
state,
|
||||||
// structured. It should be functionally equivalent.
|
validator_statuses,
|
||||||
if epochs_since_finality(state, spec) <= 4 {
|
winning_root_for_shards,
|
||||||
get_proposer_deltas(
|
spec,
|
||||||
&mut deltas,
|
)?;
|
||||||
state,
|
|
||||||
validator_statuses,
|
|
||||||
winning_root_for_shards,
|
|
||||||
spec,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the deltas, over-flowing but not under-flowing (saturating at 0 instead).
|
// Apply the deltas, over-flowing but not under-flowing (saturating at 0 instead).
|
||||||
for (i, delta) in deltas.iter().enumerate() {
|
for (i, delta) in deltas.iter().enumerate() {
|
||||||
state.validator_balances[i] += delta.rewards;
|
state.balances[i] += delta.rewards;
|
||||||
state.validator_balances[i] = state.validator_balances[i].saturating_sub(delta.penalties);
|
state.balances[i] = state.balances[i].saturating_sub(delta.penalties);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies the attestation inclusion reward to each proposer for every validator who included an
|
/// For each attesting validator, reward the proposer who was first to include their attestation.
|
||||||
/// attestation in the previous epoch.
|
|
||||||
///
|
///
|
||||||
/// Spec v0.5.1
|
/// Spec v0.6.1
|
||||||
fn get_proposer_deltas(
|
fn get_proposer_deltas(
|
||||||
deltas: &mut Vec<Delta>,
|
deltas: &mut Vec<Delta>,
|
||||||
state: &mut BeaconState,
|
state: &BeaconState,
|
||||||
validator_statuses: &mut ValidatorStatuses,
|
validator_statuses: &mut ValidatorStatuses,
|
||||||
winning_root_for_shards: &WinningRootHashSet,
|
winning_root_for_shards: &WinningRootHashSet,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
@ -90,9 +89,7 @@ fn get_proposer_deltas(
|
|||||||
// Update statuses with the information from winning roots.
|
// Update statuses with the information from winning roots.
|
||||||
validator_statuses.process_winning_roots(state, winning_root_for_shards, spec)?;
|
validator_statuses.process_winning_roots(state, winning_root_for_shards, spec)?;
|
||||||
|
|
||||||
for (index, validator) in validator_statuses.statuses.iter().enumerate() {
|
for validator in &validator_statuses.statuses {
|
||||||
let mut delta = Delta::default();
|
|
||||||
|
|
||||||
if validator.is_previous_epoch_attester {
|
if validator.is_previous_epoch_attester {
|
||||||
let inclusion = validator
|
let inclusion = validator
|
||||||
.inclusion_info
|
.inclusion_info
|
||||||
@ -101,7 +98,7 @@ fn get_proposer_deltas(
|
|||||||
let base_reward = get_base_reward(
|
let base_reward = get_base_reward(
|
||||||
state,
|
state,
|
||||||
inclusion.proposer_index,
|
inclusion.proposer_index,
|
||||||
validator_statuses.total_balances.previous_epoch,
|
validator_statuses.total_balances.current_epoch,
|
||||||
spec,
|
spec,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -109,10 +106,8 @@ fn get_proposer_deltas(
|
|||||||
return Err(Error::ValidatorStatusesInconsistent);
|
return Err(Error::ValidatorStatusesInconsistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
delta.reward(base_reward / spec.attestation_inclusion_reward_quotient);
|
deltas[inclusion.proposer_index].reward(base_reward / spec.proposer_reward_quotient);
|
||||||
}
|
}
|
||||||
|
|
||||||
deltas[index] += delta;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -120,40 +115,30 @@ fn get_proposer_deltas(
|
|||||||
|
|
||||||
/// Apply rewards for participation in attestations during the previous epoch.
|
/// Apply rewards for participation in attestations during the previous epoch.
|
||||||
///
|
///
|
||||||
/// Spec v0.5.1
|
/// Spec v0.6.1
|
||||||
fn get_justification_and_finalization_deltas(
|
fn get_attestation_deltas(
|
||||||
deltas: &mut Vec<Delta>,
|
deltas: &mut Vec<Delta>,
|
||||||
state: &BeaconState,
|
state: &BeaconState,
|
||||||
validator_statuses: &ValidatorStatuses,
|
validator_statuses: &ValidatorStatuses,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let epochs_since_finality = epochs_since_finality(state, spec);
|
let finality_delay = (state.previous_epoch(spec) - state.finalized_epoch).as_u64();
|
||||||
|
|
||||||
for (index, validator) in validator_statuses.statuses.iter().enumerate() {
|
for (index, validator) in validator_statuses.statuses.iter().enumerate() {
|
||||||
let base_reward = get_base_reward(
|
let base_reward = get_base_reward(
|
||||||
state,
|
state,
|
||||||
index,
|
index,
|
||||||
validator_statuses.total_balances.previous_epoch,
|
validator_statuses.total_balances.current_epoch,
|
||||||
spec,
|
|
||||||
)?;
|
|
||||||
let inactivity_penalty = get_inactivity_penalty(
|
|
||||||
state,
|
|
||||||
index,
|
|
||||||
epochs_since_finality.as_u64(),
|
|
||||||
validator_statuses.total_balances.previous_epoch,
|
|
||||||
spec,
|
spec,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let delta = if epochs_since_finality <= 4 {
|
let delta = get_attestation_delta(
|
||||||
compute_normal_justification_and_finalization_delta(
|
&validator,
|
||||||
&validator,
|
&validator_statuses.total_balances,
|
||||||
&validator_statuses.total_balances,
|
base_reward,
|
||||||
base_reward,
|
finality_delay,
|
||||||
spec,
|
spec,
|
||||||
)
|
);
|
||||||
} else {
|
|
||||||
compute_inactivity_leak_delta(&validator, base_reward, inactivity_penalty, spec)
|
|
||||||
};
|
|
||||||
|
|
||||||
deltas[index] += delta;
|
deltas[index] += delta;
|
||||||
}
|
}
|
||||||
@ -161,24 +146,36 @@ fn get_justification_and_finalization_deltas(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine the delta for a single validator, if the chain is finalizing normally.
|
/// Determine the delta for a single validator, sans proposer rewards.
|
||||||
///
|
///
|
||||||
/// Spec v0.5.1
|
/// Spec v0.6.1
|
||||||
fn compute_normal_justification_and_finalization_delta(
|
fn get_attestation_delta(
|
||||||
validator: &ValidatorStatus,
|
validator: &ValidatorStatus,
|
||||||
total_balances: &TotalBalances,
|
total_balances: &TotalBalances,
|
||||||
base_reward: u64,
|
base_reward: u64,
|
||||||
|
finality_delay: u64,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Delta {
|
) -> Delta {
|
||||||
let mut delta = Delta::default();
|
let mut delta = Delta::default();
|
||||||
|
|
||||||
let boundary_attesting_balance = total_balances.previous_epoch_boundary_attesters;
|
// Is this validator eligible to be rewarded or penalized?
|
||||||
let total_balance = total_balances.previous_epoch;
|
// Spec: validator index in `eligible_validator_indices`
|
||||||
|
let is_eligible = validator.is_active_in_previous_epoch
|
||||||
|
|| (validator.is_slashed && !validator.is_withdrawable_in_current_epoch);
|
||||||
|
|
||||||
|
if !is_eligible {
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
let total_balance = total_balances.current_epoch;
|
||||||
let total_attesting_balance = total_balances.previous_epoch_attesters;
|
let total_attesting_balance = total_balances.previous_epoch_attesters;
|
||||||
let matching_head_balance = total_balances.previous_epoch_boundary_attesters;
|
let matching_target_balance = total_balances.previous_epoch_target_attesters;
|
||||||
|
let matching_head_balance = total_balances.previous_epoch_head_attesters;
|
||||||
|
|
||||||
// Expected FFG source.
|
// Expected FFG source.
|
||||||
if validator.is_previous_epoch_attester {
|
// Spec:
|
||||||
|
// - validator index in `get_unslashed_attesting_indices(state, matching_source_attestations)`
|
||||||
|
if validator.is_previous_epoch_attester && !validator.is_slashed {
|
||||||
delta.reward(base_reward * total_attesting_balance / total_balance);
|
delta.reward(base_reward * total_attesting_balance / total_balance);
|
||||||
// Inclusion speed bonus
|
// Inclusion speed bonus
|
||||||
let inclusion = validator
|
let inclusion = validator
|
||||||
@ -187,25 +184,43 @@ fn compute_normal_justification_and_finalization_delta(
|
|||||||
delta.reward(
|
delta.reward(
|
||||||
base_reward * spec.min_attestation_inclusion_delay / inclusion.distance.as_u64(),
|
base_reward * spec.min_attestation_inclusion_delay / inclusion.distance.as_u64(),
|
||||||
);
|
);
|
||||||
} else if validator.is_active_in_previous_epoch {
|
} else {
|
||||||
delta.penalize(base_reward);
|
delta.penalize(base_reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expected FFG target.
|
// Expected FFG target.
|
||||||
if validator.is_previous_epoch_boundary_attester {
|
// Spec:
|
||||||
delta.reward(base_reward / boundary_attesting_balance / total_balance);
|
// - validator index in `get_unslashed_attesting_indices(state, matching_target_attestations)`
|
||||||
} else if validator.is_active_in_previous_epoch {
|
if validator.is_previous_epoch_target_attester && !validator.is_slashed {
|
||||||
|
delta.reward(base_reward * matching_target_balance / total_balance);
|
||||||
|
} else {
|
||||||
delta.penalize(base_reward);
|
delta.penalize(base_reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expected head.
|
// Expected head.
|
||||||
if validator.is_previous_epoch_head_attester {
|
// Spec:
|
||||||
|
// - validator index in `get_unslashed_attesting_indices(state, matching_head_attestations)`
|
||||||
|
if validator.is_previous_epoch_head_attester && !validator.is_slashed {
|
||||||
delta.reward(base_reward * matching_head_balance / total_balance);
|
delta.reward(base_reward * matching_head_balance / total_balance);
|
||||||
} else if validator.is_active_in_previous_epoch {
|
} else {
|
||||||
delta.penalize(base_reward);
|
delta.penalize(base_reward);
|
||||||
};
|
}
|
||||||
|
|
||||||
// Proposer bonus is handled in `apply_proposer_deltas`.
|
// Inactivity penalty
|
||||||
|
if finality_delay > spec.min_epochs_to_inactivity_penalty {
|
||||||
|
// All eligible validators are penalized
|
||||||
|
delta.penalize(spec.base_rewards_per_epoch * base_reward);
|
||||||
|
|
||||||
|
// Additionally, all validators whose FFG target didn't match are penalized extra
|
||||||
|
if !validator.is_previous_epoch_target_attester {
|
||||||
|
delta.penalize(
|
||||||
|
validator.current_epoch_effective_balance * finality_delay
|
||||||
|
/ spec.inactivity_penalty_quotient,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proposer bonus is handled in `get_proposer_deltas`.
|
||||||
//
|
//
|
||||||
// This function only computes the delta for a single validator, so it cannot also return a
|
// This function only computes the delta for a single validator, so it cannot also return a
|
||||||
// delta for a validator.
|
// delta for a validator.
|
||||||
@ -213,52 +228,6 @@ fn compute_normal_justification_and_finalization_delta(
|
|||||||
delta
|
delta
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine the delta for a single delta, assuming the chain is _not_ finalizing normally.
|
|
||||||
///
|
|
||||||
/// Spec v0.5.1
|
|
||||||
fn compute_inactivity_leak_delta(
|
|
||||||
validator: &ValidatorStatus,
|
|
||||||
base_reward: u64,
|
|
||||||
inactivity_penalty: u64,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> Delta {
|
|
||||||
let mut delta = Delta::default();
|
|
||||||
|
|
||||||
if validator.is_active_in_previous_epoch {
|
|
||||||
if !validator.is_previous_epoch_attester {
|
|
||||||
delta.penalize(inactivity_penalty);
|
|
||||||
} else {
|
|
||||||
// If a validator did attest, apply a small penalty for getting attestations included
|
|
||||||
// late.
|
|
||||||
let inclusion = validator
|
|
||||||
.inclusion_info
|
|
||||||
.expect("It is a logic error for an attester not to have an inclusion distance.");
|
|
||||||
delta.reward(
|
|
||||||
base_reward * spec.min_attestation_inclusion_delay / inclusion.distance.as_u64(),
|
|
||||||
);
|
|
||||||
delta.penalize(base_reward);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !validator.is_previous_epoch_boundary_attester {
|
|
||||||
delta.reward(inactivity_penalty);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !validator.is_previous_epoch_head_attester {
|
|
||||||
delta.penalize(inactivity_penalty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Penalize slashed-but-inactive validators as though they were active but offline.
|
|
||||||
if !validator.is_active_in_previous_epoch
|
|
||||||
& validator.is_slashed
|
|
||||||
& !validator.is_withdrawable_in_current_epoch
|
|
||||||
{
|
|
||||||
delta.penalize(2 * inactivity_penalty + base_reward);
|
|
||||||
}
|
|
||||||
|
|
||||||
delta
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculate the deltas based upon the winning roots for attestations during the previous epoch.
|
/// Calculate the deltas based upon the winning roots for attestations during the previous epoch.
|
||||||
///
|
///
|
||||||
/// Spec v0.5.1
|
/// Spec v0.5.1
|
||||||
@ -295,40 +264,20 @@ fn get_crosslink_deltas(
|
|||||||
|
|
||||||
/// Returns the base reward for some validator.
|
/// Returns the base reward for some validator.
|
||||||
///
|
///
|
||||||
/// Spec v0.5.1
|
/// Spec v0.6.1
|
||||||
fn get_base_reward(
|
fn get_base_reward(
|
||||||
state: &BeaconState,
|
state: &BeaconState,
|
||||||
index: usize,
|
index: usize,
|
||||||
previous_total_balance: u64,
|
// Should be == get_total_active_balance(state, spec)
|
||||||
|
total_active_balance: u64,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<u64, BeaconStateError> {
|
) -> Result<u64, BeaconStateError> {
|
||||||
if previous_total_balance == 0 {
|
if total_active_balance == 0 {
|
||||||
Ok(0)
|
Ok(0)
|
||||||
} else {
|
} else {
|
||||||
let adjusted_quotient = previous_total_balance.integer_sqrt() / spec.base_reward_quotient;
|
let adjusted_quotient = total_active_balance.integer_sqrt() / spec.base_reward_quotient;
|
||||||
Ok(state.get_effective_balance(index, spec)? / adjusted_quotient / 5)
|
Ok(state.get_effective_balance(index, spec)?
|
||||||
|
/ adjusted_quotient
|
||||||
|
/ spec.base_rewards_per_epoch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the inactivity penalty for some validator.
|
|
||||||
///
|
|
||||||
/// Spec v0.5.1
|
|
||||||
fn get_inactivity_penalty(
|
|
||||||
state: &BeaconState,
|
|
||||||
index: usize,
|
|
||||||
epochs_since_finality: u64,
|
|
||||||
previous_total_balance: u64,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> Result<u64, BeaconStateError> {
|
|
||||||
Ok(get_base_reward(state, index, previous_total_balance, spec)?
|
|
||||||
+ state.get_effective_balance(index, spec)? * epochs_since_finality
|
|
||||||
/ spec.inactivity_penalty_quotient
|
|
||||||
/ 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the epochs since the last finalized epoch.
|
|
||||||
///
|
|
||||||
/// Spec v0.5.1
|
|
||||||
fn epochs_since_finality(state: &BeaconState, spec: &ChainSpec) -> Epoch {
|
|
||||||
state.current_epoch(spec) + 1 - state.finalized_epoch
|
|
||||||
}
|
|
||||||
|
@ -68,6 +68,8 @@ pub struct ValidatorStatus {
|
|||||||
pub is_active_in_current_epoch: bool,
|
pub is_active_in_current_epoch: bool,
|
||||||
/// True if the validator was active in the state's _previous_ epoch.
|
/// True if the validator was active in the state's _previous_ epoch.
|
||||||
pub is_active_in_previous_epoch: bool,
|
pub is_active_in_previous_epoch: bool,
|
||||||
|
/// The validator's effective balance in the _current_ epoch.
|
||||||
|
pub current_epoch_effective_balance: u64,
|
||||||
|
|
||||||
/// True if the validator had an attestation included in the _current_ epoch.
|
/// True if the validator had an attestation included in the _current_ epoch.
|
||||||
pub is_current_epoch_attester: bool,
|
pub is_current_epoch_attester: bool,
|
||||||
@ -78,7 +80,7 @@ pub struct ValidatorStatus {
|
|||||||
pub is_previous_epoch_attester: bool,
|
pub is_previous_epoch_attester: bool,
|
||||||
/// True if the validator's beacon block root attestation for the first slot of the _previous_
|
/// True if the validator's beacon block root attestation for the first slot of the _previous_
|
||||||
/// epoch matches the block root known to the state.
|
/// epoch matches the block root known to the state.
|
||||||
pub is_previous_epoch_boundary_attester: bool,
|
pub is_previous_epoch_target_attester: bool,
|
||||||
/// True if the validator's beacon block root attestation in the _previous_ epoch at the
|
/// True if the validator's beacon block root attestation in the _previous_ epoch at the
|
||||||
/// attestation's slot (`attestation_data.slot`) matches the block root known to the state.
|
/// attestation's slot (`attestation_data.slot`) matches the block root known to the state.
|
||||||
pub is_previous_epoch_head_attester: bool,
|
pub is_previous_epoch_head_attester: bool,
|
||||||
@ -108,7 +110,7 @@ impl ValidatorStatus {
|
|||||||
set_self_if_other_is_true!(self, other, is_current_epoch_attester);
|
set_self_if_other_is_true!(self, other, is_current_epoch_attester);
|
||||||
set_self_if_other_is_true!(self, other, is_current_epoch_boundary_attester);
|
set_self_if_other_is_true!(self, other, is_current_epoch_boundary_attester);
|
||||||
set_self_if_other_is_true!(self, other, is_previous_epoch_attester);
|
set_self_if_other_is_true!(self, other, is_previous_epoch_attester);
|
||||||
set_self_if_other_is_true!(self, other, is_previous_epoch_boundary_attester);
|
set_self_if_other_is_true!(self, other, is_previous_epoch_target_attester);
|
||||||
set_self_if_other_is_true!(self, other, is_previous_epoch_head_attester);
|
set_self_if_other_is_true!(self, other, is_previous_epoch_head_attester);
|
||||||
|
|
||||||
if let Some(other_info) = other.inclusion_info {
|
if let Some(other_info) = other.inclusion_info {
|
||||||
@ -138,7 +140,7 @@ pub struct TotalBalances {
|
|||||||
pub previous_epoch_attesters: u64,
|
pub previous_epoch_attesters: u64,
|
||||||
/// The total effective balance of all validators who attested during the _previous_ epoch and
|
/// The total effective balance of all validators who attested during the _previous_ epoch and
|
||||||
/// agreed with the state about the beacon block at the first slot of the _previous_ epoch.
|
/// agreed with the state about the beacon block at the first slot of the _previous_ epoch.
|
||||||
pub previous_epoch_boundary_attesters: u64,
|
pub previous_epoch_target_attesters: u64,
|
||||||
/// The total effective balance of all validators who attested during the _previous_ epoch and
|
/// The total effective balance of all validators who attested during the _previous_ epoch and
|
||||||
/// agreed with the state about the beacon block at the time of attestation.
|
/// agreed with the state about the beacon block at the time of attestation.
|
||||||
pub previous_epoch_head_attesters: u64,
|
pub previous_epoch_head_attesters: u64,
|
||||||
@ -160,27 +162,29 @@ impl ValidatorStatuses {
|
|||||||
/// - Active validators
|
/// - Active validators
|
||||||
/// - Total balances for the current and previous epochs.
|
/// - Total balances for the current and previous epochs.
|
||||||
///
|
///
|
||||||
/// Spec v0.5.1
|
/// Spec v0.6.1
|
||||||
pub fn new(state: &BeaconState, spec: &ChainSpec) -> Result<Self, BeaconStateError> {
|
pub fn new(state: &BeaconState, spec: &ChainSpec) -> Result<Self, BeaconStateError> {
|
||||||
let mut statuses = Vec::with_capacity(state.validator_registry.len());
|
let mut statuses = Vec::with_capacity(state.validator_registry.len());
|
||||||
let mut total_balances = TotalBalances::default();
|
let mut total_balances = TotalBalances::default();
|
||||||
|
|
||||||
for (i, validator) in state.validator_registry.iter().enumerate() {
|
for (i, validator) in state.validator_registry.iter().enumerate() {
|
||||||
|
let effective_balance = state.get_effective_balance(i, spec)?;
|
||||||
let mut status = ValidatorStatus {
|
let mut status = ValidatorStatus {
|
||||||
is_slashed: validator.slashed,
|
is_slashed: validator.slashed,
|
||||||
is_withdrawable_in_current_epoch: validator
|
is_withdrawable_in_current_epoch: validator
|
||||||
.is_withdrawable_at(state.current_epoch(spec)),
|
.is_withdrawable_at(state.current_epoch(spec)),
|
||||||
|
current_epoch_effective_balance: effective_balance,
|
||||||
..ValidatorStatus::default()
|
..ValidatorStatus::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
if validator.is_active_at(state.current_epoch(spec)) {
|
if validator.is_active_at(state.current_epoch(spec)) {
|
||||||
status.is_active_in_current_epoch = true;
|
status.is_active_in_current_epoch = true;
|
||||||
total_balances.current_epoch += state.get_effective_balance(i, spec)?;
|
total_balances.current_epoch += effective_balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
if validator.is_active_at(state.previous_epoch(spec)) {
|
if validator.is_active_at(state.previous_epoch(spec)) {
|
||||||
status.is_active_in_previous_epoch = true;
|
status.is_active_in_previous_epoch = true;
|
||||||
total_balances.previous_epoch += state.get_effective_balance(i, spec)?;
|
total_balances.previous_epoch += effective_balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
statuses.push(status);
|
statuses.push(status);
|
||||||
@ -208,22 +212,18 @@ impl ValidatorStatuses {
|
|||||||
{
|
{
|
||||||
let attesting_indices =
|
let attesting_indices =
|
||||||
get_attestation_participants(state, &a.data, &a.aggregation_bitfield, spec)?;
|
get_attestation_participants(state, &a.data, &a.aggregation_bitfield, spec)?;
|
||||||
let attesting_balance = state.get_total_balance(&attesting_indices, spec)?;
|
|
||||||
|
|
||||||
let mut status = ValidatorStatus::default();
|
let mut status = ValidatorStatus::default();
|
||||||
|
|
||||||
// Profile this attestation, updating the total balances and generating an
|
// Profile this attestation, updating the total balances and generating an
|
||||||
// `ValidatorStatus` object that applies to all participants in the attestation.
|
// `ValidatorStatus` object that applies to all participants in the attestation.
|
||||||
if is_from_epoch(a, state.current_epoch(spec), spec) {
|
if is_from_epoch(a, state.current_epoch(spec), spec) {
|
||||||
self.total_balances.current_epoch_attesters += attesting_balance;
|
|
||||||
status.is_current_epoch_attester = true;
|
status.is_current_epoch_attester = true;
|
||||||
|
|
||||||
if has_common_epoch_boundary_root(a, state, state.current_epoch(spec), spec)? {
|
if target_matches_epoch_start_block(a, state, state.current_epoch(spec), spec)? {
|
||||||
self.total_balances.current_epoch_boundary_attesters += attesting_balance;
|
|
||||||
status.is_current_epoch_boundary_attester = true;
|
status.is_current_epoch_boundary_attester = true;
|
||||||
}
|
}
|
||||||
} else if is_from_epoch(a, state.previous_epoch(spec), spec) {
|
} else if is_from_epoch(a, state.previous_epoch(spec), spec) {
|
||||||
self.total_balances.previous_epoch_attesters += attesting_balance;
|
|
||||||
status.is_previous_epoch_attester = true;
|
status.is_previous_epoch_attester = true;
|
||||||
|
|
||||||
// The inclusion slot and distance are only required for previous epoch attesters.
|
// The inclusion slot and distance are only required for previous epoch attesters.
|
||||||
@ -238,13 +238,11 @@ impl ValidatorStatuses {
|
|||||||
)?,
|
)?,
|
||||||
});
|
});
|
||||||
|
|
||||||
if has_common_epoch_boundary_root(a, state, state.previous_epoch(spec), spec)? {
|
if target_matches_epoch_start_block(a, state, state.previous_epoch(spec), spec)? {
|
||||||
self.total_balances.previous_epoch_boundary_attesters += attesting_balance;
|
status.is_previous_epoch_target_attester = true;
|
||||||
status.is_previous_epoch_boundary_attester = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_common_beacon_block_root(a, state, spec)? {
|
if has_common_beacon_block_root(a, state, spec)? {
|
||||||
self.total_balances.previous_epoch_head_attesters += attesting_balance;
|
|
||||||
status.is_previous_epoch_head_attester = true;
|
status.is_previous_epoch_head_attester = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,6 +253,30 @@ impl ValidatorStatuses {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compute the total balances
|
||||||
|
for (index, v) in self.statuses.iter().enumerate() {
|
||||||
|
// According to the spec, we only count unslashed validators towards the totals.
|
||||||
|
if !v.is_slashed {
|
||||||
|
let validator_balance = state.get_effective_balance(index, spec)?;
|
||||||
|
|
||||||
|
if v.is_current_epoch_attester {
|
||||||
|
self.total_balances.current_epoch_attesters += validator_balance;
|
||||||
|
}
|
||||||
|
if v.is_current_epoch_boundary_attester {
|
||||||
|
self.total_balances.current_epoch_boundary_attesters += validator_balance;
|
||||||
|
}
|
||||||
|
if v.is_previous_epoch_attester {
|
||||||
|
self.total_balances.previous_epoch_attesters += validator_balance;
|
||||||
|
}
|
||||||
|
if v.is_previous_epoch_target_attester {
|
||||||
|
self.total_balances.previous_epoch_target_attesters += validator_balance;
|
||||||
|
}
|
||||||
|
if v.is_previous_epoch_head_attester {
|
||||||
|
self.total_balances.previous_epoch_head_attesters += validator_balance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,11 +331,11 @@ fn is_from_epoch(a: &PendingAttestation, epoch: Epoch, spec: &ChainSpec) -> bool
|
|||||||
a.data.slot.epoch(spec.slots_per_epoch) == epoch
|
a.data.slot.epoch(spec.slots_per_epoch) == epoch
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for
|
/// Returns `true` if the attestation's FFG target is equal to the hash of the `state`'s first
|
||||||
/// the first slot of the given epoch.
|
/// beacon block in the given `epoch`.
|
||||||
///
|
///
|
||||||
/// Spec v0.5.1
|
/// Spec v0.6.0
|
||||||
fn has_common_epoch_boundary_root(
|
fn target_matches_epoch_start_block(
|
||||||
a: &PendingAttestation,
|
a: &PendingAttestation,
|
||||||
state: &BeaconState,
|
state: &BeaconState,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
@ -328,7 +350,7 @@ fn has_common_epoch_boundary_root(
|
|||||||
/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for
|
/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for
|
||||||
/// the current slot of the `PendingAttestation`.
|
/// the current slot of the `PendingAttestation`.
|
||||||
///
|
///
|
||||||
/// Spec v0.5.1
|
/// Spec v0.6.0
|
||||||
fn has_common_beacon_block_root(
|
fn has_common_beacon_block_root(
|
||||||
a: &PendingAttestation,
|
a: &PendingAttestation,
|
||||||
state: &BeaconState,
|
state: &BeaconState,
|
||||||
|
Loading…
Reference in New Issue
Block a user