Fork choice EF consensus tests (#3041)

* add EF fork choice tests to CI

* checkpoints

* compilation fixes and add test to preset dependent suite

* support longpaths on Windows CI

* skip minimal tests (long paths issue + impl detals tested)

* fix stackoverflow on some platforms

* rebase on top of https://github.com/status-im/nimbus-eth2/pull/3054

* fix stack usage
This commit is contained in:
Mamy Ratsimbazafy 2021-11-25 19:41:39 +01:00 committed by GitHub
parent a223d62b07
commit 97da6e1365
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 658 additions and 329 deletions

View File

@ -102,6 +102,10 @@ jobs:
with:
access_token: ${{ github.token }}
- name: Support longpaths (Windows)
if: runner.os == 'Windows'
run: git config --system core.longpaths true
- name: Checkout nimbus-eth2
uses: actions/checkout@v2
with:

View File

@ -766,6 +766,17 @@ OK: 36/36 Fail: 0/36 Skip: 0/36
+ process_light_client_update_timeout OK
```
OK: 3/3 Fail: 0/3 Skip: 0/3
## Ethereum Foundation - ForkChoice [Preset: mainnet]
```diff
+ ForkChoice - mainnet/phase0/fork_choice/get_head/pyspec_tests/chain_no_attestations OK
+ ForkChoice - mainnet/phase0/fork_choice/get_head/pyspec_tests/genesis OK
+ ForkChoice - mainnet/phase0/fork_choice/get_head/pyspec_tests/shorter_chain_but_heavier_we OK
+ ForkChoice - mainnet/phase0/fork_choice/get_head/pyspec_tests/split_tie_breaker_no_attesta OK
+ ForkChoice - mainnet/phase0/fork_choice/on_block/pyspec_tests/basic OK
+ ForkChoice - mainnet/phase0/fork_choice/on_block/pyspec_tests/on_block_bad_parent_root OK
ForkChoice - mainnet/phase0/fork_choice/on_block/pyspec_tests/on_block_future_block Skip
```
OK: 6/7 Fail: 0/7 Skip: 1/7
## Ethereum Foundation - Merge - Epoch Processing - Effective balance updates [Preset: mainnet]
```diff
+ Effective balance updates - effective_balance_hysteresis [Preset: mainnet] OK
@ -1154,4 +1165,4 @@ OK: 44/44 Fail: 0/44 Skip: 0/44
OK: 27/27 Fail: 0/27 Skip: 0/27
---TOTAL---
OK: 984/984 Fail: 0/984 Skip: 0/984
OK: 990/991 Fail: 0/991 Skip: 1/991

View File

@ -784,6 +784,27 @@ OK: 36/36 Fail: 0/36 Skip: 0/36
+ process_light_client_update_timeout OK
```
OK: 3/3 Fail: 0/3 Skip: 0/3
## Ethereum Foundation - ForkChoice [Preset: minimal]
```diff
ForkChoice - minimal/phase0/fork_choice/get_head/pyspec_tests/chain_no_attestations Skip
ForkChoice - minimal/phase0/fork_choice/get_head/pyspec_tests/filtered_block_tree Skip
ForkChoice - minimal/phase0/fork_choice/get_head/pyspec_tests/genesis Skip
ForkChoice - minimal/phase0/fork_choice/get_head/pyspec_tests/shorter_chain_but_heavier_we Skip
ForkChoice - minimal/phase0/fork_choice/get_head/pyspec_tests/split_tie_breaker_no_attesta Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/basic Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/new_finalized_slot_is_justif Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/new_finalized_slot_is_not_ju Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/new_justified_is_later_than_ Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/on_block_bad_parent_root Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/on_block_before_finalized Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/on_block_checkpoints Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/on_block_finalized_skip_slot Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/on_block_finalized_skip_slot Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/on_block_future_block Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/on_block_outside_safe_slots_ Skip
ForkChoice - minimal/phase0/fork_choice/on_block/pyspec_tests/on_block_update_justified_ch Skip
```
OK: 0/17 Fail: 0/17 Skip: 17/17
## Ethereum Foundation - Merge - Epoch Processing - Effective balance updates [Preset: minimal]
```diff
+ Effective balance updates - effective_balance_hysteresis [Preset: minimal] OK
@ -1198,4 +1219,4 @@ OK: 48/48 Fail: 0/48 Skip: 0/48
OK: 30/30 Fail: 0/30 Skip: 0/30
---TOTAL---
OK: 1020/1020 Fail: 0/1020 Skip: 0/1020
OK: 1020/1037 Fail: 0/1037 Skip: 17/1037

View File

@ -131,8 +131,8 @@ proc init*(T: type AttestationPool, dag: ChainDAGRef,
# too big - getting an EpochRef can be expensive.
forkChoice.backend.process_block(
blckRef.root, blckRef.parent.root,
epochRef.current_justified_checkpoint.epoch,
epochRef.finalized_checkpoint.epoch)
epochRef.current_justified_checkpoint,
epochRef.finalized_checkpoint)
else:
epochRef = dag.getEpochRef(blckRef, blckRef.slot.epoch)
withBlck(dag.get(blckRef).data):

View File

@ -47,22 +47,20 @@ func compute_deltas(
logScope:
topics = "fork_choice"
func init*(T: type ForkChoiceBackend,
justified_epoch: Epoch,
finalized_root: Eth2Digest,
finalized_epoch: Epoch): T =
proc init*(T: type ForkChoiceBackend,
justifiedCheckpoint: Checkpoint,
finalizedCheckpoint: Checkpoint): T =
T(
proto_array: ProtoArray.init(
justified_epoch,
finalized_root,
finalized_epoch
justifiedCheckpoint,
finalizedCheckpoint
)
)
proc init*(T: type ForkChoice,
epochRef: EpochRef,
blck: BlockRef): T =
## Initialize a fork choice context for a genesis state - in the genesis
## Initialize a fork choice context for a finalized state - in the finalized
## state, the justified and finalized checkpoints are the same, so only one
## is used here
debug "Initializing fork choice",
@ -70,14 +68,14 @@ proc init*(T: type ForkChoice,
let
justified = BalanceCheckpoint(
blck: blck, epoch: epochRef.epoch, balances: epochRef.effective_balances)
checkpoint: Checkpoint(root: blck.root, epoch: epochRef.epoch),
balances: epochRef.effective_balances)
finalized = Checkpoint(root: blck.root, epoch: epochRef.epoch)
best_justified = Checkpoint(
root: justified.blck.root, epoch: justified.epoch)
root: blck.root, epoch: epochRef.epoch)
ForkChoice(
backend: ForkChoiceBackend.init(
epochRef.epoch, blck.root, epochRef.epoch),
backend: ForkChoiceBackend.init(best_justified, finalized),
checkpoints: Checkpoints(
justified: justified,
finalized: finalized,
@ -102,18 +100,19 @@ proc on_tick(self: var Checkpoints, dag: ChainDAGRef, time: Slot): FcResult[void
self.time = time
if newEpoch and
self.best_justified.epoch > self.justified.epoch:
self.best_justified.epoch > self.justified.checkpoint.epoch:
let blck = dag.getRef(self.best_justified.root)
if blck.isNil:
return err ForkChoiceError(
kind: fcJustifiedNodeUnknown,
blockRoot: self.best_justified.root)
let epochRef = dag.getEpochRef(blck, self.best_justified.epoch)
self.justified = BalanceCheckpoint(
blck: blck,
epoch: epochRef.epoch,
balances: epochRef.effective_balances)
let ancestor = blck.atEpochStart(self.finalized.epoch)
if ancestor.blck.root == self.finalized.root:
let epochRef = dag.getEpochRef(blck, self.best_justified.epoch)
self.justified = BalanceCheckpoint(
checkpoint: Checkpoint(root: blck.root, epoch: epochRef.epoch),
balances: epochRef.effective_balances)
ok()
func process_attestation_queue(self: var ForkChoice) {.gcsafe.}
@ -207,7 +206,7 @@ func should_update_justified_checkpoint(
return ok(true)
let
justified_slot = compute_start_slot_at_epoch(self.justified.epoch)
justified_slot = compute_start_slot_at_epoch(self.justified.checkpoint.epoch)
new_justified_checkpoint = epochRef.current_justified_checkpoint
justified_blck = dag.getRef(new_justified_checkpoint.root)
@ -218,7 +217,7 @@ func should_update_justified_checkpoint(
let justified_ancestor = justified_blck.atSlot(justified_slot)
if justified_ancestor.blck.root != self.justified.blck.root:
if justified_ancestor.blck.root != self.justified.checkpoint.root:
return ok(false)
ok(true)
@ -234,53 +233,57 @@ proc process_state(self: var Checkpoints,
trace "Processing epoch",
epoch = epochRef.epoch,
state_justified_epoch = state_justified_epoch,
current_justified = self.justified.epoch,
current_justified = self.justified.checkpoint.epoch,
state_finalized_epoch = state_finalized_epoch,
current_finalized = self.finalized.epoch
if state_justified_epoch > self.justified.epoch:
if state_justified_epoch > self.justified.checkpoint.epoch:
if state_justified_epoch > self.best_justified.epoch:
self.best_justified = epochRef.current_justified_checkpoint
if ? should_update_justified_checkpoint(self, dag, epochRef):
let
justifiedBlck = blck.atEpochStart(state_justified_epoch)
justifiedEpoch = dag.getEpochRef(justifiedBlck.blck, state_justified_epoch)
justifiedEpochRef = dag.getEpochRef(justifiedBlck.blck, state_justified_epoch)
self.justified =
BalanceCheckpoint(
blck: justifiedBlck.blck,
epoch: justifiedEpoch.epoch,
balances: justifiedEpoch.effective_balances)
checkpoint: Checkpoint(
root: justifiedBlck.blck.root,
epoch: justifiedEpochRef.epoch
),
balances: justifiedEpochRef.effective_balances)
if state_finalized_epoch > self.finalized.epoch:
self.finalized = epochRef.finalized_checkpoint
if self.justified.epoch != state_justified_epoch or
self.justified.blck.root != epochRef.current_justified_checkpoint.root:
if self.justified.checkpoint.epoch != state_justified_epoch or
self.justified.checkpoint.root != epochRef.current_justified_checkpoint.root:
if (state_justified_epoch > self.justified.epoch) or
(self.justified.blck.atEpochStart(self.finalized.epoch).blck.root !=
if (state_justified_epoch > self.justified.checkpoint.epoch) or
(dag.getRef(self.justified.checkpoint.root).atEpochStart(self.finalized.epoch).blck.root !=
self.finalized.root):
let
justifiedBlck = blck.atEpochStart(state_justified_epoch)
justifiedEpoch = dag.getEpochRef(justifiedBlck.blck, state_justified_epoch)
justifiedEpochRef = dag.getEpochRef(justifiedBlck.blck, state_justified_epoch)
self.justified =
BalanceCheckpoint(
blck: justifiedBlck.blck,
epoch: justifiedEpoch.epoch,
balances: justifiedEpoch.effective_balances)
checkpoint: Checkpoint(
root: justifiedBlck.blck.root,
epoch: justifiedEpochRef.epoch
),
balances: justifiedEpochRef.effective_balances)
ok()
func process_block*(self: var ForkChoiceBackend,
block_root: Eth2Digest,
parent_root: Eth2Digest,
justified_epoch: Epoch,
finalized_epoch: Epoch): FcResult[void] =
justified_checkpoint: Checkpoint,
finalized_checkpoint: Checkpoint): FcResult[void] =
self.proto_array.onBlock(
block_root, parent_root, justified_epoch, finalized_epoch)
block_root, parent_root, justified_checkpoint, finalized_checkpoint)
# TODO workaround for https://github.com/nim-lang/Nim/issues/18095
# it expresses as much of:
@ -320,8 +323,8 @@ proc process_block*(self: var ForkChoice,
? process_block(
self.backend, blckRef.root, blck.parent_root,
epochRef.current_justified_checkpoint.epoch,
epochRef.finalized_checkpoint.epoch
epochRef.current_justified_checkpoint,
epochRef.finalized_checkpoint
)
trace "Integrating block in fork choice",
@ -331,9 +334,8 @@ proc process_block*(self: var ForkChoice,
func find_head*(
self: var ForkChoiceBackend,
justified_epoch: Epoch,
justified_root: Eth2Digest,
finalized_epoch: Epoch,
justifiedCheckpoint: Checkpoint,
finalizedCheckpoint: Checkpoint,
justified_state_balances: seq[Gwei]
): FcResult[Eth2Digest] =
## Returns the new blockchain head
@ -351,20 +353,19 @@ func find_head*(
# Apply score changes
? self.proto_array.applyScoreChanges(
deltas, justified_epoch, finalized_epoch
deltas, justifiedCheckpoint, finalizedCheckpoint
)
self.balances = justified_state_balances
# Find the best block
var new_head{.noInit.}: Eth2Digest
? self.proto_array.findHead(new_head, justified_root)
? self.proto_array.findHead(new_head, justifiedCheckpoint.root)
{.noSideEffect.}:
trace "Fork choice requested",
justified_epoch = justified_epoch,
justified_root = shortLog(justified_root),
finalized_epoch = finalized_epoch,
justifiedCheckpoint = shortLog(justifiedCheckpoint),
finalizedCheckpoint = shortLog(finalizedCheckpoint),
fork_choice_head = shortLog(new_head)
return ok(new_head)
@ -376,9 +377,8 @@ proc get_head*(self: var ForkChoice,
? self.update_time(dag, wallSlot)
self.backend.find_head(
self.checkpoints.justified.epoch,
self.checkpoints.justified.blck.root,
self.checkpoints.finalized.epoch,
self.checkpoints.justified.checkpoint,
self.checkpoints.finalized,
self.checkpoints.justified.balances,
)

View File

@ -69,11 +69,11 @@ type
indicesLen*: int
of fcInvalidBestNode:
startRoot*: Eth2Digest
justifiedEpoch*: Epoch
finalizedEpoch*: Epoch
fkChoiceJustifiedCheckpoint*: Checkpoint
fkChoiceFinalizedCheckpoint*: Checkpoint
headRoot*: Eth2Digest
headJustifiedEpoch*: Epoch
headFinalizedEpoch*: Epoch
headJustifiedCheckpoint*: Checkpoint
headFinalizedCheckpoint*: Checkpoint
of fcUnknownParent:
childRoot*: Eth2Digest
parentRoot*: Eth2Digest
@ -89,23 +89,22 @@ type
## to get the physical index
ProtoArray* = object
justifiedEpoch*: Epoch
finalizedEpoch*: Epoch
nodes*: Protonodes
justifiedCheckpoint*: Checkpoint
finalizedCheckpoint*: Checkpoint
nodes*: ProtoNodes
indices*: Table[Eth2Digest, Index]
ProtoNode* = object
root*: Eth2Digest
parent*: Option[Index]
justifiedEpoch*: Epoch
finalizedEpoch*: Epoch
justifiedCheckpoint*: Checkpoint
finalizedCheckpoint*: Checkpoint
weight*: int64
bestChild*: Option[Index]
bestDescendant*: Option[Index]
BalanceCheckpoint* = object
blck*: BlockRef
epoch*: Epoch
checkpoint*: Checkpoint
balances*: seq[Gwei]
Checkpoints* = object
@ -137,7 +136,6 @@ type
ForkChoice* = object
backend*: ForkChoiceBackend
checkpoints*: Checkpoints
finalizedBlock*: BlockRef ## Any finalized block used at startup
queuedAttestations*: seq[QueuedAttestation]
func shortlog*(vote: VoteTracker): auto =

View File

@ -84,30 +84,29 @@ func nodeLeadsToViableHead(self: ProtoArray, node: ProtoNode): FcResult[bool]
# ----------------------------------------------------------------------
func init*(T: type ProtoArray,
justifiedEpoch: Epoch,
finalizedRoot: Eth2Digest,
finalizedEpoch: Epoch): T =
justifiedCheckpoint: Checkpoint,
finalizedCheckpoint: Checkpoint): T =
let node = ProtoNode(
root: finalizedRoot,
root: finalizedCheckpoint.root,
parent: none(int),
justifiedEpoch: justifiedEpoch,
finalizedEpoch: finalizedEpoch,
justifiedCheckpoint: justifiedCheckpoint,
finalizedCheckpoint: finalizedCheckpoint,
weight: 0,
bestChild: none(int),
bestDescendant: none(int)
)
T(
justifiedEpoch: justifiedEpoch,
finalizedEpoch: finalizedEpoch,
justifiedCheckpoint: justifiedCheckpoint,
finalizedCheckpoint: finalizedCheckpoint,
nodes: ProtoNodes(buf: @[node], offset: 0),
indices: {node.root: 0}.toTable()
)
func applyScoreChanges*(self: var ProtoArray,
deltas: var openArray[Delta],
justifiedEpoch: Epoch,
finalizedEpoch: Epoch): FcResult[void] =
justifiedCheckpoint: Checkpoint,
finalizedCheckpoint: Checkpoint): FcResult[void] =
## Iterate backwards through the array, touching all nodes and their parents
## and potentially the best-child of each parent.
##
@ -128,8 +127,8 @@ func applyScoreChanges*(self: var ProtoArray,
deltasLen: deltas.len,
indicesLen: self.indices.len)
self.justifiedEpoch = justifiedEpoch
self.finalizedEpoch = finalizedEpoch
self.justifiedCheckpoint = justifiedCheckpoint
self.finalizedCheckpoint = finalizedCheckpoint
## Alias
# This cannot raise the IndexError exception, how to tell compiler?
@ -206,8 +205,8 @@ func applyScoreChanges*(self: var ProtoArray,
func onBlock*(self: var ProtoArray,
root: Eth2Digest,
parent: Eth2Digest,
justifiedEpoch: Epoch,
finalizedEpoch: Epoch): FcResult[void] =
justifiedCheckpoint: Checkpoint,
finalizedCheckpoint: Checkpoint): FcResult[void] =
## Register a block with the fork choice
## A block `hasParentInForkChoice` may be false
## on fork choice initialization:
@ -234,8 +233,8 @@ func onBlock*(self: var ProtoArray,
let node = ProtoNode(
root: root,
parent: some(parentIdx),
justifiedEpoch: justifiedEpoch,
finalizedEpoch: finalizedEpoch,
justifiedCheckpoint: justifiedCheckpoint,
finalizedCheckpoint: finalizedCheckpoint,
weight: 0,
bestChild: none(int),
bestDescendant: none(int)
@ -283,11 +282,11 @@ func findHead*(self: var ProtoArray,
return err ForkChoiceError(
kind: fcInvalidBestNode,
startRoot: justifiedRoot,
justifiedEpoch: self.justifiedEpoch,
finalizedEpoch: self.finalizedEpoch,
fkChoiceJustifiedCheckpoint: self.justifiedCheckpoint,
fkChoiceFinalizedCheckpoint: self.finalizedCheckpoint,
headRoot: justifiedNode.get().root,
headJustifiedEpoch: justifiedNode.get().justifiedEpoch,
headFinalizedEpoch: justifiedNode.get().finalizedEpoch)
headJustifiedCheckpoint: justifiedNode.get().justifiedCheckpoint,
headFinalizedCheckpoint: justifiedNode.get().finalizedCheckpoint)
head = bestNode.get().root
ok()
@ -456,11 +455,11 @@ func nodeIsViableForHead(self: ProtoArray, node: ProtoNode): bool =
## Any node that has a different finalized or justified epoch
## should not be viable for the head.
(
(node.justifiedEpoch == self.justifiedEpoch) or
(self.justifiedEpoch == Epoch(0))
(node.justifiedCheckpoint == self.justifiedCheckpoint) or
(self.justifiedCheckpoint.epoch == Epoch(0))
) and (
(node.finalizedEpoch == self.finalizedEpoch) or
(self.finalizedEpoch == Epoch(0))
(node.finalizedCheckpoint == self.finalizedCheckpoint) or
(self.finalizedCheckpoint.epoch == Epoch(0))
)
# Sanity checks

View File

@ -14,6 +14,7 @@ import
import
./phase0/all_phase0_fixtures,
./altair/all_altair_fixtures,
./merge/all_merge_fixtures
./merge/all_merge_fixtures,
./test_fixture_fork_choice
summarizeLongTests("ConsensusSpecPreset")

View File

@ -0,0 +1,346 @@
# beacon_chain
# Copyright (c) 2018-2021 Status Research & Development GmbH
# 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
std/[strformat, tables, options, json, os, strutils],
# Status libraries
stew/[results, endians2], snappy, chronicles,
eth/keys, taskpools,
# Internals
../../beacon_chain/spec/[helpers, forks],
../../beacon_chain/spec/datatypes/[
base,
phase0, altair, merge],
../../beacon_chain/fork_choice/[fork_choice, fork_choice_types],
../../beacon_chain/beacon_chain_db,
../../beacon_chain/consensus_object_pools/[
blockchain_dag, block_quarantine, block_clearance, spec_cache],
# Third-party
yaml,
# Test
../testutil,
./fixtures_utils
# Test format described at https://github.com/ethereum/consensus-specs/tree/v1.1.0/tests/formats/fork_choice
# Note that our implementation has been optimized with "ProtoArray"
# instead of following the spec (in particular the "store").
type
OpKind = enum
opOnTick
opOnAttestation
opOnBlock
opOnMergeBlock
opChecks
Operation = object
valid: bool
# variant specific fields
case kind*: OpKind
of opOnTick:
tick: int
of opOnAttestation:
att: Attestation
of opOnBlock:
blk: ForkedSignedBeaconBlock
of opOnMergeBlock:
powBlock: PowBlock
of opChecks:
checks: JsonNode
proc initialLoad(
path: string, db: BeaconChainDB,
StateType, BlockType: typedesc): tuple[
dag: ChainDagRef, fkChoice: ref ForkChoice
] =
# TODO: support more than phase 0 genesis
let state = newClone(parseTest(
path/"anchor_state.ssz_snappy",
SSZ, StateType
))
# TODO stack usage. newClone and assignClone do not seem to
# prevent temporaries created by case objects
let forkedState = new ForkedHashedBeaconState
forkedState.kind = BeaconStateFork.Phase0
forkedState.phase0Data.data = state[]
forkedState.phase0Data.root = hash_tree_root(state[])
let blk = parseTest(
path/"anchor_block.ssz_snappy",
SSZ, BlockType
)
let signedBlock = ForkedSignedBeaconBlock.init(phase0.SignedBeaconBlock(
message: blk,
# signature: - unused as it's trusted
root: hashTreeRoot(blk)
))
ChainDagRef.preInit(
db,
forkedState[], forkedState[],
asTrusted(signedBlock)
)
let dag = ChainDAGRef.init(
defaultRuntimeConfig,
db,
updateFlags = {}
)
let fkChoice = newClone(ForkChoice.init(
dag.getFinalizedEpochRef(),
dag.finalizedHead.blck
))
(dag, fkChoice)
proc loadOps(path: string, fork: BeaconBlockFork): seq[Operation] =
let stepsYAML = readFile(path/"steps.yaml")
let steps = yaml.loadToJson(stepsYAML)
result = @[]
for step in steps[0]:
if step.hasKey"tick":
result.add Operation(kind: opOnTick, tick: step["tick"].getInt())
elif step.hasKey"block":
let filename = step["block"].getStr()
case fork
of BeaconBlockFork.Phase0:
let blk = parseTest(
path/filename & ".ssz_snappy",
SSZ, phase0.SignedBeaconBlock
)
result.add Operation(kind: opOnBlock,
blk: ForkedSignedBeaconBlock.init(blk))
of BeaconBlockFork.Altair:
let blk = parseTest(
path/filename & ".ssz_snappy",
SSZ, altair.SignedBeaconBlock
)
result.add Operation(kind: opOnBlock,
blk: ForkedSignedBeaconBlock.init(blk))
of BeaconBlockFork.Merge:
let blk = parseTest(
path/filename & ".ssz_snappy",
SSZ, merge.SignedBeaconBlock
)
result.add Operation(kind: opOnBlock,
blk: ForkedSignedBeaconBlock.init(blk))
elif step.hasKey"attestation":
let filename = step["attestation"].getStr()
let att = parseTest(
path/filename & ".ssz_snappy",
SSZ, Attestation
)
result.add Operation(kind: opOnAttestation,
att: att)
elif step.hasKey"checks":
result.add Operation(kind: opChecks,
checks: step["checks"])
else:
doAssert false, "Unreachable: " & $step
if step.hasKey"valid":
doAssert step.len == 2
result[^1].valid = step["valid"].getBool()
elif not step.hasKey"checks":
doAssert step.len == 1
result[^1].valid = true
proc stepOnBlock(
dag: ChainDagRef,
fkChoice: ref ForkChoice,
quarantine: QuarantineRef,
state: var StateData,
stateCache: var StateCache,
signedBlock: phase0.SignedBeaconBlock | altair.SignedBeaconBlock | merge.SignedBeaconBlock,
time: Slot): Result[BlockRef, (ValidationResult, BlockError)] =
# 1. Move state to proper slot.
dag.updateStateData(
state,
dag.head.atSlot(time),
save = false,
stateCache
)
# 2. Add block to DAG
when signedBlock is phase0.SignedBeaconBlock:
type TrustedBlock = phase0.TrustedSignedBeaconBlock
elif signedBlock is altair.SignedBeaconBlock:
type TrustedBlock = altair.TrustedSignedBeaconBlock
else:
type TrustedBlock = merge.TrustedSignedBeaconBlock
let blockAdded = dag.addRawBlock(quarantine, signedBlock) do (
blckRef: BlockRef, signedBlock: TrustedBlock, epochRef: EpochRef
):
# 3. Update fork choice if valid
let status = fkChoice[].process_block(
dag,
epochRef,
blckRef,
signedBlock.message,
time
)
doAssert status.isOk()
return blockAdded
proc stepOnAttestation(
dag: ChainDagRef,
fkChoice: ref ForkChoice,
att: Attestation,
time: Slot): FcResult[void] =
let epochRef = dag.getEpochRef(dag.head, time.compute_epoch_at_slot())
let attesters = epochRef.get_attesting_indices(att.data, att.aggregation_bits)
let status = fkChoice[].on_attestation(
dag,
att.data.slot, att.data.beacon_block_root, attesters,
time
)
status
proc stepChecks(
checks: JsonNode,
dag: ChainDagRef,
fkChoice: ref ForkChoice,
time: Slot
) =
doAssert checks.len >= 1, "No checks found"
for check, val in checks:
if check == "time":
doAssert time == Slot(val.getInt())
doAssert fkChoice.checkpoints.time == time
elif check == "head":
let headRoot = fkChoice[].get_head(dag, time).get()
let headRef = dag.getRef(headRoot)
doAssert headRef.slot == Slot(val["slot"].getInt())
doAssert headRef.root == Eth2Digest.fromHex(val["root"].getStr())
elif check == "justified_checkpoint":
let checkpointRoot = fkChoice.checkpoints.justified.checkpoint.root
let checkpointEpoch = fkChoice.checkpoints.justified.checkpoint.epoch
doAssert checkpointEpoch == Epoch(val["epoch"].getInt())
doAssert checkpointRoot == Eth2Digest.fromHex(val["root"].getStr())
elif check == "justified_checkpoint_root": # undocumented check
let checkpointRoot = fkChoice.checkpoints.justified.checkpoint.root
doAssert checkpointRoot == Eth2Digest.fromHex(val.getStr())
elif check == "finalized_checkpoint":
let checkpointRoot = fkChoice.checkpoints.finalized.root
let checkpointEpoch = fkChoice.checkpoints.finalized.epoch
doAssert checkpointEpoch == Epoch(val["epoch"].getInt())
doAssert checkpointRoot == Eth2Digest.fromHex(val["root"].getStr())
elif check == "best_justified_checkpoint":
let checkpointRoot = fkChoice.checkpoints.best_justified.root
let checkpointEpoch = fkChoice.checkpoints.best_justified.epoch
doAssert checkpointEpoch == Epoch(val["epoch"].getInt())
doAssert checkpointRoot == Eth2Digest.fromHex(val["root"].getStr())
elif check == "genesis_time":
# The fork choice is pruned regularly
# and does not store the genesis time,
# hence we check the DAG
doAssert dag.genesis.slot == Slot(val.getInt())
else:
doAssert false, "Unsupported check '" & $check & "'"
proc runTest(path: string, fork: BeaconBlockFork) =
let db = BeaconChainDB.new("", inMemory = true)
defer:
db.close()
var stores = case fork
of BeaconBlockFork.Phase0:
initialLoad(
path, db,
phase0.BeaconState, phase0.BeaconBlock
)
else:
doAssert false, "Unsupported fork: " & $fork
(ChainDAGRef(), (ref ForkChoice)())
# of BeaconBlockFork.Altair:
# initialLoad(
# path, db,
# # The tests always use phase 0 block for anchor - https://github.com/ethereum/eth2.0-specs/pull/2323
# # TODO: support altair genesis state
# altair.BeaconState, phase0.BeaconBlock
# )
# of BeaconBlockFork.Merge:
# initialLoad(
# path, db,
# # The tests always use phase 0 block for anchor - https://github.com/ethereum/eth2.0-specs/pull/2323
# # TODO: support merge genesis state
# merge.BeaconState, phase0.BeaconBlock
# )
let taskpool = Taskpool.new(numThreads = 1)
let quarantine = QuarantineRef.init(keys.newRng(), taskpool)
let steps = loadOps(path, fork)
var time = stores.fkChoice.checkpoints.time
let state = newClone(stores.dag.headState)
var stateCache = StateCache()
for step in steps:
case step.kind
of opOnTick:
time = Slot(step.tick)
of opOnBlock:
withBlck(step.blk):
let status = stepOnBlock(
stores.dag, stores.fkChoice,
quarantine,
state[], stateCache,
blck,
time)
doAssert status.isOk == step.valid
of opOnAttestation:
let status = stepOnAttestation(
stores.dag, stores.fkChoice,
step.att, time)
doAssert status.isOk == step.valid
of opChecks:
stepChecks(step.checks, stores.dag, stores.fkChoice, time)
else:
doAssert false, "Unsupported"
suite "Ethereum Foundation - ForkChoice" & preset():
const SKIP = [
# protoArray can handle blocks in the future gracefully
# spec: https://github.com/ethereum/consensus-specs/blame/v1.1.3/specs/phase0/fork-choice.md#L349
# test: tests/fork_choice/scenarios/no_votes.nim
# "Ensure the head is still 4 whilst the justified epoch is 0."
"on_block_future_block",
]
for fork in [BeaconBlockFork.Phase0]: # TODO: init ChainDAG from Merge/Altair
let forkStr = toLowerAscii($fork)
for testKind in ["get_head", "on_block"]:
let basePath = SszTestsDir/const_preset/forkStr/"fork_choice"/testKind/"pyspec_tests"
for kind, path in walkDir(basePath, relative = true, checkDir = true):
test "ForkChoice - " & const_preset/forkStr/"fork_choice"/testKind/"pyspec_tests"/path:
if const_preset == "minimal":
# TODO: Minimal tests have long paths issues on Windows
# and some are testing implementation details:
# - assertion that input block is not in the future
# - block slot is later than finalized slot
# - ...
# that ProtoArray handles gracefully
skip()
elif path in SKIP:
skip()
else:
runTest(basePath/path, fork)

View File

@ -41,16 +41,15 @@ type
# variant specific fields
case kind*: OpKind
of FindHead, InvalidFindHead:
justified_epoch*: Epoch
justified_root*: Eth2Digest
finalized_epoch*: Epoch
justified_checkpoint*: Checkpoint
finalized_checkpoint*: Checkpoint
justified_state_balances*: seq[Gwei]
expected_head*: Eth2Digest
of ProcessBlock:
root*: Eth2Digest
parent_root*: Eth2Digest
blk_justified_epoch*: Epoch
blk_finalized_epoch*: Epoch
blk_justified_checkpoint*: Checkpoint
blk_finalized_checkpoint*: Checkpoint
of ProcessAttestation:
validator_index*: ValidatorIndex
block_root*: Eth2Digest
@ -67,27 +66,26 @@ func apply(ctx: var ForkChoiceBackend, id: int, op: Operation) =
case op.kind
of FindHead, InvalidFindHead:
let r = ctx.find_head(
op.justified_epoch,
op.justified_root,
op.finalized_epoch,
op.justified_checkpoint,
op.finalized_checkpoint,
op.justified_state_balances
)
if op.kind == FindHead:
doAssert r.isOk(), &"find_head (op #{id}) returned an error: {r.error}"
doAssert r.get() == op.expected_head, &"find_head (op #{id}) returned an incorrect result: {r.get()} (expected: {op.expected_head})"
debugEcho " Found expected head: 0x", op.expected_head, " from justified checkpoint(epoch: ", op.justified_epoch, ", root: 0x", op.justified_root, ")"
doAssert r.get() == op.expected_head, &"find_head (op #{id}) returned an incorrect result: {r.get()} (expected: {op.expected_head}, from justified checkpoint: {op.justified_checkpoint}, finalized checkpoint: {op.finalized_checkpoint})"
debugEcho &" Found expected head: 0x{op.expected_head} from justified checkpoint {op.justified_checkpoint}, finalized checkpoint {op.finalized_checkpoint}"
else:
doAssert r.isErr(), "find_head was unexpectedly successful"
debugEcho " Detected an expected invalid head"
doAssert r.isErr(), &"invalid_find_head (op #{id}) was unexpectedly successful, head {op.expected_head} from justified checkpoint {op.justified_checkpoint}, finalized checkpoint {op.finalized_checkpoint}"
debugEcho &" Detected an expected invalid head from justified checkpoint {op.justified_checkpoint}, finalized checkpoint {op.finalized_checkpoint}"
of ProcessBlock:
let r = ctx.process_block(
block_root = op.root,
parent_root = op.parent_root,
justified_epoch = op.blk_justified_epoch,
finalized_epoch = op.blk_finalized_epoch
justified_checkpoint = op.blk_justified_checkpoint,
finalized_checkpoint = op.blk_finalized_checkpoint
)
doAssert r.isOk(), &"process_block (op #{id}) returned an error: {r.error}"
debugEcho " Processed block 0x", op.root, " with parent 0x", op.parent_root, " and justified epoch ", op.blk_justified_epoch
debugEcho " Processed block 0x", op.root, " with parent 0x", op.parent_root, " and justified checkpoint ", op.blk_justified_checkpoint
of ProcessAttestation:
ctx.process_attestation(
validator_index = op.validator_index,

View File

@ -13,9 +13,8 @@ func setup_finality_01(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Initialize the fork choice context
result.fork_choice = ForkChoiceBackend.init(
justified_epoch = Epoch(1),
finalized_epoch = Epoch(1),
finalized_root = GenesisRoot
justifiedCheckpoint = Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
finalizedCheckpoint = Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
# ----------------------------------
@ -23,9 +22,8 @@ func setup_finality_01(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Head should be genesis
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: GenesisRoot
)
@ -43,22 +41,22 @@ func setup_finality_01(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
kind: ProcessBlock,
root: fakeHash(1),
parent_root: GenesisRoot,
blk_justified_epoch: Epoch(0),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(2),
parent_root: fakeHash(1),
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: fakeHash(1), epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(3),
parent_root: fakeHash(2),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: fakeHash(2), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: fakeHash(1), epoch: Epoch(1))
)
# Ensure that with justified epoch 0 we find 3
@ -72,9 +70,8 @@ func setup_finality_01(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# 3 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(0),
justified_root: GenesisRoot,
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(3)
)
@ -90,9 +87,8 @@ func setup_finality_01(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# 3 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: fakeHash(2),
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakeHash(1), epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(2)
)
@ -108,9 +104,8 @@ func setup_finality_01(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# 3 <- start + head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(3),
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: fakeHash(2), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: fakeHash(1), epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(3)
)

View File

@ -13,9 +13,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Initialize the fork choice context
result.fork_choice = ForkChoiceBackend.init(
justified_epoch = Epoch(1),
finalized_epoch = Epoch(1),
finalized_root = GenesisRoot
justifiedCheckpoint = Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalizedCheckpoint = Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# ----------------------------------
@ -23,9 +22,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Head should be genesis
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: GenesisRoot
)
@ -49,36 +47,36 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
kind: ProcessBlock,
root: fakeHash(1),
parent_root: GenesisRoot,
blk_justified_epoch: Epoch(0),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(3),
parent_root: fakeHash(1),
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: fakeHash(1), epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(5),
parent_root: fakeHash(3),
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: fakeHash(1), epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(7),
parent_root: fakeHash(5),
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: fakeHash(1), epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(9),
parent_root: fakeHash(7),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: fakehash(3), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
# Build the following tree.
@ -100,36 +98,36 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
kind: ProcessBlock,
root: fakeHash(2),
parent_root: GenesisRoot,
blk_justified_epoch: Epoch(0),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(4),
parent_root: fakeHash(2),
blk_justified_epoch: Epoch(0),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(6),
parent_root: fakeHash(4),
blk_justified_epoch: Epoch(0),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(8),
parent_root: fakeHash(6),
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: fakehash(2), epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(10),
parent_root: fakeHash(8),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(0)
blk_justified_checkpoint: Checkpoint(root: fakehash(4), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0))
)
# Ensure that if we start at 0 we find 10 (just: 0, fin: 0).
@ -147,9 +145,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# 9 10 <-- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(0),
justified_root: GenesisRoot,
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(10)
)
@ -157,9 +154,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Same with justified_epoch 2
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: GenesisRoot,
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakehash(4), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(10)
)
@ -167,9 +163,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Justified epoch 3 is invalid
result.ops.add Operation(
kind: InvalidFindHead,
justified_epoch: Epoch(3), # <--- Wrong epoch
justified_root: GenesisRoot,
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakehash(4), epoch: Epoch(3)), # <--- Wrong epoch
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances
)
@ -208,9 +203,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# head -> 9 10
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(0),
justified_root: GenesisRoot,
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(9)
)
@ -218,9 +212,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Same with justified_epoch 2
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: GenesisRoot,
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakehash(3), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(9)
)
@ -228,9 +221,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Justified epoch 3 is invalid
result.ops.add Operation(
kind: InvalidFindHead,
justified_epoch: Epoch(3), # <--- Wrong epoch
justified_root: GenesisRoot,
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(3)), # <--- Wrong epoch
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances
)
@ -269,9 +261,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# 9 10 <-- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(0),
justified_root: GenesisRoot,
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(10)
)
@ -279,9 +270,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Same with justified_epoch 2
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: GenesisRoot,
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakeHash(4), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(10)
)
@ -289,9 +279,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Justified epoch 3 is invalid
result.ops.add Operation(
kind: InvalidFindHead,
justified_epoch: Epoch(3), # <--- Wrong epoch
justified_root: GenesisRoot,
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(3)), # <--- Wrong epoch
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances
)
@ -310,9 +299,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# head -> 9 10
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(0),
justified_root: fakeHash(1),
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakehash(1), epoch: Epoch(0)), # <- in production the root/epoch mismatch isn't used.
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(9)
)
@ -320,9 +308,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Same with justified_epoch 2
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(1),
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakehash(3), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(9)
)
@ -330,9 +317,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Justified epoch 3 is invalid
result.ops.add Operation(
kind: InvalidFindHead,
justified_epoch: Epoch(3), # <--- Wrong epoch
justified_root: fakeHash(1),
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakehash(5), epoch: Epoch(3)), # <--- Wrong epoch
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances
)
@ -351,9 +337,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# 9 10 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(0),
justified_root: fakeHash(2),
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakehash(2), epoch: Epoch(0)), # In production this can't happen
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(10)
)
@ -361,9 +346,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Same with justified_epoch 2
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(2),
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakehash(4), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances,
expected_head: fakeHash(10)
)
@ -371,9 +355,8 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Justified epoch 3 is invalid
result.ops.add Operation(
kind: InvalidFindHead,
justified_epoch: Epoch(3), # <--- Wrong epoch
justified_root: fakeHash(2),
finalized_epoch: Epoch(0),
justified_checkpoint: Checkpoint(root: fakehash(4), epoch: Epoch(3)), # <--- Wrong epoch
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
justified_state_balances: balances
)

View File

@ -12,10 +12,10 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
let GenesisRoot = fakeHash(0)
# Initialize the fork choice context
# We start with epoch 0 fully finalized to avoid epoch 0 special cases.
result.fork_choice = ForkChoiceBackend.init(
justified_epoch = Epoch(1),
finalized_epoch = Epoch(1),
finalized_root = GenesisRoot
justifiedCheckpoint = Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalizedCheckpoint = Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# ----------------------------------
@ -23,9 +23,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# Head should be genesis
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: GenesisRoot
)
@ -39,8 +38,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
kind: ProcessBlock,
root: fakeHash(2),
parent_root: GenesisRoot,
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Head should be 2
@ -50,9 +49,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# 2 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(2)
)
@ -66,8 +64,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
kind: ProcessBlock,
root: fakeHash(1),
parent_root: GenesisRoot,
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Head is still 2 due to tiebreaker as fakeHash(2) (0xD8...) > fakeHash(1) (0x7C...)
@ -77,9 +75,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# head-> 2 1
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(2)
)
@ -95,8 +92,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
kind: ProcessBlock,
root: fakeHash(3),
parent_root: fakeHash(1),
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Head is still 2
@ -108,9 +105,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# 3
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(2)
)
@ -126,8 +122,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
kind: ProcessBlock,
root: fakeHash(4),
parent_root: fakeHash(2),
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Check that head is 4
@ -139,9 +135,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# head-> 4 3
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(4)
)
@ -159,8 +154,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
kind: ProcessBlock,
root: fakeHash(5),
parent_root: fakeHash(4),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Ensure the head is still 4 whilst the justified epoch is 0.
@ -174,9 +169,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# 5
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(4)
)
@ -188,12 +182,11 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# | |
# 4 3
# |
# 5 <- starting from 5 with justified epoch 1 should error.
# 5 <- starting from 5 with justified epoch 0 should error.
result.ops.add Operation(
kind: InvalidFindHead,
justified_epoch: Epoch(1), # <--- Wrong epoch
justified_root: fakeHash(5),
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances
)
@ -204,12 +197,11 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# | |
# 4 3
# |
# 5 <- head + justified
# 5 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(5)
)
@ -229,8 +221,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
kind: ProcessBlock,
root: fakeHash(6),
parent_root: fakeHash(5),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Ensure 6 is the head
@ -245,9 +237,8 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# 6 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(6)
)

View File

@ -12,10 +12,10 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
let GenesisRoot = fakeHash(0)
# Initialize the fork choice context
# We start with epoch 0 fully finalized to avoid epoch 0 special cases.
result.fork_choice = ForkChoiceBackend.init(
justified_epoch = Epoch(1),
finalized_epoch = Epoch(1),
finalized_root = GenesisRoot
justifiedCheckpoint = Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalizedCheckpoint = Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# ----------------------------------
@ -23,9 +23,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# Head should be genesis
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: GenesisRoot
)
@ -39,8 +38,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
kind: ProcessBlock,
root: fakeHash(2),
parent_root: GenesisRoot,
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Head should be 2
@ -50,9 +49,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 2 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(2)
)
@ -66,8 +64,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
kind: ProcessBlock,
root: fakeHash(1),
parent_root: GenesisRoot,
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Head is still 2 due to tiebreaker as fakeHash(2) (0xD8...) > fakeHash(1) (0x7C...)
@ -77,9 +75,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# head-> 2 1
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(2)
)
@ -103,9 +100,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 2 1 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(1)
)
@ -129,9 +125,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# head-> 2 1
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(2)
)
@ -147,8 +142,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
kind: ProcessBlock,
root: fakeHash(3),
parent_root: fakeHash(1),
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Head is still 2
@ -158,9 +153,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# head-> 2 1
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(2)
)
@ -186,9 +180,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# head-> 2 1
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(2)
)
@ -217,9 +210,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 3 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(3)
)
@ -237,8 +229,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
kind: ProcessBlock,
root: fakeHash(4),
parent_root: fakeHash(3),
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Head is now 4
@ -252,9 +244,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 4 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(4)
)
@ -274,8 +265,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
kind: ProcessBlock,
root: fakeHash(5),
parent_root: fakeHash(4),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(2)
blk_justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2))
)
# Ensure that 5 is filtered out and the head stays at 4.
@ -291,9 +282,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 5
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(4)
)
@ -313,8 +303,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
kind: ProcessBlock,
root: fakeHash(6),
parent_root: fakeHash(4),
blk_justified_epoch: Epoch(1),
blk_finalized_epoch: Epoch(1)
blk_justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Move both votes to 5.
@ -363,22 +353,24 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
kind: ProcessBlock,
root: fakeHash(7),
parent_root: fakeHash(5),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(2)
blk_justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(8),
parent_root: fakeHash(7),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(2)
blk_justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1))
)
# Finalizes 5
result.ops.add Operation(
kind: ProcessBlock,
root: fakeHash(9),
parent_root: fakeHash(8),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(2)
blk_justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2))
)
# Ensure that 6 is the head, even though 5 has all the votes. This is testing to ensure
@ -401,9 +393,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 9
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(1),
justified_root: GenesisRoot,
finalized_epoch: Epoch(1),
justified_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
finalized_checkpoint: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
justified_state_balances: balances,
expected_head: fakeHash(6)
)
@ -430,9 +421,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# head-> 9
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(2),
justified_checkpoint: Checkpoint(root: fakehash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
justified_state_balances: balances,
expected_head: fakeHash(9)
)
@ -469,14 +459,13 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# Head should still be 9
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(2),
justified_checkpoint: Checkpoint(root: fakehash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
justified_state_balances: balances,
expected_head: fakeHash(9)
)
# Add block 10
# Add block 10 (also finalizes 5)
# 0
# / \
# 2 1
@ -496,16 +485,15 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
kind: ProcessBlock,
root: fakeHash(10),
parent_root: fakeHash(8),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(2)
blk_justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2))
)
# Head should still be 9
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(2),
justified_checkpoint: Checkpoint(root: fakehash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
justified_state_balances: balances,
expected_head: fakeHash(9)
)
@ -561,9 +549,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 9 10 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(2),
justified_checkpoint: Checkpoint(root: fakehash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
justified_state_balances: balances,
expected_head: fakeHash(10)
)
@ -579,9 +566,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# head -> 9 10
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(2),
justified_checkpoint: Checkpoint(root: fakehash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
justified_state_balances: balances,
expected_head: fakeHash(9)
)
@ -597,9 +583,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 9 10 <- head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(2),
justified_checkpoint: Checkpoint(root: fakehash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
justified_state_balances: balances,
expected_head: fakeHash(10)
)
@ -615,9 +600,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# head -> 9 10
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(2),
justified_checkpoint: Checkpoint(root: fakehash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
justified_state_balances: balances,
expected_head: fakeHash(9)
)
@ -652,9 +636,8 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# Prune shouldn't have changed the head
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(2),
justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
justified_state_balances: balances,
expected_head: fakeHash(9)
)
@ -674,16 +657,15 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
kind: ProcessBlock,
root: fakeHash(11),
parent_root: fakeHash(9),
blk_justified_epoch: Epoch(2),
blk_finalized_epoch: Epoch(2)
blk_justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
blk_finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2))
)
# Head is now 11
result.ops.add Operation(
kind: FindHead,
justified_epoch: Epoch(2),
justified_root: fakeHash(5),
finalized_epoch: Epoch(2),
justified_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
finalized_checkpoint: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
justified_state_balances: balances,
expected_head: fakeHash(11)
)