track slot as part of fork choice debug API (#4565)

Extends fork choice state to also track slot numbers to improve accuracy
of `/eth/v1/debug/fork_choice` endpoint. Autoenable this API on devnet,
and disable some extra checks on devnet to aid focused testing efforts.
Align fork choice pruning logic with API based on checkpoints vs root.
This commit is contained in:
Etan Kissling 2023-01-31 13:35:01 +01:00 committed by GitHub
parent 58ed9308d2
commit ea6a6b1acd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 252 additions and 142 deletions

View File

@ -261,8 +261,8 @@ local-testnet-mainnet:
# test binaries that can output an XML report # test binaries that can output an XML report
XML_TEST_BINARIES_CORE := \ XML_TEST_BINARIES_CORE := \
consensus_spec_tests_mainnet \ consensus_spec_tests_minimal \
consensus_spec_tests_minimal consensus_spec_tests_mainnet
XML_TEST_BINARIES := \ XML_TEST_BINARIES := \
$(XML_TEST_BINARIES_CORE) \ $(XML_TEST_BINARIES_CORE) \

View File

@ -134,7 +134,7 @@ proc init*(T: type AttestationPool, dag: ChainDAGRef,
# and then to make sure the fork choice data structure doesn't grow # and then to make sure the fork choice data structure doesn't grow
# too big - getting an EpochRef can be expensive. # too big - getting an EpochRef can be expensive.
forkChoice.backend.process_block( forkChoice.backend.process_block(
blckRef.root, blckRef.parent.root, epochRef.checkpoints) blckRef.bid, blckRef.parent.root, epochRef.checkpoints)
else: else:
epochRef = dag.getEpochRef(blckRef, blckRef.slot.epoch, false).expect( epochRef = dag.getEpochRef(blckRef, blckRef.slot.epoch, false).expect(
"Getting an EpochRef should always work for non-finalized blocks") "Getting an EpochRef should always work for non-finalized blocks")

View File

@ -79,15 +79,18 @@ template withUpdatedState*(
else: else:
failureBody failureBody
func get_effective_balances(validators: openArray[Validator], epoch: Epoch): func get_effective_balances(
seq[Gwei] = validators: openArray[Validator],
epoch: Epoch,
ignoreSlashed: bool): seq[Gwei] =
## Get the balances from a state as counted for fork choice ## Get the balances from a state as counted for fork choice
result.newSeq(validators.len) # zero-init result.newSeq(validators.len) # zero-init
for i in 0 ..< result.len: for i in 0 ..< result.len:
# All non-active validators have a 0 balance # All non-active validators have a 0 balance
let validator = unsafeAddr validators[i] let validator = unsafeAddr validators[i]
if validator[].is_active_validator(epoch): if validator[].is_active_validator(epoch) and (
ignoreSlashed or not validator[].slashed):
result[i] = validator[].effective_balance result[i] = validator[].effective_balance
proc updateValidatorKeys*(dag: ChainDAGRef, validators: openArray[Validator]) = proc updateValidatorKeys*(dag: ChainDAGRef, validators: openArray[Validator]) =
@ -569,7 +572,9 @@ func init*(
epochRef.effective_balances_bytes = epochRef.effective_balances_bytes =
snappyEncode(SSZ.encode( snappyEncode(SSZ.encode(
List[Gwei, Limit VALIDATOR_REGISTRY_LIMIT]( List[Gwei, Limit VALIDATOR_REGISTRY_LIMIT](
get_effective_balances(getStateField(state, validators).asSeq, epoch)))) get_effective_balances(
getStateField(state, validators).asSeq, epoch,
experimental notin dag.updateFlags))))
epochRef epochRef
@ -898,7 +903,7 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
cfg.checkForkConsistency() cfg.checkForkConsistency()
doAssert updateFlags - { doAssert updateFlags - {
strictVerification, enableTestFeatures, lowParticipation strictVerification, experimental, enableTestFeatures, lowParticipation
} == {}, "Other flags not supported in ChainDAG" } == {}, "Other flags not supported in ChainDAG"
# TODO we require that the db contains both a head and a tail block - # TODO we require that the db contains both a head and a tail block -
@ -928,7 +933,7 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
# The only allowed flag right now is strictVerification, as the others all # The only allowed flag right now is strictVerification, as the others all
# allow skipping some validation. # allow skipping some validation.
updateFlags: updateFlags * { updateFlags: updateFlags * {
strictVerification, enableTestFeatures, lowParticipation strictVerification, experimental, enableTestFeatures, lowParticipation
}, },
cfg: cfg, cfg: cfg,

View File

@ -36,6 +36,8 @@ type
## When process_slots() is being called as part of a state_transition(), ## When process_slots() is being called as part of a state_transition(),
## the hash_tree_root() from the block will fill in the state.root so it ## the hash_tree_root() from the block will fill in the state.root so it
## should skip calculating that last state root. ## should skip calculating that last state root.
experimental ##\
## Whether to enable extra features in development.
enableTestFeatures ##\ enableTestFeatures ##\
## Whether to enable extra features for testing. ## Whether to enable extra features for testing.
lowParticipation ##\ lowParticipation ##\

View File

@ -50,12 +50,13 @@ logScope: topics = "fork_choice"
func init*( func init*(
T: type ForkChoiceBackend, checkpoints: FinalityCheckpoints, T: type ForkChoiceBackend, checkpoints: FinalityCheckpoints,
hasLowParticipation = false): T = experimental = false, hasLowParticipation = false): T =
T(proto_array: ProtoArray.init(checkpoints, hasLowParticipation)) T(proto_array: ProtoArray.init(
checkpoints, experimental, hasLowParticipation))
proc init*( proc init*(
T: type ForkChoice, epochRef: EpochRef, blck: BlockRef, T: type ForkChoice, epochRef: EpochRef, blck: BlockRef,
hasLowParticipation = false): T = experimental = false, hasLowParticipation = false): T =
## Initialize a fork choice context for a finalized state - in the finalized ## Initialize a fork choice context for a finalized state - in the finalized
## state, the justified and finalized checkpoints are the same, so only one ## state, the justified and finalized checkpoints are the same, so only one
## is used here ## is used here
@ -68,8 +69,9 @@ proc init*(
FinalityCheckpoints( FinalityCheckpoints(
justified: checkpoint, justified: checkpoint,
finalized: checkpoint), finalized: checkpoint),
hasLowParticipation), experimental, hasLowParticipation),
checkpoints: Checkpoints( checkpoints: Checkpoints(
experimental: experimental,
justified: BalanceCheckpoint( justified: BalanceCheckpoint(
checkpoint: checkpoint, checkpoint: checkpoint,
balances: epochRef.effective_balances), balances: epochRef.effective_balances),
@ -116,7 +118,7 @@ proc update_justified(
epochRef = dag.getEpochRef(blck, epoch, false).valueOr: epochRef = dag.getEpochRef(blck, epoch, false).valueOr:
# Shouldn't happen for justified data unless out of sync with ChainDAG # Shouldn't happen for justified data unless out of sync with ChainDAG
warn "Skipping justified checkpoint update, no EpochRef - report bug", warn "Skipping justified checkpoint update, no EpochRef - report bug",
blck, epoch, best = self.best_justified.epoch, error blck, epoch, error
return return
justified = Checkpoint(root: blck.root, epoch: epochRef.epoch) justified = Checkpoint(root: blck.root, epoch: epochRef.epoch)
@ -144,10 +146,13 @@ proc update_checkpoints(
## Update checkpoints in store if necessary ## Update checkpoints in store if necessary
# Update justified checkpoint # Update justified checkpoint
if checkpoints.justified.epoch > self.justified.checkpoint.epoch: if checkpoints.justified.epoch > self.justified.checkpoint.epoch:
if checkpoints.justified.epoch > self.best_justified.epoch: if not self.experimental:
self.best_justified = checkpoints.justified if checkpoints.justified.epoch > self.best_justified.epoch:
self.best_justified = checkpoints.justified
if ? should_update_justified_checkpoint(self, dag, checkpoints.justified): if ? should_update_justified_checkpoint(self, dag, checkpoints.justified):
? self.update_justified(dag, checkpoints.justified)
else:
? self.update_justified(dag, checkpoints.justified) ? self.update_justified(dag, checkpoints.justified)
# Update finalized checkpoint # Update finalized checkpoint
@ -155,8 +160,9 @@ proc update_checkpoints(
trace "Updating finalized", trace "Updating finalized",
store = self.finalized, state = checkpoints.finalized store = self.finalized, state = checkpoints.finalized
self.finalized = checkpoints.finalized self.finalized = checkpoints.finalized
if checkpoints.justified != self.justified.checkpoint: if not self.experimental:
? self.update_justified(dag, checkpoints.justified) if checkpoints.justified != self.justified.checkpoint:
? self.update_justified(dag, checkpoints.justified)
ok() ok()
@ -185,18 +191,19 @@ proc on_tick(
# Update store.justified_checkpoint if a better checkpoint on the # Update store.justified_checkpoint if a better checkpoint on the
# store.finalized_checkpoint chain # store.finalized_checkpoint chain
let if not self.checkpoints.experimental:
best_justified_epoch = self.checkpoints.best_justified.epoch
store_justified_epoch = self.checkpoints.justified.checkpoint.epoch
if best_justified_epoch > store_justified_epoch:
let let
blck = dag.getBlockRef(self.checkpoints.best_justified.root).valueOr: best_justified_epoch = self.checkpoints.best_justified.epoch
return err ForkChoiceError( store_justified_epoch = self.checkpoints.justified.checkpoint.epoch
kind: fcJustifiedNodeUnknown, if best_justified_epoch > store_justified_epoch:
blockRoot: self.checkpoints.best_justified.root) let
finalized_ancestor = blck.atEpochStart(self.checkpoints.finalized.epoch) blck = dag.getBlockRef(self.checkpoints.best_justified.root).valueOr:
if finalized_ancestor.blck.root == self.checkpoints.finalized.root: return err ForkChoiceError(
self.checkpoints.update_justified(dag, blck, best_justified_epoch) kind: fcJustifiedNodeUnknown,
blockRoot: self.checkpoints.best_justified.root)
finalized_ancestor = blck.atEpochStart(self.checkpoints.finalized.epoch)
if finalized_ancestor.blck.root == self.checkpoints.finalized.root:
self.checkpoints.update_justified(dag, blck, best_justified_epoch)
# Pull-up chain tips from previous epoch # Pull-up chain tips from previous epoch
for realized in self.backend.proto_array.realizePendingCheckpoints(): for realized in self.backend.proto_array.realizePendingCheckpoints():
@ -307,11 +314,11 @@ func process_equivocation*(
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/phase0/fork-choice.md#on_block # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/phase0/fork-choice.md#on_block
func process_block*(self: var ForkChoiceBackend, func process_block*(self: var ForkChoiceBackend,
block_root: Eth2Digest, bid: BlockId,
parent_root: Eth2Digest, parent_root: Eth2Digest,
checkpoints: FinalityCheckpoints, checkpoints: FinalityCheckpoints,
unrealized = none(FinalityCheckpoints)): FcResult[void] = unrealized = none(FinalityCheckpoints)): FcResult[void] =
self.proto_array.onBlock(block_root, parent_root, checkpoints, unrealized) self.proto_array.onBlock(bid, parent_root, checkpoints, unrealized)
proc process_block*(self: var ForkChoice, proc process_block*(self: var ForkChoice,
dag: ChainDAGRef, dag: ChainDAGRef,
@ -358,14 +365,14 @@ proc process_block*(self: var ForkChoice,
blck = shortLog(blckRef), checkpoints = epochRef.checkpoints, unrealized blck = shortLog(blckRef), checkpoints = epochRef.checkpoints, unrealized
? update_checkpoints(self.checkpoints, dag, unrealized) ? update_checkpoints(self.checkpoints, dag, unrealized)
? process_block( ? process_block(
self.backend, blckRef.root, blck.parent_root, unrealized) self.backend, blckRef.bid, blck.parent_root, unrealized)
else: else:
? process_block( ? process_block(
self.backend, blckRef.root, blck.parent_root, self.backend, blckRef.bid, blck.parent_root,
epochRef.checkpoints, some unrealized) # Realized in `on_tick` epochRef.checkpoints, some unrealized) # Realized in `on_tick`
else: else:
? process_block( ? process_block(
self.backend, blckRef.root, blck.parent_root, epochRef.checkpoints) self.backend, blckRef.bid, blck.parent_root, epochRef.checkpoints)
ok() ok()
@ -424,13 +431,16 @@ func get_safe_beacon_block_root*(self: ForkChoice): Eth2Digest =
self.checkpoints.justified.checkpoint.root self.checkpoints.justified.checkpoint.root
func prune*( func prune*(
self: var ForkChoiceBackend, finalized_root: Eth2Digest self: var ForkChoiceBackend, checkpoints: FinalityCheckpoints
): FcResult[void] = ): FcResult[void] =
## Prune blocks preceding the finalized root as they are now unneeded. ## Prune blocks preceding the finalized root as they are now unneeded.
self.proto_array.prune(finalized_root) self.proto_array.prune(checkpoints)
func prune*(self: var ForkChoice): FcResult[void] = func prune*(self: var ForkChoice): FcResult[void] =
self.backend.prune(self.checkpoints.finalized.root) self.backend.prune(
FinalityCheckpoints(
justified: self.checkpoints.justified.checkpoint,
finalized: self.checkpoints.finalized))
func mark_root_invalid*(self: var ForkChoice, root: Eth2Digest) = func mark_root_invalid*(self: var ForkChoice, root: Eth2Digest) =
try: try:

View File

@ -88,6 +88,7 @@ type
## Subtracted from logical index to get the physical index ## Subtracted from logical index to get the physical index
ProtoArray* = object ProtoArray* = object
experimental*: bool
hasLowParticipation*: bool hasLowParticipation*: bool
currentEpoch*: Epoch currentEpoch*: Epoch
checkpoints*: FinalityCheckpoints checkpoints*: FinalityCheckpoints
@ -98,7 +99,7 @@ type
previousProposerBoostScore*: uint64 previousProposerBoostScore*: uint64
ProtoNode* = object ProtoNode* = object
root*: Eth2Digest bid*: BlockId
parent*: Option[Index] parent*: Option[Index]
checkpoints*: FinalityCheckpoints checkpoints*: FinalityCheckpoints
weight*: int64 weight*: int64
@ -111,6 +112,7 @@ type
balances*: seq[Gwei] balances*: seq[Gwei]
Checkpoints* = object Checkpoints* = object
experimental*: bool
time*: BeaconTime time*: BeaconTime
justified*: BalanceCheckpoint justified*: BalanceCheckpoint
finalized*: Checkpoint finalized*: Checkpoint

View File

@ -81,17 +81,21 @@ func maybeUpdateBestChildAndDescendant(self: var ProtoArray,
parentIdx: Index, parentIdx: Index,
childIdx: Index): FcResult[void] childIdx: Index): FcResult[void]
func nodeIsViableForHead(self: ProtoArray, node: ProtoNode): bool func nodeIsViableForHead(
func nodeLeadsToViableHead(self: ProtoArray, node: ProtoNode): FcResult[bool] self: ProtoArray, node: ProtoNode, nodeIdx: Index): bool
func nodeLeadsToViableHead(
self: ProtoArray, node: ProtoNode, nodeIdx: Index): FcResult[bool]
# ProtoArray routines # ProtoArray routines
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
func init*( func init*(
T: type ProtoArray, checkpoints: FinalityCheckpoints, T: type ProtoArray, checkpoints: FinalityCheckpoints,
hasLowParticipation: bool): T = experimental, hasLowParticipation: bool): T =
let node = ProtoNode( let node = ProtoNode(
root: checkpoints.finalized.root, bid: BlockId(
slot: checkpoints.finalized.epoch.start_slot,
root: checkpoints.finalized.root),
parent: none(int), parent: none(int),
checkpoints: checkpoints, checkpoints: checkpoints,
weight: 0, weight: 0,
@ -99,10 +103,11 @@ func init*(
bestChild: none(int), bestChild: none(int),
bestDescendant: none(int)) bestDescendant: none(int))
T(hasLowParticipation: hasLowParticipation, T(experimental: experimental,
hasLowParticipation: hasLowParticipation,
checkpoints: checkpoints, checkpoints: checkpoints,
nodes: ProtoNodes(buf: @[node], offset: 0), nodes: ProtoNodes(buf: @[node], offset: 0),
indices: {node.root: 0}.toTable()) indices: {node.bid.root: 0}.toTable())
iterator realizePendingCheckpoints*( iterator realizePendingCheckpoints*(
self: var ProtoArray, resetTipTracking = true): FinalityCheckpoints = self: var ProtoArray, resetTipTracking = true): FinalityCheckpoints =
@ -111,7 +116,7 @@ iterator realizePendingCheckpoints*(
let physicalIdx = idx - self.nodes.offset let physicalIdx = idx - self.nodes.offset
if unrealized != self.nodes.buf[physicalIdx].checkpoints: if unrealized != self.nodes.buf[physicalIdx].checkpoints:
trace "Pulling up chain tip", trace "Pulling up chain tip",
blck = self.nodes.buf[physicalIdx].root, blck = self.nodes.buf[physicalIdx].bid.root,
checkpoints = self.nodes.buf[physicalIdx].checkpoints, checkpoints = self.nodes.buf[physicalIdx].checkpoints,
unrealized unrealized
self.nodes.buf[physicalIdx].checkpoints = unrealized self.nodes.buf[physicalIdx].checkpoints = unrealized
@ -173,7 +178,7 @@ func applyScoreChanges*(self: var ProtoArray,
self.checkpoints = checkpoints self.checkpoints = checkpoints
# If previous epoch is justified, pull up all current tips to previous epoch # If previous epoch is justified, pull up all current tips to previous epoch
if self.hasLowParticipation and self.isPreviousEpochJustified: if self.experimental and self.isPreviousEpochJustified:
for realized in self.realizePendingCheckpoints(resetTipTracking = false): for realized in self.realizePendingCheckpoints(resetTipTracking = false):
discard discard
@ -187,7 +192,7 @@ func applyScoreChanges*(self: var ProtoArray,
# Iterate backwards through all the indices in `self.nodes` # Iterate backwards through all the indices in `self.nodes`
for nodePhysicalIdx in countdown(self.nodes.len - 1, 0): for nodePhysicalIdx in countdown(self.nodes.len - 1, 0):
if node.root.isZero: if node.bid.root.isZero:
continue continue
var nodeDelta = deltas[nodePhysicalIdx] var nodeDelta = deltas[nodePhysicalIdx]
@ -195,7 +200,7 @@ func applyScoreChanges*(self: var ProtoArray,
# If we find the node for which the proposer boost was previously applied, # If we find the node for which the proposer boost was previously applied,
# decrease the delta by the previous score amount. # decrease the delta by the previous score amount.
if (not self.previousProposerBoostRoot.isZero) and if (not self.previousProposerBoostRoot.isZero) and
self.previousProposerBoostRoot == node.root: self.previousProposerBoostRoot == node.bid.root:
if nodeDelta < 0 and if nodeDelta < 0 and
nodeDelta - low(Delta) < self.previousProposerBoostScore.int64: nodeDelta - low(Delta) < self.previousProposerBoostScore.int64:
return err ForkChoiceError( return err ForkChoiceError(
@ -207,7 +212,7 @@ func applyScoreChanges*(self: var ProtoArray,
# the delta by the new score amount. # the delta by the new score amount.
# #
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/fork-choice.md#get_latest_attesting_balance # https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/fork-choice.md#get_latest_attesting_balance
if (not proposerBoostRoot.isZero) and proposerBoostRoot == node.root: if (not proposerBoostRoot.isZero) and proposerBoostRoot == node.bid.root:
proposerBoostScore = calculateProposerBoost(newBalances) proposerBoostScore = calculateProposerBoost(newBalances)
if nodeDelta >= 0 and if nodeDelta >= 0 and
high(Delta) - nodeDelta < proposerBoostScore.int64: high(Delta) - nodeDelta < proposerBoostScore.int64:
@ -265,7 +270,7 @@ func applyScoreChanges*(self: var ProtoArray,
self.previousProposerBoostScore = proposerBoostScore self.previousProposerBoostScore = proposerBoostScore
for nodePhysicalIdx in countdown(self.nodes.len - 1, 0): for nodePhysicalIdx in countdown(self.nodes.len - 1, 0):
if node.root.isZero: if node.bid.root.isZero:
continue continue
if node.parent.isSome(): if node.parent.isSome():
@ -281,7 +286,7 @@ func applyScoreChanges*(self: var ProtoArray,
ok() ok()
func onBlock*(self: var ProtoArray, func onBlock*(self: var ProtoArray,
root: Eth2Digest, bid: BlockId,
parent: Eth2Digest, parent: Eth2Digest,
checkpoints: FinalityCheckpoints, checkpoints: FinalityCheckpoints,
unrealized = none(FinalityCheckpoints)): FcResult[void] = unrealized = none(FinalityCheckpoints)): FcResult[void] =
@ -294,7 +299,7 @@ func onBlock*(self: var ProtoArray,
# Note: if parent is an "Option" type, we can run out of stack space. # Note: if parent is an "Option" type, we can run out of stack space.
# If the block is already known, ignore it # If the block is already known, ignore it
if root in self.indices: if bid.root in self.indices:
return ok() return ok()
var parentIdx: Index var parentIdx: Index
@ -303,13 +308,13 @@ func onBlock*(self: var ProtoArray,
do: do:
return err ForkChoiceError( return err ForkChoiceError(
kind: fcUnknownParent, kind: fcUnknownParent,
childRoot: root, childRoot: bid.root,
parentRoot: parent) parentRoot: parent)
let nodeLogicalIdx = self.nodes.offset + self.nodes.buf.len let nodeLogicalIdx = self.nodes.offset + self.nodes.buf.len
let node = ProtoNode( let node = ProtoNode(
root: root, bid: bid,
parent: some(parentIdx), parent: some(parentIdx),
checkpoints: checkpoints, checkpoints: checkpoints,
weight: 0, weight: 0,
@ -317,11 +322,11 @@ func onBlock*(self: var ProtoArray,
bestChild: none(int), bestChild: none(int),
bestDescendant: none(int)) bestDescendant: none(int))
self.indices[node.root] = nodeLogicalIdx self.indices[node.bid.root] = nodeLogicalIdx
self.nodes.add node self.nodes.add node
self.currentEpochTips.del parentIdx
if unrealized.isSome: if unrealized.isSome:
self.currentEpochTips.del parentIdx
self.currentEpochTips[nodeLogicalIdx] = unrealized.get self.currentEpochTips[nodeLogicalIdx] = unrealized.get
? self.maybeUpdateBestChildAndDescendant(parentIdx, nodeLogicalIdx) ? self.maybeUpdateBestChildAndDescendant(parentIdx, nodeLogicalIdx)
@ -359,34 +364,38 @@ func findHead*(self: var ProtoArray,
index: bestDescendantIdx) index: bestDescendantIdx)
# Perform a sanity check to ensure the node can be head # Perform a sanity check to ensure the node can be head
if not self.nodeIsViableForHead(bestNode.get()): if not self.nodeIsViableForHead(bestNode.get(), bestDescendantIdx):
return err ForkChoiceError( return err ForkChoiceError(
kind: fcInvalidBestNode, kind: fcInvalidBestNode,
startRoot: justifiedRoot, startRoot: justifiedRoot,
fkChoiceCheckpoints: self.checkpoints, fkChoiceCheckpoints: self.checkpoints,
headRoot: justifiedNode.get().root, headRoot: justifiedNode.get().bid.root,
headCheckpoints: justifiedNode.get().checkpoints) headCheckpoints: justifiedNode.get().checkpoints)
head = bestNode.get().root head = bestNode.get().bid.root
ok() ok()
func prune*(self: var ProtoArray, finalizedRoot: Eth2Digest): FcResult[void] = func prune*(
self: var ProtoArray,
checkpoints: FinalityCheckpoints): FcResult[void] =
## Update the tree with new finalization information. ## Update the tree with new finalization information.
## The tree is pruned if and only if: ## The tree is pruned if and only if:
## - The `finalizedRoot` and finalized epoch are different from current ## - `checkpoints.finalized.root` and finalized epoch
## are different from current
## ##
## Returns error if: ## Returns error if:
## - The finalized epoch is less than the current one ## - The finalized epoch is less than the current one
## - The finalized epoch matches the current one but the finalized root is different ## - The finalized epoch matches the current one but the f
## inalized root is different
## - Internal error due to invalid indices in `self` ## - Internal error due to invalid indices in `self`
var finalizedIdx: int var finalizedIdx: int
self.indices.withValue(finalizedRoot, value) do: self.indices.withValue(checkpoints.finalized.root, value) do:
finalizedIdx = value[] finalizedIdx = value[]
do: do:
return err ForkChoiceError( return err ForkChoiceError(
kind: fcFinalizedNodeUnknown, kind: fcFinalizedNodeUnknown,
blockRoot: finalizedRoot) blockRoot: checkpoints.finalized.root)
if finalizedIdx == self.nodes.offset: if finalizedIdx == self.nodes.offset:
# Nothing to do # Nothing to do
@ -395,15 +404,14 @@ func prune*(self: var ProtoArray, finalizedRoot: Eth2Digest): FcResult[void] =
if finalizedIdx < self.nodes.offset: if finalizedIdx < self.nodes.offset:
return err ForkChoiceError( return err ForkChoiceError(
kind: fcPruningFromOutdatedFinalizedRoot, kind: fcPruningFromOutdatedFinalizedRoot,
finalizedRoot: finalizedRoot) finalizedRoot: checkpoints.finalized.root)
trace "Pruning blocks from fork choice", trace "Pruning blocks from fork choice", checkpoints
finalizedRoot = shortLog(finalizedRoot)
let finalPhysicalIdx = finalizedIdx - self.nodes.offset let finalPhysicalIdx = finalizedIdx - self.nodes.offset
for nodeIdx in 0 ..< finalPhysicalIdx: for nodeIdx in 0 ..< finalPhysicalIdx:
self.currentEpochTips.del nodeIdx self.currentEpochTips.del nodeIdx
self.indices.del(self.nodes.buf[nodeIdx].root) self.indices.del(self.nodes.buf[nodeIdx].bid.root)
# Drop all nodes prior to finalization. # Drop all nodes prior to finalization.
# This is done in-place with `moveMem` to avoid costly reallocations. # This is done in-place with `moveMem` to avoid costly reallocations.
@ -445,7 +453,8 @@ func maybeUpdateBestChildAndDescendant(self: var ProtoArray,
kind: fcInvalidNodeIndex, kind: fcInvalidNodeIndex,
index: parentIdx) index: parentIdx)
let childLeadsToViableHead = ? self.nodeLeadsToViableHead(child.get()) let childLeadsToViableHead =
? self.nodeLeadsToViableHead(child.get(), childIdx)
let # Aliases to the 3 possible (bestChild, bestDescendant) tuples let # Aliases to the 3 possible (bestChild, bestDescendant) tuples
changeToNone = (none(Index), none(Index)) changeToNone = (none(Index), none(Index))
@ -477,7 +486,7 @@ func maybeUpdateBestChildAndDescendant(self: var ProtoArray,
index: bestChildIdx) index: bestChildIdx)
let bestChildLeadsToViableHead = let bestChildLeadsToViableHead =
? self.nodeLeadsToViableHead(bestChild.get()) ? self.nodeLeadsToViableHead(bestChild.get(), bestChildIdx)
if childLeadsToViableHead and not bestChildLeadsToViableHead: if childLeadsToViableHead and not bestChildLeadsToViableHead:
# The child leads to a viable head, but the current best-child doesn't # The child leads to a viable head, but the current best-child doesn't
@ -487,7 +496,7 @@ func maybeUpdateBestChildAndDescendant(self: var ProtoArray,
noChange noChange
elif child.get().weight == bestChild.get().weight: elif child.get().weight == bestChild.get().weight:
# Tie-breaker of equal weights by root # Tie-breaker of equal weights by root
if child.get().root.tiebreak(bestChild.get().root): if child.get().bid.root.tiebreak(bestChild.get().bid.root):
changeToChild changeToChild
else: else:
noChange noChange
@ -511,7 +520,8 @@ func maybeUpdateBestChildAndDescendant(self: var ProtoArray,
ok() ok()
func nodeLeadsToViableHead(self: ProtoArray, node: ProtoNode): FcResult[bool] = func nodeLeadsToViableHead(
self: ProtoArray, node: ProtoNode, nodeIdx: Index): FcResult[bool] =
## Indicates if the node itself or its best-descendant are viable ## Indicates if the node itself or its best-descendant are viable
## for blockchain head ## for blockchain head
let bestDescendantIsViableForHead = block: let bestDescendantIsViableForHead = block:
@ -522,13 +532,14 @@ func nodeLeadsToViableHead(self: ProtoArray, node: ProtoNode): FcResult[bool] =
return err ForkChoiceError( return err ForkChoiceError(
kind: fcInvalidBestDescendant, kind: fcInvalidBestDescendant,
index: bestDescendantIdx) index: bestDescendantIdx)
self.nodeIsViableForHead(bestDescendant.get()) self.nodeIsViableForHead(bestDescendant.get(), bestDescendantIdx)
else: else:
false false
ok(bestDescendantIsViableForHead or self.nodeIsViableForHead(node)) ok(bestDescendantIsViableForHead or self.nodeIsViableForHead(node, nodeIdx))
func nodeIsViableForHead(self: ProtoArray, node: ProtoNode): bool = func nodeIsViableForHead(
self: ProtoArray, node: ProtoNode, nodeIdx: Index): bool =
## This is the equivalent of `filter_block_tree` function in eth2 spec ## This is the equivalent of `filter_block_tree` function in eth2 spec
## https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/phase0/fork-choice.md#filter_block_tree ## https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/phase0/fork-choice.md#filter_block_tree
@ -545,6 +556,35 @@ func nodeIsViableForHead(self: ProtoArray, node: ProtoNode): bool =
node.checkpoints.finalized == self.checkpoints.finalized or node.checkpoints.finalized == self.checkpoints.finalized or
self.checkpoints.finalized.epoch == GENESIS_EPOCH self.checkpoints.finalized.epoch == GENESIS_EPOCH
if self.experimental:
var correctJustified =
self.checkpoints.justified.epoch == GENESIS_EPOCH or
node.checkpoints.justified.epoch == self.checkpoints.justified.epoch
if not correctJustified and self.isPreviousEpochJustified and
node.bid.slot.epoch == self.currentEpoch:
let unrealized =
self.currentEpochTips.getOrDefault(nodeIdx, node.checkpoints)
correctJustified =
unrealized.justified.epoch >= self.checkpoints.justified.epoch and
node.checkpoints.justified.epoch + 2 >= self.currentEpoch
return
if not correctJustified:
false
elif self.checkpoints.finalized.epoch == GENESIS_EPOCH:
true
else:
let finalizedSlot = self.checkpoints.finalized.epoch.start_slot
var ancestor = some node
while ancestor.isSome and ancestor.unsafeGet.bid.slot > finalizedSlot:
if ancestor.unsafeGet.parent.isSome:
ancestor = self.nodes[ancestor.unsafeGet.parent.unsafeGet]
else:
ancestor.reset()
if ancestor.isSome:
ancestor.unsafeGet.bid.root == self.checkpoints.finalized.root
else:
false
## Any node that has a different finalized or justified epoch ## Any node that has a different finalized or justified epoch
## should not be viable for the head. ## should not be viable for the head.
( (
@ -582,7 +622,7 @@ func propagateInvalidity*(
# Helpers to dump internal state # Helpers to dump internal state
type ProtoArrayItem* = object type ProtoArrayItem* = object
root*: Eth2Digest bid*: BlockId
parent*: Eth2Digest parent*: Eth2Digest
checkpoints*: FinalityCheckpoints checkpoints*: FinalityCheckpoints
unrealized*: Option[FinalityCheckpoints] unrealized*: Option[FinalityCheckpoints]
@ -597,13 +637,13 @@ func root(self: ProtoNodes, logicalIdx: Option[Index]): Eth2Digest =
let node = self[logicalIdx.unsafeGet] let node = self[logicalIdx.unsafeGet]
if node.isNone: if node.isNone:
return ZERO_HASH return ZERO_HASH
node.unsafeGet.root node.unsafeGet.bid.root
iterator items*(self: ProtoArray): ProtoArrayItem = iterator items*(self: ProtoArray): ProtoArrayItem =
## Iterate over all nodes known by fork choice. ## Iterate over all nodes known by fork choice.
doAssert self.indices.len == self.nodes.len doAssert self.indices.len == self.nodes.len
for nodePhysicalIdx, node in self.nodes.buf: for nodePhysicalIdx, node in self.nodes.buf:
if node.root.isZero: if node.bid.root.isZero:
continue continue
let unrealized = block: let unrealized = block:
@ -614,7 +654,7 @@ iterator items*(self: ProtoArray): ProtoArrayItem =
none(FinalityCheckpoints) none(FinalityCheckpoints)
yield ProtoArrayItem( yield ProtoArrayItem(
root: node.root, bid: node.bid,
parent: self.nodes.root(node.parent), parent: self.nodes.root(node.parent),
checkpoints: node.checkpoints, checkpoints: node.checkpoints,
unrealized: unrealized, unrealized: unrealized,

View File

@ -142,8 +142,7 @@ proc loadChainDag(
db: BeaconChainDB, db: BeaconChainDB,
eventBus: EventBus, eventBus: EventBus,
validatorMonitor: ref ValidatorMonitor, validatorMonitor: ref ValidatorMonitor,
networkGenesisValidatorsRoot: Opt[Eth2Digest], networkGenesisValidatorsRoot: Opt[Eth2Digest]): ChainDAGRef =
shouldEnableTestFeatures: bool): ChainDAGRef =
info "Loading block DAG from database", path = config.databaseDir info "Loading block DAG from database", path = config.databaseDir
var dag: ChainDAGRef var dag: ChainDAGRef
@ -170,10 +169,12 @@ proc loadChainDag(
jsonVersion: contextFork, jsonVersion: contextFork,
sszContext: dag.forkDigests[].atStateFork(contextFork))) sszContext: dag.forkDigests[].atStateFork(contextFork)))
var extraFlags = {enableTestFeatures}
if config.deploymentPhase <= DeploymentPhase.Testnet:
extraFlags.incl experimental
if config.deploymentPhase <= DeploymentPhase.Devnet:
extraFlags.incl lowParticipation
let let
extraFlags =
if shouldEnableTestFeatures: {enableTestFeatures, lowParticipation}
else: {enableTestFeatures}
chainDagFlags = chainDagFlags =
if config.strictVerification: {strictVerification} if config.strictVerification: {strictVerification}
else: {} else: {}
@ -548,8 +549,7 @@ proc init*(T: type BeaconNode,
dag = loadChainDag( dag = loadChainDag(
config, cfg, db, eventBus, config, cfg, db, eventBus,
validatorMonitor, networkGenesisValidatorsRoot, validatorMonitor, networkGenesisValidatorsRoot)
config.deploymentPhase <= DeploymentPhase.Testnet)
genesisTime = getStateField(dag.headState, genesis_time) genesisTime = getStateField(dag.headState, genesis_time)
beaconClock = BeaconClock.init(genesisTime) beaconClock = BeaconClock.init(genesisTime)
getBeaconTime = beaconClock.getBeaconTimeFn() getBeaconTime = beaconClock.getBeaconTimeFn()

View File

@ -1,5 +1,5 @@
# beacon_chain # beacon_chain
# Copyright (c) 2021-2022 Status Research & Development GmbH # Copyright (c) 2021-2023 Status Research & Development GmbH
# Licensed and distributed under either of # Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * 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). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -82,7 +82,7 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
# https://github.com/ethereum/beacon-APIs/pull/232 # https://github.com/ethereum/beacon-APIs/pull/232
if node.config.debugForkChoice: if node.config.debugForkChoice or experimental in node.dag.updateFlags:
router.api(MethodGet, router.api(MethodGet,
"/eth/v1/debug/fork_choice") do () -> RestApiResponse: "/eth/v1/debug/fork_choice") do () -> RestApiResponse:
type type
@ -109,17 +109,6 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
var responses: seq[ForkChoiceResponse] var responses: seq[ForkChoiceResponse]
for item in node.attestationPool[].forkChoice.backend.proto_array: for item in node.attestationPool[].forkChoice.backend.proto_array:
let let
bid = node.dag.getBlockId(item.root)
slot =
if bid.isOk:
bid.unsafeGet.slot
else:
FAR_FUTURE_SLOT
executionPayloadRoot =
if bid.isOk:
node.dag.loadExecutionBlockRoot(bid.unsafeGet)
else:
ZERO_HASH
unrealized = item.unrealized.get(item.checkpoints) unrealized = item.unrealized.get(item.checkpoints)
u_justified_checkpoint = u_justified_checkpoint =
if unrealized.justified != item.checkpoints.justified: if unrealized.justified != item.checkpoints.justified:
@ -133,14 +122,14 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
none(Checkpoint) none(Checkpoint)
responses.add ForkChoiceResponse( responses.add ForkChoiceResponse(
slot: slot, slot: item.bid.slot,
block_root: item.root, block_root: item.bid.root,
parent_root: item.parent, parent_root: item.parent,
justified_epoch: item.checkpoints.justified.epoch, justified_epoch: item.checkpoints.justified.epoch,
finalized_epoch: item.checkpoints.finalized.epoch, finalized_epoch: item.checkpoints.finalized.epoch,
weight: cast[uint64](item.weight), weight: cast[uint64](item.weight),
execution_optimistic: node.dag.is_optimistic(item.root), execution_optimistic: node.dag.is_optimistic(item.bid.root),
execution_payload_root: executionPayloadRoot, execution_payload_root: node.dag.loadExecutionBlockRoot(item.bid),
extra_data: some ForkChoiceResponseExtraData( extra_data: some ForkChoiceResponseExtraData(
justified_root: item.checkpoints.justified.root, justified_root: item.checkpoints.justified.root,
finalized_root: item.checkpoints.finalized.root, finalized_root: item.checkpoints.finalized.root,

View File

@ -1,5 +1,5 @@
# beacon_chain # beacon_chain
# Copyright (c) 2018-2022 Status Research & Development GmbH # Copyright (c) 2018-2023 Status Research & Development GmbH
# Licensed and distributed under either of # Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * 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). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -46,7 +46,7 @@ type
justified_state_balances*: seq[Gwei] justified_state_balances*: seq[Gwei]
expected_head*: Eth2Digest expected_head*: Eth2Digest
of ProcessBlock: of ProcessBlock:
root*: Eth2Digest bid*: BlockId
parent_root*: Eth2Digest parent_root*: Eth2Digest
blk_checkpoints*: FinalityCheckpoints blk_checkpoints*: FinalityCheckpoints
of ProcessAttestation: of ProcessAttestation:
@ -54,7 +54,7 @@ type
block_root*: Eth2Digest block_root*: Eth2Digest
target_epoch*: Epoch target_epoch*: Epoch
of Prune: # ProtoArray specific of Prune: # ProtoArray specific
finalized_root*: Eth2Digest prune_checkpoints*: FinalityCheckpoints
expected_len*: int expected_len*: int
func apply(ctx: var ForkChoiceBackend, id: int, op: Operation) = func apply(ctx: var ForkChoiceBackend, id: int, op: Operation) =
@ -79,11 +79,11 @@ func apply(ctx: var ForkChoiceBackend, id: int, op: Operation) =
debugEcho &" Detected an expected invalid 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}"
of ProcessBlock: of ProcessBlock:
let r = ctx.process_block( let r = ctx.process_block(
block_root = op.root, bid = op.bid,
parent_root = op.parent_root, parent_root = op.parent_root,
checkpoints = op.blk_checkpoints) checkpoints = op.blk_checkpoints)
doAssert r.isOk(), &"process_block (op #{id}) returned an error: {r.error}" 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 checkpoint ", op.blk_checkpoints.justified debugEcho " Processed block 0x", op.bid.root, " with parent 0x", op.parent_root, " and justified checkpoint ", op.blk_checkpoints.justified
of ProcessAttestation: of ProcessAttestation:
ctx.process_attestation( ctx.process_attestation(
validator_index = op.validator_index, validator_index = op.validator_index,
@ -91,11 +91,11 @@ func apply(ctx: var ForkChoiceBackend, id: int, op: Operation) =
target_epoch = op.target_epoch) target_epoch = op.target_epoch)
debugEcho " Processed att target 0x", op.block_root, " from validator ", op.validator_index, " for epoch ", op.target_epoch debugEcho " Processed att target 0x", op.block_root, " from validator ", op.validator_index, " for epoch ", op.target_epoch
of Prune: of Prune:
let r = ctx.prune(op.finalized_root) let r = ctx.prune(op.prune_checkpoints)
doAssert r.isOk(), &"prune (op #{id}) returned an error: {r.error}" doAssert r.isOk(), &"prune (op #{id}) returned an error: {r.error}"
doAssert ctx.proto_array.nodes.len == op.expected_len, 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})" &"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 debugEcho " Maybe_pruned block preceding finalized block 0x", op.prune_checkpoints.finalized.root
func run*(ctx: var ForkChoiceBackend, ops: seq[Operation]) = func run*(ctx: var ForkChoiceBackend, ops: seq[Operation]) =
## Apply a sequence of fork-choice operations on a store ## Apply a sequence of fork-choice operations on a store

View File

@ -41,7 +41,9 @@ func setup_finality_01(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# 3 <- just: 2, fin: 1 # 3 <- just: 2, fin: 1
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(1), bid: BlockId(
slot: Epoch(1).start_slot,
root: fakeHash(1)),
parent_root: GenesisRoot, parent_root: GenesisRoot,
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(0)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
@ -49,7 +51,9 @@ func setup_finality_01(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(2), bid: BlockId(
slot: Epoch(2).start_slot,
root: fakeHash(2)),
parent_root: fakeHash(1), parent_root: fakeHash(1),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(1), epoch: Epoch(1)), justified: Checkpoint(root: fakeHash(1), epoch: Epoch(1)),
@ -57,7 +61,9 @@ func setup_finality_01(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(3), bid: BlockId(
slot: Epoch(3).start_slot,
root: fakeHash(3)),
parent_root: fakeHash(2), parent_root: fakeHash(2),
blk_Checkpoints: FinalityCheckpoints( blk_Checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(2), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(2), epoch: Epoch(2)),

View File

@ -47,7 +47,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Left branch # Left branch
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(1), bid: BlockId(
slot: Slot(1),
root: fakeHash(1)),
parent_root: GenesisRoot, parent_root: GenesisRoot,
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(0)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
@ -55,7 +57,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(3), bid: BlockId(
slot: Epoch(2).start_slot,
root: fakeHash(3)),
parent_root: fakeHash(1), parent_root: fakeHash(1),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(1), epoch: Epoch(1)), justified: Checkpoint(root: fakeHash(1), epoch: Epoch(1)),
@ -63,7 +67,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(5), bid: BlockId(
slot: Epoch(2).start_slot + 2,
root: fakeHash(5)),
parent_root: fakeHash(3), parent_root: fakeHash(3),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(1), epoch: Epoch(1)), justified: Checkpoint(root: fakeHash(1), epoch: Epoch(1)),
@ -71,7 +77,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(7), bid: BlockId(
slot: Epoch(2).start_slot + 4,
root: fakeHash(7)),
parent_root: fakeHash(5), parent_root: fakeHash(5),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(1), epoch: Epoch(1)), justified: Checkpoint(root: fakeHash(1), epoch: Epoch(1)),
@ -79,7 +87,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(9), bid: BlockId(
slot: Epoch(3).start_slot,
root: fakeHash(9)),
parent_root: fakeHash(7), parent_root: fakeHash(7),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(3), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(3), epoch: Epoch(2)),
@ -102,7 +112,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
# Right branch # Right branch
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(2), bid: BlockId(
slot: Slot(2),
root: fakeHash(2)),
parent_root: GenesisRoot, parent_root: GenesisRoot,
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(0)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
@ -110,7 +122,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(4), bid: BlockId(
slot: Epoch(1).start_slot + 1,
root: fakeHash(4)),
parent_root: fakeHash(2), parent_root: fakeHash(2),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(0)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
@ -118,7 +132,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(6), bid: BlockId(
slot: Epoch(2).start_slot + 3,
root: fakeHash(6)),
parent_root: fakeHash(4), parent_root: fakeHash(4),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(0)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(0)),
@ -126,7 +142,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(8), bid: BlockId(
slot: Epoch(3).start_slot + 1,
root: fakeHash(8)),
parent_root: fakeHash(6), parent_root: fakeHash(6),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(2), epoch: Epoch(1)), justified: Checkpoint(root: fakeHash(2), epoch: Epoch(1)),
@ -134,7 +152,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(10), bid: BlockId(
slot: Epoch(5).start_slot + 1,
root: fakeHash(10)),
parent_root: fakeHash(8), parent_root: fakeHash(8),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(4), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(4), epoch: Epoch(2)),

View File

@ -38,7 +38,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# 2 # 2
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(2), bid: BlockId(
slot: Epoch(3).start_slot + 2,
root: fakeHash(2)),
parent_root: GenesisRoot, parent_root: GenesisRoot,
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
@ -64,7 +66,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# 2 1 # 2 1
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(1), bid: BlockId(
slot: Epoch(3).start_slot + 1,
root: fakeHash(1)),
parent_root: GenesisRoot, parent_root: GenesisRoot,
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
@ -92,7 +96,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# 3 # 3
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(3), bid: BlockId(
slot: Epoch(3).start_slot + 3,
root: fakeHash(3)),
parent_root: fakeHash(1), parent_root: fakeHash(1),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
@ -122,7 +128,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# 4 3 # 4 3
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(4), bid: BlockId(
slot: Epoch(3).start_slot + 4,
root: fakeHash(4)),
parent_root: fakeHash(2), parent_root: fakeHash(2),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
@ -154,7 +162,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# 5 <- justified epoch = 2 # 5 <- justified epoch = 2
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(5), bid: BlockId(
slot: Epoch(4).start_slot,
root: fakeHash(5)),
parent_root: fakeHash(4), parent_root: fakeHash(4),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
@ -221,7 +231,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
# 6 # 6
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(6), bid: BlockId(
slot: Epoch(4).start_slot + 1,
root: fakeHash(6)),
parent_root: fakeHash(5), parent_root: fakeHash(5),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),

View File

@ -38,7 +38,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 2 # 2
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(2), bid: BlockId(
slot: Epoch(1).start_slot + 2,
root: fakeHash(2)),
parent_root: GenesisRoot, parent_root: GenesisRoot,
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
@ -64,7 +66,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 2 1 # 2 1
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(1), bid: BlockId(
slot: Epoch(1).start_slot + 1,
root: fakeHash(1)),
parent_root: GenesisRoot, parent_root: GenesisRoot,
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
@ -140,7 +144,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 3 # 3
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(3), bid: BlockId(
slot: Epoch(1).start_slot + 3,
root: fakeHash(3)),
parent_root: fakeHash(1), parent_root: fakeHash(1),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
@ -226,7 +232,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 4 # 4
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(4), bid: BlockId(
slot: Epoch(1).start_slot + 4,
root: fakeHash(4)),
parent_root: fakeHash(3), parent_root: fakeHash(3),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
@ -262,7 +270,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 5 <- justified epoch = 2 # 5 <- justified epoch = 2
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(5), bid: BlockId(
slot: Epoch(2).start_slot,
root: fakeHash(5)),
parent_root: fakeHash(4), parent_root: fakeHash(4),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
@ -300,7 +310,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 5 6 <- justified epoch = 0 # 5 6 <- justified epoch = 0
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(6), bid: BlockId(
slot: Epoch(2).start_slot + 1,
root: fakeHash(6)),
parent_root: fakeHash(4), parent_root: fakeHash(4),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)), justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
@ -349,7 +361,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 9 # 9
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(7), bid: BlockId(
slot: Epoch(2).start_slot + 2,
root: fakeHash(7)),
parent_root: fakeHash(5), parent_root: fakeHash(5),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
@ -357,7 +371,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(8), bid: BlockId(
slot: Epoch(2).start_slot + 3,
root: fakeHash(8)),
parent_root: fakeHash(7), parent_root: fakeHash(7),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
@ -366,7 +382,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# Finalizes 5 # Finalizes 5
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(9), bid: BlockId(
slot: Epoch(2).start_slot + 4,
root: fakeHash(9)),
parent_root: fakeHash(8), parent_root: fakeHash(8),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
@ -481,7 +499,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 9 10 # 9 10
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(10), bid: BlockId(
slot: Epoch(3).start_slot,
root: fakeHash(10)),
parent_root: fakeHash(8), parent_root: fakeHash(8),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
@ -626,7 +646,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# - 6 is a discarded chain # - 6 is a discarded chain
result.ops.add Operation( result.ops.add Operation(
kind: Prune, kind: Prune,
finalized_root: fakeHash(5), prune_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
finalized: Checkpoint(root: fakeHash(5), epoch: Epoch(2))),
expected_len: 6) expected_len: 6)
# Prune shouldn't have changed the head # Prune shouldn't have changed the head
@ -651,7 +673,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
# 11 # 11
result.ops.add Operation( result.ops.add Operation(
kind: ProcessBlock, kind: ProcessBlock,
root: fakeHash(11), bid: BlockId(
slot: Epoch(3).start_slot + 1,
root: fakeHash(11)),
parent_root: fakeHash(9), parent_root: fakeHash(9),
blk_checkpoints: FinalityCheckpoints( blk_checkpoints: FinalityCheckpoints(
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)), justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),