2020-04-09 16:15:00 +00:00
# beacon_chain
2022-02-04 11:59:40 +00:00
# Copyright (c) 2018-2022 Status Research & Development GmbH
2020-04-09 16:15:00 +00:00
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
# Standard library
2022-02-04 11:59:40 +00:00
std / [ options , strformat , tables ] ,
2020-04-09 16:15:00 +00:00
# Status libraries
2020-04-22 05:53:02 +00:00
stew / [ results , endians2 ] ,
2020-04-09 16:15:00 +00:00
# Internals
2021-06-21 08:35:24 +00:00
.. / .. / beacon_chain / spec / datatypes / base ,
2022-07-10 15:26:29 +00:00
.. / .. / beacon_chain / spec / helpers ,
2020-04-09 16:15:00 +00:00
.. / .. / beacon_chain / fork_choice / [ fork_choice , fork_choice_types ]
2022-07-10 15:26:29 +00:00
export results , base , helpers , fork_choice , fork_choice_types , tables , options
2020-04-09 16:15:00 +00:00
func fakeHash * ( index : SomeInteger ) : Eth2Digest =
## Create fake hashes
## Those are just the value serialized in big-endian
2022-07-06 10:33:02 +00:00
## We add 16x16 to avoid having a zero hash as those are special cased
2020-04-09 16:15:00 +00:00
## We store them in the first 8 bytes
2022-07-06 10:33:02 +00:00
## as those are the ones used in hash tables Table[Eth2Digest, T]
2020-04-09 16:15:00 +00:00
result . data [ 0 .. < 8 ] = ( 16 * 16 + index ) . uint64 . toBytesBE ( )
# The fork choice tests are quite complex.
# For flexibility in block arrival, timers, operations sequencing, ...
# we create a small interpreter that will trigger events in proper order
# before fork choice.
type
OpKind * = enum
FindHead
InvalidFindHead
ProcessBlock
ProcessAttestation
Prune
Operation * = object
# variant specific fields
case kind * : OpKind
of FindHead , InvalidFindHead :
2022-07-06 10:33:02 +00:00
checkpoints * : FinalityCheckpoints
2020-04-09 16:15:00 +00:00
justified_state_balances * : seq [ Gwei ]
expected_head * : Eth2Digest
of ProcessBlock :
root * : Eth2Digest
parent_root * : Eth2Digest
2022-07-06 10:33:02 +00:00
blk_checkpoints * : FinalityCheckpoints
2020-04-09 16:15:00 +00:00
of ProcessAttestation :
validator_index * : ValidatorIndex
block_root * : Eth2Digest
target_epoch * : Epoch
of Prune : # ProtoArray specific
finalized_root * : Eth2Digest
expected_len * : int
2020-07-25 19:41:12 +00:00
func apply ( ctx : var ForkChoiceBackend , id : int , op : Operation ) =
2020-04-09 16:15:00 +00:00
## Apply the specified operation to a ForkChoice context
## ``id`` is additional debugging info. It is the
## operation index.
# debugEcho " ========================================================================================="
case op . kind
of FindHead , InvalidFindHead :
let r = ctx . find_head (
2022-08-29 07:26:01 +00:00
GENESIS_EPOCH ,
2022-07-06 10:33:02 +00:00
op . checkpoints ,
2022-02-04 11:59:40 +00:00
op . justified_state_balances ,
# Don't use proposer boosting
2022-07-06 10:33:02 +00:00
ZERO_HASH )
2020-04-09 16:15:00 +00:00
if op . kind = = FindHead :
doAssert r . isOk ( ) , & " find_head (op #{id}) returned an error: {r.error} "
2022-07-06 10:33:02 +00:00
doAssert r . get ( ) = = op . expected_head , & " find_head (op #{id}) returned an incorrect result: {r.get()} (expected: {op.expected_head}, from justified checkpoint: {op.checkpoints.justified}, finalized checkpoint: {op.checkpoints.finalized}) "
debugEcho & " Found expected head: 0x{op.expected_head} from justified checkpoint {op.checkpoints.justified}, finalized checkpoint {op.checkpoints.justified} "
2020-04-09 16:15:00 +00:00
else :
2022-07-06 10:33:02 +00:00
doAssert r . isErr ( ) , & " invalid_find_head (op #{id}) was unexpectedly successful, head {op.expected_head} from justified checkpoint {op.checkpoints.justified}, finalized checkpoint {op.checkpoints.finalized} "
debugEcho & " Detected an expected invalid head from justified checkpoint {op.checkpoints.justified}, finalized checkpoint {op.checkpoints.finalized} "
2020-04-09 16:15:00 +00:00
of ProcessBlock :
let r = ctx . process_block (
block_root = op . root ,
parent_root = op . parent_root ,
2022-07-06 10:33:02 +00:00
checkpoints = op . blk_checkpoints )
2020-04-09 16:15:00 +00:00
doAssert r . isOk ( ) , & " process_block (op #{id}) returned an error: {r.error} "
2022-07-06 10:33:02 +00:00
debugEcho " Processed block 0x " , op . root , " with parent 0x " , op . parent_root , " and justified checkpoint " , op . blk_checkpoints . justified
2020-04-09 16:15:00 +00:00
of ProcessAttestation :
ctx . process_attestation (
validator_index = op . validator_index ,
block_root = op . block_root ,
2022-07-06 10:33:02 +00:00
target_epoch = op . target_epoch )
2020-04-09 16:15:00 +00:00
debugEcho " Processed att target 0x " , op . block_root , " from validator " , op . validator_index , " for epoch " , op . target_epoch
of Prune :
2020-08-26 15:23:34 +00:00
let r = ctx . prune ( op . finalized_root )
2020-04-09 16:15:00 +00:00
doAssert r . isOk ( ) , & " prune (op #{id}) returned an error: {r.error} "
doAssert ctx . proto_array . nodes . len = = op . expected_len ,
& " prune (op #{id}): the resulting length ({ctx.proto_array.nodes.len}) was not expected ({op.expected_len}) "
debugEcho " Maybe_pruned block preceding finalized block 0x " , op . finalized_root
2020-07-25 19:41:12 +00:00
func run * ( ctx : var ForkChoiceBackend , ops : seq [ Operation ] ) =
2020-04-09 16:15:00 +00:00
## Apply a sequence of fork-choice operations on a store
for i , op in ops :
ctx . apply ( i , op )