remove the scaffolding get_empty_per_epoch_cache() calls and the pointless construction and return/discarding of zero-filled arrays
This commit is contained in:
parent
5582ecc1f4
commit
35f67cec60
|
@ -15,19 +15,12 @@
|
||||||
# The entry point is `process_epoch` which is at the bottom of this file.
|
# The entry point is `process_epoch` which is at the bottom of this file.
|
||||||
#
|
#
|
||||||
# General notes about the code (TODO):
|
# General notes about the code (TODO):
|
||||||
# * It's inefficient - we quadratically copy, allocate and iterate when there
|
|
||||||
# are faster options
|
|
||||||
# * Weird styling - the sections taken from the spec use python styling while
|
# * Weird styling - the sections taken from the spec use python styling while
|
||||||
# the others use NEP-1 - helps grepping identifiers in spec
|
# the others use NEP-1 - helps grepping identifiers in spec
|
||||||
# * We mix procedural and functional styles for no good reason, except that the
|
# * We mix procedural and functional styles for no good reason, except that the
|
||||||
# spec does so also.
|
# spec does so also.
|
||||||
# * There are likely lots of bugs.
|
|
||||||
# * For indices, we get a mix of uint64, ValidatorIndex and int - this is currently
|
# * For indices, we get a mix of uint64, ValidatorIndex and int - this is currently
|
||||||
# swept under the rug with casts
|
# swept under the rug with casts
|
||||||
# * The spec uses uint64 for data types, but functions in the spec often assume
|
|
||||||
# signed bigint semantics - under- and overflows ensue
|
|
||||||
# * Sane error handling is missing in most cases (yay, we'll get the chance to
|
|
||||||
# debate exceptions again!)
|
|
||||||
# When updating the code, add TODO sections to mark where there are clear
|
# 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
|
# improvements to be made - other than that, keep things similar to spec for
|
||||||
# now.
|
# now.
|
||||||
|
@ -72,7 +65,7 @@ func get_total_active_balance*(state: BeaconState, cache: var StateCache): Gwei
|
||||||
# Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei
|
# Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei
|
||||||
# minimum to avoid divisions by zero.
|
# minimum to avoid divisions by zero.
|
||||||
|
|
||||||
# TODO refactor get_epoch_per_epoch_cache() not to be, well, empty, so can
|
# 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
|
# avoid this ever refilling, and raiseAssert, and get rid of var
|
||||||
let
|
let
|
||||||
epoch = state.slot.compute_epoch_at_slot
|
epoch = state.slot.compute_epoch_at_slot
|
||||||
|
@ -261,10 +254,8 @@ func get_base_reward(state: BeaconState, index: ValidatorIndex,
|
||||||
effective_balance * BASE_REWARD_FACTOR div
|
effective_balance * BASE_REWARD_FACTOR div
|
||||||
integer_squareroot(total_balance) div BASE_REWARDS_PER_EPOCH
|
integer_squareroot(total_balance) div BASE_REWARDS_PER_EPOCH
|
||||||
|
|
||||||
func get_proposer_reward(state: BeaconState, attesting_index: ValidatorIndex):
|
func get_proposer_reward(state: BeaconState, attesting_index: ValidatorIndex,
|
||||||
Gwei =
|
cache: var StateCache): Gwei =
|
||||||
# TODO REMOVEMEREMOVEME
|
|
||||||
var cache = get_empty_per_epoch_cache()
|
|
||||||
let total_balance = get_total_active_balance(state, cache)
|
let total_balance = get_total_active_balance(state, cache)
|
||||||
|
|
||||||
get_base_reward(state, attesting_index, total_balance) div PROPOSER_REWARD_QUOTIENT
|
get_base_reward(state, attesting_index, total_balance) div PROPOSER_REWARD_QUOTIENT
|
||||||
|
@ -285,14 +276,14 @@ func get_eligible_validator_indices(state: BeaconState): seq[ValidatorIndex] =
|
||||||
result.add idx.ValidatorIndex
|
result.add idx.ValidatorIndex
|
||||||
|
|
||||||
func get_attestation_component_deltas(state: BeaconState,
|
func get_attestation_component_deltas(state: BeaconState,
|
||||||
attestations: seq[PendingAttestation]
|
attestations: seq[PendingAttestation],
|
||||||
): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
cache: var StateCache,
|
||||||
|
): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||||
# Helper with shared logic for use by get source, target, and head deltas
|
# Helper with shared logic for use by get source, target, and head deltas
|
||||||
# functions
|
# functions
|
||||||
var
|
var
|
||||||
rewards = repeat(0'u64, len(state.validators))
|
rewards = repeat(0'u64, len(state.validators))
|
||||||
penalties = repeat(0'u64, len(state.validators))
|
penalties = repeat(0'u64, len(state.validators))
|
||||||
cache = get_empty_per_epoch_cache() # REMOVEMEBEFOREMERGE
|
|
||||||
let
|
let
|
||||||
total_balance = get_total_active_balance(state, cache)
|
total_balance = get_total_active_balance(state, cache)
|
||||||
unslashed_attesting_indices =
|
unslashed_attesting_indices =
|
||||||
|
@ -316,31 +307,34 @@ func get_attestation_component_deltas(state: BeaconState,
|
||||||
(rewards, penalties)
|
(rewards, penalties)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#components-of-attestation-deltas
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#components-of-attestation-deltas
|
||||||
func get_source_deltas(state: BeaconState): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
func get_source_deltas(state: BeaconState, cache: var StateCache):
|
||||||
|
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||||
# Return attester micro-rewards/penalties for source-vote for each validator.
|
# Return attester micro-rewards/penalties for source-vote for each validator.
|
||||||
let matching_source_attestations =
|
let matching_source_attestations =
|
||||||
get_matching_source_attestations(state, get_previous_epoch(state))
|
get_matching_source_attestations(state, get_previous_epoch(state))
|
||||||
get_attestation_component_deltas(state, matching_source_attestations)
|
get_attestation_component_deltas(state, matching_source_attestations, cache)
|
||||||
|
|
||||||
func get_target_deltas(state: BeaconState): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
func get_target_deltas(state: BeaconState, cache: var StateCache):
|
||||||
|
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||||
# Return attester micro-rewards/penalties for target-vote for each validator.
|
# Return attester micro-rewards/penalties for target-vote for each validator.
|
||||||
let matching_target_attestations =
|
let matching_target_attestations =
|
||||||
get_matching_target_attestations(state, get_previous_epoch(state))
|
get_matching_target_attestations(state, get_previous_epoch(state))
|
||||||
get_attestation_component_deltas(state, matching_target_attestations)
|
get_attestation_component_deltas(state, matching_target_attestations, cache)
|
||||||
|
|
||||||
func get_head_deltas(state: BeaconState): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
func get_head_deltas(state: BeaconState, cache: var StateCache):
|
||||||
|
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||||
# Return attester micro-rewards/penalties for head-vote for each validator.
|
# Return attester micro-rewards/penalties for head-vote for each validator.
|
||||||
let matching_head_attestations =
|
let matching_head_attestations =
|
||||||
get_matching_head_attestations(state, get_previous_epoch(state))
|
get_matching_head_attestations(state, get_previous_epoch(state))
|
||||||
get_attestation_component_deltas(state, matching_head_attestations)
|
get_attestation_component_deltas(state, matching_head_attestations, cache)
|
||||||
|
|
||||||
func get_inclusion_delay_deltas(state: BeaconState): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
func get_inclusion_delay_deltas(state: BeaconState, cache: var StateCache):
|
||||||
|
seq[Gwei] =
|
||||||
# Return proposer and inclusion delay micro-rewards/penalties for each validator.
|
# Return proposer and inclusion delay micro-rewards/penalties for each validator.
|
||||||
var rewards = repeat(0'u64, len(state.validators))
|
var rewards = repeat(0'u64, len(state.validators))
|
||||||
let matching_source_attestations =
|
let matching_source_attestations =
|
||||||
get_matching_source_attestations(state, get_previous_epoch(state))
|
get_matching_source_attestations(state, get_previous_epoch(state))
|
||||||
# TODO some fun big-O's here; see get_attestation_deltas_old() for linear version
|
# TODO some fun big-O's here; see get_attestation_deltas_old() for linear version
|
||||||
var cache = get_empty_per_epoch_cache()
|
|
||||||
let total_balance = get_total_active_balance(state, cache)
|
let total_balance = get_total_active_balance(state, cache)
|
||||||
for index in get_unslashed_attesting_indices(state, matching_source_attestations, cache):
|
for index in get_unslashed_attesting_indices(state, matching_source_attestations, cache):
|
||||||
# this is bad; it's to be just-correct-enough to check if v0.12.1 beacon chain is working
|
# this is bad; it's to be just-correct-enough to check if v0.12.1 beacon chain is working
|
||||||
|
@ -348,19 +342,20 @@ func get_inclusion_delay_deltas(state: BeaconState): tuple[a: seq[Gwei], b: seq[
|
||||||
let admi = minIndex(mapIt(attestations, it.inclusion_delay))
|
let admi = minIndex(mapIt(attestations, it.inclusion_delay))
|
||||||
let attestation = attestations[admi]
|
let attestation = attestations[admi]
|
||||||
|
|
||||||
rewards[attestation.proposer_index] += get_proposer_reward(state, index)
|
# TODO remove duplicate calculation of get_proposer_reward()
|
||||||
|
rewards[attestation.proposer_index] += get_proposer_reward(state, index, cache)
|
||||||
let max_attester_reward =
|
let max_attester_reward =
|
||||||
get_base_reward(state, index, total_balance) - get_proposer_reward(state, index)
|
get_base_reward(state, index, total_balance) - get_proposer_reward(state, index, cache)
|
||||||
rewards[index] += Gwei(max_attester_reward div attestation.inclusion_delay)
|
rewards[index] += Gwei(max_attester_reward div attestation.inclusion_delay)
|
||||||
|
|
||||||
# No penalties associated with inclusion delay
|
# No penalties associated with inclusion delay
|
||||||
let penalties = repeat(0'u64, len(state.validators))
|
# Spec constructs both and returns both; this doesn't
|
||||||
(rewards, penalties)
|
rewards
|
||||||
|
|
||||||
func get_inactivity_penalty_deltas(state: BeaconState): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
func get_inactivity_penalty_deltas(state: BeaconState, cache: var StateCache):
|
||||||
|
seq[Gwei] =
|
||||||
# Return inactivity reward/penalty deltas for each validator.
|
# Return inactivity reward/penalty deltas for each validator.
|
||||||
var penalties = repeat(0'u64, len(state.validators))
|
var penalties = repeat(0'u64, len(state.validators))
|
||||||
var cache = get_empty_per_epoch_cache() # REMOVEME
|
|
||||||
let total_balance = get_total_active_balance(state, cache) # DO NOT KEEP RECALCULATING THIS
|
let total_balance = get_total_active_balance(state, cache) # DO NOT KEEP RECALCULATING THIS
|
||||||
if is_in_inactivity_leak(state):
|
if is_in_inactivity_leak(state):
|
||||||
let
|
let
|
||||||
|
@ -372,133 +367,91 @@ func get_inactivity_penalty_deltas(state: BeaconState): tuple[a: seq[Gwei], b: s
|
||||||
for index in get_eligible_validator_indices(state):
|
for index in get_eligible_validator_indices(state):
|
||||||
# If validator is performing optimally this cancels all rewards for a neutral balance
|
# If validator is performing optimally this cancels all rewards for a neutral balance
|
||||||
let base_reward = get_base_reward(state, index, total_balance)
|
let base_reward = get_base_reward(state, index, total_balance)
|
||||||
penalties[index] += Gwei(BASE_REWARDS_PER_EPOCH * base_reward - get_proposer_reward(state, index))
|
penalties[index] +=
|
||||||
|
Gwei(BASE_REWARDS_PER_EPOCH * base_reward -
|
||||||
|
get_proposer_reward(state, index, cache))
|
||||||
if index notin matching_target_attesting_indices:
|
if index notin matching_target_attesting_indices:
|
||||||
let effective_balance = state.validators[index].effective_balance
|
let effective_balance = state.validators[index].effective_balance
|
||||||
penalties[index] +=
|
penalties[index] +=
|
||||||
Gwei(effective_balance * get_finality_delay(state) div INACTIVITY_PENALTY_QUOTIENT)
|
Gwei(effective_balance * get_finality_delay(state) div INACTIVITY_PENALTY_QUOTIENT)
|
||||||
|
|
||||||
# No rewards associated with inactivity penalties
|
# No rewards associated with inactivity penalties
|
||||||
let rewards = repeat(0'u64, len(state.validators))
|
# Spec constructs rewards anyway; this doesn't
|
||||||
(rewards, penalties)
|
penalties
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#get_attestation_deltas
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#get_attestation_deltas
|
||||||
func get_attestation_deltas_new(state: BeaconState, cache: var StateCache): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
func get_attestation_deltas(state: BeaconState, cache: var StateCache): tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||||
# Return attestation reward/penalty deltas for each validator.
|
# Return attestation reward/penalty deltas for each validator.
|
||||||
let
|
let
|
||||||
(source_rewards, source_penalties) = get_source_deltas(state)
|
(source_rewards, source_penalties) = get_source_deltas(state, cache)
|
||||||
(target_rewards, target_penalties) = get_target_deltas(state)
|
(target_rewards, target_penalties) = get_target_deltas(state, cache)
|
||||||
(head_rewards, head_penalties) = get_head_deltas(state)
|
(head_rewards, head_penalties) = get_head_deltas(state, cache)
|
||||||
(inclusion_delay_rewards, _) = get_inclusion_delay_deltas(state)
|
inclusion_delay_rewards = get_inclusion_delay_deltas(state, cache)
|
||||||
(_, inactivity_penalties) = get_inactivity_penalty_deltas(state)
|
inactivity_penalties = get_inactivity_penalty_deltas(state, cache)
|
||||||
|
|
||||||
let rewards = mapIt(0 ..< len(state.validators),
|
let rewards = mapIt(0 ..< len(state.validators),
|
||||||
source_rewards[it] + target_rewards[it] + head_rewards[it] + inclusion_delay_rewards[it])
|
source_rewards[it] + target_rewards[it] + head_rewards[it] +
|
||||||
|
inclusion_delay_rewards[it])
|
||||||
|
|
||||||
let penalties = mapIt(0 ..< len(state.validators),
|
let penalties = mapIt(0 ..< len(state.validators),
|
||||||
source_penalties[it] + target_penalties[it] + head_penalties[it] + inactivity_penalties[it])
|
source_penalties[it] + target_penalties[it] + head_penalties[it] +
|
||||||
|
inactivity_penalties[it])
|
||||||
|
|
||||||
(rewards, penalties)
|
(rewards, penalties)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
|
||||||
func get_attestation_deltas_old(state: BeaconState, stateCache: var StateCache):
|
func get_attestation_deltas_old(state: BeaconState, stateCache: var StateCache):
|
||||||
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||||
let
|
when false:
|
||||||
previous_epoch = get_previous_epoch(state)
|
## This depends on matching_source_attestations being an indexable seq, not a
|
||||||
total_balance = get_total_active_balance(state, stateCache)
|
## set, hash table, etc.
|
||||||
var
|
let source_attestation_attesting_indices =
|
||||||
rewards = repeat(0'u64, len(state.validators))
|
mapIt(
|
||||||
penalties = repeat(0'u64, len(state.validators))
|
matching_source_attestations,
|
||||||
eligible_validator_indices : seq[ValidatorIndex] = @[]
|
get_attesting_indices(state, it.data, it.aggregation_bits, stateCache))
|
||||||
|
|
||||||
for index, v in state.validators:
|
for index in get_unslashed_attesting_indices(
|
||||||
if is_active_validator(v, previous_epoch) or
|
state, matching_source_attestations, stateCache):
|
||||||
(v.slashed and previous_epoch + 1 < v.withdrawable_epoch):
|
# Translation of attestation = min([...])
|
||||||
eligible_validator_indices.add index.ValidatorIndex
|
doAssert matching_source_attestations.len > 0
|
||||||
|
|
||||||
# Micro-incentives for matching FFG source, FFG target, and head
|
# Start by filtering the right attestations
|
||||||
let
|
var filtered_matching_source_attestations: seq[PendingAttestation]
|
||||||
matching_source_attestations =
|
|
||||||
get_matching_source_attestations(state, previous_epoch)
|
|
||||||
matching_target_attestations =
|
|
||||||
get_matching_target_attestations(state, previous_epoch)
|
|
||||||
matching_head_attestations =
|
|
||||||
get_matching_head_attestations(state, previous_epoch)
|
|
||||||
|
|
||||||
for attestations in
|
for source_attestation_index, a in matching_source_attestations:
|
||||||
[matching_source_attestations, matching_target_attestations,
|
if index notin
|
||||||
matching_head_attestations]:
|
source_attestation_attesting_indices[source_attestation_index]:
|
||||||
let
|
continue
|
||||||
unslashed_attesting_indices =
|
filtered_matching_source_attestations.add a
|
||||||
get_unslashed_attesting_indices(state, attestations, stateCache)
|
|
||||||
attesting_balance = get_total_balance(state, unslashed_attesting_indices)
|
|
||||||
for index in eligible_validator_indices:
|
|
||||||
if index in unslashed_attesting_indices:
|
|
||||||
# Factored out from balance totals to avoid uint64 overflow
|
|
||||||
const increment = EFFECTIVE_BALANCE_INCREMENT
|
|
||||||
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)
|
|
||||||
|
|
||||||
# Proposer and inclusion delay micro-rewards
|
# The first filtered attestation serves as min until we find something
|
||||||
## This depends on matching_source_attestations being an indexable seq, not a
|
# better
|
||||||
## set, hash table, etc.
|
var attestation = filtered_matching_source_attestations[0]
|
||||||
let source_attestation_attesting_indices =
|
for source_attestation_index, a in filtered_matching_source_attestations:
|
||||||
mapIt(
|
if a.inclusion_delay < attestation.inclusion_delay:
|
||||||
matching_source_attestations,
|
attestation = a
|
||||||
get_attesting_indices(state, it.data, it.aggregation_bits, stateCache))
|
|
||||||
|
|
||||||
for index in get_unslashed_attesting_indices(
|
let
|
||||||
state, matching_source_attestations, stateCache):
|
base_reward = get_base_reward(state, index, total_balance)
|
||||||
# Translation of attestation = min([...])
|
proposer_reward = (base_reward div PROPOSER_REWARD_QUOTIENT).Gwei
|
||||||
doAssert matching_source_attestations.len > 0
|
|
||||||
|
|
||||||
# Start by filtering the right attestations
|
rewards[attestation.proposer_index.int] += proposer_reward
|
||||||
var filtered_matching_source_attestations: seq[PendingAttestation]
|
let max_attester_reward = base_reward - proposer_reward
|
||||||
|
|
||||||
for source_attestation_index, a in matching_source_attestations:
|
rewards[index] += max_attester_reward div attestation.inclusion_delay
|
||||||
if index notin
|
|
||||||
source_attestation_attesting_indices[source_attestation_index]:
|
|
||||||
continue
|
|
||||||
filtered_matching_source_attestations.add a
|
|
||||||
|
|
||||||
# The first filtered attestation serves as min until we find something
|
# Inactivity penalty
|
||||||
# better
|
let finality_delay = previous_epoch - state.finalized_checkpoint.epoch
|
||||||
var attestation = filtered_matching_source_attestations[0]
|
if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY:
|
||||||
for source_attestation_index, a in filtered_matching_source_attestations:
|
let matching_target_attesting_indices =
|
||||||
if a.inclusion_delay < attestation.inclusion_delay:
|
get_unslashed_attesting_indices(
|
||||||
attestation = a
|
state, matching_target_attestations, stateCache)
|
||||||
|
for index in eligible_validator_indices:
|
||||||
let
|
|
||||||
base_reward = get_base_reward(state, index, total_balance)
|
|
||||||
proposer_reward = (base_reward div PROPOSER_REWARD_QUOTIENT).Gwei
|
|
||||||
|
|
||||||
rewards[attestation.proposer_index.int] += proposer_reward
|
|
||||||
let max_attester_reward = base_reward - proposer_reward
|
|
||||||
|
|
||||||
rewards[index] += max_attester_reward div attestation.inclusion_delay
|
|
||||||
|
|
||||||
# Inactivity penalty
|
|
||||||
let finality_delay = previous_epoch - state.finalized_checkpoint.epoch
|
|
||||||
if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY:
|
|
||||||
let matching_target_attesting_indices =
|
|
||||||
get_unslashed_attesting_indices(
|
|
||||||
state, matching_target_attestations, stateCache)
|
|
||||||
for index in eligible_validator_indices:
|
|
||||||
penalties[index] +=
|
|
||||||
BASE_REWARDS_PER_EPOCH.uint64 * get_base_reward(state, index, total_balance)
|
|
||||||
if index notin matching_target_attesting_indices:
|
|
||||||
penalties[index] +=
|
penalties[index] +=
|
||||||
state.validators[index].effective_balance *
|
BASE_REWARDS_PER_EPOCH.uint64 * get_base_reward(state, index, total_balance)
|
||||||
finality_delay div INACTIVITY_PENALTY_QUOTIENT
|
if index notin matching_target_attesting_indices:
|
||||||
|
penalties[index] +=
|
||||||
(rewards, penalties)
|
state.validators[index].effective_balance *
|
||||||
|
finality_delay div INACTIVITY_PENALTY_QUOTIENT
|
||||||
func get_attestation_deltas(state: BeaconState, stateCache: var StateCache):
|
|
||||||
tuple[a: seq[Gwei], b: seq[Gwei]] {.nbench.}=
|
|
||||||
get_attestation_deltas_new(state, stateCache)
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
||||||
func process_rewards_and_penalties(
|
func process_rewards_and_penalties(
|
||||||
|
|
Loading…
Reference in New Issue