mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-09 03:31:45 +00:00
Use checked arithmetic in types and state proc (#1009)
This commit is contained in:
@@ -27,6 +27,7 @@ eth2_ssz = "0.1.2"
|
||||
eth2_ssz_types = { path = "../utils/ssz_types" }
|
||||
merkle_proof = { path = "../utils/merkle_proof" }
|
||||
log = "0.4.8"
|
||||
safe_arith = { path = "../utils/safe_arith" }
|
||||
tree_hash = "0.1.0"
|
||||
tree_hash_derive = "0.2"
|
||||
types = { path = "../types" }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use eth2_hashing::hash;
|
||||
use int_to_bytes::int_to_bytes32;
|
||||
use merkle_proof::{MerkleTree, MerkleTreeError};
|
||||
use safe_arith::SafeArith;
|
||||
use types::Hash256;
|
||||
|
||||
/// Emulates the eth1 deposit contract merkle tree.
|
||||
@@ -46,7 +47,7 @@ impl DepositDataTree {
|
||||
/// Add a deposit to the merkle tree.
|
||||
pub fn push_leaf(&mut self, leaf: Hash256) -> Result<(), MerkleTreeError> {
|
||||
self.tree.push_leaf(leaf, self.depth)?;
|
||||
self.mix_in_length += 1;
|
||||
self.mix_in_length.increment()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use integer_sqrt::IntegerSquareRoot;
|
||||
use safe_arith::SafeArith;
|
||||
use types::*;
|
||||
|
||||
/// Returns the base reward for some validator.
|
||||
@@ -14,10 +15,10 @@ pub fn get_base_reward<T: EthSpec>(
|
||||
if total_active_balance == 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
Ok(
|
||||
state.get_effective_balance(index, spec)? * spec.base_reward_factor
|
||||
/ total_active_balance.integer_sqrt()
|
||||
/ spec.base_rewards_per_epoch,
|
||||
)
|
||||
Ok(state
|
||||
.get_effective_balance(index, spec)?
|
||||
.safe_mul(spec.base_reward_factor)?
|
||||
.safe_div(total_active_balance.integer_sqrt())?
|
||||
.safe_div(spec.base_rewards_per_epoch)?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::common::initiate_validator_exit;
|
||||
use safe_arith::SafeArith;
|
||||
use std::cmp;
|
||||
use types::{BeaconStateError as Error, *};
|
||||
|
||||
@@ -27,18 +28,21 @@ pub fn slash_validator<T: EthSpec>(
|
||||
let validator_effective_balance = state.get_effective_balance(slashed_index, spec)?;
|
||||
state.set_slashings(
|
||||
epoch,
|
||||
state.get_slashings(epoch)? + validator_effective_balance,
|
||||
state
|
||||
.get_slashings(epoch)?
|
||||
.safe_add(validator_effective_balance)?,
|
||||
)?;
|
||||
safe_sub_assign!(
|
||||
state.balances[slashed_index],
|
||||
validator_effective_balance / spec.min_slashing_penalty_quotient
|
||||
validator_effective_balance.safe_div(spec.min_slashing_penalty_quotient)?
|
||||
);
|
||||
|
||||
// Apply proposer and whistleblower rewards
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec)?;
|
||||
let whistleblower_index = opt_whistleblower_index.unwrap_or(proposer_index);
|
||||
let whistleblower_reward = validator_effective_balance / spec.whistleblower_reward_quotient;
|
||||
let proposer_reward = whistleblower_reward / spec.proposer_reward_quotient;
|
||||
let whistleblower_reward =
|
||||
validator_effective_balance.safe_div(spec.whistleblower_reward_quotient)?;
|
||||
let proposer_reward = whistleblower_reward.safe_div(spec.proposer_reward_quotient)?;
|
||||
|
||||
safe_add_assign!(state.balances[proposer_index], proposer_reward);
|
||||
safe_add_assign!(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::per_block_processing::{errors::BlockProcessingError, process_deposit};
|
||||
use crate::common::DepositDataTree;
|
||||
use safe_arith::SafeArith;
|
||||
use tree_hash::TreeHash;
|
||||
use types::DEPOSIT_TREE_DEPTH;
|
||||
use types::*;
|
||||
@@ -14,8 +15,9 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
|
||||
deposits: Vec<Deposit>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BeaconState<T>, BlockProcessingError> {
|
||||
let genesis_time =
|
||||
eth1_timestamp - eth1_timestamp % spec.min_genesis_delay + 2 * spec.min_genesis_delay;
|
||||
let genesis_time = eth1_timestamp
|
||||
.safe_sub(eth1_timestamp.safe_rem(spec.min_genesis_delay)?)?
|
||||
.safe_add(2.safe_mul(spec.min_genesis_delay)?)?;
|
||||
let eth1_data = Eth1Data {
|
||||
// Temporary deposit root
|
||||
deposit_root: Hash256::zero(),
|
||||
@@ -37,7 +39,7 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
|
||||
process_deposit(&mut state, &deposit, spec, true)?;
|
||||
}
|
||||
|
||||
process_activations(&mut state, spec);
|
||||
process_activations(&mut state, spec)?;
|
||||
|
||||
// Now that we have our validators, initialize the caches (including the committees)
|
||||
state.build_all_caches(spec)?;
|
||||
@@ -60,11 +62,14 @@ pub fn is_valid_genesis_state<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSp
|
||||
/// Activate genesis validators, if their balance is acceptable.
|
||||
///
|
||||
/// Spec v0.11.1
|
||||
pub fn process_activations<T: EthSpec>(state: &mut BeaconState<T>, spec: &ChainSpec) {
|
||||
pub fn process_activations<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
for (index, validator) in state.validators.iter_mut().enumerate() {
|
||||
let balance = state.balances[index];
|
||||
validator.effective_balance = std::cmp::min(
|
||||
balance - balance % spec.effective_balance_increment,
|
||||
balance.safe_sub(balance.safe_rem(spec.effective_balance_increment)?)?,
|
||||
spec.max_effective_balance,
|
||||
);
|
||||
if validator.effective_balance == spec.max_effective_balance {
|
||||
@@ -72,4 +77,5 @@ pub fn process_activations<T: EthSpec>(state: &mut BeaconState<T>, spec: &ChainS
|
||||
validator.activation_epoch = T::genesis_epoch();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![deny(clippy::integer_arithmetic)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::common::{initiate_validator_exit, slash_validator};
|
||||
use errors::{BlockOperationError, BlockProcessingError, HeaderInvalid, IntoWithIndex};
|
||||
use rayon::prelude::*;
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
use signature_sets::{block_proposal_signature_set, get_pubkey_from_state, randao_signature_set};
|
||||
use std::convert::TryInto;
|
||||
use tree_hash::TreeHash;
|
||||
@@ -239,7 +240,7 @@ pub fn process_eth1_data<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
eth1_data: &Eth1Data,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(new_eth1_data) = get_new_eth1_data(state, eth1_data) {
|
||||
if let Some(new_eth1_data) = get_new_eth1_data(state, eth1_data)? {
|
||||
state.eth1_data = new_eth1_data;
|
||||
}
|
||||
|
||||
@@ -248,14 +249,14 @@ pub fn process_eth1_data<T: EthSpec>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `Some(eth1_data)` if adding the given `eth1_data` to `state.eth1_data_votes` would
|
||||
/// Returns `Ok(Some(eth1_data))` if adding the given `eth1_data` to `state.eth1_data_votes` would
|
||||
/// result in a change to `state.eth1_data`.
|
||||
///
|
||||
/// Spec v0.11.1
|
||||
pub fn get_new_eth1_data<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
eth1_data: &Eth1Data,
|
||||
) -> Option<Eth1Data> {
|
||||
) -> Result<Option<Eth1Data>, ArithError> {
|
||||
let num_votes = state
|
||||
.eth1_data_votes
|
||||
.iter()
|
||||
@@ -263,10 +264,10 @@ pub fn get_new_eth1_data<T: EthSpec>(
|
||||
.count();
|
||||
|
||||
// The +1 is to account for the `eth1_data` supplied to the function.
|
||||
if 2 * (num_votes + 1) > T::SlotsPerEth1VotingPeriod::to_usize() {
|
||||
Some(eth1_data.clone())
|
||||
if num_votes.safe_add(1)?.safe_mul(2)? > T::SlotsPerEth1VotingPeriod::to_usize() {
|
||||
Ok(Some(eth1_data.clone()))
|
||||
} else {
|
||||
None
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +319,8 @@ pub fn process_attester_slashings<T: EthSpec>(
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
// Verify the `IndexedAttestation`s in parallel (these are the resource-consuming objects, not
|
||||
// the `AttesterSlashing`s themselves).
|
||||
let mut indexed_attestations: Vec<&_> = Vec::with_capacity(attester_slashings.len() * 2);
|
||||
let mut indexed_attestations: Vec<&_> =
|
||||
Vec::with_capacity(attester_slashings.len().safe_mul(2)?);
|
||||
for attester_slashing in attester_slashings {
|
||||
indexed_attestations.push(&attester_slashing.attestation_1);
|
||||
indexed_attestations.push(&attester_slashing.attestation_2);
|
||||
@@ -432,8 +434,13 @@ pub fn process_deposits<T: EthSpec>(
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.try_for_each(|(i, deposit)| {
|
||||
verify_deposit_merkle_proof(state, deposit, state.eth1_deposit_index + i as u64, spec)
|
||||
.map_err(|e| e.into_with_index(i))
|
||||
verify_deposit_merkle_proof(
|
||||
state,
|
||||
deposit,
|
||||
state.eth1_deposit_index.safe_add(i as u64)?,
|
||||
spec,
|
||||
)
|
||||
.map_err(|e| e.into_with_index(i))
|
||||
})?;
|
||||
|
||||
// Update the state in series.
|
||||
@@ -459,7 +466,7 @@ pub fn process_deposit<T: EthSpec>(
|
||||
.map_err(|e| e.into_with_index(deposit_index))?;
|
||||
}
|
||||
|
||||
state.eth1_deposit_index += 1;
|
||||
state.eth1_deposit_index.increment()?;
|
||||
|
||||
// Ensure the state's pubkey cache is fully up-to-date, it will be used to check to see if the
|
||||
// depositing validator already exists in the registry.
|
||||
@@ -495,7 +502,7 @@ pub fn process_deposit<T: EthSpec>(
|
||||
exit_epoch: spec.far_future_epoch,
|
||||
withdrawable_epoch: spec.far_future_epoch,
|
||||
effective_balance: std::cmp::min(
|
||||
amount - amount % spec.effective_balance_increment,
|
||||
amount.safe_sub(amount.safe_rem(spec.effective_balance_increment)?)?,
|
||||
spec.max_effective_balance,
|
||||
),
|
||||
slashed: false,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
|
||||
use super::signature_sets::{Error as SignatureSetError, Result as SignatureSetResult, *};
|
||||
|
||||
use crate::common::get_indexed_attestation;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use super::signature_sets::Error as SignatureSetError;
|
||||
use merkle_proof::MerkleTreeError;
|
||||
use safe_arith::ArithError;
|
||||
use types::*;
|
||||
|
||||
/// The error returned from the `per_block_processing` function. Indicates that a block is either
|
||||
/// invalid, or we were unable to determine it's validity (we encountered an unexpected error).
|
||||
/// invalid, or we were unable to determine its validity (we encountered an unexpected error).
|
||||
///
|
||||
/// Any of the `...Error` variants indicate that at some point during block (and block operation)
|
||||
/// verification, there was an error. There is no indication as to _where_ that error happened
|
||||
@@ -48,6 +49,7 @@ pub enum BlockProcessingError {
|
||||
SignatureSetError(SignatureSetError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
MerkleTreeError(MerkleTreeError),
|
||||
ArithError(ArithError),
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for BlockProcessingError {
|
||||
@@ -68,6 +70,12 @@ impl From<ssz_types::Error> for BlockProcessingError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArithError> for BlockProcessingError {
|
||||
fn from(e: ArithError) -> Self {
|
||||
BlockProcessingError::ArithError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
|
||||
fn from(e: BlockOperationError<HeaderInvalid>) -> BlockProcessingError {
|
||||
match e {
|
||||
@@ -75,6 +83,7 @@ impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
|
||||
BlockOperationError::BeaconStateError(e) => BlockProcessingError::BeaconStateError(e),
|
||||
BlockOperationError::SignatureSetError(e) => BlockProcessingError::SignatureSetError(e),
|
||||
BlockOperationError::SszTypesError(e) => BlockProcessingError::SszTypesError(e),
|
||||
BlockOperationError::ArithError(e) => BlockProcessingError::ArithError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,6 +110,7 @@ macro_rules! impl_into_block_processing_error_with_index {
|
||||
BlockOperationError::BeaconStateError(e) => BlockProcessingError::BeaconStateError(e),
|
||||
BlockOperationError::SignatureSetError(e) => BlockProcessingError::SignatureSetError(e),
|
||||
BlockOperationError::SszTypesError(e) => BlockProcessingError::SszTypesError(e),
|
||||
BlockOperationError::ArithError(e) => BlockProcessingError::ArithError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +140,7 @@ pub enum BlockOperationError<T> {
|
||||
BeaconStateError(BeaconStateError),
|
||||
SignatureSetError(SignatureSetError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
ArithError(ArithError),
|
||||
}
|
||||
|
||||
impl<T> BlockOperationError<T> {
|
||||
@@ -155,6 +166,12 @@ impl<T> From<ssz_types::Error> for BlockOperationError<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<ArithError> for BlockOperationError<T> {
|
||||
fn from(e: ArithError) -> Self {
|
||||
BlockOperationError::ArithError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum HeaderInvalid {
|
||||
ProposalSignatureInvalid,
|
||||
@@ -265,6 +282,7 @@ impl From<BlockOperationError<IndexedAttestationInvalid>>
|
||||
BlockOperationError::BeaconStateError(e) => BlockOperationError::BeaconStateError(e),
|
||||
BlockOperationError::SignatureSetError(e) => BlockOperationError::SignatureSetError(e),
|
||||
BlockOperationError::SszTypesError(e) => BlockOperationError::SszTypesError(e),
|
||||
BlockOperationError::ArithError(e) => BlockOperationError::ArithError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::per_block_processing::signature_sets::{
|
||||
deposit_pubkey_signature_message, deposit_signature_set,
|
||||
};
|
||||
use merkle_proof::verify_merkle_proof;
|
||||
use safe_arith::SafeArith;
|
||||
use tree_hash::TreeHash;
|
||||
use types::*;
|
||||
|
||||
@@ -59,7 +60,7 @@ pub fn verify_deposit_merkle_proof<T: EthSpec>(
|
||||
verify_merkle_proof(
|
||||
leaf,
|
||||
&deposit.proof[..],
|
||||
spec.deposit_contract_tree_depth as usize + 1,
|
||||
spec.deposit_contract_tree_depth.safe_add(1)? as usize,
|
||||
deposit_index as usize,
|
||||
state.eth1_data.deposit_root,
|
||||
),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use errors::EpochProcessingError as Error;
|
||||
use safe_arith::SafeArith;
|
||||
use tree_hash::TreeHash;
|
||||
use types::*;
|
||||
|
||||
@@ -90,7 +91,11 @@ pub fn process_justification_and_finalization<T: EthSpec>(
|
||||
state.previous_justified_checkpoint = state.current_justified_checkpoint.clone();
|
||||
state.justification_bits.shift_up(1)?;
|
||||
|
||||
if total_balances.previous_epoch_target_attesters() * 3 >= total_balances.current_epoch() * 2 {
|
||||
if total_balances
|
||||
.previous_epoch_target_attesters()
|
||||
.safe_mul(3)?
|
||||
>= total_balances.current_epoch().safe_mul(2)?
|
||||
{
|
||||
state.current_justified_checkpoint = Checkpoint {
|
||||
epoch: previous_epoch,
|
||||
root: *state.get_block_root_at_epoch(previous_epoch)?,
|
||||
@@ -98,7 +103,11 @@ pub fn process_justification_and_finalization<T: EthSpec>(
|
||||
state.justification_bits.set(1, true)?;
|
||||
}
|
||||
// If the current epoch gets justified, fill the last bit.
|
||||
if total_balances.current_epoch_target_attesters() * 3 >= total_balances.current_epoch() * 2 {
|
||||
if total_balances
|
||||
.current_epoch_target_attesters()
|
||||
.safe_mul(3)?
|
||||
>= total_balances.current_epoch().safe_mul(2)?
|
||||
{
|
||||
state.current_justified_checkpoint = Checkpoint {
|
||||
epoch: current_epoch,
|
||||
root: *state.get_block_root_at_epoch(current_epoch)?,
|
||||
@@ -152,17 +161,19 @@ pub fn process_final_updates<T: EthSpec>(
|
||||
}
|
||||
|
||||
// Update effective balances with hysteresis (lag).
|
||||
let hysteresis_increment = spec.effective_balance_increment / spec.hysteresis_quotient;
|
||||
let downward_threshold = hysteresis_increment * spec.hysteresis_downward_multiplier;
|
||||
let upward_threshold = hysteresis_increment * spec.hysteresis_upward_multiplier;
|
||||
let hysteresis_increment = spec
|
||||
.effective_balance_increment
|
||||
.safe_div(spec.hysteresis_quotient)?;
|
||||
let downward_threshold = hysteresis_increment.safe_mul(spec.hysteresis_downward_multiplier)?;
|
||||
let upward_threshold = hysteresis_increment.safe_mul(spec.hysteresis_upward_multiplier)?;
|
||||
for (index, validator) in state.validators.iter_mut().enumerate() {
|
||||
let balance = state.balances[index];
|
||||
|
||||
if balance + downward_threshold < validator.effective_balance
|
||||
|| validator.effective_balance + upward_threshold < balance
|
||||
if balance.safe_add(downward_threshold)? < validator.effective_balance
|
||||
|| validator.effective_balance.safe_add(upward_threshold)? < balance
|
||||
{
|
||||
validator.effective_balance = std::cmp::min(
|
||||
balance - balance % spec.effective_balance_increment,
|
||||
balance.safe_sub(balance.safe_rem(spec.effective_balance_increment)?)?,
|
||||
spec.max_effective_balance,
|
||||
);
|
||||
}
|
||||
@@ -175,7 +186,11 @@ pub fn process_final_updates<T: EthSpec>(
|
||||
state.set_randao_mix(next_epoch, *state.get_randao_mix(current_epoch)?)?;
|
||||
|
||||
// Set historical root accumulator
|
||||
if next_epoch.as_u64() % (T::SlotsPerHistoricalRoot::to_u64() / T::slots_per_epoch()) == 0 {
|
||||
if next_epoch
|
||||
.as_u64()
|
||||
.safe_rem(T::SlotsPerHistoricalRoot::to_u64().safe_div(T::slots_per_epoch())?)?
|
||||
== 0
|
||||
{
|
||||
let historical_batch = state.historical_batch();
|
||||
state
|
||||
.historical_roots
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::super::common::get_base_reward;
|
||||
use super::validator_statuses::{TotalBalances, ValidatorStatus, ValidatorStatuses};
|
||||
use super::Error;
|
||||
use safe_arith::SafeArith;
|
||||
|
||||
use types::*;
|
||||
|
||||
@@ -13,21 +14,21 @@ pub struct Delta {
|
||||
|
||||
impl Delta {
|
||||
/// Reward the validator with the `reward`.
|
||||
pub fn reward(&mut self, reward: u64) {
|
||||
self.rewards += reward;
|
||||
pub fn reward(&mut self, reward: u64) -> Result<(), Error> {
|
||||
self.rewards = self.rewards.safe_add(reward)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Penalize the validator with the `penalty`.
|
||||
pub fn penalize(&mut self, penalty: u64) {
|
||||
self.penalties += penalty;
|
||||
pub fn penalize(&mut self, penalty: u64) -> Result<(), Error> {
|
||||
self.penalties = self.penalties.safe_add(penalty)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign for Delta {
|
||||
/// Use wrapping addition as that is how it's defined in the spec.
|
||||
fn add_assign(&mut self, other: Delta) {
|
||||
self.rewards += other.rewards;
|
||||
self.penalties += other.penalties;
|
||||
/// Combine two deltas.
|
||||
fn combine(&mut self, other: Delta) -> Result<(), Error> {
|
||||
self.reward(other.rewards)?;
|
||||
self.penalize(other.penalties)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,9 +57,10 @@ pub fn process_rewards_and_penalties<T: EthSpec>(
|
||||
|
||||
get_proposer_deltas(&mut deltas, state, validator_statuses, spec)?;
|
||||
|
||||
// Apply the deltas, over-flowing but not under-flowing (saturating at 0 instead).
|
||||
// Apply the deltas, erroring on overflow above but not on overflow below (saturating at 0
|
||||
// instead).
|
||||
for (i, delta) in deltas.iter().enumerate() {
|
||||
state.balances[i] += delta.rewards;
|
||||
state.balances[i] = state.balances[i].safe_add(delta.rewards)?;
|
||||
state.balances[i] = state.balances[i].saturating_sub(delta.penalties);
|
||||
}
|
||||
|
||||
@@ -91,7 +93,8 @@ fn get_proposer_deltas<T: EthSpec>(
|
||||
return Err(Error::ValidatorStatusesInconsistent);
|
||||
}
|
||||
|
||||
deltas[inclusion.proposer_index].reward(base_reward / spec.proposer_reward_quotient);
|
||||
deltas[inclusion.proposer_index]
|
||||
.reward(base_reward.safe_div(spec.proposer_reward_quotient)?)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,9 +126,9 @@ fn get_attestation_deltas<T: EthSpec>(
|
||||
base_reward,
|
||||
finality_delay,
|
||||
spec,
|
||||
);
|
||||
)?;
|
||||
|
||||
deltas[index] += delta;
|
||||
deltas[index].combine(delta)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -140,7 +143,7 @@ fn get_attestation_delta<T: EthSpec>(
|
||||
base_reward: u64,
|
||||
finality_delay: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Delta {
|
||||
) -> Result<Delta, Error> {
|
||||
let mut delta = Delta::default();
|
||||
|
||||
// Is this validator eligible to be rewarded or penalized?
|
||||
@@ -149,7 +152,7 @@ fn get_attestation_delta<T: EthSpec>(
|
||||
|| (validator.is_slashed && !validator.is_withdrawable_in_current_epoch);
|
||||
|
||||
if !is_eligible {
|
||||
return delta;
|
||||
return Ok(delta);
|
||||
}
|
||||
|
||||
// Handle integer overflow by dividing these quantities by EFFECTIVE_BALANCE_INCREMENT
|
||||
@@ -157,59 +160,78 @@ fn get_attestation_delta<T: EthSpec>(
|
||||
// - increment = EFFECTIVE_BALANCE_INCREMENT
|
||||
// - reward_numerator = get_base_reward(state, index) * (attesting_balance // increment)
|
||||
// - rewards[index] = reward_numerator // (total_balance // increment)
|
||||
let total_balance_ebi = total_balances.current_epoch() / spec.effective_balance_increment;
|
||||
let total_attesting_balance_ebi =
|
||||
total_balances.previous_epoch_attesters() / spec.effective_balance_increment;
|
||||
let matching_target_balance_ebi =
|
||||
total_balances.previous_epoch_target_attesters() / spec.effective_balance_increment;
|
||||
let matching_head_balance_ebi =
|
||||
total_balances.previous_epoch_head_attesters() / spec.effective_balance_increment;
|
||||
let total_balance_ebi = total_balances
|
||||
.current_epoch()
|
||||
.safe_div(spec.effective_balance_increment)?;
|
||||
let total_attesting_balance_ebi = total_balances
|
||||
.previous_epoch_attesters()
|
||||
.safe_div(spec.effective_balance_increment)?;
|
||||
let matching_target_balance_ebi = total_balances
|
||||
.previous_epoch_target_attesters()
|
||||
.safe_div(spec.effective_balance_increment)?;
|
||||
let matching_head_balance_ebi = total_balances
|
||||
.previous_epoch_head_attesters()
|
||||
.safe_div(spec.effective_balance_increment)?;
|
||||
|
||||
// Expected FFG source.
|
||||
// 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_ebi / total_balance_ebi);
|
||||
delta.reward(
|
||||
base_reward
|
||||
.safe_mul(total_attesting_balance_ebi)?
|
||||
.safe_div(total_balance_ebi)?,
|
||||
)?;
|
||||
// Inclusion speed bonus
|
||||
let proposer_reward = base_reward / spec.proposer_reward_quotient;
|
||||
let max_attester_reward = base_reward - proposer_reward;
|
||||
let proposer_reward = base_reward.safe_div(spec.proposer_reward_quotient)?;
|
||||
let max_attester_reward = base_reward.safe_sub(proposer_reward)?;
|
||||
let inclusion = validator
|
||||
.inclusion_info
|
||||
.expect("It is a logic error for an attester not to have an inclusion delay.");
|
||||
delta.reward(max_attester_reward / inclusion.delay);
|
||||
delta.reward(max_attester_reward.safe_div(inclusion.delay)?)?;
|
||||
} else {
|
||||
delta.penalize(base_reward);
|
||||
delta.penalize(base_reward)?;
|
||||
}
|
||||
|
||||
// Expected FFG target.
|
||||
// Spec:
|
||||
// - validator index in `get_unslashed_attesting_indices(state, matching_target_attestations)`
|
||||
if validator.is_previous_epoch_target_attester && !validator.is_slashed {
|
||||
delta.reward(base_reward * matching_target_balance_ebi / total_balance_ebi);
|
||||
delta.reward(
|
||||
base_reward
|
||||
.safe_mul(matching_target_balance_ebi)?
|
||||
.safe_div(total_balance_ebi)?,
|
||||
)?;
|
||||
} else {
|
||||
delta.penalize(base_reward);
|
||||
delta.penalize(base_reward)?;
|
||||
}
|
||||
|
||||
// Expected head.
|
||||
// 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_ebi / total_balance_ebi);
|
||||
delta.reward(
|
||||
base_reward
|
||||
.safe_mul(matching_head_balance_ebi)?
|
||||
.safe_div(total_balance_ebi)?,
|
||||
)?;
|
||||
} else {
|
||||
delta.penalize(base_reward);
|
||||
delta.penalize(base_reward)?;
|
||||
}
|
||||
|
||||
// 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);
|
||||
delta.penalize(spec.base_rewards_per_epoch.safe_mul(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,
|
||||
);
|
||||
validator
|
||||
.current_epoch_effective_balance
|
||||
.safe_mul(finality_delay)?
|
||||
.safe_div(spec.inactivity_penalty_quotient)?,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,5 +240,5 @@ fn get_attestation_delta<T: EthSpec>(
|
||||
// This function only computes the delta for a single validator, so it cannot also return a
|
||||
// delta for a validator.
|
||||
|
||||
delta
|
||||
Ok(delta)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ pub enum EpochProcessingError {
|
||||
BeaconStateError(BeaconStateError),
|
||||
InclusionError(InclusionError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
ArithError(safe_arith::ArithError),
|
||||
}
|
||||
|
||||
impl From<InclusionError> for EpochProcessingError {
|
||||
@@ -38,6 +39,12 @@ impl From<ssz_types::Error> for EpochProcessingError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<safe_arith::ArithError> for EpochProcessingError {
|
||||
fn from(e: safe_arith::ArithError) -> EpochProcessingError {
|
||||
EpochProcessingError::ArithError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InclusionError {
|
||||
/// The validator did not participate in an attestation in this period.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use safe_arith::SafeArith;
|
||||
use types::{BeaconStateError as Error, *};
|
||||
|
||||
/// Process slashings.
|
||||
@@ -13,12 +14,17 @@ pub fn process_slashings<T: EthSpec>(
|
||||
|
||||
for (index, validator) in state.validators.iter().enumerate() {
|
||||
if validator.slashed
|
||||
&& epoch + T::EpochsPerSlashingsVector::to_u64() / 2 == validator.withdrawable_epoch
|
||||
&& epoch + T::EpochsPerSlashingsVector::to_u64().safe_div(2)?
|
||||
== validator.withdrawable_epoch
|
||||
{
|
||||
let increment = spec.effective_balance_increment;
|
||||
let penalty_numerator = validator.effective_balance / increment
|
||||
* std::cmp::min(sum_slashings * 3, total_balance);
|
||||
let penalty = penalty_numerator / total_balance * increment;
|
||||
let penalty_numerator = validator
|
||||
.effective_balance
|
||||
.safe_div(increment)?
|
||||
.safe_mul(std::cmp::min(sum_slashings.safe_mul(3)?, total_balance))?;
|
||||
let penalty = penalty_numerator
|
||||
.safe_div(total_balance)?
|
||||
.safe_mul(increment)?;
|
||||
|
||||
safe_sub_assign!(state.balances[index], penalty);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::common::get_attesting_indices;
|
||||
use safe_arith::SafeArith;
|
||||
use types::*;
|
||||
|
||||
/// Sets the boolean `var` on `self` to be true if it is true on `other`. Otherwise leaves `self`
|
||||
@@ -198,12 +199,16 @@ impl ValidatorStatuses {
|
||||
|
||||
if validator.is_active_at(state.current_epoch()) {
|
||||
status.is_active_in_current_epoch = true;
|
||||
total_balances.current_epoch += effective_balance;
|
||||
total_balances
|
||||
.current_epoch
|
||||
.safe_add_assign(effective_balance)?;
|
||||
}
|
||||
|
||||
if validator.is_active_at(state.previous_epoch()) {
|
||||
status.is_active_in_previous_epoch = true;
|
||||
total_balances.previous_epoch += effective_balance;
|
||||
total_balances
|
||||
.previous_epoch
|
||||
.safe_add_assign(effective_balance)?;
|
||||
}
|
||||
|
||||
statuses.push(status);
|
||||
@@ -275,19 +280,29 @@ impl ValidatorStatuses {
|
||||
let validator_balance = state.get_effective_balance(index, spec)?;
|
||||
|
||||
if v.is_current_epoch_attester {
|
||||
self.total_balances.current_epoch_attesters += validator_balance;
|
||||
self.total_balances
|
||||
.current_epoch_attesters
|
||||
.safe_add_assign(validator_balance)?;
|
||||
}
|
||||
if v.is_current_epoch_target_attester {
|
||||
self.total_balances.current_epoch_target_attesters += validator_balance;
|
||||
self.total_balances
|
||||
.current_epoch_target_attesters
|
||||
.safe_add_assign(validator_balance)?;
|
||||
}
|
||||
if v.is_previous_epoch_attester {
|
||||
self.total_balances.previous_epoch_attesters += validator_balance;
|
||||
self.total_balances
|
||||
.previous_epoch_attesters
|
||||
.safe_add_assign(validator_balance)?;
|
||||
}
|
||||
if v.is_previous_epoch_target_attester {
|
||||
self.total_balances.previous_epoch_target_attesters += validator_balance;
|
||||
self.total_balances
|
||||
.previous_epoch_target_attesters
|
||||
.safe_add_assign(validator_balance)?;
|
||||
}
|
||||
if v.is_previous_epoch_head_attester {
|
||||
self.total_balances.previous_epoch_head_attesters += validator_balance;
|
||||
self.total_balances
|
||||
.previous_epoch_head_attesters
|
||||
.safe_add_assign(validator_balance)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user