2019-06-28 13:44:44 +00:00
|
|
|
# beacon_chain
|
2020-01-27 11:36:17 +00:00
|
|
|
# Copyright (c) 2018-2020 Status Research & Development GmbH
|
2019-06-28 13:44:44 +00:00
|
|
|
# Licensed and distributed under either of
|
2019-11-25 15:30:02 +00:00
|
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
2019-06-28 13:44:44 +00:00
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
|
|
|
# State transition - epoch processing, as described in
|
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function
|
|
|
|
#
|
2020-06-09 09:42:53 +00:00
|
|
|
# The entry point is `process_epoch`, which is at the bottom of this file.
|
2019-06-28 13:44:44 +00:00
|
|
|
#
|
|
|
|
# General notes about the code (TODO):
|
|
|
|
# * Weird styling - the sections taken from the spec use python styling while
|
|
|
|
# the others use NEP-1 - helps grepping identifiers in spec
|
|
|
|
# * For indices, we get a mix of uint64, ValidatorIndex and int - this is currently
|
|
|
|
# swept under the rug with casts
|
|
|
|
# When updating the code, add TODO sections to mark where there are clear
|
|
|
|
# improvements to be made - other than that, keep things similar to spec for
|
|
|
|
# now.
|
|
|
|
|
2020-04-22 05:53:02 +00:00
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
2019-11-12 05:35:52 +00:00
|
|
|
import
|
2020-04-29 20:12:07 +00:00
|
|
|
math, sequtils, tables,
|
2020-06-03 13:52:02 +00:00
|
|
|
stew/[bitops2], chronicles, json_serialization/std/sets,
|
|
|
|
metrics, ../extras, ../ssz/merkleization,
|
2019-09-23 15:00:10 +00:00
|
|
|
beaconstate, crypto, datatypes, digest, helpers, validator,
|
2019-12-20 16:14:43 +00:00
|
|
|
state_transition_helpers,
|
|
|
|
../../nbench/bench_lab
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2019-09-23 13:48:25 +00:00
|
|
|
# Logging utilities
|
|
|
|
# --------------------------------------------------------
|
|
|
|
|
|
|
|
logScope: topics = "consens"
|
|
|
|
|
2019-10-06 04:31:50 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md#interop-metrics
|
2019-10-03 10:36:31 +00:00
|
|
|
declareGauge beacon_finalized_epoch, "Current finalized epoch" # On epoch transition
|
|
|
|
declareGauge beacon_finalized_root, "Current finalized root" # On epoch transition
|
|
|
|
declareGauge beacon_current_justified_epoch, "Current justified epoch" # On epoch transition
|
|
|
|
declareGauge beacon_current_justified_root, "Current justified root" # On epoch transition
|
|
|
|
declareGauge beacon_previous_justified_epoch, "Current previously justified epoch" # On epoch transition
|
|
|
|
declareGauge beacon_previous_justified_root, "Current previously justified root" # On epoch transition
|
|
|
|
|
2019-11-12 05:35:52 +00:00
|
|
|
# Non-spec
|
|
|
|
declareGauge epoch_transition_justification_and_finalization, "Epoch transition justification and finalization time"
|
|
|
|
declareGauge epoch_transition_times_rewards_and_penalties, "Epoch transition reward and penalty time"
|
|
|
|
declareGauge epoch_transition_registry_updates, "Epoch transition registry updates time"
|
|
|
|
declareGauge epoch_transition_slashings, "Epoch transition slashings time"
|
|
|
|
declareGauge epoch_transition_final_updates, "Epoch transition final updates time"
|
2020-05-11 06:25:49 +00:00
|
|
|
declareGauge beacon_current_epoch, "Current epoch"
|
2019-11-12 05:35:52 +00:00
|
|
|
|
2019-09-23 13:48:25 +00:00
|
|
|
# Spec
|
|
|
|
# --------------------------------------------------------
|
|
|
|
|
2020-06-17 08:25:07 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#get_total_active_balance
|
2020-06-03 05:42:08 +00:00
|
|
|
func get_total_active_balance*(state: BeaconState, cache: var StateCache): Gwei =
|
initial 0.9.0 spec sync (#509)
* rename compute_epoch_of_slot(...) to compute_epoch_at_slot(...)
* remove some unnecessary imports; remove some crosslink-related code and tests; complete renaming of compute_epoch_of_slot(...) to compute_epoch_at_slot(...)
* rm more transfer-related code and tests; rm more unnecessary strutils imports
* rm remaining unused imports
* remove useless get_empty_per_epoch_cache(...)/compute_start_slot_of_epoch(...) calls
* rename compute_start_slot_of_epoch(...) to compute_start_slot_at_epoch(...)
* rename ACTIVATION_EXIT_DELAY to MAX_SEED_LOOKAHEAD
* update domain types to 0.9.0
* mark AttesterSlashing, IndexedAttestation, AttestationDataAndCustodyBit, DepositData, BeaconBlockHeader, Fork, integer_squareroot(...), and process_voluntary_exit(...) as 0.9.0
* mark increase_balance(...), decrease_balance(...), get_block_root(...), CheckPoint, Deposit, PendingAttestation, HistoricalBatch, is_active_validator(...), and is_slashable_attestation_data(...) as 0.9.0
* mark compute_activation_exit_epoch(...), bls_verify(...), Validator, get_active_validator_indices(...), get_current_epoch(...), get_total_active_balance(...), and get_previous_epoch(...) as 0.9.0
* mark get_block_root_at_slot(...), ProposerSlashing, get_domain(...), VoluntaryExit, mainnet preset Gwei values, minimal preset max operations, process_block_header(...), and is_slashable_validator(...) as 0.9.0
* mark makeWithdrawalCredentials(...), get_validator_churn_limit(...), get_total_balance(...), is_valid_indexed_attestation(...), bls_aggregate_pubkeys(...), initial genesis value/constants, Attestation, get_randao_mix(...), mainnet preset max operations per block constants, minimal preset Gwei values and time parameters, process_eth1_data(...), get_shuffled_seq(...), compute_committee(...), and process_slots(...) as 0.9.0; partially update get_indexed_attestation(...) to 0.9.0 by removing crosslink refs and associated tests
* mark initiate_validator_exit(...), process_registry_updates(...), BeaconBlock, Eth1Data, compute_domain(...), process_randao(...), process_attester_slashing(...), get_base_reward(...), and process_slot(...) as 0.9.0
2019-10-30 19:41:19 +00:00
|
|
|
# Return the combined effective balance of the active validators.
|
2020-03-30 23:40:24 +00:00
|
|
|
# Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei
|
|
|
|
# minimum to avoid divisions by zero.
|
2020-06-03 05:42:08 +00:00
|
|
|
|
|
|
|
let
|
|
|
|
epoch = state.slot.compute_epoch_at_slot
|
|
|
|
try:
|
2020-06-09 09:42:53 +00:00
|
|
|
# TODO refactor get_empty_per_epoch_cache() not to be, well, empty, so can
|
|
|
|
# avoid this ever refilling, and raiseAssert, and get rid of var
|
2020-06-03 05:42:08 +00:00
|
|
|
if epoch notin cache.shuffled_active_validator_indices:
|
|
|
|
cache.shuffled_active_validator_indices[epoch] =
|
|
|
|
get_shuffled_active_validator_indices(state, epoch)
|
|
|
|
|
|
|
|
get_total_balance(state, cache.shuffled_active_validator_indices[epoch])
|
|
|
|
except KeyError:
|
|
|
|
raiseAssert("get_total_active_balance(): cache always filled before usage")
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2020-05-20 08:46:31 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#helper-functions-1
|
2020-05-18 17:49:22 +00:00
|
|
|
func get_matching_source_attestations(state: BeaconState,
|
|
|
|
epoch: Epoch): seq[PendingAttestation] =
|
2019-07-10 12:23:02 +00:00
|
|
|
doAssert epoch in [get_current_epoch(state), get_previous_epoch(state)]
|
2019-06-28 13:44:44 +00:00
|
|
|
if epoch == get_current_epoch(state):
|
2020-05-18 17:49:22 +00:00
|
|
|
state.current_epoch_attestations.asSeq
|
2019-06-28 13:44:44 +00:00
|
|
|
else:
|
2020-05-18 17:49:22 +00:00
|
|
|
state.previous_epoch_attestations.asSeq
|
2019-06-28 13:44:44 +00:00
|
|
|
|
|
|
|
func get_matching_target_attestations(state: BeaconState, epoch: Epoch):
|
|
|
|
seq[PendingAttestation] =
|
|
|
|
filterIt(
|
|
|
|
get_matching_source_attestations(state, epoch),
|
2019-07-02 21:14:55 +00:00
|
|
|
it.data.target.root == get_block_root(state, epoch)
|
2019-06-28 13:44:44 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func get_matching_head_attestations(state: BeaconState, epoch: Epoch):
|
|
|
|
seq[PendingAttestation] =
|
|
|
|
filterIt(
|
2020-03-14 21:54:45 +00:00
|
|
|
get_matching_target_attestations(state, epoch),
|
2019-06-28 13:44:44 +00:00
|
|
|
it.data.beacon_block_root ==
|
2019-11-08 10:30:22 +00:00
|
|
|
get_block_root_at_slot(state, it.data.slot)
|
2019-06-28 13:44:44 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func get_attesting_balance(
|
|
|
|
state: BeaconState, attestations: seq[PendingAttestation],
|
|
|
|
stateCache: var StateCache): Gwei =
|
2020-03-30 23:40:24 +00:00
|
|
|
# Return the combined effective balance of the set of unslashed validators
|
|
|
|
# participating in ``attestations``.
|
|
|
|
# Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei
|
|
|
|
# minimum to avoid divisions by zero.
|
2019-06-28 13:44:44 +00:00
|
|
|
get_total_balance(state, get_unslashed_attesting_indices(
|
|
|
|
state, attestations, stateCache))
|
|
|
|
|
2019-12-20 16:50:09 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.4/specs/core/0_beacon-chain.md#justification-and-finalization
|
2020-05-09 12:43:15 +00:00
|
|
|
proc process_justification_and_finalization*(state: var BeaconState,
|
|
|
|
stateCache: var StateCache, updateFlags: UpdateFlags = {}) {.nbench.} =
|
2019-09-23 13:48:25 +00:00
|
|
|
|
|
|
|
logScope: pcs = "process_justification_and_finalization"
|
|
|
|
|
2019-06-28 13:44:44 +00:00
|
|
|
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
|
|
|
|
return
|
|
|
|
|
|
|
|
let
|
|
|
|
previous_epoch = get_previous_epoch(state)
|
|
|
|
current_epoch = get_current_epoch(state)
|
2019-07-16 07:34:11 +00:00
|
|
|
old_previous_justified_checkpoint = state.previous_justified_checkpoint
|
|
|
|
old_current_justified_checkpoint = state.current_justified_checkpoint
|
|
|
|
|
2019-06-28 13:44:44 +00:00
|
|
|
# Process justifications
|
2019-07-16 07:34:11 +00:00
|
|
|
state.previous_justified_checkpoint = state.current_justified_checkpoint
|
|
|
|
|
|
|
|
## Spec:
|
|
|
|
## state.justification_bits[1:] = state.justification_bits[:-1]
|
|
|
|
## state.justification_bits[0] = 0b0
|
2019-09-27 16:40:33 +00:00
|
|
|
# TODO JUSTIFICATION_BITS_LENGTH is a constant in spec, move there or fix
|
|
|
|
# BitVector serialization in SSZ layer
|
|
|
|
const JUSTIFICATION_BITS_LENGTH = 4
|
|
|
|
state.justification_bits = (state.justification_bits shl 1) and
|
|
|
|
cast[uint8]((2^JUSTIFICATION_BITS_LENGTH) - 1)
|
2019-07-16 07:34:11 +00:00
|
|
|
|
|
|
|
# This is a somewhat expensive approach
|
2020-02-25 08:40:07 +00:00
|
|
|
let active_validator_indices {.used.} =
|
2019-11-13 13:18:01 +00:00
|
|
|
toHashSet(mapIt(
|
2019-07-16 07:34:11 +00:00
|
|
|
get_active_validator_indices(state, get_current_epoch(state)), it.int))
|
|
|
|
|
|
|
|
let matching_target_attestations_previous =
|
|
|
|
get_matching_target_attestations(state, previous_epoch) # Previous epoch
|
2019-07-16 09:16:04 +00:00
|
|
|
|
|
|
|
## This epoch processing is the last time these previous attestations can
|
|
|
|
## matter -- in the next epoch, they'll be 2 epochs old, when BeaconState
|
|
|
|
## tracks current_epoch_attestations and previous_epoch_attestations only
|
|
|
|
## per
|
2020-06-17 08:25:07 +00:00
|
|
|
## https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#attestations
|
2019-07-16 09:16:04 +00:00
|
|
|
## and `get_matching_source_attestations(...)` via
|
2020-06-17 08:25:07 +00:00
|
|
|
## https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#helper-functions-1
|
2019-07-16 09:16:04 +00:00
|
|
|
## and
|
2020-06-17 08:25:07 +00:00
|
|
|
## https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#final-updates
|
2019-07-16 09:16:04 +00:00
|
|
|
## after which the state.previous_epoch_attestations is replaced.
|
2020-06-03 05:42:08 +00:00
|
|
|
let total_active_balance = get_total_active_balance(state, stateCache)
|
2019-08-21 11:21:08 +00:00
|
|
|
trace "Non-attesting indices in previous epoch",
|
2019-07-16 07:34:11 +00:00
|
|
|
missing_all_validators=
|
|
|
|
difference(active_validator_indices,
|
2019-11-13 13:18:01 +00:00
|
|
|
toHashSet(mapIt(get_attesting_indices(state,
|
2019-07-16 07:34:11 +00:00
|
|
|
matching_target_attestations_previous, stateCache), it.int))),
|
|
|
|
missing_unslashed_validators=
|
|
|
|
difference(active_validator_indices,
|
2019-11-13 13:18:01 +00:00
|
|
|
toHashSet(mapIt(get_unslashed_attesting_indices(state,
|
2019-07-16 07:34:11 +00:00
|
|
|
matching_target_attestations_previous, stateCache), it.int))),
|
2019-08-14 08:56:32 +00:00
|
|
|
prev_attestations_len=len(state.previous_epoch_attestations),
|
|
|
|
cur_attestations_len=len(state.current_epoch_attestations),
|
|
|
|
num_active_validators=len(active_validator_indices),
|
2020-06-03 05:42:08 +00:00
|
|
|
required_balance = total_active_balance * 2,
|
2019-08-14 08:56:32 +00:00
|
|
|
attesting_balance_prev = get_attesting_balance(state, matching_target_attestations_previous, stateCache)
|
2019-07-16 07:34:11 +00:00
|
|
|
if get_attesting_balance(state, matching_target_attestations_previous,
|
2020-06-03 05:42:08 +00:00
|
|
|
stateCache) * 3 >= total_active_balance * 2:
|
2019-07-16 07:34:11 +00:00
|
|
|
state.current_justified_checkpoint =
|
|
|
|
Checkpoint(epoch: previous_epoch,
|
|
|
|
root: get_block_root(state, previous_epoch))
|
2019-12-20 13:25:33 +00:00
|
|
|
state.justification_bits.setBit 1
|
2019-07-16 07:34:11 +00:00
|
|
|
|
2020-06-25 10:23:10 +00:00
|
|
|
debug "Justified with previous epoch",
|
2019-09-23 13:48:25 +00:00
|
|
|
current_epoch = current_epoch,
|
|
|
|
checkpoint = shortLog(state.current_justified_checkpoint),
|
|
|
|
cat = "justification"
|
|
|
|
|
2019-07-16 07:34:11 +00:00
|
|
|
let matching_target_attestations_current =
|
|
|
|
get_matching_target_attestations(state, current_epoch) # Current epoch
|
|
|
|
if get_attesting_balance(state, matching_target_attestations_current,
|
2020-06-03 05:42:08 +00:00
|
|
|
stateCache) * 3 >= total_active_balance * 2:
|
2019-07-16 07:34:11 +00:00
|
|
|
state.current_justified_checkpoint =
|
|
|
|
Checkpoint(epoch: current_epoch,
|
|
|
|
root: get_block_root(state, current_epoch))
|
2019-12-20 13:25:33 +00:00
|
|
|
state.justification_bits.setBit 0
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2020-06-25 10:23:10 +00:00
|
|
|
debug "Justified with current epoch",
|
2019-09-23 13:48:25 +00:00
|
|
|
current_epoch = current_epoch,
|
|
|
|
checkpoint = shortLog(state.current_justified_checkpoint),
|
|
|
|
cat = "justification"
|
|
|
|
|
2019-06-28 13:44:44 +00:00
|
|
|
# Process finalizations
|
2019-07-01 09:42:37 +00:00
|
|
|
let bitfield = state.justification_bits
|
2019-06-28 13:44:44 +00:00
|
|
|
|
|
|
|
## The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th
|
|
|
|
## as source
|
2019-07-03 07:35:05 +00:00
|
|
|
if (bitfield and 0b1110) == 0b1110 and
|
|
|
|
old_previous_justified_checkpoint.epoch + 3 == current_epoch:
|
2019-07-16 07:34:11 +00:00
|
|
|
state.finalized_checkpoint = old_previous_justified_checkpoint
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2020-06-25 10:23:10 +00:00
|
|
|
debug "Finalized with rule 234",
|
2019-09-23 13:48:25 +00:00
|
|
|
current_epoch = current_epoch,
|
|
|
|
checkpoint = shortLog(state.finalized_checkpoint),
|
|
|
|
cat = "finalization"
|
|
|
|
|
2019-06-28 13:44:44 +00:00
|
|
|
## The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as
|
|
|
|
## source
|
2019-07-03 07:35:05 +00:00
|
|
|
if (bitfield and 0b110) == 0b110 and
|
|
|
|
old_previous_justified_checkpoint.epoch + 2 == current_epoch:
|
2019-07-16 07:34:11 +00:00
|
|
|
state.finalized_checkpoint = old_previous_justified_checkpoint
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2020-06-25 10:23:10 +00:00
|
|
|
debug "Finalized with rule 23",
|
2019-09-23 13:48:25 +00:00
|
|
|
current_epoch = current_epoch,
|
|
|
|
checkpoint = shortLog(state.finalized_checkpoint),
|
|
|
|
cat = "finalization"
|
|
|
|
|
2019-06-28 13:44:44 +00:00
|
|
|
## The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as
|
|
|
|
## source
|
2019-07-03 07:35:05 +00:00
|
|
|
if (bitfield and 0b111) == 0b111 and
|
|
|
|
old_current_justified_checkpoint.epoch + 2 == current_epoch:
|
2019-07-16 07:34:11 +00:00
|
|
|
state.finalized_checkpoint = old_current_justified_checkpoint
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2020-06-25 10:23:10 +00:00
|
|
|
debug "Finalized with rule 123",
|
2019-09-23 13:48:25 +00:00
|
|
|
current_epoch = current_epoch,
|
|
|
|
checkpoint = shortLog(state.finalized_checkpoint),
|
|
|
|
cat = "finalization"
|
|
|
|
|
2019-06-28 13:44:44 +00:00
|
|
|
## The 1st/2nd most recent epochs are justified, the 1st using the 2nd as
|
|
|
|
## source
|
2019-07-03 07:35:05 +00:00
|
|
|
if (bitfield and 0b11) == 0b11 and
|
|
|
|
old_current_justified_checkpoint.epoch + 1 == current_epoch:
|
2019-07-16 07:34:11 +00:00
|
|
|
state.finalized_checkpoint = old_current_justified_checkpoint
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2020-06-25 10:23:10 +00:00
|
|
|
debug "Finalized with rule 12",
|
2019-09-23 13:48:25 +00:00
|
|
|
current_epoch = current_epoch,
|
|
|
|
checkpoint = shortLog(state.finalized_checkpoint),
|
|
|
|
cat = "finalization"
|
|
|
|
|
2020-06-08 18:41:50 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#helpers
|
2019-11-15 22:37:39 +00:00
|
|
|
func get_base_reward(state: BeaconState, index: ValidatorIndex,
|
|
|
|
total_balance: auto): Gwei =
|
|
|
|
# Spec function recalculates total_balance every time, which creates an
|
|
|
|
# O(n^2) situation.
|
|
|
|
let effective_balance = state.validators[index].effective_balance
|
2019-06-28 13:44:44 +00:00
|
|
|
effective_balance * BASE_REWARD_FACTOR div
|
|
|
|
integer_squareroot(total_balance) div BASE_REWARDS_PER_EPOCH
|
|
|
|
|
2020-06-09 07:55:28 +00:00
|
|
|
func get_proposer_reward(state: BeaconState, attesting_index: ValidatorIndex,
|
|
|
|
cache: var StateCache): Gwei =
|
2020-06-08 18:41:50 +00:00
|
|
|
let total_balance = get_total_active_balance(state, cache)
|
|
|
|
|
|
|
|
get_base_reward(state, attesting_index, total_balance) div PROPOSER_REWARD_QUOTIENT
|
|
|
|
|
|
|
|
func get_finality_delay(state: BeaconState): uint64 =
|
|
|
|
get_previous_epoch(state) - state.finalized_checkpoint.epoch
|
|
|
|
|
|
|
|
func is_in_inactivity_leak(state: BeaconState): bool =
|
|
|
|
get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY
|
|
|
|
|
|
|
|
func get_eligible_validator_indices(state: BeaconState): seq[ValidatorIndex] =
|
|
|
|
# TODO iterator/yield, also, probably iterates multiple times over epoch
|
|
|
|
# transitions
|
|
|
|
let previous_epoch = get_previous_epoch(state)
|
|
|
|
for idx, v in state.validators:
|
|
|
|
if is_active_validator(v, previous_epoch) or
|
|
|
|
(v.slashed and previous_epoch + 1 < v.withdrawable_epoch):
|
|
|
|
result.add idx.ValidatorIndex
|
|
|
|
|
|
|
|
func get_attestation_component_deltas(state: BeaconState,
|
2020-06-09 07:55:28 +00:00
|
|
|
attestations: seq[PendingAttestation],
|
|
|
|
cache: var StateCache,
|
|
|
|
): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
2020-06-08 18:41:50 +00:00
|
|
|
# Helper with shared logic for use by get source, target, and head deltas
|
|
|
|
# functions
|
|
|
|
var
|
|
|
|
rewards = repeat(0'u64, len(state.validators))
|
|
|
|
penalties = repeat(0'u64, len(state.validators))
|
|
|
|
let
|
|
|
|
total_balance = get_total_active_balance(state, cache)
|
|
|
|
unslashed_attesting_indices =
|
|
|
|
get_unslashed_attesting_indices(state, attestations, cache)
|
|
|
|
attesting_balance = get_total_balance(state, unslashed_attesting_indices)
|
|
|
|
|
|
|
|
for index in get_eligible_validator_indices(state):
|
|
|
|
if index in unslashed_attesting_indices:
|
|
|
|
const increment = EFFECTIVE_BALANCE_INCREMENT # \
|
|
|
|
# Factored out from balance totals to avoid uint64 overflow
|
|
|
|
|
|
|
|
if is_in_inactivity_leak(state):
|
|
|
|
# Since full base reward will be canceled out by inactivity penalty deltas,
|
|
|
|
# optimal participation receives full base reward compensation here.
|
|
|
|
rewards[index] += get_base_reward(state, index, total_balance)
|
|
|
|
else:
|
|
|
|
let reward_numerator = get_base_reward(state, index, total_balance) * (attesting_balance div increment)
|
|
|
|
rewards[index] += reward_numerator div (total_balance div increment)
|
|
|
|
else:
|
|
|
|
penalties[index] += get_base_reward(state, index, total_balance)
|
|
|
|
(rewards, penalties)
|
|
|
|
|
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#components-of-attestation-deltas
|
2020-06-09 07:55:28 +00:00
|
|
|
func get_source_deltas(state: BeaconState, cache: var StateCache):
|
|
|
|
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
2020-06-08 18:41:50 +00:00
|
|
|
# Return attester micro-rewards/penalties for source-vote for each validator.
|
|
|
|
let matching_source_attestations =
|
|
|
|
get_matching_source_attestations(state, get_previous_epoch(state))
|
2020-06-09 07:55:28 +00:00
|
|
|
get_attestation_component_deltas(state, matching_source_attestations, cache)
|
2020-06-08 18:41:50 +00:00
|
|
|
|
2020-06-09 07:55:28 +00:00
|
|
|
func get_target_deltas(state: BeaconState, cache: var StateCache):
|
|
|
|
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
2020-06-08 18:41:50 +00:00
|
|
|
# Return attester micro-rewards/penalties for target-vote for each validator.
|
|
|
|
let matching_target_attestations =
|
|
|
|
get_matching_target_attestations(state, get_previous_epoch(state))
|
2020-06-09 07:55:28 +00:00
|
|
|
get_attestation_component_deltas(state, matching_target_attestations, cache)
|
2020-06-08 18:41:50 +00:00
|
|
|
|
2020-06-09 07:55:28 +00:00
|
|
|
func get_head_deltas(state: BeaconState, cache: var StateCache):
|
|
|
|
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
2020-06-08 18:41:50 +00:00
|
|
|
# Return attester micro-rewards/penalties for head-vote for each validator.
|
|
|
|
let matching_head_attestations =
|
|
|
|
get_matching_head_attestations(state, get_previous_epoch(state))
|
2020-06-09 07:55:28 +00:00
|
|
|
get_attestation_component_deltas(state, matching_head_attestations, cache)
|
2020-06-08 18:41:50 +00:00
|
|
|
|
2020-06-09 07:55:28 +00:00
|
|
|
func get_inclusion_delay_deltas(state: BeaconState, cache: var StateCache):
|
|
|
|
seq[Gwei] =
|
2020-06-08 18:41:50 +00:00
|
|
|
# Return proposer and inclusion delay micro-rewards/penalties for each validator.
|
|
|
|
var rewards = repeat(0'u64, len(state.validators))
|
2020-06-09 09:42:53 +00:00
|
|
|
let
|
|
|
|
matching_source_attestations =
|
|
|
|
get_matching_source_attestations(state, get_previous_epoch(state))
|
|
|
|
source_attestation_attesting_indices = mapIt(
|
|
|
|
matching_source_attestations,
|
|
|
|
get_attesting_indices(state, it.data, it.aggregation_bits, cache))
|
|
|
|
|
2020-06-08 18:41:50 +00:00
|
|
|
let total_balance = get_total_active_balance(state, cache)
|
|
|
|
for index in get_unslashed_attesting_indices(state, matching_source_attestations, cache):
|
2020-06-09 09:42:53 +00:00
|
|
|
# Translation of attestation = min([...])
|
|
|
|
# Start by filtering the right attestations
|
|
|
|
var filtered_matching_source_attestations: seq[PendingAttestation]
|
|
|
|
|
|
|
|
for source_attestation_index, a in matching_source_attestations:
|
|
|
|
if index notin
|
|
|
|
source_attestation_attesting_indices[source_attestation_index]:
|
|
|
|
continue
|
|
|
|
filtered_matching_source_attestations.add a
|
|
|
|
|
|
|
|
if filtered_matching_source_attestations.len == 0:
|
|
|
|
continue
|
|
|
|
|
|
|
|
# The first filtered attestation serves as min until we find something
|
|
|
|
# better
|
|
|
|
var attestation = filtered_matching_source_attestations[0]
|
|
|
|
for source_attestation_index, a in filtered_matching_source_attestations:
|
|
|
|
if a.inclusion_delay < attestation.inclusion_delay:
|
|
|
|
attestation = a
|
2020-06-08 18:41:50 +00:00
|
|
|
|
2020-06-09 07:55:28 +00:00
|
|
|
# TODO remove duplicate calculation of get_proposer_reward()
|
|
|
|
rewards[attestation.proposer_index] += get_proposer_reward(state, index, cache)
|
2020-06-08 18:41:50 +00:00
|
|
|
let max_attester_reward =
|
2020-06-09 07:55:28 +00:00
|
|
|
get_base_reward(state, index, total_balance) - get_proposer_reward(state, index, cache)
|
2020-06-08 18:41:50 +00:00
|
|
|
rewards[index] += Gwei(max_attester_reward div attestation.inclusion_delay)
|
|
|
|
|
|
|
|
# No penalties associated with inclusion delay
|
2020-06-09 07:55:28 +00:00
|
|
|
# Spec constructs both and returns both; this doesn't
|
|
|
|
rewards
|
2020-06-08 18:41:50 +00:00
|
|
|
|
2020-06-09 07:55:28 +00:00
|
|
|
func get_inactivity_penalty_deltas(state: BeaconState, cache: var StateCache):
|
|
|
|
seq[Gwei] =
|
2020-06-08 18:41:50 +00:00
|
|
|
# Return inactivity reward/penalty deltas for each validator.
|
|
|
|
var penalties = repeat(0'u64, len(state.validators))
|
|
|
|
let total_balance = get_total_active_balance(state, cache) # DO NOT KEEP RECALCULATING THIS
|
|
|
|
if is_in_inactivity_leak(state):
|
|
|
|
let
|
|
|
|
matching_target_attestations =
|
|
|
|
get_matching_target_attestations(state, get_previous_epoch(state))
|
|
|
|
matching_target_attesting_indices =
|
|
|
|
get_unslashed_attesting_indices(state, matching_target_attestations, cache)
|
|
|
|
for index in get_eligible_validator_indices(state):
|
|
|
|
# If validator is performing optimally this cancels all rewards for a neutral balance
|
|
|
|
let base_reward = get_base_reward(state, index, total_balance)
|
2020-06-09 07:55:28 +00:00
|
|
|
penalties[index] +=
|
|
|
|
Gwei(BASE_REWARDS_PER_EPOCH * base_reward -
|
|
|
|
get_proposer_reward(state, index, cache))
|
2020-06-09 09:42:53 +00:00
|
|
|
# matching_target_attesting_indices is a HashSet
|
2020-06-08 18:41:50 +00:00
|
|
|
if index notin matching_target_attesting_indices:
|
|
|
|
let effective_balance = state.validators[index].effective_balance
|
|
|
|
penalties[index] +=
|
2020-06-09 09:42:53 +00:00
|
|
|
Gwei(effective_balance * get_finality_delay(state) div
|
|
|
|
INACTIVITY_PENALTY_QUOTIENT)
|
2020-06-08 18:41:50 +00:00
|
|
|
|
|
|
|
# No rewards associated with inactivity penalties
|
2020-06-09 07:55:28 +00:00
|
|
|
# Spec constructs rewards anyway; this doesn't
|
|
|
|
penalties
|
2020-06-08 18:41:50 +00:00
|
|
|
|
2020-06-29 18:08:58 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#get_attestation_deltas
|
|
|
|
func get_attestation_deltas(state: BeaconState, cache: var StateCache): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
|
|
|
# Return attestation reward/penalty deltas for each validator.
|
|
|
|
let
|
|
|
|
(source_rewards, source_penalties) = get_source_deltas(state, cache)
|
|
|
|
(target_rewards, target_penalties) = get_target_deltas(state, cache)
|
|
|
|
(head_rewards, head_penalties) = get_head_deltas(state, cache)
|
|
|
|
inclusion_delay_rewards = get_inclusion_delay_deltas(state, cache)
|
|
|
|
inactivity_penalties = get_inactivity_penalty_deltas(state, cache)
|
|
|
|
|
|
|
|
let rewards = mapIt(0 ..< len(state.validators),
|
|
|
|
source_rewards[it] + target_rewards[it] + head_rewards[it] +
|
|
|
|
inclusion_delay_rewards[it])
|
2020-06-08 18:41:50 +00:00
|
|
|
|
2020-06-29 18:08:58 +00:00
|
|
|
let penalties = mapIt(0 ..< len(state.validators),
|
|
|
|
source_penalties[it] + target_penalties[it] + head_penalties[it] +
|
|
|
|
inactivity_penalties[it])
|
|
|
|
|
|
|
|
(rewards, penalties)
|
2020-06-08 18:41:50 +00:00
|
|
|
|
2020-06-18 08:16:05 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#process_rewards_and_penalties
|
2019-06-28 13:44:44 +00:00
|
|
|
func process_rewards_and_penalties(
|
2019-12-20 16:14:43 +00:00
|
|
|
state: var BeaconState, cache: var StateCache) {.nbench.}=
|
2019-06-28 13:44:44 +00:00
|
|
|
if get_current_epoch(state) == GENESIS_EPOCH:
|
|
|
|
return
|
|
|
|
|
2019-11-08 00:12:24 +00:00
|
|
|
let (rewards, penalties) = get_attestation_deltas(state, cache)
|
2019-10-01 14:40:41 +00:00
|
|
|
|
2019-07-01 09:13:14 +00:00
|
|
|
for i in 0 ..< len(state.validators):
|
2019-11-08 00:12:24 +00:00
|
|
|
increase_balance(state, i.ValidatorIndex, rewards[i])
|
|
|
|
decrease_balance(state, i.ValidatorIndex, penalties[i])
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2019-12-20 16:50:09 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.4/specs/core/0_beacon-chain.md#slashings
|
2020-06-03 05:42:08 +00:00
|
|
|
func process_slashings*(state: var BeaconState, cache: var StateCache) {.nbench.}=
|
2019-06-28 13:44:44 +00:00
|
|
|
let
|
More 0.8.0 updates (#311)
* replace BeaconState.finalized_{epoch,root} with BeaconState.finalized_checkpoint; rename get_delayed_activation_exit_epoch(...) to compute_activation_exit_epoch(...) and mark as 0.8.0; update get_churn_limit(...)/get_validator_churn_limit(...) to 0.8.0; update process_registry_updates(...) to 0.8.0
* update process_crosslinks(...) to 0.8.0; mark compute_start_slot_of_epoch(...) and get_committee_count(...) as 0.8.0
* mark Fork, is_slashable_validator(...), and get_beacon_proposer_index(...) as 0.8.0
* rename LATEST_SLASHED_EXIT_LENGTH to EPOCHS_PER_SLASHINGS_VECTOR; update process_slashings(...) to 0.8.0; remove pointless type conversion warning in get_previous_epoch(...)
* convert remaining references to finalized_epoch to finalized_checkpoint.epoch
* update slash_validator(...) to 0.8.0; mark inital value, Gwei, and time constants as 0.8.0; mark hash(...) and processBlockHeader(...) as 0.8.0
* rename WHISTLEBLOWING_REWARD_QUOTIENT to WHISTLEBLOWER_REWARD_QUOTIENT; rename LATEST_ACTIVE_INDEX_ROOTS_LENGTH to EPOCHS_PER_HISTORICAL_VECTOR (randao will also get merged into this); remove get_active_index_root(...); mark time parameter, signature domain types, and max operations per block constants as 0.8.0; update rewards and penalties constants to 0.8.0
* update is_valid_indexed_attestation(...) to 0.8.0; mark process_slot(...) as 0.8.0
* replace BeaconState.{current,previous}_justified_{epoch,root} with BeaconState.{current,previous}_justified_checkpoint
2019-07-05 08:30:05 +00:00
|
|
|
epoch = get_current_epoch(state)
|
2020-06-03 05:42:08 +00:00
|
|
|
total_balance = get_total_active_balance(state, cache)
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2019-07-01 09:13:14 +00:00
|
|
|
for index, validator in state.validators:
|
More 0.8.0 updates (#311)
* replace BeaconState.finalized_{epoch,root} with BeaconState.finalized_checkpoint; rename get_delayed_activation_exit_epoch(...) to compute_activation_exit_epoch(...) and mark as 0.8.0; update get_churn_limit(...)/get_validator_churn_limit(...) to 0.8.0; update process_registry_updates(...) to 0.8.0
* update process_crosslinks(...) to 0.8.0; mark compute_start_slot_of_epoch(...) and get_committee_count(...) as 0.8.0
* mark Fork, is_slashable_validator(...), and get_beacon_proposer_index(...) as 0.8.0
* rename LATEST_SLASHED_EXIT_LENGTH to EPOCHS_PER_SLASHINGS_VECTOR; update process_slashings(...) to 0.8.0; remove pointless type conversion warning in get_previous_epoch(...)
* convert remaining references to finalized_epoch to finalized_checkpoint.epoch
* update slash_validator(...) to 0.8.0; mark inital value, Gwei, and time constants as 0.8.0; mark hash(...) and processBlockHeader(...) as 0.8.0
* rename WHISTLEBLOWING_REWARD_QUOTIENT to WHISTLEBLOWER_REWARD_QUOTIENT; rename LATEST_ACTIVE_INDEX_ROOTS_LENGTH to EPOCHS_PER_HISTORICAL_VECTOR (randao will also get merged into this); remove get_active_index_root(...); mark time parameter, signature domain types, and max operations per block constants as 0.8.0; update rewards and penalties constants to 0.8.0
* update is_valid_indexed_attestation(...) to 0.8.0; mark process_slot(...) as 0.8.0
* replace BeaconState.{current,previous}_justified_{epoch,root} with BeaconState.{current,previous}_justified_checkpoint
2019-07-05 08:30:05 +00:00
|
|
|
if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR div 2 ==
|
|
|
|
validator.withdrawable_epoch:
|
2019-09-03 03:12:09 +00:00
|
|
|
let increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty
|
|
|
|
# numerator to avoid uint64 overflow
|
|
|
|
let penalty_numerator =
|
|
|
|
validator.effective_balance div increment *
|
|
|
|
min(sum(state.slashings) * 3, total_balance)
|
|
|
|
let penalty = penalty_numerator div total_balance * increment
|
2019-06-28 13:44:44 +00:00
|
|
|
decrease_balance(state, index.ValidatorIndex, penalty)
|
|
|
|
|
2020-06-15 09:38:05 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#final-updates
|
2019-12-20 16:14:43 +00:00
|
|
|
func process_final_updates*(state: var BeaconState) {.nbench.}=
|
2019-06-28 13:44:44 +00:00
|
|
|
let
|
|
|
|
current_epoch = get_current_epoch(state)
|
|
|
|
next_epoch = current_epoch + 1
|
|
|
|
|
|
|
|
# Reset eth1 data votes
|
2020-03-14 21:54:45 +00:00
|
|
|
if next_epoch mod EPOCHS_PER_ETH1_VOTING_PERIOD == 0:
|
2020-05-27 11:36:02 +00:00
|
|
|
state.eth1_data_votes = default(type state.eth1_data_votes)
|
2019-06-28 13:44:44 +00:00
|
|
|
|
|
|
|
# Update effective balances with hysteresis
|
2019-07-01 09:13:14 +00:00
|
|
|
for index, validator in state.validators:
|
2019-06-28 13:44:44 +00:00
|
|
|
let balance = state.balances[index]
|
2020-03-14 21:54:45 +00:00
|
|
|
const
|
|
|
|
HYSTERESIS_INCREMENT =
|
|
|
|
EFFECTIVE_BALANCE_INCREMENT div HYSTERESIS_QUOTIENT
|
|
|
|
DOWNWARD_THRESHOLD =
|
|
|
|
HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER
|
|
|
|
UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER
|
|
|
|
if balance + DOWNWARD_THRESHOLD < validator.effective_balance or
|
|
|
|
validator.effective_balance + UPWARD_THRESHOLD < balance:
|
2019-07-01 09:13:14 +00:00
|
|
|
state.validators[index].effective_balance =
|
2019-06-28 13:44:44 +00:00
|
|
|
min(
|
|
|
|
balance - balance mod EFFECTIVE_BALANCE_INCREMENT,
|
|
|
|
MAX_EFFECTIVE_BALANCE)
|
|
|
|
|
2019-07-10 12:23:02 +00:00
|
|
|
# Reset slashings
|
2020-06-12 16:43:20 +00:00
|
|
|
state.slashings[int(next_epoch mod EPOCHS_PER_SLASHINGS_VECTOR)] = 0.Gwei
|
2019-06-28 13:44:44 +00:00
|
|
|
|
|
|
|
# Set randao mix
|
2019-09-04 13:57:18 +00:00
|
|
|
state.randao_mixes[next_epoch mod EPOCHS_PER_HISTORICAL_VECTOR] =
|
2019-06-28 13:44:44 +00:00
|
|
|
get_randao_mix(state, current_epoch)
|
|
|
|
|
|
|
|
# Set historical root accumulator
|
|
|
|
if next_epoch mod (SLOTS_PER_HISTORICAL_ROOT div SLOTS_PER_EPOCH).uint64 == 0:
|
2020-04-23 11:39:38 +00:00
|
|
|
# Equivalent to hash_tree_root(foo: HistoricalBatch), but without using
|
|
|
|
# significant additional stack or heap.
|
2020-06-12 19:37:28 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#historicalbatch
|
2020-04-23 11:39:38 +00:00
|
|
|
# In response to https://github.com/status-im/nim-beacon-chain/issues/921
|
|
|
|
state.historical_roots.add hash_tree_root(
|
|
|
|
[hash_tree_root(state.block_roots), hash_tree_root(state.state_roots)])
|
2019-06-28 13:44:44 +00:00
|
|
|
|
|
|
|
# Rotate current/previous epoch attestations
|
|
|
|
state.previous_epoch_attestations = state.current_epoch_attestations
|
2020-05-27 11:36:02 +00:00
|
|
|
state.current_epoch_attestations = default(type state.current_epoch_attestations)
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2020-06-12 19:37:28 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#epoch-processing
|
2020-06-01 07:44:50 +00:00
|
|
|
proc process_epoch*(state: var BeaconState, updateFlags: UpdateFlags,
|
|
|
|
per_epoch_cache: var StateCache) {.nbench.} =
|
2020-05-11 06:25:49 +00:00
|
|
|
let currentEpoch = get_current_epoch(state)
|
2019-08-19 16:41:13 +00:00
|
|
|
trace "process_epoch",
|
2020-05-11 06:25:49 +00:00
|
|
|
current_epoch = currentEpoch
|
2019-08-14 08:56:32 +00:00
|
|
|
|
2020-06-12 19:37:28 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#justification-and-finalization
|
2020-05-09 12:43:15 +00:00
|
|
|
process_justification_and_finalization(state, per_epoch_cache, updateFlags)
|
|
|
|
|
|
|
|
# state.slot hasn't been incremented yet.
|
2020-05-11 06:25:49 +00:00
|
|
|
if verifyFinalization in updateFlags and currentEpoch >= 3:
|
2020-05-09 12:43:15 +00:00
|
|
|
# Rule 2/3/4 finalization results in the most pessimal case. The other
|
|
|
|
# three finalization rules finalize more quickly as long as the any of
|
|
|
|
# the finalization rules triggered.
|
2020-05-11 06:25:49 +00:00
|
|
|
doAssert state.finalized_checkpoint.epoch + 3 >= currentEpoch
|
2019-08-14 08:56:32 +00:00
|
|
|
|
2020-06-12 19:37:28 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
2019-06-28 13:44:44 +00:00
|
|
|
process_rewards_and_penalties(state, per_epoch_cache)
|
|
|
|
|
2020-06-12 19:37:28 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#registry-updates
|
2020-06-03 05:42:08 +00:00
|
|
|
process_registry_updates(state, per_epoch_cache)
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2020-06-12 19:37:28 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#slashings
|
2020-06-03 05:42:08 +00:00
|
|
|
process_slashings(state, per_epoch_cache)
|
2019-06-28 13:44:44 +00:00
|
|
|
|
2020-06-12 19:37:28 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#final-updates
|
2019-06-28 13:44:44 +00:00
|
|
|
process_final_updates(state)
|
2019-07-15 21:10:40 +00:00
|
|
|
|
2019-10-03 10:36:31 +00:00
|
|
|
# Once per epoch metrics
|
2020-05-11 06:25:49 +00:00
|
|
|
beacon_current_epoch.set(currentEpoch.int64)
|
2020-05-08 23:13:57 +00:00
|
|
|
beacon_finalized_epoch.set(state.finalized_checkpoint.epoch.int64)
|
|
|
|
beacon_finalized_root.set(state.finalized_checkpoint.root.toGaugeValue)
|
|
|
|
beacon_current_justified_epoch.set(
|
|
|
|
state.current_justified_checkpoint.epoch.int64)
|
|
|
|
beacon_current_justified_root.set(
|
|
|
|
state.current_justified_checkpoint.root.toGaugeValue)
|
|
|
|
beacon_previous_justified_epoch.set(
|
|
|
|
state.previous_justified_checkpoint.epoch.int64)
|
|
|
|
beacon_previous_justified_root.set(
|
|
|
|
state.previous_justified_checkpoint.root.toGaugeValue)
|