2018-08-21 16:21:45 +00:00
# beacon_chain
2019-02-25 14:48:36 +00:00
# Copyright (c) 2018-2019 Status Research & Development GmbH
2018-08-21 16:21:45 +00:00
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
2018-12-04 18:45:30 +00:00
# State transition, as described in
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function
2018-08-21 16:21:45 +00:00
#
2018-12-04 18:45:30 +00:00
# The purpose of this code right is primarily educational, to help piece
# together the mechanics of the beacon state and to discover potential problem
2018-12-13 16:00:55 +00:00
# areas. The entry point is `updateState` which is at the bottom of the file!
2018-12-04 18:45:30 +00:00
#
# 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
# the others use NEP-1 - helps grepping identifiers in spec
# * We mix procedural and functional styles for no good reason, except that the
# spec does so also.
# * There are no tests, and likely lots of bugs.
2019-01-29 04:15:00 +00:00
# * For indices, we get a mix of uint64, ValidatorIndex and int - this is currently
2018-12-04 18:45:30 +00:00
# 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
# improvements to be made - other than that, keep things similar to spec for
# now.
2018-08-21 16:21:45 +00:00
2018-09-26 16:26:39 +00:00
import
2019-02-26 09:54:08 +00:00
algorithm , chronicles , math , options , sequtils ,
2018-12-27 23:40:22 +00:00
. / extras , . / ssz ,
2019-02-05 16:13:29 +00:00
. / spec / [ beaconstate , crypto , datatypes , digest , helpers , validator ]
2018-11-29 05:23:40 +00:00
2018-12-13 16:00:55 +00:00
func flatten [ T ] ( v : openArray [ seq [ T ] ] ) : seq [ T ] =
# TODO not in nim - doh.
for x in v : result . add x
2018-11-29 22:11:05 +00:00
2019-02-22 09:56:45 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#proposer-signature
2019-02-14 21:41:04 +00:00
func verifyProposerSignature ( state : BeaconState , blck : BeaconBlock ) : bool =
2018-12-27 20:14:37 +00:00
## When creating a block, the proposer will sign a version of the block that
2018-12-19 04:36:10 +00:00
## doesn't contain the data (chicken and egg), then add the signature to that
2018-12-27 20:14:37 +00:00
## block. Here, we check that the signature is correct by repeating the same
## process.
2019-02-14 21:41:04 +00:00
var blck_without_signature = blck
blck_without_signature . signature = ValidatorSig ( )
2018-11-29 22:11:05 +00:00
let
2018-12-13 16:00:55 +00:00
signed_data = ProposalSignedData (
2018-12-11 17:55:45 +00:00
slot : state . slot ,
2018-12-13 16:00:55 +00:00
shard : BEACON_CHAIN_SHARD_NUMBER ,
2019-02-14 21:41:04 +00:00
block_root : hash_tree_root_final ( blck_without_signature )
2018-12-13 16:00:55 +00:00
)
2019-02-14 21:41:04 +00:00
proposal_root = hash_tree_root_final ( signed_data )
2018-12-19 04:36:10 +00:00
proposer_index = get_beacon_proposer_index ( state , state . slot )
2018-11-29 22:11:05 +00:00
2018-12-17 19:36:17 +00:00
bls_verify (
2018-12-13 16:00:55 +00:00
state . validator_registry [ proposer_index ] . pubkey ,
2019-02-14 21:41:04 +00:00
proposal_root . data , blck . signature ,
get_domain ( state . fork , get_current_epoch ( state ) , DOMAIN_PROPOSAL ) )
2018-11-29 22:11:05 +00:00
2019-02-22 09:56:45 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#randao
2019-01-25 17:35:22 +00:00
proc processRandao (
2018-12-21 22:37:46 +00:00
state : var BeaconState , blck : BeaconBlock , flags : UpdateFlags ) : bool =
2018-12-04 18:45:30 +00:00
let
2018-12-11 17:55:45 +00:00
proposer_index = get_beacon_proposer_index ( state , state . slot )
proposer = addr state . validator_registry [ proposer_index ]
2018-11-29 22:11:05 +00:00
2018-12-21 22:37:46 +00:00
if skipValidation notin flags :
2019-02-14 21:41:04 +00:00
if not bls_verify (
proposer . pubkey ,
int_to_bytes32 ( get_current_epoch ( state ) ) ,
blck . randao_reveal ,
get_domain ( state . fork , get_current_epoch ( state ) , DOMAIN_RANDAO ) ) :
notice " Randao mismatch " , proposer_pubkey = proposer . pubkey ,
message = get_current_epoch ( state ) ,
signature = blck . randao_reveal ,
slot = state . slot ,
blck_slot = blck . slot
return false
2018-11-29 22:11:05 +00:00
# Update state and proposer now that we're alright
2019-02-07 10:43:21 +00:00
let
mix = get_current_epoch ( state ) mod LATEST_RANDAO_MIXES_LENGTH
rr = hash_tree_root_final ( blck . randao_reveal ) . data
2018-11-29 22:11:05 +00:00
2019-02-07 10:43:21 +00:00
for i , b in state . latest_randao_mixes [ mix ] . data :
state . latest_randao_mixes [ mix ] . data [ i ] = b xor rr [ i ]
2018-11-29 22:11:05 +00:00
2019-02-14 21:41:04 +00:00
true
2018-12-13 16:00:55 +00:00
2019-02-22 09:56:45 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#eth1-data
2019-01-16 10:21:06 +00:00
func processDepositRoot ( state : var BeaconState , blck : BeaconBlock ) =
2019-02-22 09:56:45 +00:00
# TODO verify that there's at most one match
2019-01-18 00:14:22 +00:00
for x in state . eth1_data_votes . mitems ( ) :
if blck . eth1_data = = x . eth1_data :
2018-12-27 20:14:37 +00:00
x . vote_count + = 1
2018-12-13 16:00:55 +00:00
return
2018-12-03 21:41:24 +00:00
2019-01-18 00:14:22 +00:00
state . eth1_data_votes . add Eth1DataVote (
eth1_data : blck . eth1_data ,
2018-12-27 20:14:37 +00:00
vote_count : 1
2018-12-03 21:41:24 +00:00
)
2018-12-13 16:00:55 +00:00
2019-02-22 09:56:45 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#slashValidator
func slashValidator ( state : var BeaconState , index : ValidatorIndex ) =
## Slash the validator of the given ``index``.
2019-02-14 03:16:51 +00:00
## Note that this function mutates ``state``.
var validator = addr state . validator_registry [ index ]
2019-02-22 09:56:45 +00:00
doAssert state . slot < get_epoch_start_slot ( validator . withdrawable_epoch ) ##\
## [TO BE REMOVED IN PHASE 2]
exit_validator ( state , index )
2019-02-19 23:07:56 +00:00
state . latest_slashed_balances [ ( get_current_epoch ( state ) mod
LATEST_SLASHED_EXIT_LENGTH ) . int ] + = get_effective_balance ( state ,
2019-02-14 03:16:51 +00:00
index . ValidatorIndex )
2019-01-16 12:57:49 +00:00
let
whistleblower_index = get_beacon_proposer_index ( state , state . slot )
2019-02-22 09:56:45 +00:00
whistleblower_reward = get_effective_balance ( state , index ) div
WHISTLEBLOWER_REWARD_QUOTIENT
2019-01-16 12:57:49 +00:00
state . validator_balances [ whistleblower_index ] + = whistleblower_reward
state . validator_balances [ index ] - = whistleblower_reward
2019-02-19 23:07:56 +00:00
validator . slashed_epoch = get_current_epoch ( state )
2019-02-22 09:56:45 +00:00
# v0.3.0 spec bug, fixed later, involving renamed constants. Use v0.3.0 name.
2019-02-19 23:07:56 +00:00
validator . withdrawable_epoch = get_current_epoch ( state ) + LATEST_SLASHED_EXIT_LENGTH
2019-01-16 12:57:49 +00:00
2019-02-22 09:56:45 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#proposer-slashings-1
2018-12-27 23:40:22 +00:00
proc processProposerSlashings (
state : var BeaconState , blck : BeaconBlock , flags : UpdateFlags ) : bool =
2018-12-13 16:00:55 +00:00
if len ( blck . body . proposer_slashings ) > MAX_PROPOSER_SLASHINGS :
2019-01-25 17:35:22 +00:00
notice " PropSlash: too many! " ,
proposer_slashings = len ( blck . body . proposer_slashings )
2018-12-13 16:00:55 +00:00
return false
for proposer_slashing in blck . body . proposer_slashings :
2019-02-11 14:32:27 +00:00
let proposer = state . validator_registry [ proposer_slashing . proposer_index . int ]
2018-12-27 23:40:22 +00:00
if skipValidation notin flags :
if not bls_verify (
proposer . pubkey ,
hash_tree_root_final ( proposer_slashing . proposal_data_1 ) . data ,
proposer_slashing . proposal_signature_1 ,
get_domain (
2019-02-12 15:56:58 +00:00
state . fork , slot_to_epoch ( proposer_slashing . proposal_data_1 . slot ) ,
2018-12-27 23:40:22 +00:00
DOMAIN_PROPOSAL ) ) :
2019-01-25 17:35:22 +00:00
notice " PropSlash: invalid signature 1 "
2018-12-27 23:40:22 +00:00
return false
if not bls_verify (
proposer . pubkey ,
hash_tree_root_final ( proposer_slashing . proposal_data_2 ) . data ,
proposer_slashing . proposal_signature_2 ,
get_domain (
2019-02-12 15:56:58 +00:00
state . fork , slot_to_epoch ( proposer_slashing . proposal_data_2 . slot ) ,
2018-12-27 23:40:22 +00:00
DOMAIN_PROPOSAL ) ) :
2019-01-25 17:35:22 +00:00
notice " PropSlash: invalid signature 2 "
2018-12-27 23:40:22 +00:00
return false
2018-12-13 16:00:55 +00:00
if not ( proposer_slashing . proposal_data_1 . slot = =
proposer_slashing . proposal_data_2 . slot ) :
2019-01-25 17:35:22 +00:00
notice " PropSlash: slot mismatch "
2018-12-13 16:00:55 +00:00
return false
if not ( proposer_slashing . proposal_data_1 . shard = =
proposer_slashing . proposal_data_2 . shard ) :
2019-01-25 17:35:22 +00:00
notice " PropSlash: shard mismatch "
2018-12-13 16:00:55 +00:00
return false
if not ( proposer_slashing . proposal_data_1 . block_root = =
proposer_slashing . proposal_data_2 . block_root ) :
2019-01-25 17:35:22 +00:00
notice " PropSlash: block root mismatch "
2018-12-13 16:00:55 +00:00
return false
2019-02-19 23:07:56 +00:00
if not ( proposer . slashed_epoch > get_current_epoch ( state ) ) :
2019-01-25 17:35:22 +00:00
notice " PropSlash: penalized slot "
2018-12-13 16:00:55 +00:00
return false
2019-02-22 09:56:45 +00:00
slashValidator ( state , proposer_slashing . proposer_index . ValidatorIndex )
2018-12-13 16:00:55 +00:00
2018-12-03 21:41:24 +00:00
return true
2019-02-20 20:35:27 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#verify_slashable_attestation
2019-02-05 18:40:27 +00:00
func verify_slashable_attestation ( state : BeaconState , slashable_attestation : SlashableAttestation ) : bool =
# Verify validity of ``slashable_attestation`` fields.
if anyIt ( slashable_attestation . custody_bitfield , it ! = 0 ) : # [TO BE REMOVED IN PHASE 1]
2018-12-13 16:00:55 +00:00
return false
2019-02-05 18:40:27 +00:00
if len ( slashable_attestation . validator_indices ) = = 0 :
return false
2018-12-13 16:00:55 +00:00
2019-02-05 18:40:27 +00:00
for i in 0 .. < ( len ( slashable_attestation . validator_indices ) - 1 ) :
if slashable_attestation . validator_indices [ i ] > = slashable_attestation . validator_indices [ i + 1 ] :
return false
2018-12-13 16:00:55 +00:00
2019-02-05 18:40:27 +00:00
if not verify_bitfield ( slashable_attestation . custody_bitfield , len ( slashable_attestation . validator_indices ) ) :
return false
if len ( slashable_attestation . validator_indices ) > MAX_INDICES_PER_SLASHABLE_VOTE :
return false
var
custody_bit_0_indices : seq [ uint64 ] = @ [ ]
custody_bit_1_indices : seq [ uint64 ] = @ [ ]
2018-12-13 16:00:55 +00:00
2019-02-07 23:07:15 +00:00
for i , validator_index in slashable_attestation . validator_indices :
if get_bitfield_bit ( slashable_attestation . custody_bitfield , i ) = = 0b0 :
custody_bit_0_indices . add ( validator_index )
else :
custody_bit_1_indices . add ( validator_index )
bls_verify_multiple (
@ [
bls_aggregate_pubkeys ( mapIt ( custody_bit_0_indices , state . validator_registry [ it . int ] . pubkey ) ) ,
bls_aggregate_pubkeys ( mapIt ( custody_bit_1_indices , state . validator_registry [ it . int ] . pubkey ) ) ,
] ,
@ [
hash_tree_root ( AttestationDataAndCustodyBit (
data : slashable_attestation . data , custody_bit : false ) ) ,
hash_tree_root ( AttestationDataAndCustodyBit (
data : slashable_attestation . data , custody_bit : true ) ) ,
] ,
slashable_attestation . aggregate_signature ,
get_domain (
state . fork ,
slot_to_epoch ( slashable_attestation . data . slot ) ,
DOMAIN_ATTESTATION ,
) ,
)
2018-12-13 16:00:55 +00:00
2019-02-22 09:56:45 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attester-slashings-1
2019-01-30 15:35:42 +00:00
proc processAttesterSlashings ( state : var BeaconState , blck : BeaconBlock ) : bool =
2019-02-05 18:40:27 +00:00
if len ( blck . body . attester_slashings ) > MAX_ATTESTER_SLASHINGS :
2019-01-25 17:35:22 +00:00
notice " CaspSlash: too many! "
2018-12-13 16:00:55 +00:00
return false
2019-02-05 18:40:27 +00:00
for attester_slashing in blck . body . attester_slashings :
2018-12-19 04:36:10 +00:00
let
2019-02-05 18:40:27 +00:00
slashable_attestation_1 = attester_slashing . slashable_attestation_1
slashable_attestation_2 = attester_slashing . slashable_attestation_2
2018-12-19 04:36:10 +00:00
2019-02-05 18:40:27 +00:00
if not ( slashable_attestation_1 . data ! = slashable_attestation_2 . data ) :
2019-01-25 17:35:22 +00:00
notice " CaspSlash: invalid data "
2018-12-13 16:00:55 +00:00
return false
if not (
2019-02-05 18:40:27 +00:00
is_double_vote ( slashable_attestation_1 . data , slashable_attestation_2 . data ) or
is_surround_vote ( slashable_attestation_1 . data , slashable_attestation_2 . data ) ) :
2019-01-25 17:35:22 +00:00
notice " CaspSlash: surround or double vote check failed "
2018-12-19 04:36:10 +00:00
return false
2019-02-05 18:40:27 +00:00
if not verify_slashable_attestation ( state , slashable_attestation_1 ) :
2019-01-25 17:35:22 +00:00
notice " CaspSlash: invalid votes 1 "
2018-12-19 04:36:10 +00:00
return false
2019-02-05 18:40:27 +00:00
if not verify_slashable_attestation ( state , slashable_attestation_2 ) :
2019-01-25 17:35:22 +00:00
notice " CaspSlash: invalid votes 2 "
2018-12-13 16:00:55 +00:00
return false
2019-02-05 18:40:27 +00:00
let
indices2 = slashable_attestation_2 . validator_indices
slashable_indices =
slashable_attestation_1 . validator_indices . filterIt ( it in indices2 )
if not ( len ( slashable_indices ) > = 1 ) :
notice " CaspSlash: no intersection "
return false
for index in slashable_indices :
2019-02-19 23:07:56 +00:00
if state . validator_registry [ index . int ] . slashed_epoch > get_current_epoch ( state ) :
2019-02-22 09:56:45 +00:00
slash_validator ( state , index . ValidatorIndex )
2018-12-13 16:00:55 +00:00
2019-02-22 09:56:45 +00:00
true
2018-12-13 16:00:55 +00:00
2019-02-22 17:53:37 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attestations-1
2018-12-27 23:40:22 +00:00
proc processAttestations (
state : var BeaconState , blck : BeaconBlock , flags : UpdateFlags ) : bool =
2018-12-13 16:00:55 +00:00
## Each block includes a number of attestations that the proposer chose. Each
## attestation represents an update to a specific shard and is signed by a
## committee of validators.
## Here we make sanity checks for each attestation and it to the state - most
## updates will happen at the epoch boundary where state updates happen in
## bulk.
if blck . body . attestations . len > MAX_ATTESTATIONS :
2019-01-25 17:35:22 +00:00
notice " Attestation: too many! " , attestations = blck . body . attestations . len
2018-12-13 16:00:55 +00:00
return false
2018-12-27 23:40:22 +00:00
if not blck . body . attestations . allIt ( checkAttestation ( state , it , flags ) ) :
2018-12-13 16:00:55 +00:00
return false
# All checks passed - update state
2018-12-27 23:40:22 +00:00
state . latest_attestations . add blck . body . attestations . mapIt (
2019-01-30 15:35:42 +00:00
PendingAttestation (
2018-12-13 16:00:55 +00:00
data : it . data ,
2019-02-07 16:55:48 +00:00
aggregation_bitfield : it . aggregation_bitfield ,
2018-12-13 16:00:55 +00:00
custody_bitfield : it . custody_bitfield ,
2019-02-07 16:55:48 +00:00
inclusion_slot : state . slot ,
2018-12-13 16:00:55 +00:00
)
)
true
2019-02-22 17:53:37 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#deposits-1
func processDeposits ( state : var BeaconState , blck : BeaconBlock ) : bool =
# TODO! Spec writing in progress as of v0.3.0
true
2019-01-16 11:39:16 +00:00
2019-02-22 17:53:37 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#voluntary-exits-1
2018-12-27 23:40:22 +00:00
proc processExits (
state : var BeaconState , blck : BeaconBlock , flags : UpdateFlags ) : bool =
2019-02-19 23:07:56 +00:00
if len ( blck . body . voluntary_exits ) > MAX_VOLUNTARY_EXITS :
2019-01-25 17:35:22 +00:00
notice " Exit: too many! "
2018-12-13 16:00:55 +00:00
return false
2019-02-19 23:07:56 +00:00
for exit in blck . body . voluntary_exits :
2019-02-11 14:32:27 +00:00
let validator = state . validator_registry [ exit . validator_index . int ]
2018-12-13 16:00:55 +00:00
2019-02-22 17:53:37 +00:00
if not ( validator . exit_epoch > get_entry_exit_effect_epoch ( get_current_epoch ( state ) ) ) :
notice " Exit: exit/entry too close "
return false
if not ( get_current_epoch ( state ) > = exit . epoch ) :
notice " Exit: bad epoch "
return false
2018-12-27 23:40:22 +00:00
if skipValidation notin flags :
2019-02-22 17:53:37 +00:00
let exit_message = hash_tree_root (
# In 0.3.0 spec, this is "Exit", but that's a renaming mismatch
VoluntaryExit (
epoch : exit . epoch , validator_index : exit . validator_index ,
signature : EMPTY_SIGNATURE ) )
2018-12-27 23:40:22 +00:00
if not bls_verify (
2019-02-22 17:53:37 +00:00
validator . pubkey , exit_message , exit . signature ,
2019-02-07 10:43:21 +00:00
get_domain ( state . fork , exit . epoch , DOMAIN_EXIT ) ) :
2019-01-25 17:35:22 +00:00
notice " Exit: invalid signature "
2018-12-27 23:40:22 +00:00
return false
2018-12-13 16:00:55 +00:00
2019-02-22 17:53:37 +00:00
initiate_validator_exit ( state , exit . validator_index . ValidatorIndex )
true
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#transfers-1
proc processTransfers ( state : var BeaconState , blck : BeaconBlock ,
flags : UpdateFlags ) : bool =
## Note: Transfers are a temporary functionality for phases 0 and 1, to be
## removed in phase 2.
if not ( len ( blck . body . transfers ) < = MAX_TRANSFERS ) :
notice " Transfer: too many transfers "
return false
for transfer in blck . body . transfers :
let from_balance = state . validator_balances [ transfer . from_field . int ]
if not ( from_balance > = transfer . amount ) :
notice " Transfer: source balance too low for amount "
2018-12-13 16:00:55 +00:00
return false
2019-02-22 17:53:37 +00:00
if not ( from_balance > = transfer . fee ) :
notice " Transfer: source balance too low for fee "
2018-12-13 16:00:55 +00:00
return false
2019-02-22 17:53:37 +00:00
if not ( from_balance = = transfer . amount + transfer . fee or from_balance > =
transfer . amount + transfer . fee + MIN_DEPOSIT_AMOUNT ) :
notice " Transfer: source balance too low for amount + fee "
return false
2018-12-13 16:00:55 +00:00
2019-02-22 17:53:37 +00:00
if not ( state . slot = = transfer . slot ) :
notice " Transfer: slot mismatch "
return false
if not ( get_current_epoch ( state ) > =
state . validator_registry [ transfer . from_field . int ] . withdrawable_epoch ) :
notice " Transfer: epoch mismatch "
return false
let wc = state . validator_registry [ transfer . from_field . int ] .
withdrawal_credentials
if not ( wc . data [ 0 ] = = BLS_WITHDRAWAL_PREFIX_BYTE and
wc . data [ 1 .. ^ 1 ] = = eth2hash ( transfer . pubkey . getBytes ) . data [ 1 .. ^ 1 ] ) :
notice " Transfer: incorrect withdrawal credentials "
return false
if skipValidation notin flags :
let transfer_message = hash_tree_root (
Transfer (
from_field : transfer . from_field , to : transfer . to ,
amount : transfer . amount , fee : transfer . fee , slot : transfer . slot ,
signature : EMPTY_SIGNATURE ) )
2019-02-22 19:17:53 +00:00
if not bls_verify (
2019-02-22 17:53:37 +00:00
pubkey = transfer . pubkey , transfer_message , transfer . signature ,
get_domain ( state . fork , slot_to_epoch ( transfer . slot ) , DOMAIN_TRANSFER ) ) :
notice " Transfer: incorrect signature "
return false
state . validator_balances [
transfer . from_field . int ] - = transfer . amount + transfer . fee
state . validator_balances [ transfer . to . int ] + = transfer . amount
state . validator_balances [
get_beacon_proposer_index ( state , state . slot ) ] + = transfer . fee
true
2018-12-13 16:00:55 +00:00
2019-02-27 13:58:07 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#ejections
func process_ejections ( state : var BeaconState ) =
2018-12-13 16:00:55 +00:00
## Iterate through the validator registry and eject active validators with
## balance below ``EJECTION_BALANCE``
2019-02-27 13:58:07 +00:00
for index in get_active_validator_indices (
# TODO minor 0.3.0 bug: it says just current_epoch(state)
state . validator_registry , get_current_epoch ( state ) ) :
if state . validator_balances [ index ] < EJECTION_BALANCE :
exit_validator ( state , index )
2018-12-13 16:00:55 +00:00
2019-02-14 21:41:04 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.2.0/specs/core/0_beacon-chain.md#per-slot-processing
2018-12-21 22:37:46 +00:00
func processSlot ( state : var BeaconState , previous_block_root : Eth2Digest ) =
2018-12-19 04:36:10 +00:00
## Time on the beacon chain moves in slots. Every time we make it to a new
2018-12-27 20:14:37 +00:00
## slot, a proposer creates a block to represent the state of the beacon
2018-12-19 04:36:10 +00:00
## chain at that time. In case the proposer is missing, it may happen that
## the no block is produced during the slot.
2019-02-22 09:56:45 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#slot
2018-12-19 04:36:10 +00:00
state . slot + = 1
2019-02-22 09:56:45 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#block-roots
2018-12-21 22:37:46 +00:00
state . latest_block_roots [ ( state . slot - 1 ) mod LATEST_BLOCK_ROOTS_LENGTH ] =
2018-12-19 04:36:10 +00:00
previous_block_root
if state . slot mod LATEST_BLOCK_ROOTS_LENGTH = = 0 :
state . batched_block_roots . add ( merkle_root ( state . latest_block_roots ) )
proc processBlock (
2018-12-21 22:37:46 +00:00
state : var BeaconState , blck : BeaconBlock , flags : UpdateFlags ) : bool =
2018-12-13 16:00:55 +00:00
## When there's a new block, we need to verify that the block is sane and
## update the state accordingly
# TODO when there's a failure, we should reset the state!
# TODO probably better to do all verification first, then apply state changes
2019-02-22 09:56:45 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#slot-1
2018-12-19 04:36:10 +00:00
if not ( blck . slot = = state . slot ) :
2019-01-25 17:35:22 +00:00
notice " Unexpected block slot number " ,
2019-02-21 04:42:17 +00:00
blockSlot = humaneSlotNum ( blck . slot ) ,
stateSlot = humaneSlotNum ( state . slot )
2018-12-13 16:00:55 +00:00
return false
2018-12-21 22:37:46 +00:00
# Spec does not have this check explicitly, but requires that this condition
# holds - so we give verify it as well - this would happen naturally if
# `blck.parent_root` was used in `processSlot` - but that doesn't cut it for
# blockless slot processing.
2019-01-25 17:35:22 +00:00
let stateParentRoot =
state . latest_block_roots [ ( state . slot - 1 ) mod LATEST_BLOCK_ROOTS_LENGTH ]
if not ( blck . parent_root = = stateParentRoot ) :
notice " Unexpected parent root " ,
blockParentRoot = blck . parent_root ,
stateParentRoot
2018-12-13 16:00:55 +00:00
return false
2018-12-21 22:37:46 +00:00
if skipValidation notin flags :
2018-12-19 04:36:10 +00:00
# TODO Technically, we could make processBlock take a generic type instead
# of BeaconBlock - we would then have an intermediate `ProposedBlock`
# type that omits some fields - this way, the compiler would guarantee
# that we don't try to access fields that don't have a value yet
if not verifyProposerSignature ( state , blck ) :
2019-02-12 15:56:58 +00:00
notice " Proposer signature not valid " , slot = humaneSlotNum ( state . slot )
2018-12-19 04:36:10 +00:00
return false
2018-12-21 22:37:46 +00:00
if not processRandao ( state , blck , flags ) :
2018-12-13 16:00:55 +00:00
return false
2019-01-16 10:21:06 +00:00
processDepositRoot ( state , blck )
2018-12-13 16:00:55 +00:00
2018-12-27 23:40:22 +00:00
if not processProposerSlashings ( state , blck , flags ) :
2018-12-13 16:00:55 +00:00
return false
2019-01-30 15:35:42 +00:00
if not processAttesterSlashings ( state , blck ) :
2018-12-13 16:00:55 +00:00
return false
2018-12-03 21:41:24 +00:00
2018-12-27 23:40:22 +00:00
if not processAttestations ( state , blck , flags ) :
2018-12-13 16:00:55 +00:00
return false
if not processDeposits ( state , blck ) :
return false
2018-12-27 23:40:22 +00:00
if not processExits ( state , blck , flags ) :
2018-12-13 16:00:55 +00:00
return false
2019-02-22 17:53:37 +00:00
if not processTransfers ( state , blck , flags ) :
return false
2018-12-13 16:00:55 +00:00
2019-02-22 17:53:37 +00:00
true
2018-12-13 16:00:55 +00:00
2018-12-27 20:14:37 +00:00
func get_attester_indices (
2018-12-03 21:41:24 +00:00
state : BeaconState ,
2019-01-30 15:35:42 +00:00
attestations : openArray [ PendingAttestation ] ) : seq [ ValidatorIndex ] =
2018-12-19 04:36:10 +00:00
# Union of attesters that participated in some attestations
2018-12-04 18:45:30 +00:00
# TODO spec - add as helper?
2018-12-27 23:40:22 +00:00
attestations .
mapIt (
2019-02-07 16:55:48 +00:00
get_attestation_participants ( state , it . data , it . aggregation_bitfield ) ) .
2018-12-27 23:40:22 +00:00
flatten ( ) .
deduplicate ( )
2018-12-03 21:41:24 +00:00
2018-12-04 18:45:30 +00:00
func boundary_attestations (
state : BeaconState , boundary_hash : Eth2Digest ,
2019-01-30 15:35:42 +00:00
attestations : openArray [ PendingAttestation ]
) : seq [ PendingAttestation ] =
2018-12-04 18:45:30 +00:00
# TODO spec - add as helper?
2019-02-26 09:54:08 +00:00
attestations . filterIt ( it . data . epoch_boundary_root = = boundary_hash )
2018-12-04 18:45:30 +00:00
func lowerThan ( candidate , current : Eth2Digest ) : bool =
# return true iff candidate is "lower" than current, per spec rule:
2018-12-11 21:53:18 +00:00
# "ties broken by favoring lower `shard_block_root` values"
2018-12-04 18:45:30 +00:00
# TODO spec - clarify hash ordering..
for i , v in current . data :
if v > candidate . data [ i ] : return true
2019-02-22 18:35:58 +00:00
false
2018-12-04 18:45:30 +00:00
2019-02-26 09:54:08 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
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 (
state . validator_registry , current_epoch )
# TODO 0.3.0 spec doesn't use this helper function?
total_balance = get_total_balance ( state , active_validator_indices )
for index , validator in state . validator_registry :
if current_epoch = =
validator . slashed_epoch + LATEST_SLASHED_EXIT_LENGTH div 2 :
let
epoch_index = current_epoch mod LATEST_SLASHED_EXIT_LENGTH
total_at_start = state . latest_slashed_balances [
( epoch_index + 1 ) mod LATEST_SLASHED_EXIT_LENGTH ]
total_at_end = state . latest_slashed_balances [ epoch_index ]
total_penalties = total_at_end - total_at_start
penalty = max (
get_effective_balance ( state , index . ValidatorIndex ) *
min ( total_penalties * 3 , total_balance ) div total_balance ,
get_effective_balance ( state , index . ValidatorIndex ) div
MIN_PENALTY_QUOTIENT )
state . validator_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 +
# TODO in future versions, remove workaround for 0.3.0 spec bug
# but for 0.3.0 use MIN_VALIDATOR_WITHDRAWAL_DELAY constant
MIN_VALIDATOR_WITHDRAWAL_DELAY
# TODO try again with filterIt
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 > = MAX_EXIT_DEQUEUES_PER_EPOCH :
break
prepare_validator_for_withdrawal ( state , index )
2019-02-27 13:58:07 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#per-epoch-processing
2018-12-14 16:12:39 +00:00
func processEpoch ( state : var BeaconState ) =
2019-02-20 01:33:58 +00:00
if ( state . slot + 1 ) mod SLOTS_PER_EPOCH ! = 0 :
2018-12-14 16:12:39 +00:00
return
2018-12-03 21:41:24 +00:00
2019-02-22 17:53:37 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#helper-variables
2018-12-14 16:12:39 +00:00
let
2019-01-29 03:22:22 +00:00
current_epoch = get_current_epoch ( state )
2019-02-22 18:35:58 +00:00
previous_epoch =
if current_epoch > GENESIS_EPOCH :
current_epoch - 1
else :
current_epoch
2019-02-20 01:33:58 +00:00
next_epoch = ( current_epoch + 1 ) . Epoch
2019-01-29 03:22:22 +00:00
2019-02-22 18:35:58 +00:00
current_total_balance = get_total_balance (
state , get_active_validator_indices (
state . validator_registry , current_epoch ) )
# TODO doing this with iterators failed:
# https://github.com/nim-lang/Nim/issues/9827
2018-12-27 23:40:22 +00:00
current_epoch_attestations =
state . latest_attestations . filterIt (
2019-02-22 18:35:58 +00:00
current_epoch = = slot_to_epoch ( it . data . slot ) )
2018-12-03 21:41:24 +00:00
2018-12-27 23:40:22 +00:00
current_epoch_boundary_attestations =
2018-12-14 16:12:39 +00:00
boundary_attestations (
2019-02-22 18:35:58 +00:00
state , get_block_root ( state , get_epoch_start_slot ( current_epoch ) ) ,
2018-12-27 23:40:22 +00:00
current_epoch_attestations )
2018-12-03 21:41:24 +00:00
2018-12-27 23:40:22 +00:00
current_epoch_boundary_attester_indices =
2019-02-22 18:35:58 +00:00
get_attester_indices ( state , current_epoch_boundary_attestations )
2018-12-03 21:41:24 +00:00
2018-12-27 23:40:22 +00:00
current_epoch_boundary_attesting_balance =
2019-02-22 18:35:58 +00:00
get_total_balance ( state , current_epoch_boundary_attester_indices )
2018-12-03 21:41:24 +00:00
2019-02-26 09:54:08 +00:00
# Validators attesting during the previous epoch
## Validators that made an attestation during the previous epoch, targeting
## the previous justified slot
2018-12-14 16:12:39 +00:00
let
2019-02-26 09:54:08 +00:00
previous_total_balance = get_total_balance (
state , get_active_validator_indices (
state . validator_registry , previous_epoch ) )
2018-12-27 23:40:22 +00:00
previous_epoch_attestations =
state . latest_attestations . filterIt (
2019-02-06 12:31:41 +00:00
previous_epoch = = slot_to_epoch ( it . data . slot ) )
2018-12-14 16:12:39 +00:00
2018-12-27 20:14:37 +00:00
previous_epoch_attester_indices =
get_attester_indices ( state , previous_epoch_attestations )
2018-12-14 16:12:39 +00:00
2019-02-26 09:54:08 +00:00
previous_epoch_attesting_balance =
get_total_balance ( state , previous_epoch_attester_indices )
2018-12-14 16:12:39 +00:00
2019-02-26 09:54:08 +00:00
## Validators justifying the epoch boundary block at the start of the
## previous epoch
let
2018-12-14 16:12:39 +00:00
previous_epoch_boundary_attestations =
boundary_attestations (
2019-02-26 09:54:08 +00:00
state , get_block_root ( state , get_epoch_start_slot ( previous_epoch ) ) ,
2018-12-14 16:12:39 +00:00
previous_epoch_attestations )
2018-12-27 20:14:37 +00:00
previous_epoch_boundary_attester_indices =
get_attester_indices ( state , previous_epoch_boundary_attestations )
2018-12-14 16:12:39 +00:00
previous_epoch_boundary_attesting_balance =
2019-02-22 18:46:45 +00:00
get_total_balance ( state , previous_epoch_boundary_attester_indices )
2018-12-14 16:12:39 +00:00
2019-02-26 09:54:08 +00:00
## Validators attesting to the expected beacon chain head during the
## previous epoch
let
2018-12-14 16:12:39 +00:00
previous_epoch_head_attestations =
2018-12-27 23:40:22 +00:00
previous_epoch_attestations . filterIt (
2018-12-14 16:12:39 +00:00
it . data . beacon_block_root = = get_block_root ( state , it . data . slot ) )
2018-12-27 20:14:37 +00:00
previous_epoch_head_attester_indices =
get_attester_indices ( state , previous_epoch_head_attestations )
2018-12-14 16:12:39 +00:00
previous_epoch_head_attesting_balance =
2019-02-22 18:46:45 +00:00
get_total_balance ( state , previous_epoch_head_attester_indices )
2018-12-14 16:12:39 +00:00
# TODO this is really hairy - we cannot capture `state` directly, but we
# can capture a pointer to it - this is safe because we don't leak
# these closures outside this scope, but still..
let statePtr = state . addr
2018-12-27 20:14:37 +00:00
func attesting_validator_indices (
2019-02-06 19:37:25 +00:00
crosslink_committee : CrosslinkCommittee , shard_block_root : Eth2Digest ) : seq [ ValidatorIndex ] =
2018-12-19 04:36:10 +00:00
let shard_block_attestations =
2018-12-27 23:40:22 +00:00
concat ( current_epoch_attestations , previous_epoch_attestations ) .
2019-02-06 19:37:25 +00:00
filterIt ( it . data . shard = = crosslink_committee . shard and
2018-12-27 23:40:22 +00:00
it . data . shard_block_root = = shard_block_root )
2018-12-27 20:14:37 +00:00
get_attester_indices ( statePtr [ ] , shard_block_attestations )
2018-12-14 16:12:39 +00:00
2019-02-06 19:37:25 +00:00
func winning_root ( crosslink_committee : CrosslinkCommittee ) : Eth2Digest =
2019-01-26 19:32:10 +00:00
# * Let `winning_root(crosslink_committee)` be equal to the value of
2018-12-27 20:14:37 +00:00
# `shard_block_root` such that
2019-01-26 19:32:10 +00:00
# `sum([get_effective_balance(state, i) for i in attesting_validator_indices(crosslink_committee, shard_block_root)])`
2018-12-27 20:14:37 +00:00
# is maximized (ties broken by favoring lower `shard_block_root` values).
2018-12-14 16:12:39 +00:00
let candidates =
2018-12-27 23:40:22 +00:00
concat ( current_epoch_attestations , previous_epoch_attestations ) .
2019-02-06 19:37:25 +00:00
filterIt ( it . data . shard = = crosslink_committee . shard ) .
2018-12-27 23:40:22 +00:00
mapIt ( it . data . shard_block_root )
2018-12-14 16:12:39 +00:00
# TODO not covered by spec!
if candidates . len = = 0 :
return
var max_hash = candidates [ 0 ]
var max_val =
2019-02-22 18:46:45 +00:00
get_total_balance (
2019-01-26 19:32:10 +00:00
statePtr [ ] , attesting_validator_indices ( crosslink_committee , max_hash ) )
2018-12-14 16:12:39 +00:00
for candidate in candidates [ 1 .. ^ 1 ] :
2019-02-22 18:46:45 +00:00
let val = get_total_balance (
2019-01-26 19:32:10 +00:00
statePtr [ ] , attesting_validator_indices ( crosslink_committee , candidate ) )
2018-12-14 16:12:39 +00:00
if val > max_val or ( val = = max_val and candidate . lowerThan ( max_hash ) ) :
max_hash = candidate
max_val = val
max_hash
2019-02-06 19:37:25 +00:00
func attesting_validators ( crosslink_committee : CrosslinkCommittee ) : seq [ ValidatorIndex ] =
2019-01-26 19:32:10 +00:00
attesting_validator_indices ( crosslink_committee , winning_root ( crosslink_committee ) )
2018-12-14 16:12:39 +00:00
2019-02-06 19:37:25 +00:00
func attesting_validator_indices ( crosslink_committee : CrosslinkCommittee ) : seq [ ValidatorIndex ] =
2019-01-26 19:32:10 +00:00
attesting_validator_indices ( crosslink_committee , winning_root ( crosslink_committee ) )
2019-02-06 19:37:25 +00:00
func total_attesting_balance ( crosslink_committee : CrosslinkCommittee ) : uint64 =
2019-02-22 18:46:45 +00:00
get_total_balance (
2019-01-26 19:32:10 +00:00
statePtr [ ] , attesting_validator_indices ( crosslink_committee ) )
2018-12-14 16:12:39 +00:00
2019-02-06 19:37:25 +00:00
func total_balance_sac ( crosslink_committee : CrosslinkCommittee ) : uint64 =
2019-02-22 18:46:45 +00:00
get_total_balance ( statePtr [ ] , crosslink_committee . committee )
2018-12-14 16:12:39 +00:00
2019-02-27 13:58:07 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#eth1-data-1
block :
if next_epoch mod EPOCHS_PER_ETH1_VOTING_PERIOD = = 0 :
2019-01-18 00:14:22 +00:00
for x in state . eth1_data_votes :
2019-02-27 13:58:07 +00:00
if x . vote_count * 2 > = EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH :
# more than half the votes in this voting period were for that value
2019-01-18 00:14:22 +00:00
state . latest_eth1_data = x . eth1_data
2018-12-14 16:12:39 +00:00
break
2019-01-18 00:14:22 +00:00
state . eth1_data_votes = @ [ ]
2018-12-14 16:12:39 +00:00
2019-02-27 13:58:07 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#justification
block :
2019-01-29 03:22:22 +00:00
# First, update the justification bitfield
var new_justified_epoch = state . justified_epoch
2018-12-14 16:12:39 +00:00
state . justification_bitfield = state . justification_bitfield shl 1
2019-01-29 03:22:22 +00:00
if 3 'u64 * previous_epoch_boundary_attesting_balance > = 2 'u64 * previous_total_balance :
state . justification_bitfield = state . justification_bitfield or 2
new_justified_epoch = previous_epoch
if 3 'u64 * current_epoch_boundary_attesting_balance > = 2 'u64 * current_total_balance :
state . justification_bitfield = state . justification_bitfield or 1
new_justified_epoch = current_epoch
# Next, update last finalized epoch if possible
if ( state . justification_bitfield shr 1 ) mod 8 = = 0b111 and state . previous_justified_epoch = = previous_epoch - 2 :
state . finalized_epoch = state . previous_justified_epoch
if ( state . justification_bitfield shr 1 ) mod 4 = = 0b11 and state . previous_justified_epoch = = previous_epoch - 1 :
state . finalized_epoch = state . previous_justified_epoch
if ( state . justification_bitfield shr 0 ) mod 8 = = 0b111 and state . justified_epoch = = previous_epoch - 1 :
state . finalized_epoch = state . justified_epoch
if ( state . justification_bitfield shr 0 ) mod 4 = = 0b11 and state . justified_epoch = = previous_epoch :
state . finalized_epoch = state . justified_epoch
# Finally, update the following
state . previous_justified_epoch = state . justified_epoch
state . justified_epoch = new_justified_epoch
2018-12-14 16:12:39 +00:00
2019-02-27 13:58:07 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#crosslinks
block :
2019-01-29 03:22:22 +00:00
for slot in get_epoch_start_slot ( previous_epoch ) .. < get_epoch_start_slot ( next_epoch ) :
let crosslink_committees_at_slot = get_crosslink_committees_at_slot ( state , slot )
2019-02-27 13:58:07 +00:00
for crosslink_committee in crosslink_committees_at_slot :
if 3 'u64 * total_attesting_balance ( crosslink_committee ) > =
2 'u64 * get_total_balance ( state , crosslink_committee . committee ) :
state . latest_crosslinks [ crosslink_committee . shard ] = Crosslink (
epoch : slot_to_epoch ( slot ) ,
shard_block_root : winning_root ( crosslink_committee ) )
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#rewards-and-penalties
## First, we define some additional helpers
## Note: When applying penalties in the following balance recalculations
## implementers should make sure the uint64 does not underflow
let base_reward_quotient =
integer_squareroot ( previous_total_balance ) div BASE_REWARD_QUOTIENT
2019-02-05 18:40:27 +00:00
func base_reward ( state : BeaconState , index : ValidatorIndex ) : uint64 =
2019-02-27 13:58:07 +00:00
get_effective_balance ( state , index ) div base_reward_quotient . uint64 div 5
2019-02-05 18:40:27 +00:00
func inactivity_penalty (
state : BeaconState , index : ValidatorIndex , epochs_since_finality : uint64 ) : uint64 =
base_reward ( state , index ) +
2019-02-27 13:58:07 +00:00
get_effective_balance ( state , index ) * epochs_since_finality div
INACTIVITY_PENALTY_QUOTIENT div 2
2019-02-05 18:40:27 +00:00
2019-02-27 13:58:07 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#justification-and-finalization
2019-02-06 12:31:41 +00:00
func inclusion_slot ( state : BeaconState , v : ValidatorIndex ) : uint64 =
for a in previous_epoch_attestations :
2019-02-07 16:55:48 +00:00
if v in get_attestation_participants ( state , a . data , a . aggregation_bitfield ) :
return a . inclusion_slot
2019-02-27 13:58:07 +00:00
doAssert false
2019-02-06 12:31:41 +00:00
func inclusion_distance ( state : BeaconState , v : ValidatorIndex ) : uint64 =
for a in previous_epoch_attestations :
2019-02-07 16:55:48 +00:00
if v in get_attestation_participants ( state , a . data , a . aggregation_bitfield ) :
return a . inclusion_slot - a . data . slot
2019-02-27 13:58:07 +00:00
doAssert false
2019-01-29 03:22:22 +00:00
2019-02-22 18:35:58 +00:00
let active_validator_indices =
get_active_validator_indices ( state . validator_registry , state . slot )
2018-12-14 16:12:39 +00:00
block : # Justification and finalization
2019-01-29 03:22:22 +00:00
let epochs_since_finality = next_epoch - state . finalized_epoch
2018-12-14 16:12:39 +00:00
2019-01-29 04:15:00 +00:00
proc update_balance ( attesters : openArray [ ValidatorIndex ] , attesting_balance : uint64 ) =
2018-12-19 04:36:10 +00:00
# TODO Spec - add helper?
for v in attesters :
2018-12-27 20:14:37 +00:00
statePtr . validator_balances [ v ] + =
base_reward ( statePtr [ ] , v ) *
2019-02-26 09:54:08 +00:00
attesting_balance div previous_total_balance
2018-12-14 16:12:39 +00:00
for v in active_validator_indices :
2018-12-19 04:36:10 +00:00
if v notin attesters :
2018-12-27 23:40:22 +00:00
# TODO underflows?
2018-12-27 20:14:37 +00:00
statePtr . validator_balances [ v ] - = base_reward ( statePtr [ ] , v )
2018-12-14 16:12:39 +00:00
2019-02-26 09:54:08 +00:00
if epochs_since_finality < = 4 'u64 :
2018-12-19 04:36:10 +00:00
# Expected FFG source
update_balance (
2019-02-26 09:54:08 +00:00
previous_epoch_attester_indices ,
previous_epoch_attesting_balance )
2018-12-14 16:12:39 +00:00
2018-12-19 04:36:10 +00:00
# Expected FFG target:
update_balance (
2018-12-27 20:14:37 +00:00
previous_epoch_boundary_attester_indices ,
2018-12-19 04:36:10 +00:00
previous_epoch_boundary_attesting_balance )
2018-12-14 16:12:39 +00:00
# Expected beacon chain head:
2018-12-19 04:36:10 +00:00
update_balance (
2018-12-27 20:14:37 +00:00
previous_epoch_head_attester_indices ,
2018-12-19 04:36:10 +00:00
previous_epoch_head_attesting_balance )
2018-12-14 16:12:39 +00:00
2018-12-27 20:14:37 +00:00
# Inclusion distance
for v in previous_epoch_attester_indices :
statePtr . validator_balances [ v ] + =
base_reward ( state , v ) *
MIN_ATTESTATION_INCLUSION_DELAY div inclusion_distance ( state , v )
2018-12-14 16:12:39 +00:00
else :
2018-12-27 20:14:37 +00:00
for index in active_validator_indices :
2018-12-27 23:40:22 +00:00
# TODO underflows?
2019-02-26 09:54:08 +00:00
if index notin previous_epoch_attester_indices :
2018-12-27 20:14:37 +00:00
state . validator_balances [ index ] - =
inactivity_penalty ( state , index , epochs_since_finality )
if index notin previous_epoch_boundary_attester_indices :
state . validator_balances [ index ] - =
inactivity_penalty ( state , index , epochs_since_finality )
if index notin previous_epoch_head_attester_indices :
state . validator_balances [ index ] - =
inactivity_penalty ( state , index , epochs_since_finality )
2018-12-14 16:12:39 +00:00
block : # Attestation inclusion
2018-12-27 20:14:37 +00:00
for v in previous_epoch_attester_indices :
let proposer_index =
get_beacon_proposer_index ( state , inclusion_slot ( state , v ) )
state . validator_balances [ proposer_index ] + =
2019-02-19 23:07:56 +00:00
base_reward ( state , v ) div ATTESTATION_INCLUSION_REWARD_QUOTIENT
2018-12-14 16:12:39 +00:00
block : # Crosslinks
2019-01-29 03:22:22 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#crosslinks-1
for slot in get_epoch_start_slot ( previous_epoch ) .. < get_epoch_start_slot ( current_epoch ) :
2019-01-26 19:32:10 +00:00
let crosslink_committees_at_slot = get_crosslink_committees_at_slot ( state , slot )
for crosslink_committee in crosslink_committees_at_slot :
# TODO https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#crosslinks-1
# but this is a best guess based on reasonableness of what "index" is
2019-02-06 19:37:25 +00:00
for index in crosslink_committee . committee :
2019-01-26 19:32:10 +00:00
if index in attesting_validators ( crosslink_committee ) :
2019-02-06 19:37:25 +00:00
state . validator_balances [ index . int ] + =
base_reward ( state , index ) *
total_attesting_balance ( crosslink_committee ) div
total_balance_sac ( crosslink_committee )
2018-12-14 16:12:39 +00:00
else :
2018-12-27 23:40:22 +00:00
# TODO underflows?
2018-12-27 20:14:37 +00:00
state . validator_balances [ index ] - = base_reward ( state , index )
2018-12-14 16:12:39 +00:00
2019-02-27 13:58:07 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#ejections
process_ejections ( state )
2019-01-26 19:32:10 +00:00
2019-02-27 13:58:07 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
block :
2019-02-19 23:07:56 +00:00
state . previous_shuffling_epoch = state . current_shuffling_epoch
state . previous_shuffling_start_shard = state . current_shuffling_start_shard
state . previous_shuffling_seed = state . current_shuffling_seed
2019-01-26 19:32:10 +00:00
2019-02-27 13:58:07 +00:00
# TODO verify this shard list
2019-01-29 03:22:22 +00:00
if state . finalized_epoch > state . validator_registry_update_epoch and
2019-01-26 19:32:10 +00:00
allIt (
2019-02-20 01:33:58 +00:00
0 .. < get_current_epoch_committee_count ( state ) . int * SLOTS_PER_EPOCH ,
2019-02-19 23:07:56 +00:00
state . latest_crosslinks [ ( state . current_shuffling_start_shard + it . uint64 ) mod SHARD_COUNT ] . epoch > state . validator_registry_update_epoch ) :
2018-12-14 16:12:39 +00:00
update_validator_registry ( state )
2019-01-29 03:22:22 +00:00
2019-02-19 23:07:56 +00:00
state . current_shuffling_epoch = next_epoch
2019-02-27 13:58:07 +00:00
state . current_shuffling_start_shard =
( state . current_shuffling_start_shard +
get_current_epoch_committee_count ( state ) ) mod SHARD_COUNT
state . current_shuffling_seed = generate_seed (
state , state . current_shuffling_epoch )
2018-12-14 16:12:39 +00:00
else :
# If a validator registry change does NOT happen
2019-01-29 03:22:22 +00:00
let epochs_since_last_registry_change = current_epoch - state . validator_registry_update_epoch
2019-01-26 19:32:10 +00:00
if is_power_of_2 ( epochs_since_last_registry_change ) :
2019-02-19 23:07:56 +00:00
state . current_shuffling_epoch = next_epoch
state . current_shuffling_seed = generate_seed ( state , state . current_shuffling_epoch )
# /Note/ that state.current_shuffling_start_shard is left unchanged
2018-12-27 20:14:37 +00:00
2019-02-26 09:54:08 +00:00
## 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.3.0/specs/core/0_beacon-chain.md#final-updates
block :
state . latest_active_index_roots [
( next_epoch + ACTIVATION_EXIT_DELAY ) mod
LATEST_ACTIVE_INDEX_ROOTS_LENGTH ] =
Eth2Digest ( data : hash_tree_root ( get_active_validator_indices (
# TODO 0.3.0 spec here has bug: get_active_validator_indices(state, ...)
state . validator_registry , next_epoch + ACTIVATION_EXIT_DELAY ) ) )
state . latest_slashed_balances [ ( next_epoch mod
LATEST_SLASHED_EXIT_LENGTH ) . int ] =
state . latest_slashed_balances [
( current_epoch mod LATEST_SLASHED_EXIT_LENGTH ) . int ]
state . latest_randao_mixes [ next_epoch mod LATEST_RANDAO_MIXES_LENGTH ] =
get_randao_mix ( state , current_epoch )
2018-12-14 16:12:39 +00:00
state . latest_attestations . keepItIf (
2019-02-26 09:54:08 +00:00
not ( slot_to_epoch ( it . data . slot ) < current_epoch )
2018-12-14 16:12:39 +00:00
)
2018-12-03 21:41:24 +00:00
2019-02-26 09:54:08 +00:00
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#state-root-verification
2018-12-21 22:37:46 +00:00
proc verifyStateRoot ( state : BeaconState , blck : BeaconBlock ) : bool =
2018-12-21 23:47:55 +00:00
let state_root = hash_tree_root_final ( state )
2018-12-21 22:37:46 +00:00
if state_root ! = blck . state_root :
2019-01-25 17:35:22 +00:00
notice " Block: root verification failed " ,
block_state_root = blck . state_root , state_root
2018-12-21 22:37:46 +00:00
false
else :
true
2018-12-27 20:14:37 +00:00
proc updateState * ( state : var BeaconState , previous_block_root : Eth2Digest ,
new_block : Option [ BeaconBlock ] , flags : UpdateFlags ) : bool =
2018-12-13 16:00:55 +00:00
## Time in the beacon chain moves by slots. Every time (haha.) that happens,
## we will update the beacon state. Normally, the state updates will be driven
## by the contents of a new block, but it may happen that the block goes
## missing - the state updates happen regardless.
## Each call to this function will advance the state by one slot - new_block,
## if present, must match that slot.
2018-12-19 04:36:10 +00:00
##
2018-12-21 22:37:46 +00:00
## The flags are used to specify that certain validations should be skipped
## for the new block. This is done during block proposal, to create a state
## whose hash can be included in the new block.
2018-12-13 16:00:55 +00:00
#
# TODO this function can be written with a loop inside to handle all empty
# slots up to the slot of the new_block - but then again, why not eagerly
# update the state as time passes? Something to ponder...
2018-12-21 22:37:46 +00:00
# One reason to keep it this way is that you need to look ahead if you're
# the block proposer, though in reality we only need a partial update for
# that
2018-12-04 18:45:30 +00:00
# TODO check to which extent this copy can be avoided (considering forks etc),
# for now, it serves as a reminder that we need to handle invalid blocks
# somewhere..
# TODO many functions will mutate `state` partially without rolling back
# the changes in case of failure (look out for `var BeaconState` and
# bool return values...)
2018-12-13 16:00:55 +00:00
# TODO There's a discussion about what this function should do, and when:
# https://github.com/ethereum/eth2.0-specs/issues/284
2018-12-27 20:14:37 +00:00
var old_state = state
2018-12-13 16:00:55 +00:00
# Per-slot updates - these happen regardless if there is a block or not
2018-12-27 20:14:37 +00:00
processSlot ( state , previous_block_root )
2018-12-21 22:37:46 +00:00
if new_block . isSome ( ) :
# Block updates - these happen when there's a new block being suggested
# by the block proposer. Every actor in the network will update its state
# according to the contents of this block - but first they will validate
# that the block is sane.
# TODO what should happen if block processing fails?
# https://github.com/ethereum/eth2.0-specs/issues/293
2018-12-27 20:14:37 +00:00
if processBlock ( state , new_block . get ( ) , flags ) :
2018-12-21 22:37:46 +00:00
# Block ok so far, proceed with state update
2018-12-27 20:14:37 +00:00
processEpoch ( state )
2018-12-03 21:41:24 +00:00
2018-12-21 22:37:46 +00:00
# This is a bit awkward - at the end of processing we verify that the
# state we arrive at is what the block producer thought it would be -
# meaning that potentially, it could fail verification
2018-12-27 20:14:37 +00:00
if skipValidation in flags or verifyStateRoot ( state , new_block . get ( ) ) :
2018-12-21 22:37:46 +00:00
# State root is what it should be - we're done!
2018-12-27 20:14:37 +00:00
return true
2018-12-21 22:37:46 +00:00
# Block processing failed, have to start over
2018-12-27 20:14:37 +00:00
state = old_state
processSlot ( state , previous_block_root )
processEpoch ( state )
false
2018-12-21 22:37:46 +00:00
else :
# Skip all per-block processing. Move directly to epoch processing
# prison. Do not do any block updates when passing go.
# Heavy updates that happen for every epoch - these never fail (or so we hope)
2018-12-27 20:14:37 +00:00
processEpoch ( state )
true
# TODO document this:
# Jacek Sieka
# @arnetheduck
# Dec 21 11:32
# question about making attestations: in the attestation we carry slot and a justified_slot - just to make sure, this justified_slot is the slot that was justified when the state was at slot, not whatever the client may be seeing now? effectively, because we're attesting to MIN_ATTESTATION_INCLUSION_DELAYold states, it might be that we know about a newer justified slot, but don't include it - correct?
# Danny Ryan
# @djrtwo
# Dec 21 11:34
# You are attesting to what you see as the head of the chain at that slot
# The MIN_ATTESTATION_INCLUSION_DELAY is just how many slots must past before this message can be included on chain
# so whatever the justified_slot was inside the state that was associate with the head you are attesting to
# Jacek Sieka
# @arnetheduck
# Dec 21 11:37
# can I revise an attestation, once I get new information (about the shard or state)?
# Danny Ryan
# @djrtwo
# Dec 21 11:37
# We are attesting to the exact current state at that slot. The MIN_ATTESTATION_INCLUSION_DELAY is to attempt to reduce centralization risk in light of fast block times (ensure attestations have time to fully propagate so fair playing field on including them on chain)
# No, if you create two attestations for the same slot, you can be slashed
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_double_vote
# Jacek Sieka
# @arnetheduck
# Dec 21 11:39
# is there any interest for me to publish my attestation before MIN_ATTESTATION_INCLUSION_DELAY-1 time has passed?
# (apart from the risk of it not being picked up on time)
# Danny Ryan
# @djrtwo
# Dec 21 11:40
# that’ s the main risk.
# Note, we’ re a bit unsure about MIN_ATTESTATION_INCLUSION_DELAY because it might open up an attestors timing strategy too much. In the case where MIN_ATTESTATION_INCLUSION_DELAY is removed, we just set it to 1
# part of validator honesty assumption is to attest during your slot. That said, a rational actor might act in any number of interesting ways..
# Jacek Sieka
# @arnetheduck
# Dec 21 11:59
# I can probably google this somehow, but bls signatures, anyone knows off the top of their head if they have to be combined one by one, or can two group signatures be combined? what happens to overlap then?
# Danny Ryan
# @djrtwo
# Dec 21 12:00
# Yeah, you can do any linear combination of signatures. but you have to remember the linear combination of pubkeys that constructed
# if you have two instances of a signature from pubkey p, then you need 2*p in the group pubkey
# because the attestation bitfield is only 1 bit per pubkey right now, attestations do not support this
# it could be extended to support N overlaps up to N times per pubkey if we had N bits per validator instead of 1
# We are shying away from this for the time being. If there end up being substantial difficulties in network layer aggregation, then adding bits to aid in supporting overlaps is one potential solution
# Jacek Sieka
# @arnetheduck
# Dec 21 12:02
# ah nice, you anticipated my followup question there :) so it's not a straight-off set union operation
# Danny Ryan
# @djrtwo
# Dec 21 12:02
# depending on the particular network level troubles we run into
# right
# aggregatng sigs and pubkeys are both just ec adds https://github.com/ethereum/py-evm/blob/d82b10ae361cde6abbac62f171fcea7809c4e3cf/eth/_utils/bls.py#L191-L202
# subtractions work too (i suppose this is obvious). You can linearly combine sigs or pubs in any way
# Jacek Sieka
# @arnetheduck
# Dec 21 12:05
# hm.. well, because one thing I'm thinking of is the scenario where someone attests to some shard head and I receive that attestation.. now, let's say that's an honest attestation, but within that same slot, I have more fresh information about a shard for example.. now, I can either sign the data in the old attestation or churn out a new one, risking that neither of these get enough votes to be useful - does that sound.. accurate?
# Danny Ryan
# @djrtwo
# Dec 21 12:08
# So you won’ t just be signing the head of the shard. This isn’ t specified yet, but it would be targeting some recent epoch boundary to ensure higher chance of consensus.
# If your recent info is about a better fork in the shard than the one you see the other attester signed, then you are better off signing that fork because if it is winning in your few of the shard chain fork choice, then you would assume it is winning in the view of most attesters shard fork choice
# If some strange circumstance arose in which you saw a majority of attestations that signed something you think is unexpected before you signed, a rational actor might defect to this majority. An honest actor would sign what they believe to be true
# in practice, the actor would have to wait some amount of time past when they should have attested to gather this info.
# also, at the end of the day the validator has to compute the non-outsourcable proof of custody bit, so if the other validators are signing off on some shard chain fork they don’ t know about, then they can’ t attest to that data anyway
# (for fear of signing a bad custody bit)
# so their rational move is to just attest to the data they acutally know about and can accurately compute their proof of custody bit on
# Jacek Sieka
# @arnetheduck
# Dec 21 12:58
# what's justified_block_root doing in attestation_data? isn't that available already as get_block_root(state, attestation.data.justified_slot)?
# also, when we sign hash_tree_root(attestation.data) + bytes1(0) - what's the purpose of the 0 byte, given we have domain already?
# Danny Ryan
# @djrtwo
# Dec 21 13:03
# 0 byte is a stub for the proof of custody bit in phase 0
# If the attestation is included in a short range fork but still votes for the chain it is added to’ s justified_block_root/slot, then we want to count the casper vote
# likely if I see the head of the chain as different from what ends up being the canonical chain, my view of the latest justified block might still be in accordance with the canonical chain
# if my attesation is included in a fork, the head i voted on doesn’ t necessarily lead back to the justified block in the fork. Without including justified_block_root, my vote could be used in any fork for the same epoch even if the block at that justified_slot height was different
# Danny Ryan
# @djrtwo
# Dec 21 13:14
# Long story short, because attestations can be included in forks of the head they are actually attesting to, we can’ t be sure of the justified_block that was being voted on by just usng the justified_slot. The security of properties of Casper FFG require that the voter makes a firm commitment to the actual source block, not just the height of the source block
# Jacek Sieka
# @arnetheduck
# Dec 21 13:23
# ok. that's quite a piece. I'm gonna have to draw some diagrams I think :)
# ah. ok. actually makes sense.. I think :)
# Jacek Sieka
# @arnetheduck
# 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).
# Danny Ryan
# @djrtwo
# Dec 21 13:32
# ah, my bad above. We only include an attestation on chain if it is for the correct source
# That’ s one of the bare minimum requirements to get it included on chain. Without the justified_block_root, we can’ t do that check
# essentially that checks if this attestation is relevant at all to the current fork’ s consensus.
# if the justified_block is wrong, then we know the target of the vote and the head of the attestation are wrong too
# sorry for the slight mix up there
# logic still holds — the justified_slot alone is not actually a firm commitment to a particular chain history. You need the associated hash
# Jacek Sieka
# @arnetheduck
# Dec 21 13:35
# can't you just follow Block.parent_root?
# well, that, and ultimately.. Block.state_root
# Danny Ryan
# @djrtwo
# Dec 21 13:37
# The block the attestation is included in might not be for the same fork the attestation was made
# we first make sure that the attestation and the block that it’ s included in match at the justified_slot. if not, throw it out
# then in the incentives, we give some extra reward if the epoch_boundary_root and the chain match
# and some extra reward if the beacon_block_root match
# if all three match, then the attestation is fully agreeing with the canonical chain. +1 casper vote and strengthening the head of the fork choice
# if just justified_block_root and epoch_boundary_root match then the attestation agrees enough to successfully cast an ffg vote
# if just justified_block_root match, then at least the attestation agrees on the base of the fork choice, but this isn’ t enough to cast an FFG vote
# Jacek Sieka
# @arnetheduck
# Dec 21 13:41
# if not, throw it out
# it = block or attestation?
# Danny Ryan
# @djrtwo
# Dec 21 13:42
# well, if you are buildling the block ,you shouldn’ t include it (thus throw it out of current consideration). If you are validating a block you just received and that conditon fails for an attestation, throw the block out because it included a bad attestation and is thus invalid
# The block producer knows when producing the block if they are including bad attestations or other data that will fail state transition
# and should not do that
# Jacek Sieka
# @arnetheduck
# Dec 21 13:43
# yeah, that makes sense, just checking
# ok, I think I'm gonna let that sink in a bit before asking more questions.. thanks :)