mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-17 00:47:03 +00:00
More 0.6.1 updates (#274)
* implement get_churn_limit from 0.6.1; update initiate_validator_exit and get_previous_epoch to 0.6.1; remove obsoleted/non-spec reduce_balance, replaced by decrease_balance; mark BeaconBlock as 0.6.1 * rename get_block_root to get_block_root_at_slot and introduce epoch-oriented get_block_root; implement get_attestation_slot, get_matching_source_attestations, get_matching_target_attestations, get_matching_head_attestations, 0.6.1 get_attesting_indices, get_unslashed_attesting_indices, get_attestation_deltas, process_rewards_and_penalties; rm get_inactivity_penalty * update Validator and processVoluntaryExits to 0.6.1; rm obsolete inclusion_slots/inclusion_distances * rm removed activate_validator; mark DepositData, misc values, Gwei values, Randao processing, state caching as 0.6.1; update GENESIS_SLOT to 64, since it doesn't quite yet work at 0 * mark BeaconBlockHeader as 0.6.1; update BeaconBlockBody to 0.6.1; rename WHISTLEBLOWER_REWARD_QUOTIENT to WHISTLEBLOWING_REWARD_QUOTIENT; update slash_validator to 0.6.1 * implement 0.6.2 is_slashable_validator; update processProposerSlashings to 0.6.2; mark state caching as 0.6.2 * mark get_total_active_balance and process_slashings as 0.6.2; update get_base_reward to 0.6.2 * rm prepare_validator_for_withdrawal, process_exit_queue; mark mainnet misc constants as 0.6.2; update process block header to 0.6.2 * address mratsim's code review comment
This commit is contained in:
parent
f679677995
commit
6a47ca3c46
@ -497,7 +497,6 @@ proc handleProposal(node: BeaconNode, head: BlockRef, slot: Slot):
|
||||
# revisit this - we should be able to advance behind
|
||||
node.blockPool.withState(node.stateCache, BlockSlot(blck: head, slot: slot)):
|
||||
let
|
||||
# TODO this probably isn't correct, check re blob/v0.5.1
|
||||
proposerIdx = get_beacon_proposer_index(state)
|
||||
validator = node.getAttachedValidator(state, proposerIdx)
|
||||
|
||||
|
@ -120,27 +120,43 @@ func get_delayed_activation_exit_epoch*(epoch: Epoch): Epoch =
|
||||
## takes effect.
|
||||
epoch + 1 + ACTIVATION_EXIT_DELAY
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#activate_validator
|
||||
func activate_validator(state: var BeaconState,
|
||||
index: ValidatorIndex,
|
||||
is_genesis: bool) =
|
||||
## Activate the validator with the given ``index``.
|
||||
## Note that this function mutates ``state``.
|
||||
let validator = addr state.validator_registry[index]
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_churn_limit
|
||||
func get_churn_limit(state: BeaconState): uint64 =
|
||||
max(
|
||||
MIN_PER_EPOCH_CHURN_LIMIT,
|
||||
len(get_active_validator_indices(state, get_current_epoch(state))) div
|
||||
CHURN_LIMIT_QUOTIENT
|
||||
).uint64
|
||||
|
||||
validator.activation_epoch =
|
||||
if is_genesis:
|
||||
GENESIS_EPOCH
|
||||
else:
|
||||
get_delayed_activation_exit_epoch(get_current_epoch(state))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#initiate_validator_exit
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#initiate_validator_exit
|
||||
func initiate_validator_exit*(state: var BeaconState,
|
||||
index: ValidatorIndex) =
|
||||
## Initiate exit for the validator with the given ``index``.
|
||||
## Note that this function mutates ``state``.
|
||||
var validator = addr state.validator_registry[index]
|
||||
validator.initiated_exit = true
|
||||
# Initiate the validator of the given ``index``.
|
||||
|
||||
# Return if validator already initiated exit
|
||||
let validator = addr state.validator_registry[index]
|
||||
if validator.exit_epoch != FAR_FUTURE_EPOCH:
|
||||
return
|
||||
|
||||
# Compute exit queue epoch
|
||||
let exit_epochs = mapIt(
|
||||
filterIt(state.validator_registry, it.exit_epoch != FAR_FUTURE_EPOCH),
|
||||
it.exit_epoch)
|
||||
var exit_queue_epoch =
|
||||
max(max(exit_epochs),
|
||||
get_delayed_activation_exit_epoch(get_current_epoch(state)))
|
||||
let exit_queue_churn = foldl(
|
||||
state.validator_registry,
|
||||
a + (if b.exit_epoch == exit_queue_epoch: 1'u64 else: 0'u64),
|
||||
0'u64)
|
||||
|
||||
if exit_queue_churn >= get_churn_limit(state):
|
||||
exit_queue_epoch += 1
|
||||
|
||||
# Set validator exit epoch and withdrawable epoch
|
||||
validator.exit_epoch = exit_queue_epoch
|
||||
validator.withdrawable_epoch =
|
||||
validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#exit_validator
|
||||
func exit_validator*(state: var BeaconState,
|
||||
@ -159,37 +175,28 @@ func exit_validator*(state: var BeaconState,
|
||||
|
||||
validator.exit_epoch = delayed_activation_exit_epoch
|
||||
|
||||
func reduce_balance*(balance: var uint64, amount: uint64) =
|
||||
# Not in spec, but useful to avoid underflow.
|
||||
balance -= min(amount, balance)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#slash_validator
|
||||
func slash_validator*(state: var BeaconState, index: ValidatorIndex) =
|
||||
## Slash the validator with index ``index``.
|
||||
## Note that this function mutates ``state``.
|
||||
|
||||
let validator = addr state.validator_registry[index]
|
||||
doAssert state.slot < get_epoch_start_slot(validator.withdrawable_epoch) ##\
|
||||
## [TO BE REMOVED IN PHASE 2]
|
||||
|
||||
exit_validator(state, index)
|
||||
state.latest_slashed_balances[
|
||||
(get_current_epoch(state) mod LATEST_SLASHED_EXIT_LENGTH).int
|
||||
] += get_effective_balance(state, index)
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#slash_validator
|
||||
func slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex) =
|
||||
# Slash the validator with index ``index``.
|
||||
let current_epoch = get_current_epoch(state)
|
||||
initiate_validator_exit(state, slashed_index)
|
||||
state.validator_registry[slashed_index].slashed = true
|
||||
state.validator_registry[slashed_index].withdrawable_epoch =
|
||||
current_epoch + LATEST_SLASHED_EXIT_LENGTH
|
||||
let slashed_balance =
|
||||
state.validator_registry[slashed_index].effective_balance
|
||||
state.latest_slashed_balances[current_epoch mod LATEST_SLASHED_EXIT_LENGTH] +=
|
||||
slashed_balance
|
||||
|
||||
let
|
||||
whistleblower_index = get_beacon_proposer_index(state)
|
||||
whistleblower_reward = get_effective_balance(state, index) div
|
||||
WHISTLEBLOWER_REWARD_QUOTIENT
|
||||
|
||||
## TODO here and elsewhere, if reduce_balance can't reduce balance by full
|
||||
## whistleblower_reward (to prevent underflow) should increase be full? It
|
||||
## seems wrong for the amounts to differ.
|
||||
state.balances[whistleblower_index] += whistleblower_reward
|
||||
reduce_balance(state.balances[index], whistleblower_reward)
|
||||
validator.slashed = true
|
||||
validator.withdrawable_epoch =
|
||||
get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH
|
||||
proposer_index = get_beacon_proposer_index(state)
|
||||
whistleblower_index = proposer_index
|
||||
whistleblowing_reward = slashed_balance div WHISTLEBLOWING_REWARD_QUOTIENT
|
||||
proposer_reward = whistleblowing_reward div PROPOSER_REWARD_QUOTIENT
|
||||
increase_balance(state, proposer_index, proposer_reward)
|
||||
increase_balance(
|
||||
state, whistleblower_index, whistleblowing_reward - proposer_reward)
|
||||
decrease_balance(state, slashed_index, whistleblowing_reward)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#get_temporary_block_header
|
||||
func get_temporary_block_header*(blck: BeaconBlock): BeaconBlockHeader =
|
||||
@ -284,9 +291,10 @@ func get_genesis_beacon_state*(
|
||||
|
||||
# Process genesis activations
|
||||
for validator_index in 0 ..< state.validator_registry.len:
|
||||
let vi = validator_index.ValidatorIndex
|
||||
if get_effective_balance(state, vi) >= MAX_EFFECTIVE_BALANCE:
|
||||
activate_validator(state, vi, true)
|
||||
let validator = addr state.validator_registry[validator_index]
|
||||
if validator.effective_balance >= MAX_EFFECTIVE_BALANCE:
|
||||
validator.activation_eligibility_epoch = GENESIS_EPOCH
|
||||
validator.activation_epoch = GENESIS_EPOCH
|
||||
|
||||
let genesis_active_index_root = hash_tree_root(
|
||||
get_active_validator_indices(state, GENESIS_EPOCH))
|
||||
@ -306,15 +314,37 @@ func get_initial_beacon_block*(state: BeaconState): BeaconBlock =
|
||||
# initialized to default values.
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#get_block_root
|
||||
func get_block_root*(state: BeaconState,
|
||||
slot: Slot): Eth2Digest =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_attestation_slot
|
||||
func get_attestation_slot*(state: BeaconState,
|
||||
attestation: Attestation|PendingAttestation,
|
||||
committee_count: uint64): Slot =
|
||||
let
|
||||
epoch = attestation.data.target_epoch
|
||||
offset = (attestation.data.shard + SHARD_COUNT -
|
||||
get_epoch_start_shard(state, epoch)) mod SHARD_COUNT
|
||||
result = get_epoch_start_slot(epoch) + offset div (committee_count div SLOTS_PER_EPOCH)
|
||||
|
||||
# This is the slower (O(n)), spec-compatible signature.
|
||||
func get_attestation_slot*(state: BeaconState,
|
||||
attestation: Attestation|PendingAttestation): Slot =
|
||||
let epoch = attestation.data.target_epoch
|
||||
get_attestation_slot(
|
||||
state, attestation, get_epoch_committee_count(state, epoch))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_block_root_at_slot
|
||||
func get_block_root_at_slot*(state: BeaconState,
|
||||
slot: Slot): Eth2Digest =
|
||||
# Return the block root at a recent ``slot``.
|
||||
|
||||
doAssert state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT
|
||||
doAssert slot < state.slot
|
||||
state.latest_block_roots[slot mod SLOTS_PER_HISTORICAL_ROOT]
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_block_root
|
||||
func get_block_root*(state: BeaconState, epoch: Epoch): Eth2Digest =
|
||||
# Return the block root at a recent ``epoch``.
|
||||
get_block_root_at_slot(state, get_epoch_start_slot(epoch))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#get_attestation_participants
|
||||
func get_attestation_participants*(state: BeaconState,
|
||||
attestation_data: AttestationData,
|
||||
@ -443,13 +473,13 @@ func update_validator_registry*(state: var BeaconState) =
|
||||
break
|
||||
|
||||
# Activate validator
|
||||
activate_validator(state, index.ValidatorIndex, false)
|
||||
state.validator_registry[index].activation_eligibility_epoch =
|
||||
get_current_epoch(state)
|
||||
|
||||
# Exit validators within the allowable balance churn
|
||||
balance_churn = 0
|
||||
for index, validator in state.validator_registry:
|
||||
if validator.activation_epoch == FAR_FUTURE_EPOCH and
|
||||
validator.initiated_exit:
|
||||
if validator.activation_epoch == FAR_FUTURE_EPOCH:
|
||||
# Check the balance churn would be within the allowance
|
||||
balance_churn += get_effective_balance(state, index.ValidatorIndex)
|
||||
if balance_churn > max_balance_churn:
|
||||
@ -609,15 +639,6 @@ proc checkAttestation*(
|
||||
|
||||
true
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#prepare_validator_for_withdrawal
|
||||
func prepare_validator_for_withdrawal*(state: var BeaconState, index: ValidatorIndex) =
|
||||
## Set the validator with the given ``index`` as withdrawable
|
||||
## ``MIN_VALIDATOR_WITHDRAWABILITY_DELAY`` after the current epoch.
|
||||
## Note that this function mutates ``state``.
|
||||
var validator = addr state.validator_registry[index]
|
||||
validator.withdrawable_epoch = get_current_epoch(state) +
|
||||
MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
|
||||
proc makeAttestationData*(
|
||||
state: BeaconState, shard: uint64,
|
||||
beacon_block_root: Eth2Digest): AttestationData =
|
||||
@ -629,7 +650,7 @@ proc makeAttestationData*(
|
||||
epoch_start_slot = get_epoch_start_slot(slot_to_epoch(state.slot))
|
||||
target_root =
|
||||
if epoch_start_slot == state.slot: beacon_block_root
|
||||
else: get_block_root(state, epoch_start_slot)
|
||||
else: get_block_root_at_slot(state, epoch_start_slot)
|
||||
|
||||
AttestationData(
|
||||
slot: state.slot,
|
||||
@ -639,5 +660,6 @@ proc makeAttestationData*(
|
||||
crosslink_data_root: Eth2Digest(), # Stub in phase0
|
||||
previous_crosslink: state.latest_crosslinks[shard],
|
||||
source_epoch: state.current_justified_epoch,
|
||||
source_root: state.current_justified_root
|
||||
source_root: state.current_justified_root,
|
||||
target_epoch: slot_to_epoch(epoch_start_slot)
|
||||
)
|
||||
|
@ -53,7 +53,7 @@ else:
|
||||
{.fatal: "Preset \"" & const_preset ".nim\" is not supported.".}
|
||||
|
||||
const
|
||||
SPEC_VERSION* = "0.5.1" ## \
|
||||
SPEC_VERSION* = "0.6.1" ## \
|
||||
## Spec version we're aiming to be compatible with, right now
|
||||
## TODO: improve this scheme once we can negotiate versions in protocol
|
||||
|
||||
@ -68,7 +68,7 @@ const
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#gwei-values
|
||||
|
||||
# TODO remove erstwhile blob/v0.5.0
|
||||
# TODO remove erstwhile blob/v0.6.2
|
||||
FORK_CHOICE_BALANCE_INCREMENT* = 2'u64^0 * 10'u64^9
|
||||
|
||||
# Initial values
|
||||
@ -141,6 +141,7 @@ type
|
||||
# FFG vote
|
||||
source_epoch*: Epoch
|
||||
source_root*: Eth2Digest
|
||||
target_epoch*: Epoch
|
||||
target_root*: Eth2Digest
|
||||
|
||||
# Crosslink vote
|
||||
@ -164,7 +165,7 @@ type
|
||||
data*: DepositData ##\
|
||||
## Data
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#depositdata
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#depositdata
|
||||
DepositData* = object
|
||||
pubkey*: ValidatorPubKey ##\
|
||||
## BLS pubkey
|
||||
@ -212,7 +213,7 @@ type
|
||||
signature*: ValidatorSig ##\
|
||||
## Sender signature
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#beaconblock
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#beaconblock
|
||||
BeaconBlock* = object
|
||||
## For each slot, a proposer is chosen from the validator pool to propose
|
||||
## a new block. Once the block as been proposed, it is transmitted to
|
||||
@ -233,7 +234,7 @@ type
|
||||
signature*: ValidatorSig ##\
|
||||
## Proposer signature
|
||||
|
||||
#https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#beaconblockheader
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#beaconblockheader
|
||||
BeaconBlockHeader* = object
|
||||
slot*: Slot
|
||||
previous_block_root*: Eth2Digest
|
||||
@ -256,10 +257,11 @@ type
|
||||
signature*: ValidatorSig
|
||||
body*: Eth2Digest
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#beaconblockbody
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#beaconblockbody
|
||||
BeaconBlockBody* = object
|
||||
randao_reveal*: ValidatorSig
|
||||
eth1_data*: Eth1Data
|
||||
graffiti*: Eth2Digest
|
||||
proposer_slashings*: seq[ProposerSlashing]
|
||||
attester_slashings*: seq[AttesterSlashing]
|
||||
attestations*: seq[Attestation]
|
||||
@ -321,7 +323,7 @@ type
|
||||
eth1_data_votes*: seq[Eth1Data]
|
||||
deposit_index*: uint64
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#validator
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#validator
|
||||
Validator* = object
|
||||
pubkey*: ValidatorPubKey ##\
|
||||
## BLS public key
|
||||
@ -341,9 +343,6 @@ type
|
||||
withdrawable_epoch*: Epoch ##\
|
||||
## Epoch when validator is eligible to withdraw
|
||||
|
||||
initiated_exit*: bool ##\
|
||||
## Did the validator initiate an exit
|
||||
|
||||
slashed*: bool ##\
|
||||
## Was the validator slashed
|
||||
|
||||
@ -365,7 +364,11 @@ type
|
||||
PendingAttestation* = object
|
||||
aggregation_bitfield*: BitField ## Attester participation bitfield
|
||||
data*: AttestationData ## Attestation data
|
||||
|
||||
# TODO remove
|
||||
inclusion_slot*: Slot ## Inclusion slot
|
||||
|
||||
inclusion_delay*: uint64 ## Inclusion delay
|
||||
proposer_index*: ValidatorIndex ## Proposer index
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.0/specs/core/0_beacon-chain.md#historicalbatch
|
||||
|
@ -26,7 +26,7 @@ type
|
||||
const
|
||||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#misc
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#misc
|
||||
|
||||
SHARD_COUNT* {.intdefine.} = 1024 ##\
|
||||
## Number of shards supported by the network - validators will jump around
|
||||
@ -66,7 +66,7 @@ const
|
||||
|
||||
# Gwei values
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#gwei-values
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#gwei-values
|
||||
|
||||
MIN_DEPOSIT_AMOUNT* = 2'u64^0 * 10'u64^9 ##\
|
||||
## Minimum amounth of ETH that can be deposited in one call - deposits can
|
||||
@ -86,7 +86,7 @@ const
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#initial-values
|
||||
|
||||
GENESIS_FORK_VERSION* = [0'u8, 0'u8, 0'u8, 0'u8]
|
||||
GENESIS_SLOT* = (2'u64^32).Slot
|
||||
GENESIS_SLOT* = 64.Slot
|
||||
FAR_FUTURE_EPOCH* = (not 0'u64).Epoch # 2^64 - 1 in spec
|
||||
BLS_WITHDRAWAL_PREFIX_BYTE* = 0'u8
|
||||
|
||||
@ -150,7 +150,7 @@ const
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.0/specs/core/0_beacon-chain.md#reward-and-penalty-quotients
|
||||
BASE_REWARD_QUOTIENT* = 2'u64^5
|
||||
WHISTLEBLOWER_REWARD_QUOTIENT* = 2'u64^9
|
||||
WHISTLEBLOWING_REWARD_QUOTIENT* = 2'u64^9
|
||||
PROPOSER_REWARD_QUOTIENT* = 2'u64^3
|
||||
INACTIVITY_PENALTY_QUOTIENT* = 2'u64^25
|
||||
MIN_PENALTY_QUOTIENT* = 32 # 2^5
|
||||
|
@ -26,7 +26,7 @@ type
|
||||
const
|
||||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#misc
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#misc
|
||||
|
||||
# Changed
|
||||
SHARD_COUNT* {.intdefine.} = 8
|
||||
@ -51,7 +51,7 @@ const
|
||||
|
||||
# Gwei values
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#gwei-values
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#gwei-values
|
||||
|
||||
# Unchanged
|
||||
MIN_DEPOSIT_AMOUNT* = 2'u64^0 * 10'u64^9
|
||||
@ -65,7 +65,7 @@ const
|
||||
|
||||
# Unchanged
|
||||
GENESIS_FORK_VERSION* = [0'u8, 0'u8, 0'u8, 0'u8]
|
||||
GENESIS_SLOT* = (2'u64^32).Slot
|
||||
GENESIS_SLOT* = 64.Slot
|
||||
FAR_FUTURE_EPOCH* = (not 0'u64).Epoch # 2^64 - 1 in spec
|
||||
BLS_WITHDRAWAL_PREFIX_BYTE* = 0'u8
|
||||
|
||||
@ -109,7 +109,7 @@ const
|
||||
|
||||
# Unchanged
|
||||
BASE_REWARD_QUOTIENT* = 2'u64^5
|
||||
WHISTLEBLOWER_REWARD_QUOTIENT* = 2'u64^9
|
||||
WHISTLEBLOWING_REWARD_QUOTIENT* = 2'u64^9
|
||||
PROPOSER_REWARD_QUOTIENT* = 2'u64^3
|
||||
INACTIVITY_PENALTY_QUOTIENT* = 2'u64^25
|
||||
MIN_PENALTY_QUOTIENT* = 32 # 2^5
|
||||
|
@ -138,17 +138,15 @@ func get_shuffling*(seed: Eth2Digest,
|
||||
result = split(shuffled_seq, committees_per_epoch)
|
||||
doAssert result.len() == committees_per_epoch # what split should do..
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#get_previous_epoch
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_previous_epoch
|
||||
func get_previous_epoch*(state: BeaconState): Epoch =
|
||||
## Return the previous epoch of the given ``state``.
|
||||
# Note: This is allowed to underflow internally (this is why GENESIS_EPOCH != 0)
|
||||
# however when interfacing with peers for example for attestations
|
||||
# this should not underflow.
|
||||
# TODO or not - it causes issues: https://github.com/ethereum/eth2.0-specs/issues/849
|
||||
|
||||
let epoch = get_current_epoch(state)
|
||||
max(GENESIS_EPOCH, epoch - 1) # TODO max here to work around the above issue
|
||||
|
||||
## Return the current epoch if it's genesis epoch.
|
||||
let current_epoch = get_current_epoch(state)
|
||||
if current_epoch > GENESIS_EPOCH:
|
||||
current_epoch - 1
|
||||
else:
|
||||
current_epoch
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#get_crosslink_committees_at_slot
|
||||
func get_crosslink_committees_at_slot*(state: BeaconState, slot: Slot|uint64,
|
||||
@ -258,7 +256,7 @@ func get_shard_delta(state: BeaconState, epoch: Epoch): uint64 =
|
||||
(SHARD_COUNT - SHARD_COUNT div SLOTS_PER_EPOCH).uint64)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_epoch_start_shard
|
||||
func get_epoch_start_shard(state: BeaconState, epoch: Epoch): Shard =
|
||||
func get_epoch_start_shard*(state: BeaconState, epoch: Epoch): Shard =
|
||||
doAssert epoch <= get_current_epoch(state) + 1
|
||||
var
|
||||
check_epoch = get_current_epoch(state) + 1
|
||||
@ -284,7 +282,7 @@ func compute_committee(indices: seq[ValidatorIndex], seed: Eth2Digest,
|
||||
get_shuffled_index(it.ValidatorIndex, len(indices).uint64, seed).int])
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_crosslink_committee
|
||||
func get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard):
|
||||
func get_crosslink_committee*(state: BeaconState, epoch: Epoch, shard: Shard):
|
||||
seq[ValidatorIndex] =
|
||||
compute_committee(
|
||||
get_active_validator_indices(state, epoch),
|
||||
|
@ -264,7 +264,7 @@ proc readValue*(r: var SszReader, result: var auto) =
|
||||
|
||||
# Sample hash_tree_root implementation based on:
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.0/specs/simple-serialize.md
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/utils/phase0/minimal_ssz.py
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/test_libs/pyspec/eth2spec/utils/minimal_ssz.py
|
||||
# TODO Probably wrong - the spec is pretty bare-bones and no test vectors yet
|
||||
|
||||
const
|
||||
|
@ -31,11 +31,11 @@
|
||||
# now.
|
||||
|
||||
import
|
||||
algorithm, collections/sets, chronicles, math, options, sequtils, tables,
|
||||
algorithm, collections/sets, chronicles, math, options, sequtils, sets, tables,
|
||||
./extras, ./ssz, ./beacon_node_types,
|
||||
./spec/[beaconstate, bitfield, crypto, datatypes, digest, helpers, validator]
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#block-header
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#block-header
|
||||
proc processBlockHeader(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
# Verify that the slots match
|
||||
@ -45,7 +45,7 @@ proc processBlockHeader(
|
||||
state_slot = humaneSlotNum(state.slot)
|
||||
return false
|
||||
|
||||
# state_root not set yet, when skipping validation
|
||||
# Verify that the parent matches
|
||||
if skipValidation notin flags and not (blck.previous_block_root ==
|
||||
signing_root(state.latest_block_header)):
|
||||
notice "Block header: previous block root mismatch",
|
||||
@ -54,15 +54,25 @@ proc processBlockHeader(
|
||||
latest_block_header_root = shortLog(signing_root(state.latest_block_header))
|
||||
return false
|
||||
|
||||
state.latest_block_header = get_temporary_block_header(blck)
|
||||
# Save current block as the new latest block
|
||||
state.latest_block_header = BeaconBlockHeader(
|
||||
slot: blck.slot,
|
||||
previous_block_root: blck.previous_block_root,
|
||||
block_body_root: hash_tree_root(blck.body),
|
||||
)
|
||||
|
||||
let proposer =
|
||||
state.validator_registry[get_beacon_proposer_index(state)]
|
||||
# Verify proposer is not slashed
|
||||
let proposer = state.validator_registry[get_beacon_proposer_index(state)]
|
||||
if proposer.slashed:
|
||||
notice "Block header: proposer slashed"
|
||||
return false
|
||||
|
||||
# Verify proposer signature
|
||||
if skipValidation notin flags and not bls_verify(
|
||||
proposer.pubkey,
|
||||
signing_root(blck).data,
|
||||
blck.signature,
|
||||
get_domain(state, DOMAIN_BEACON_PROPOSER, get_current_epoch(state))):
|
||||
get_domain(state, DOMAIN_BEACON_PROPOSER)):
|
||||
notice "Block header: invalid block header",
|
||||
proposer_pubkey = proposer.pubkey,
|
||||
block_root = shortLog(signing_root(blck)),
|
||||
@ -71,13 +81,14 @@ proc processBlockHeader(
|
||||
|
||||
true
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#randao
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#randao
|
||||
proc processRandao(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
let
|
||||
proposer_index = get_beacon_proposer_index(state)
|
||||
proposer = addr state.validator_registry[proposer_index]
|
||||
|
||||
# Verify that the provided randao value is valid
|
||||
if skipValidation notin flags:
|
||||
if not bls_verify(
|
||||
proposer.pubkey,
|
||||
@ -95,7 +106,6 @@ proc processRandao(
|
||||
# Mix it in
|
||||
let
|
||||
mix = get_current_epoch(state) mod LATEST_RANDAO_MIXES_LENGTH
|
||||
# TODO hash_tree_root has some overloading for this
|
||||
rr = eth2hash(blck.body.randao_reveal.getBytes()).data
|
||||
|
||||
for i, b in state.latest_randao_mixes[mix].data:
|
||||
@ -110,7 +120,14 @@ func processEth1Data(state: var BeaconState, blck: BeaconBlock) =
|
||||
SLOTS_PER_ETH1_VOTING_PERIOD:
|
||||
state.latest_eth1_data = blck.body.eth1_data
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#proposer-slashings
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#is_slashable_validator
|
||||
func is_slashable_validator(validator: Validator, epoch: Epoch): bool =
|
||||
# Check if ``validator`` is slashable.
|
||||
(not validator.slashed) and
|
||||
(validator.activation_epoch <= epoch) and
|
||||
(epoch < validator.withdrawable_epoch)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#proposer-slashings
|
||||
proc processProposerSlashings(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
if len(blck.body.proposer_slashings) > MAX_PROPOSER_SLASHINGS:
|
||||
@ -121,19 +138,23 @@ proc processProposerSlashings(
|
||||
for proposer_slashing in blck.body.proposer_slashings:
|
||||
let proposer = state.validator_registry[proposer_slashing.proposer_index.int]
|
||||
|
||||
# Verify that the epoch is the same
|
||||
if not (slot_to_epoch(proposer_slashing.header_1.slot) ==
|
||||
slot_to_epoch(proposer_slashing.header_2.slot)):
|
||||
notice "PropSlash: epoch mismatch"
|
||||
return false
|
||||
|
||||
# But the headers are different
|
||||
if not (proposer_slashing.header_1 != proposer_slashing.header_2):
|
||||
notice "PropSlash: headers not different"
|
||||
return false
|
||||
|
||||
if not (proposer.slashed == false):
|
||||
# Check proposer is slashable
|
||||
if not is_slashable_validator(proposer, get_current_epoch(state)):
|
||||
notice "PropSlash: slashed proposer"
|
||||
return false
|
||||
|
||||
# Signatures are valid
|
||||
if skipValidation notin flags:
|
||||
for i, header in @[proposer_slashing.header_1, proposer_slashing.header_2]:
|
||||
if not bls_verify(
|
||||
@ -265,11 +286,23 @@ proc processAttestations(
|
||||
|
||||
# All checks passed - update state
|
||||
# Apply the attestations
|
||||
var committee_count_cache = initTable[Epoch, uint64]()
|
||||
|
||||
for attestation in blck.body.attestations:
|
||||
let
|
||||
epoch = attestation.data.target_epoch
|
||||
committee_count = if epoch in committee_count_cache:
|
||||
committee_count_cache[epoch]
|
||||
else:
|
||||
get_epoch_committee_count(state, epoch)
|
||||
committee_count_cache[epoch] = committee_count
|
||||
let attestation_slot =
|
||||
get_attestation_slot(state, attestation, committee_count)
|
||||
let pending_attestation = PendingAttestation(
|
||||
data: attestation.data,
|
||||
aggregation_bitfield: attestation.aggregation_bitfield,
|
||||
inclusion_slot: state.slot,
|
||||
inclusion_delay: state.slot - attestation_slot,
|
||||
proposer_index: get_beacon_proposer_index(state),
|
||||
)
|
||||
|
||||
@ -293,11 +326,10 @@ proc processDeposits(state: var BeaconState, blck: BeaconBlock): bool =
|
||||
|
||||
true
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#voluntary-exits
|
||||
proc processExits(
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#voluntary-exits
|
||||
proc processVoluntaryExits(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
## Process ``VoluntaryExit`` transaction.
|
||||
## Note that this function mutates ``state``.
|
||||
# Process ``VoluntaryExit`` transaction.
|
||||
if len(blck.body.voluntary_exits) > MAX_VOLUNTARY_EXITS:
|
||||
notice "Exit: too many!"
|
||||
return false
|
||||
@ -305,23 +337,23 @@ proc processExits(
|
||||
for exit in blck.body.voluntary_exits:
|
||||
let validator = state.validator_registry[exit.validator_index.int]
|
||||
|
||||
# Verify the validator is active
|
||||
if not is_active_validator(validator, get_current_epoch(state)):
|
||||
notice "Exit: validator not active"
|
||||
return false
|
||||
|
||||
# Verify the validator has not yet exited
|
||||
if not (validator.exit_epoch == FAR_FUTURE_EPOCH):
|
||||
notice "Exit: validator has exited"
|
||||
return false
|
||||
|
||||
# Verify the validator has not initiated an exit
|
||||
if not (not validator.initiated_exit):
|
||||
notice "Exit: validator has initiated an exit"
|
||||
return false
|
||||
|
||||
## Exits must specify an epoch when they become valid; they are not valid
|
||||
## before then
|
||||
if not (get_current_epoch(state) >= exit.epoch):
|
||||
notice "Exit: exit epoch not passed"
|
||||
return false
|
||||
|
||||
# Must have been in the validator set long enough
|
||||
# Verify the validator has been active long enough
|
||||
if not (get_current_epoch(state) - validator.activation_epoch >=
|
||||
PERSISTENT_COMMITTEE_PERIOD):
|
||||
notice "Exit: not in validator set long enough"
|
||||
@ -335,7 +367,7 @@ proc processExits(
|
||||
notice "Exit: invalid signature"
|
||||
return false
|
||||
|
||||
# Run the exit
|
||||
# Initiate exit
|
||||
initiate_validator_exit(state, exit.validator_index.ValidatorIndex)
|
||||
|
||||
true
|
||||
@ -445,7 +477,7 @@ proc processTransfers(state: var BeaconState, blck: BeaconBlock,
|
||||
|
||||
true
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#per-slot-processing
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#per-slot-processing
|
||||
func advance_slot(state: var BeaconState) =
|
||||
## Time on the beacon chain moves in slots. Every time we make it to a new
|
||||
## slot, a proposer creates a block to represent the state of the beacon
|
||||
@ -454,7 +486,7 @@ func advance_slot(state: var BeaconState) =
|
||||
|
||||
state.slot += 1
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#state-caching
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#state-caching
|
||||
func cacheState(state: var BeaconState) =
|
||||
let previous_slot_state_root = hash_tree_root(state)
|
||||
|
||||
@ -504,7 +536,7 @@ proc processBlock(
|
||||
debug "[Block processing] Deposit processing failure", slot = humaneSlotNum(state.slot)
|
||||
return false
|
||||
|
||||
if not processExits(state, blck, flags):
|
||||
if not processVoluntaryExits(state, blck, flags):
|
||||
debug "[Block processing] Exit processing failure", slot = humaneSlotNum(state.slot)
|
||||
return false
|
||||
|
||||
@ -514,17 +546,54 @@ proc processBlock(
|
||||
|
||||
true
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#helper-functions-1
|
||||
func get_current_total_balance(state: BeaconState): Gwei =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#helper-functions-1
|
||||
func get_total_active_balance(state: BeaconState): Gwei =
|
||||
return get_total_balance(
|
||||
state,
|
||||
get_active_validator_indices(state, get_current_epoch(state)))
|
||||
|
||||
func get_previous_total_balance(state: BeaconState): Gwei =
|
||||
get_total_balance(
|
||||
state,
|
||||
get_active_validator_indices(state, get_previous_epoch(state)))
|
||||
func get_matching_source_attestations(state: BeaconState, epoch: Epoch):
|
||||
seq[PendingAttestation] =
|
||||
doAssert epoch in @[get_current_epoch(state), get_previous_epoch(state)]
|
||||
if epoch == get_current_epoch(state):
|
||||
state.current_epoch_attestations
|
||||
else:
|
||||
state.previous_epoch_attestations
|
||||
|
||||
func get_matching_target_attestations(state: BeaconState, epoch: Epoch):
|
||||
seq[PendingAttestation] =
|
||||
filterIt(
|
||||
get_matching_source_attestations(state, epoch),
|
||||
it.data.target_root == get_block_root(state, epoch)
|
||||
)
|
||||
|
||||
func get_matching_head_attestations(state: BeaconState, epoch: Epoch):
|
||||
seq[PendingAttestation] =
|
||||
filterIt(
|
||||
get_matching_source_attestations(state, epoch),
|
||||
it.data.beacon_block_root ==
|
||||
get_block_root_at_slot(state, get_attestation_slot(state, it))
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_attesting_indices
|
||||
func get_attesting_indices(state: BeaconState,
|
||||
attestation_data: AttestationData,
|
||||
bitfield: BitField): HashSet[ValidatorIndex] =
|
||||
## Return the sorted attesting indices corresponding to ``attestation_data``
|
||||
## and ``bitfield``.
|
||||
## The spec goes through a lot of hoops to sort things, and sometimes
|
||||
## constructs sets from the results here. The basic idea is to always
|
||||
## just do the right thing and keep it in a HashSet.
|
||||
result = initSet[ValidatorIndex]()
|
||||
let committee =
|
||||
get_crosslink_committee(state, attestation_data.target_epoch,
|
||||
attestation_data.shard)
|
||||
doAssert verify_bitfield(bitfield, len(committee))
|
||||
for i, index in committee:
|
||||
if get_bitfield_bit(bitfield, i):
|
||||
result.incl index
|
||||
|
||||
# TODO remove this 0.5ish one when callers disappear
|
||||
func get_attesting_indices(
|
||||
state: BeaconState,
|
||||
attestations: openArray[PendingAttestation]): HashSet[ValidatorIndex] =
|
||||
@ -535,6 +604,7 @@ func get_attesting_indices(
|
||||
state, attestation.data, attestation.aggregation_bitfield):
|
||||
result.incl validator_index
|
||||
|
||||
# TODO this cached version corresponds to the 0.5ish get_attesting_indices
|
||||
func get_attesting_indices_cached(
|
||||
state: BeaconState,
|
||||
attestations: openArray[PendingAttestation], cache: var StateCache):
|
||||
@ -547,6 +617,24 @@ func get_attesting_indices_cached(
|
||||
cache):
|
||||
result.incl validator_index
|
||||
|
||||
func get_unslashed_attesting_indices(
|
||||
state: BeaconState, attestations: seq[PendingAttestation]):
|
||||
HashSet[ValidatorIndex] =
|
||||
result = initSet[ValidatorIndex]()
|
||||
for a in attestations:
|
||||
result = result.union(get_attesting_indices(
|
||||
state, a.data, a.aggregation_bitfield))
|
||||
|
||||
for index in result:
|
||||
if state.validator_registry[index].slashed:
|
||||
result.excl index
|
||||
|
||||
# TODO check for blob/v0.5.0 removal
|
||||
func get_previous_total_balance(state: BeaconState): Gwei =
|
||||
get_total_balance(
|
||||
state,
|
||||
get_active_validator_indices(state, get_previous_epoch(state)))
|
||||
|
||||
func get_attesting_balance(state: BeaconState,
|
||||
attestations: seq[PendingAttestation]): Gwei =
|
||||
get_total_balance(state, get_attesting_indices(state, attestations))
|
||||
@ -561,7 +649,7 @@ func get_current_epoch_boundary_attestations(state: BeaconState):
|
||||
seq[PendingAttestation] =
|
||||
filterIt(
|
||||
state.current_epoch_attestations,
|
||||
it.data.target_root == get_block_root(
|
||||
it.data.target_root == get_block_root_at_slot(
|
||||
state, get_epoch_start_slot(get_current_epoch(state))))
|
||||
|
||||
func get_previous_epoch_boundary_attestations(state: BeaconState):
|
||||
@ -569,13 +657,13 @@ func get_previous_epoch_boundary_attestations(state: BeaconState):
|
||||
filterIt(
|
||||
state.previous_epoch_attestations,
|
||||
it.data.target_root ==
|
||||
get_block_root(state, get_epoch_start_slot(get_previous_epoch(state))))
|
||||
get_block_root_at_slot(state, get_epoch_start_slot(get_previous_epoch(state))))
|
||||
|
||||
func get_previous_epoch_matching_head_attestations(state: BeaconState):
|
||||
seq[PendingAttestation] =
|
||||
filterIt(
|
||||
state.previous_epoch_attestations,
|
||||
it.data.beacon_block_root == get_block_root(state, it.data.slot))
|
||||
it.data.beacon_block_root == get_block_root_at_slot(state, it.data.slot))
|
||||
|
||||
# Not exactly in spec, but for get_winning_root_and_participants
|
||||
func lowerThan(candidate, current: Eth2Digest): bool =
|
||||
@ -632,33 +720,8 @@ func get_winning_root_and_participants(
|
||||
state,
|
||||
attestations_for.getOrDefault(winning_root), cache))
|
||||
|
||||
# Combination of earliest_attestation and inclusion_slot avoiding O(n^2)
|
||||
# TODO merge/refactor these two functions, which differ only very slightly.
|
||||
func inclusion_slots(state: BeaconState): auto =
|
||||
result = initTable[ValidatorIndex, Slot]()
|
||||
|
||||
for a in sorted(state.previous_epoch_attestations,
|
||||
func (x, y: PendingAttestation): auto =
|
||||
system.cmp(x.inclusion_slot, y.inclusion_slot)):
|
||||
for v in get_attestation_participants(
|
||||
state, a.data, a.aggregation_bitfield):
|
||||
if v notin result:
|
||||
result[v] = a.inclusion_slot
|
||||
|
||||
# Combination of earliest_attestation and inclusion_distance avoiding O(n^2)
|
||||
func inclusion_distances(state: BeaconState): auto =
|
||||
result = initTable[ValidatorIndex, Slot]()
|
||||
|
||||
for a in sorted(state.previous_epoch_attestations,
|
||||
func (x, y: PendingAttestation): auto =
|
||||
system.cmp(x.inclusion_slot, y.inclusion_slot)):
|
||||
for v in get_attestation_participants(
|
||||
state, a.data, a.aggregation_bitfield):
|
||||
if v notin result:
|
||||
result[v] = Slot(a.inclusion_slot - a.data.slot)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#justification
|
||||
func update_justification_and_finalization(state: var BeaconState) =
|
||||
func process_justification_and_finalization(state: var BeaconState) =
|
||||
var
|
||||
new_justified_epoch = state.current_justified_epoch
|
||||
new_finalized_epoch = state.finalized_epoch
|
||||
@ -681,7 +744,7 @@ func update_justification_and_finalization(state: var BeaconState) =
|
||||
get_attesting_balance(
|
||||
state, get_current_epoch_boundary_attestations(state))
|
||||
if current_boundary_attesting_balance * 3'u64 >=
|
||||
get_current_total_balance(state) * 2'u64:
|
||||
get_total_active_balance(state) * 2'u64:
|
||||
new_justified_epoch = get_current_epoch(state)
|
||||
state.justification_bitfield = state.justification_bitfield or 1
|
||||
|
||||
@ -720,11 +783,11 @@ func update_justification_and_finalization(state: var BeaconState) =
|
||||
if new_justified_epoch != state.current_justified_epoch:
|
||||
state.current_justified_epoch = new_justified_epoch
|
||||
state.current_justified_root =
|
||||
get_block_root(state, get_epoch_start_slot(new_justified_epoch))
|
||||
get_block_root_at_slot(state, get_epoch_start_slot(new_justified_epoch))
|
||||
if new_finalized_epoch != state.finalized_epoch:
|
||||
state.finalized_epoch = new_finalized_epoch
|
||||
state.finalized_root =
|
||||
get_block_root(state, get_epoch_start_slot(new_finalized_epoch))
|
||||
get_block_root_at_slot(state, get_epoch_start_slot(new_finalized_epoch))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#crosslinks
|
||||
func process_crosslinks(
|
||||
@ -769,170 +832,92 @@ func process_crosslinks(
|
||||
crosslink_data_root: winning_root
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
func get_base_reward(state: BeaconState, index: ValidatorIndex): uint64 =
|
||||
if get_previous_total_balance(state) == 0:
|
||||
return 0
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
func get_base_reward(state: BeaconState, index: ValidatorIndex): Gwei =
|
||||
let adjusted_quotient =
|
||||
integer_squareroot(get_previous_total_balance(state)) div
|
||||
BASE_REWARD_QUOTIENT
|
||||
get_effective_balance(state, index) div adjusted_quotient.uint64 div 5
|
||||
integer_squareroot(get_total_active_balance(state)) div BASE_REWARD_QUOTIENT
|
||||
if adjusted_quotient == 0:
|
||||
return 0
|
||||
state.validator_registry[index].effective_balance div adjusted_quotient div
|
||||
BASE_REWARDS_PER_EPOCH
|
||||
|
||||
func get_inactivity_penalty(
|
||||
state: BeaconState, index: ValidatorIndex,
|
||||
epochs_since_finality: uint64): uint64 =
|
||||
# TODO Left/right associativity sensitivity on * and div?
|
||||
get_base_reward(state, index) +
|
||||
get_effective_balance(state, index) * epochs_since_finality div
|
||||
INACTIVITY_PENALTY_QUOTIENT div 2
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#justification-and-finalization
|
||||
func compute_normal_justification_and_finalization_deltas(state: BeaconState):
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
func get_attestation_deltas(state: BeaconState):
|
||||
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||
# deltas[0] for rewards
|
||||
# deltas[1] for penalties
|
||||
var deltas = (
|
||||
repeat(0'u64, len(state.validator_registry)),
|
||||
repeat(0'u64, len(state.validator_registry))
|
||||
)
|
||||
# Some helper variables
|
||||
let
|
||||
boundary_attestations = get_previous_epoch_boundary_attestations(state)
|
||||
boundary_attesting_balance =
|
||||
get_attesting_balance(state, boundary_attestations)
|
||||
total_balance = get_previous_total_balance(state)
|
||||
total_attesting_balance =
|
||||
get_attesting_balance(state, state.previous_epoch_attestations)
|
||||
previous_epoch = get_previous_epoch(state)
|
||||
total_balance = get_total_active_balance(state)
|
||||
var
|
||||
rewards = repeat(0'u64, len(state.validator_registry))
|
||||
penalties = repeat(0'u64, len(state.validator_registry))
|
||||
eligible_validator_indices : seq[ValidatorIndex] = @[]
|
||||
|
||||
for index, v in state.validator_registry:
|
||||
if is_active_validator(v, previous_epoch) or
|
||||
(v.slashed and previous_epoch + 1 < v.withdrawable_epoch):
|
||||
eligible_validator_indices.add index.ValidatorIndex
|
||||
|
||||
# Micro-incentives for matching FFG source, FFG target, and head
|
||||
let
|
||||
matching_source_attestations =
|
||||
get_matching_source_attestations(state, previous_epoch)
|
||||
matching_target_attestations =
|
||||
get_matching_target_attestations(state, previous_epoch)
|
||||
matching_head_attestations =
|
||||
get_previous_epoch_matching_head_attestations(state)
|
||||
matching_head_balance =
|
||||
get_attesting_balance(state, matching_head_attestations)
|
||||
get_matching_head_attestations(state, previous_epoch)
|
||||
for attestations in
|
||||
@[matching_source_attestations, matching_target_attestations,
|
||||
matching_head_attestations]:
|
||||
let
|
||||
unslashed_attesting_indices =
|
||||
get_unslashed_attesting_indices(state, attestations)
|
||||
attesting_balance = get_attesting_balance(state, attestations)
|
||||
for index in eligible_validator_indices:
|
||||
if index in unslashed_attesting_indices:
|
||||
rewards[index] +=
|
||||
get_base_reward(state, index) * attesting_balance div total_balance
|
||||
else:
|
||||
penalties[index] += get_base_reward(state, index)
|
||||
|
||||
let
|
||||
inclusion_distance = inclusion_distances(state)
|
||||
inclusion_slot = inclusion_slots(state)
|
||||
previous_epoch_attestation_indices =
|
||||
get_attesting_indices(state, state.previous_epoch_attestations)
|
||||
boundary_attestation_indices =
|
||||
get_attesting_indices(state, boundary_attestations)
|
||||
matching_head_attestation_indices =
|
||||
get_attesting_indices(state, matching_head_attestations)
|
||||
# Process rewards or penalties for all validators
|
||||
for index in get_active_validator_indices(state, get_previous_epoch(state)):
|
||||
# Expected FFG source
|
||||
if index in previous_epoch_attestation_indices:
|
||||
deltas[0][index] +=
|
||||
get_base_reward(state, index) * total_attesting_balance div
|
||||
total_balance
|
||||
# Inclusion speed bonus
|
||||
deltas[0][index] += (
|
||||
get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY div
|
||||
inclusion_distance[index]
|
||||
)
|
||||
else:
|
||||
deltas[1][index] += get_base_reward(state, index)
|
||||
# Expected FFG target
|
||||
if index in boundary_attestation_indices:
|
||||
deltas[0][index] +=
|
||||
get_base_reward(state, index) * boundary_attesting_balance div
|
||||
total_balance
|
||||
else:
|
||||
deltas[1][index] += get_base_reward(state, index)
|
||||
# Expected head
|
||||
if index in matching_head_attestation_indices:
|
||||
deltas[0][index] +=
|
||||
get_base_reward(state, index) *
|
||||
matching_head_balance div total_balance
|
||||
else:
|
||||
deltas[1][index] += get_base_reward(state, index)
|
||||
# Proposer bonus
|
||||
if index in previous_epoch_attestation_indices:
|
||||
let proposer_index =
|
||||
get_beacon_proposer_index(state)
|
||||
deltas[0][proposer_index] +=
|
||||
get_base_reward(state, index) div PROPOSER_REWARD_QUOTIENT
|
||||
deltas
|
||||
if matching_source_attestations.len == 0:
|
||||
return (rewards, penalties)
|
||||
|
||||
func compute_inactivity_leak_deltas(state: BeaconState):
|
||||
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||
# When blocks are not finalizing normally
|
||||
# deltas[0] for rewards
|
||||
# deltas[1] for penalties
|
||||
var deltas = (
|
||||
repeat(0'u64, len(state.validator_registry)),
|
||||
repeat(0'u64, len(state.validator_registry))
|
||||
)
|
||||
# Proposer and inclusion delay micro-rewards
|
||||
for index in get_unslashed_attesting_indices(state, matching_source_attestations):
|
||||
doAssert matching_source_attestations.len > 0
|
||||
var attestation = matching_source_attestations[0]
|
||||
for a in matching_source_attestations:
|
||||
if index notin get_attesting_indices(state, a.data, a.aggregation_bitfield):
|
||||
continue
|
||||
if a.inclusion_delay < attestation.inclusion_delay:
|
||||
attestation = a
|
||||
rewards[attestation.proposer_index] += get_base_reward(state, index) div
|
||||
PROPOSER_REWARD_QUOTIENT
|
||||
rewards[index] +=
|
||||
get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY div
|
||||
attestation.inclusion_delay
|
||||
|
||||
let
|
||||
boundary_attestations = get_previous_epoch_boundary_attestations(state)
|
||||
matching_head_attestations =
|
||||
get_previous_epoch_matching_head_attestations(state)
|
||||
active_validator_indices = toSet(get_active_validator_indices(
|
||||
state, get_previous_epoch(state)))
|
||||
epochs_since_finality =
|
||||
get_current_epoch(state) + 1 - state.finalized_epoch
|
||||
let
|
||||
inclusion_distance = inclusion_distances(state)
|
||||
previous_epoch_attestation_indices =
|
||||
get_attesting_indices(state, state.previous_epoch_attestations)
|
||||
boundary_attestation_indices =
|
||||
get_attesting_indices(state, boundary_attestations)
|
||||
matching_head_attestation_indices =
|
||||
get_attesting_indices(state, matching_head_attestations)
|
||||
for index in active_validator_indices:
|
||||
if index notin previous_epoch_attestation_indices:
|
||||
deltas[1][index] +=
|
||||
get_inactivity_penalty(state, index, epochs_since_finality)
|
||||
else:
|
||||
## If a validator did attest, apply a small penalty for getting
|
||||
## attestations included late
|
||||
deltas[0][index] += (
|
||||
get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY div
|
||||
inclusion_distance[index]
|
||||
)
|
||||
deltas[1][index] += get_base_reward(state, index)
|
||||
if index notin boundary_attestation_indices:
|
||||
deltas[1][index] +=
|
||||
get_inactivity_penalty(state, index, epochs_since_finality)
|
||||
if index notin matching_head_attestation_indices:
|
||||
deltas[1][index] += get_base_reward(state, index)
|
||||
# Inactivity penalty
|
||||
let finality_delay = previous_epoch - state.finalized_epoch
|
||||
if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY:
|
||||
let matching_target_attesting_indices =
|
||||
get_unslashed_attesting_indices(state, matching_target_attestations)
|
||||
for index in eligible_validator_indices:
|
||||
penalties[index] +=
|
||||
BASE_REWARDS_PER_EPOCH.uint64 * get_base_reward(state, index)
|
||||
if index notin matching_target_attesting_indices:
|
||||
penalties[index] +=
|
||||
state.validator_registry[index].effective_balance *
|
||||
finality_delay div INACTIVITY_PENALTY_QUOTIENT
|
||||
|
||||
## Penalize slashed-but-inactive validators as though they were active but
|
||||
## offline
|
||||
for index in 0 ..< len(state.validator_registry):
|
||||
let eligible = (
|
||||
index.ValidatorIndex notin active_validator_indices and
|
||||
state.validator_registry[index].slashed and
|
||||
get_current_epoch(state) <
|
||||
state.validator_registry[index].withdrawable_epoch
|
||||
)
|
||||
if eligible:
|
||||
deltas[1][index] += (
|
||||
2'u64 * get_inactivity_penalty(
|
||||
state, index.ValidatorIndex, epochs_since_finality) +
|
||||
get_base_reward(state, index.ValidatorIndex)
|
||||
)
|
||||
deltas
|
||||
(rewards, penalties)
|
||||
|
||||
func get_justification_and_finalization_deltas(state: BeaconState):
|
||||
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||
let epochs_since_finality =
|
||||
get_current_epoch(state) + 1 - state.finalized_epoch
|
||||
if epochs_since_finality <= 4:
|
||||
compute_normal_justification_and_finalization_deltas(state)
|
||||
else:
|
||||
compute_inactivity_leak_deltas(state)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#crosslinks-1
|
||||
# blob/0.5.1
|
||||
func get_crosslink_deltas(state: BeaconState, cache: var StateCache):
|
||||
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||
# deltas[0] for rewards
|
||||
# deltas[1] for penalties
|
||||
var deltas = (
|
||||
repeat(0'u64, len(state.validator_registry)),
|
||||
repeat(0'u64, len(state.validator_registry))
|
||||
)
|
||||
var
|
||||
rewards = repeat(0'u64, len(state.validator_registry))
|
||||
penalties = repeat(0'u64, len(state.validator_registry))
|
||||
let
|
||||
previous_epoch_start_slot =
|
||||
get_epoch_start_slot(get_previous_epoch(state))
|
||||
@ -952,30 +937,26 @@ func get_crosslink_deltas(state: BeaconState, cache: var StateCache):
|
||||
total_balance = get_total_balance(state, crosslink_committee)
|
||||
for index in crosslink_committee:
|
||||
if index in participants:
|
||||
deltas[0][index] +=
|
||||
rewards[index] +=
|
||||
get_base_reward(state, index) * participating_balance div
|
||||
total_balance
|
||||
else:
|
||||
deltas[1][index] += get_base_reward(state, index)
|
||||
penalties[index] += get_base_reward(state, index)
|
||||
|
||||
deltas
|
||||
(rewards, penalties)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#apply-rewards
|
||||
func apply_rewards(state: var BeaconState, cache: var StateCache) =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
func process_rewards_and_penalties(
|
||||
state: var BeaconState, cache: var StateCache) =
|
||||
let
|
||||
deltas1 = get_justification_and_finalization_deltas(state)
|
||||
deltas2 = get_crosslink_deltas(state, cache)
|
||||
(rewards1, penalties1) = get_attestation_deltas(state)
|
||||
(rewards2, penalties2) = get_crosslink_deltas(state, cache)
|
||||
for i in 0 ..< len(state.validator_registry):
|
||||
state.balances[i] =
|
||||
max(
|
||||
0'u64,
|
||||
state.balances[i] + deltas1[0][i] + deltas2[0][i] -
|
||||
deltas1[1][i] - deltas2[1][i])
|
||||
increase_balance(state, i.ValidatorIndex, rewards1[i] + rewards2[i])
|
||||
decrease_balance(state, i.ValidatorIndex, penalties1[i] + penalties2[i])
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#slashings-and-exit-queue
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#slashings
|
||||
func process_slashings(state: var BeaconState) =
|
||||
## Process the slashings.
|
||||
## Note that this function mutates ``state``.
|
||||
let
|
||||
current_epoch = get_current_epoch(state)
|
||||
active_validator_indices = get_active_validator_indices(
|
||||
@ -999,40 +980,7 @@ func process_slashings(state: var BeaconState) =
|
||||
min(total_penalties * 3, total_balance) div total_balance,
|
||||
get_effective_balance(state, index.ValidatorIndex) div
|
||||
MIN_PENALTY_QUOTIENT)
|
||||
reduce_balance(state.balances[index], penalty)
|
||||
|
||||
func process_exit_queue(state: var BeaconState) =
|
||||
## Process the exit queue.
|
||||
## Note that this function mutates ``state``.
|
||||
|
||||
func eligible(index: ValidatorIndex): bool =
|
||||
let validator = state.validator_registry[index]
|
||||
# Filter out dequeued validators
|
||||
if validator.withdrawable_epoch != FAR_FUTURE_EPOCH:
|
||||
return false
|
||||
# Dequeue if the minimum amount of time has passed
|
||||
else:
|
||||
return get_current_epoch(state) >= validator.exit_epoch +
|
||||
MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
|
||||
var eligible_indices: seq[ValidatorIndex]
|
||||
for vi in 0 ..< len(state.validator_registry):
|
||||
if eligible(vi.ValidatorIndex):
|
||||
eligible_indices.add vi.ValidatorIndex
|
||||
let
|
||||
## Sort in order of exit epoch, and validators that exit within the same
|
||||
## epoch exit in order of validator index
|
||||
sorted_indices = sorted(
|
||||
eligible_indices,
|
||||
func(x, y: ValidatorIndex): int =
|
||||
system.cmp(
|
||||
state.validator_registry[x].exit_epoch,
|
||||
state.validator_registry[y].exit_epoch))
|
||||
|
||||
for dequeues, index in sorted_indices:
|
||||
if dequeues >= MIN_PER_EPOCH_CHURN_LIMIT:
|
||||
break
|
||||
prepare_validator_for_withdrawal(state, index)
|
||||
decrease_balance(state, index.ValidatorIndex, penalty)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#final-updates
|
||||
func finish_epoch_update(state: var BeaconState) =
|
||||
@ -1081,16 +1029,16 @@ func processEpoch(state: var BeaconState) =
|
||||
(state.slot + 1) mod SLOTS_PER_EPOCH == 0):
|
||||
return
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#justification
|
||||
update_justification_and_finalization(state)
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#justification-and-finalization
|
||||
process_justification_and_finalization(state)
|
||||
|
||||
var per_epoch_cache = get_empty_per_epoch_cache()
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#crosslinks
|
||||
process_crosslinks(state, per_epoch_cache)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#apply-rewards
|
||||
apply_rewards(state, per_epoch_cache)
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
process_rewards_and_penalties(state, per_epoch_cache)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#ejections
|
||||
process_ejections(state)
|
||||
@ -1101,7 +1049,6 @@ func processEpoch(state: var BeaconState) =
|
||||
## Regardless of whether or not a validator set change happens run
|
||||
## process_slashings(state) and process_exit_queue(state)
|
||||
process_slashings(state)
|
||||
process_exit_queue(state)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#final-updates
|
||||
finish_epoch_update(state)
|
||||
@ -1211,7 +1158,7 @@ proc skipSlots*(state: var BeaconState, slot: Slot,
|
||||
|
||||
# TODO hashed versions of above - not in spec
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#state-caching
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#state-caching
|
||||
func cacheState(state: var HashedBeaconState) =
|
||||
let previous_slot_state_root = state.root
|
||||
|
||||
@ -1367,7 +1314,7 @@ proc skipSlots*(state: var HashedBeaconState, slot: Slot,
|
||||
# Dec 21 13:31
|
||||
# how does that interact then with the following check:
|
||||
|
||||
# Verify that attestation.data.justified_block_root is equal to get_block_root(state, attestation.data.justified_slot).
|
||||
# Verify that attestation.data.justified_block_root is equal to get_block_root_at_slot(state, attestation.data.justified_slot).
|
||||
|
||||
# Danny Ryan
|
||||
# @djrtwo
|
||||
|
@ -52,7 +52,7 @@ type
|
||||
LATEST_ACTIVE_INDEX_ROOTS_LENGTH*: int
|
||||
LATEST_SLASHED_EXIT_LENGTH*: int
|
||||
BASE_REWARD_QUOTIENT*: uint64
|
||||
WHISTLEBLOWER_REWARD_QUOTIENT*: uint64
|
||||
WHISTLEBLOWING_REWARD_QUOTIENT*: uint64
|
||||
PROPOSER_REWARD_QUOTIENT*: uint64
|
||||
INACTIVITY_PENALTY_QUOTIENT*: uint64
|
||||
MIN_PENALTY_QUOTIENT*: int
|
||||
|
@ -80,7 +80,7 @@ suite "Official - State tests": # Initializing a beacon state from the deposits
|
||||
# check:
|
||||
# ok
|
||||
# tcase.expected_state.eth1_data_votes.len == state.eth1_data_votes.len + 1
|
||||
# get_block_root(tcase.expected_state, state.slot) == blck.previous_block_root
|
||||
# get_block_root_at_slot(tcase.expected_state, state.slot) == blck.previous_block_root
|
||||
|
||||
suite "[For information - non-blocking] Extra state tests":
|
||||
var initialState: BeaconState
|
||||
|
Loading…
x
Reference in New Issue
Block a user