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:
parent
58ed9308d2
commit
ea6a6b1acd
4
Makefile
4
Makefile
|
@ -261,8 +261,8 @@ local-testnet-mainnet:
|
|||
|
||||
# test binaries that can output an XML report
|
||||
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_CORE) \
|
||||
|
|
|
@ -134,7 +134,7 @@ proc init*(T: type AttestationPool, dag: ChainDAGRef,
|
|||
# and then to make sure the fork choice data structure doesn't grow
|
||||
# too big - getting an EpochRef can be expensive.
|
||||
forkChoice.backend.process_block(
|
||||
blckRef.root, blckRef.parent.root, epochRef.checkpoints)
|
||||
blckRef.bid, blckRef.parent.root, epochRef.checkpoints)
|
||||
else:
|
||||
epochRef = dag.getEpochRef(blckRef, blckRef.slot.epoch, false).expect(
|
||||
"Getting an EpochRef should always work for non-finalized blocks")
|
||||
|
|
|
@ -79,15 +79,18 @@ template withUpdatedState*(
|
|||
else:
|
||||
failureBody
|
||||
|
||||
func get_effective_balances(validators: openArray[Validator], epoch: Epoch):
|
||||
seq[Gwei] =
|
||||
func get_effective_balances(
|
||||
validators: openArray[Validator],
|
||||
epoch: Epoch,
|
||||
ignoreSlashed: bool): seq[Gwei] =
|
||||
## Get the balances from a state as counted for fork choice
|
||||
result.newSeq(validators.len) # zero-init
|
||||
|
||||
for i in 0 ..< result.len:
|
||||
# All non-active validators have a 0 balance
|
||||
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
|
||||
|
||||
proc updateValidatorKeys*(dag: ChainDAGRef, validators: openArray[Validator]) =
|
||||
|
@ -569,7 +572,9 @@ func init*(
|
|||
epochRef.effective_balances_bytes =
|
||||
snappyEncode(SSZ.encode(
|
||||
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
|
||||
|
||||
|
@ -898,7 +903,7 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
|
|||
cfg.checkForkConsistency()
|
||||
|
||||
doAssert updateFlags - {
|
||||
strictVerification, enableTestFeatures, lowParticipation
|
||||
strictVerification, experimental, enableTestFeatures, lowParticipation
|
||||
} == {}, "Other flags not supported in ChainDAG"
|
||||
|
||||
# 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
|
||||
# allow skipping some validation.
|
||||
updateFlags: updateFlags * {
|
||||
strictVerification, enableTestFeatures, lowParticipation
|
||||
strictVerification, experimental, enableTestFeatures, lowParticipation
|
||||
},
|
||||
cfg: cfg,
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@ type
|
|||
## 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
|
||||
## should skip calculating that last state root.
|
||||
experimental ##\
|
||||
## Whether to enable extra features in development.
|
||||
enableTestFeatures ##\
|
||||
## Whether to enable extra features for testing.
|
||||
lowParticipation ##\
|
||||
|
|
|
@ -50,12 +50,13 @@ logScope: topics = "fork_choice"
|
|||
|
||||
func init*(
|
||||
T: type ForkChoiceBackend, checkpoints: FinalityCheckpoints,
|
||||
hasLowParticipation = false): T =
|
||||
T(proto_array: ProtoArray.init(checkpoints, hasLowParticipation))
|
||||
experimental = false, hasLowParticipation = false): T =
|
||||
T(proto_array: ProtoArray.init(
|
||||
checkpoints, experimental, hasLowParticipation))
|
||||
|
||||
proc init*(
|
||||
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
|
||||
## state, the justified and finalized checkpoints are the same, so only one
|
||||
## is used here
|
||||
|
@ -68,8 +69,9 @@ proc init*(
|
|||
FinalityCheckpoints(
|
||||
justified: checkpoint,
|
||||
finalized: checkpoint),
|
||||
hasLowParticipation),
|
||||
experimental, hasLowParticipation),
|
||||
checkpoints: Checkpoints(
|
||||
experimental: experimental,
|
||||
justified: BalanceCheckpoint(
|
||||
checkpoint: checkpoint,
|
||||
balances: epochRef.effective_balances),
|
||||
|
@ -116,7 +118,7 @@ proc update_justified(
|
|||
epochRef = dag.getEpochRef(blck, epoch, false).valueOr:
|
||||
# Shouldn't happen for justified data unless out of sync with ChainDAG
|
||||
warn "Skipping justified checkpoint update, no EpochRef - report bug",
|
||||
blck, epoch, best = self.best_justified.epoch, error
|
||||
blck, epoch, error
|
||||
return
|
||||
justified = Checkpoint(root: blck.root, epoch: epochRef.epoch)
|
||||
|
||||
|
@ -144,17 +146,21 @@ proc update_checkpoints(
|
|||
## Update checkpoints in store if necessary
|
||||
# Update justified checkpoint
|
||||
if checkpoints.justified.epoch > self.justified.checkpoint.epoch:
|
||||
if not self.experimental:
|
||||
if checkpoints.justified.epoch > self.best_justified.epoch:
|
||||
self.best_justified = checkpoints.justified
|
||||
|
||||
if ? should_update_justified_checkpoint(self, dag, checkpoints.justified):
|
||||
? self.update_justified(dag, checkpoints.justified)
|
||||
else:
|
||||
? self.update_justified(dag, checkpoints.justified)
|
||||
|
||||
# Update finalized checkpoint
|
||||
if checkpoints.finalized.epoch > self.finalized.epoch:
|
||||
trace "Updating finalized",
|
||||
store = self.finalized, state = checkpoints.finalized
|
||||
self.finalized = checkpoints.finalized
|
||||
if not self.experimental:
|
||||
if checkpoints.justified != self.justified.checkpoint:
|
||||
? self.update_justified(dag, checkpoints.justified)
|
||||
|
||||
|
@ -185,6 +191,7 @@ proc on_tick(
|
|||
|
||||
# Update store.justified_checkpoint if a better checkpoint on the
|
||||
# store.finalized_checkpoint chain
|
||||
if not self.checkpoints.experimental:
|
||||
let
|
||||
best_justified_epoch = self.checkpoints.best_justified.epoch
|
||||
store_justified_epoch = self.checkpoints.justified.checkpoint.epoch
|
||||
|
@ -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
|
||||
func process_block*(self: var ForkChoiceBackend,
|
||||
block_root: Eth2Digest,
|
||||
bid: BlockId,
|
||||
parent_root: Eth2Digest,
|
||||
checkpoints: FinalityCheckpoints,
|
||||
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,
|
||||
dag: ChainDAGRef,
|
||||
|
@ -358,14 +365,14 @@ proc process_block*(self: var ForkChoice,
|
|||
blck = shortLog(blckRef), checkpoints = epochRef.checkpoints, unrealized
|
||||
? update_checkpoints(self.checkpoints, dag, unrealized)
|
||||
? process_block(
|
||||
self.backend, blckRef.root, blck.parent_root, unrealized)
|
||||
self.backend, blckRef.bid, blck.parent_root, unrealized)
|
||||
else:
|
||||
? process_block(
|
||||
self.backend, blckRef.root, blck.parent_root,
|
||||
self.backend, blckRef.bid, blck.parent_root,
|
||||
epochRef.checkpoints, some unrealized) # Realized in `on_tick`
|
||||
else:
|
||||
? process_block(
|
||||
self.backend, blckRef.root, blck.parent_root, epochRef.checkpoints)
|
||||
self.backend, blckRef.bid, blck.parent_root, epochRef.checkpoints)
|
||||
|
||||
ok()
|
||||
|
||||
|
@ -424,13 +431,16 @@ func get_safe_beacon_block_root*(self: ForkChoice): Eth2Digest =
|
|||
self.checkpoints.justified.checkpoint.root
|
||||
|
||||
func prune*(
|
||||
self: var ForkChoiceBackend, finalized_root: Eth2Digest
|
||||
self: var ForkChoiceBackend, checkpoints: FinalityCheckpoints
|
||||
): FcResult[void] =
|
||||
## 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] =
|
||||
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) =
|
||||
try:
|
||||
|
|
|
@ -88,6 +88,7 @@ type
|
|||
## Subtracted from logical index to get the physical index
|
||||
|
||||
ProtoArray* = object
|
||||
experimental*: bool
|
||||
hasLowParticipation*: bool
|
||||
currentEpoch*: Epoch
|
||||
checkpoints*: FinalityCheckpoints
|
||||
|
@ -98,7 +99,7 @@ type
|
|||
previousProposerBoostScore*: uint64
|
||||
|
||||
ProtoNode* = object
|
||||
root*: Eth2Digest
|
||||
bid*: BlockId
|
||||
parent*: Option[Index]
|
||||
checkpoints*: FinalityCheckpoints
|
||||
weight*: int64
|
||||
|
@ -111,6 +112,7 @@ type
|
|||
balances*: seq[Gwei]
|
||||
|
||||
Checkpoints* = object
|
||||
experimental*: bool
|
||||
time*: BeaconTime
|
||||
justified*: BalanceCheckpoint
|
||||
finalized*: Checkpoint
|
||||
|
|
|
@ -81,17 +81,21 @@ func maybeUpdateBestChildAndDescendant(self: var ProtoArray,
|
|||
parentIdx: Index,
|
||||
childIdx: Index): FcResult[void]
|
||||
|
||||
func nodeIsViableForHead(self: ProtoArray, node: ProtoNode): bool
|
||||
func nodeLeadsToViableHead(self: ProtoArray, node: ProtoNode): FcResult[bool]
|
||||
func nodeIsViableForHead(
|
||||
self: ProtoArray, node: ProtoNode, nodeIdx: Index): bool
|
||||
func nodeLeadsToViableHead(
|
||||
self: ProtoArray, node: ProtoNode, nodeIdx: Index): FcResult[bool]
|
||||
|
||||
# ProtoArray routines
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
func init*(
|
||||
T: type ProtoArray, checkpoints: FinalityCheckpoints,
|
||||
hasLowParticipation: bool): T =
|
||||
experimental, hasLowParticipation: bool): T =
|
||||
let node = ProtoNode(
|
||||
root: checkpoints.finalized.root,
|
||||
bid: BlockId(
|
||||
slot: checkpoints.finalized.epoch.start_slot,
|
||||
root: checkpoints.finalized.root),
|
||||
parent: none(int),
|
||||
checkpoints: checkpoints,
|
||||
weight: 0,
|
||||
|
@ -99,10 +103,11 @@ func init*(
|
|||
bestChild: none(int),
|
||||
bestDescendant: none(int))
|
||||
|
||||
T(hasLowParticipation: hasLowParticipation,
|
||||
T(experimental: experimental,
|
||||
hasLowParticipation: hasLowParticipation,
|
||||
checkpoints: checkpoints,
|
||||
nodes: ProtoNodes(buf: @[node], offset: 0),
|
||||
indices: {node.root: 0}.toTable())
|
||||
indices: {node.bid.root: 0}.toTable())
|
||||
|
||||
iterator realizePendingCheckpoints*(
|
||||
self: var ProtoArray, resetTipTracking = true): FinalityCheckpoints =
|
||||
|
@ -111,7 +116,7 @@ iterator realizePendingCheckpoints*(
|
|||
let physicalIdx = idx - self.nodes.offset
|
||||
if unrealized != self.nodes.buf[physicalIdx].checkpoints:
|
||||
trace "Pulling up chain tip",
|
||||
blck = self.nodes.buf[physicalIdx].root,
|
||||
blck = self.nodes.buf[physicalIdx].bid.root,
|
||||
checkpoints = self.nodes.buf[physicalIdx].checkpoints,
|
||||
unrealized
|
||||
self.nodes.buf[physicalIdx].checkpoints = unrealized
|
||||
|
@ -173,7 +178,7 @@ func applyScoreChanges*(self: var ProtoArray,
|
|||
self.checkpoints = checkpoints
|
||||
|
||||
# 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):
|
||||
discard
|
||||
|
||||
|
@ -187,7 +192,7 @@ func applyScoreChanges*(self: var ProtoArray,
|
|||
|
||||
# Iterate backwards through all the indices in `self.nodes`
|
||||
for nodePhysicalIdx in countdown(self.nodes.len - 1, 0):
|
||||
if node.root.isZero:
|
||||
if node.bid.root.isZero:
|
||||
continue
|
||||
|
||||
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,
|
||||
# decrease the delta by the previous score amount.
|
||||
if (not self.previousProposerBoostRoot.isZero) and
|
||||
self.previousProposerBoostRoot == node.root:
|
||||
self.previousProposerBoostRoot == node.bid.root:
|
||||
if nodeDelta < 0 and
|
||||
nodeDelta - low(Delta) < self.previousProposerBoostScore.int64:
|
||||
return err ForkChoiceError(
|
||||
|
@ -207,7 +212,7 @@ func applyScoreChanges*(self: var ProtoArray,
|
|||
# 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
|
||||
if (not proposerBoostRoot.isZero) and proposerBoostRoot == node.root:
|
||||
if (not proposerBoostRoot.isZero) and proposerBoostRoot == node.bid.root:
|
||||
proposerBoostScore = calculateProposerBoost(newBalances)
|
||||
if nodeDelta >= 0 and
|
||||
high(Delta) - nodeDelta < proposerBoostScore.int64:
|
||||
|
@ -265,7 +270,7 @@ func applyScoreChanges*(self: var ProtoArray,
|
|||
self.previousProposerBoostScore = proposerBoostScore
|
||||
|
||||
for nodePhysicalIdx in countdown(self.nodes.len - 1, 0):
|
||||
if node.root.isZero:
|
||||
if node.bid.root.isZero:
|
||||
continue
|
||||
|
||||
if node.parent.isSome():
|
||||
|
@ -281,7 +286,7 @@ func applyScoreChanges*(self: var ProtoArray,
|
|||
ok()
|
||||
|
||||
func onBlock*(self: var ProtoArray,
|
||||
root: Eth2Digest,
|
||||
bid: BlockId,
|
||||
parent: Eth2Digest,
|
||||
checkpoints: FinalityCheckpoints,
|
||||
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.
|
||||
|
||||
# If the block is already known, ignore it
|
||||
if root in self.indices:
|
||||
if bid.root in self.indices:
|
||||
return ok()
|
||||
|
||||
var parentIdx: Index
|
||||
|
@ -303,13 +308,13 @@ func onBlock*(self: var ProtoArray,
|
|||
do:
|
||||
return err ForkChoiceError(
|
||||
kind: fcUnknownParent,
|
||||
childRoot: root,
|
||||
childRoot: bid.root,
|
||||
parentRoot: parent)
|
||||
|
||||
let nodeLogicalIdx = self.nodes.offset + self.nodes.buf.len
|
||||
|
||||
let node = ProtoNode(
|
||||
root: root,
|
||||
bid: bid,
|
||||
parent: some(parentIdx),
|
||||
checkpoints: checkpoints,
|
||||
weight: 0,
|
||||
|
@ -317,11 +322,11 @@ func onBlock*(self: var ProtoArray,
|
|||
bestChild: none(int),
|
||||
bestDescendant: none(int))
|
||||
|
||||
self.indices[node.root] = nodeLogicalIdx
|
||||
self.indices[node.bid.root] = nodeLogicalIdx
|
||||
self.nodes.add node
|
||||
|
||||
if unrealized.isSome:
|
||||
self.currentEpochTips.del parentIdx
|
||||
if unrealized.isSome:
|
||||
self.currentEpochTips[nodeLogicalIdx] = unrealized.get
|
||||
|
||||
? self.maybeUpdateBestChildAndDescendant(parentIdx, nodeLogicalIdx)
|
||||
|
@ -359,34 +364,38 @@ func findHead*(self: var ProtoArray,
|
|||
index: bestDescendantIdx)
|
||||
|
||||
# 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(
|
||||
kind: fcInvalidBestNode,
|
||||
startRoot: justifiedRoot,
|
||||
fkChoiceCheckpoints: self.checkpoints,
|
||||
headRoot: justifiedNode.get().root,
|
||||
headRoot: justifiedNode.get().bid.root,
|
||||
headCheckpoints: justifiedNode.get().checkpoints)
|
||||
|
||||
head = bestNode.get().root
|
||||
head = bestNode.get().bid.root
|
||||
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.
|
||||
## 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:
|
||||
## - 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`
|
||||
|
||||
var finalizedIdx: int
|
||||
self.indices.withValue(finalizedRoot, value) do:
|
||||
self.indices.withValue(checkpoints.finalized.root, value) do:
|
||||
finalizedIdx = value[]
|
||||
do:
|
||||
return err ForkChoiceError(
|
||||
kind: fcFinalizedNodeUnknown,
|
||||
blockRoot: finalizedRoot)
|
||||
blockRoot: checkpoints.finalized.root)
|
||||
|
||||
if finalizedIdx == self.nodes.offset:
|
||||
# Nothing to do
|
||||
|
@ -395,15 +404,14 @@ func prune*(self: var ProtoArray, finalizedRoot: Eth2Digest): FcResult[void] =
|
|||
if finalizedIdx < self.nodes.offset:
|
||||
return err ForkChoiceError(
|
||||
kind: fcPruningFromOutdatedFinalizedRoot,
|
||||
finalizedRoot: finalizedRoot)
|
||||
finalizedRoot: checkpoints.finalized.root)
|
||||
|
||||
trace "Pruning blocks from fork choice",
|
||||
finalizedRoot = shortLog(finalizedRoot)
|
||||
trace "Pruning blocks from fork choice", checkpoints
|
||||
|
||||
let finalPhysicalIdx = finalizedIdx - self.nodes.offset
|
||||
for nodeIdx in 0 ..< finalPhysicalIdx:
|
||||
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.
|
||||
# This is done in-place with `moveMem` to avoid costly reallocations.
|
||||
|
@ -445,7 +453,8 @@ func maybeUpdateBestChildAndDescendant(self: var ProtoArray,
|
|||
kind: fcInvalidNodeIndex,
|
||||
index: parentIdx)
|
||||
|
||||
let childLeadsToViableHead = ? self.nodeLeadsToViableHead(child.get())
|
||||
let childLeadsToViableHead =
|
||||
? self.nodeLeadsToViableHead(child.get(), childIdx)
|
||||
|
||||
let # Aliases to the 3 possible (bestChild, bestDescendant) tuples
|
||||
changeToNone = (none(Index), none(Index))
|
||||
|
@ -477,7 +486,7 @@ func maybeUpdateBestChildAndDescendant(self: var ProtoArray,
|
|||
index: bestChildIdx)
|
||||
|
||||
let bestChildLeadsToViableHead =
|
||||
? self.nodeLeadsToViableHead(bestChild.get())
|
||||
? self.nodeLeadsToViableHead(bestChild.get(), bestChildIdx)
|
||||
|
||||
if childLeadsToViableHead and not bestChildLeadsToViableHead:
|
||||
# The child leads to a viable head, but the current best-child doesn't
|
||||
|
@ -487,7 +496,7 @@ func maybeUpdateBestChildAndDescendant(self: var ProtoArray,
|
|||
noChange
|
||||
elif child.get().weight == bestChild.get().weight:
|
||||
# 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
|
||||
else:
|
||||
noChange
|
||||
|
@ -511,7 +520,8 @@ func maybeUpdateBestChildAndDescendant(self: var ProtoArray,
|
|||
|
||||
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
|
||||
## for blockchain head
|
||||
let bestDescendantIsViableForHead = block:
|
||||
|
@ -522,13 +532,14 @@ func nodeLeadsToViableHead(self: ProtoArray, node: ProtoNode): FcResult[bool] =
|
|||
return err ForkChoiceError(
|
||||
kind: fcInvalidBestDescendant,
|
||||
index: bestDescendantIdx)
|
||||
self.nodeIsViableForHead(bestDescendant.get())
|
||||
self.nodeIsViableForHead(bestDescendant.get(), bestDescendantIdx)
|
||||
else:
|
||||
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
|
||||
## 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
|
||||
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
|
||||
## should not be viable for the head.
|
||||
(
|
||||
|
@ -582,7 +622,7 @@ func propagateInvalidity*(
|
|||
# Helpers to dump internal state
|
||||
|
||||
type ProtoArrayItem* = object
|
||||
root*: Eth2Digest
|
||||
bid*: BlockId
|
||||
parent*: Eth2Digest
|
||||
checkpoints*: FinalityCheckpoints
|
||||
unrealized*: Option[FinalityCheckpoints]
|
||||
|
@ -597,13 +637,13 @@ func root(self: ProtoNodes, logicalIdx: Option[Index]): Eth2Digest =
|
|||
let node = self[logicalIdx.unsafeGet]
|
||||
if node.isNone:
|
||||
return ZERO_HASH
|
||||
node.unsafeGet.root
|
||||
node.unsafeGet.bid.root
|
||||
|
||||
iterator items*(self: ProtoArray): ProtoArrayItem =
|
||||
## Iterate over all nodes known by fork choice.
|
||||
doAssert self.indices.len == self.nodes.len
|
||||
for nodePhysicalIdx, node in self.nodes.buf:
|
||||
if node.root.isZero:
|
||||
if node.bid.root.isZero:
|
||||
continue
|
||||
|
||||
let unrealized = block:
|
||||
|
@ -614,7 +654,7 @@ iterator items*(self: ProtoArray): ProtoArrayItem =
|
|||
none(FinalityCheckpoints)
|
||||
|
||||
yield ProtoArrayItem(
|
||||
root: node.root,
|
||||
bid: node.bid,
|
||||
parent: self.nodes.root(node.parent),
|
||||
checkpoints: node.checkpoints,
|
||||
unrealized: unrealized,
|
||||
|
|
|
@ -142,8 +142,7 @@ proc loadChainDag(
|
|||
db: BeaconChainDB,
|
||||
eventBus: EventBus,
|
||||
validatorMonitor: ref ValidatorMonitor,
|
||||
networkGenesisValidatorsRoot: Opt[Eth2Digest],
|
||||
shouldEnableTestFeatures: bool): ChainDAGRef =
|
||||
networkGenesisValidatorsRoot: Opt[Eth2Digest]): ChainDAGRef =
|
||||
info "Loading block DAG from database", path = config.databaseDir
|
||||
|
||||
var dag: ChainDAGRef
|
||||
|
@ -170,10 +169,12 @@ proc loadChainDag(
|
|||
jsonVersion: 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
|
||||
extraFlags =
|
||||
if shouldEnableTestFeatures: {enableTestFeatures, lowParticipation}
|
||||
else: {enableTestFeatures}
|
||||
chainDagFlags =
|
||||
if config.strictVerification: {strictVerification}
|
||||
else: {}
|
||||
|
@ -548,8 +549,7 @@ proc init*(T: type BeaconNode,
|
|||
|
||||
dag = loadChainDag(
|
||||
config, cfg, db, eventBus,
|
||||
validatorMonitor, networkGenesisValidatorsRoot,
|
||||
config.deploymentPhase <= DeploymentPhase.Testnet)
|
||||
validatorMonitor, networkGenesisValidatorsRoot)
|
||||
genesisTime = getStateField(dag.headState, genesis_time)
|
||||
beaconClock = BeaconClock.init(genesisTime)
|
||||
getBeaconTime = beaconClock.getBeaconTimeFn()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# beacon_chain
|
||||
# Copyright (c) 2021-2022 Status Research & Development GmbH
|
||||
# Copyright (c) 2021-2023 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).
|
||||
|
@ -82,7 +82,7 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
)
|
||||
|
||||
# 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,
|
||||
"/eth/v1/debug/fork_choice") do () -> RestApiResponse:
|
||||
type
|
||||
|
@ -109,17 +109,6 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
var responses: seq[ForkChoiceResponse]
|
||||
for item in node.attestationPool[].forkChoice.backend.proto_array:
|
||||
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)
|
||||
u_justified_checkpoint =
|
||||
if unrealized.justified != item.checkpoints.justified:
|
||||
|
@ -133,14 +122,14 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
none(Checkpoint)
|
||||
|
||||
responses.add ForkChoiceResponse(
|
||||
slot: slot,
|
||||
block_root: item.root,
|
||||
slot: item.bid.slot,
|
||||
block_root: item.bid.root,
|
||||
parent_root: item.parent,
|
||||
justified_epoch: item.checkpoints.justified.epoch,
|
||||
finalized_epoch: item.checkpoints.finalized.epoch,
|
||||
weight: cast[uint64](item.weight),
|
||||
execution_optimistic: node.dag.is_optimistic(item.root),
|
||||
execution_payload_root: executionPayloadRoot,
|
||||
execution_optimistic: node.dag.is_optimistic(item.bid.root),
|
||||
execution_payload_root: node.dag.loadExecutionBlockRoot(item.bid),
|
||||
extra_data: some ForkChoiceResponseExtraData(
|
||||
justified_root: item.checkpoints.justified.root,
|
||||
finalized_root: item.checkpoints.finalized.root,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# beacon_chain
|
||||
# Copyright (c) 2018-2022 Status Research & Development GmbH
|
||||
# Copyright (c) 2018-2023 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).
|
||||
|
@ -46,7 +46,7 @@ type
|
|||
justified_state_balances*: seq[Gwei]
|
||||
expected_head*: Eth2Digest
|
||||
of ProcessBlock:
|
||||
root*: Eth2Digest
|
||||
bid*: BlockId
|
||||
parent_root*: Eth2Digest
|
||||
blk_checkpoints*: FinalityCheckpoints
|
||||
of ProcessAttestation:
|
||||
|
@ -54,7 +54,7 @@ type
|
|||
block_root*: Eth2Digest
|
||||
target_epoch*: Epoch
|
||||
of Prune: # ProtoArray specific
|
||||
finalized_root*: Eth2Digest
|
||||
prune_checkpoints*: FinalityCheckpoints
|
||||
expected_len*: int
|
||||
|
||||
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}"
|
||||
of ProcessBlock:
|
||||
let r = ctx.process_block(
|
||||
block_root = op.root,
|
||||
bid = op.bid,
|
||||
parent_root = op.parent_root,
|
||||
checkpoints = op.blk_checkpoints)
|
||||
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:
|
||||
ctx.process_attestation(
|
||||
validator_index = op.validator_index,
|
||||
|
@ -91,11 +91,11 @@ func apply(ctx: var ForkChoiceBackend, id: int, op: Operation) =
|
|||
target_epoch = op.target_epoch)
|
||||
debugEcho " Processed att target 0x", op.block_root, " from validator ", op.validator_index, " for epoch ", op.target_epoch
|
||||
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 ctx.proto_array.nodes.len == op.expected_len,
|
||||
&"prune (op #{id}): the resulting length ({ctx.proto_array.nodes.len}) was not expected ({op.expected_len})"
|
||||
debugEcho " Maybe_pruned block preceding finalized block 0x", op.finalized_root
|
||||
debugEcho " Maybe_pruned block preceding finalized block 0x", op.prune_checkpoints.finalized.root
|
||||
|
||||
func run*(ctx: var ForkChoiceBackend, ops: seq[Operation]) =
|
||||
## Apply a sequence of fork-choice operations on a store
|
||||
|
|
|
@ -41,7 +41,9 @@ func setup_finality_01(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
|
|||
# 3 <- just: 2, fin: 1
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(1),
|
||||
bid: BlockId(
|
||||
slot: Epoch(1).start_slot,
|
||||
root: fakeHash(1)),
|
||||
parent_root: GenesisRoot,
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(2),
|
||||
bid: BlockId(
|
||||
slot: Epoch(2).start_slot,
|
||||
root: fakeHash(2)),
|
||||
parent_root: fakeHash(1),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(3),
|
||||
bid: BlockId(
|
||||
slot: Epoch(3).start_slot,
|
||||
root: fakeHash(3)),
|
||||
parent_root: fakeHash(2),
|
||||
blk_Checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: fakeHash(2), epoch: Epoch(2)),
|
||||
|
|
|
@ -47,7 +47,9 @@ func setup_finality_02(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operati
|
|||
# Left branch
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(1),
|
||||
bid: BlockId(
|
||||
slot: Slot(1),
|
||||
root: fakeHash(1)),
|
||||
parent_root: GenesisRoot,
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(3),
|
||||
bid: BlockId(
|
||||
slot: Epoch(2).start_slot,
|
||||
root: fakeHash(3)),
|
||||
parent_root: fakeHash(1),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(5),
|
||||
bid: BlockId(
|
||||
slot: Epoch(2).start_slot + 2,
|
||||
root: fakeHash(5)),
|
||||
parent_root: fakeHash(3),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(7),
|
||||
bid: BlockId(
|
||||
slot: Epoch(2).start_slot + 4,
|
||||
root: fakeHash(7)),
|
||||
parent_root: fakeHash(5),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(9),
|
||||
bid: BlockId(
|
||||
slot: Epoch(3).start_slot,
|
||||
root: fakeHash(9)),
|
||||
parent_root: fakeHash(7),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(2),
|
||||
bid: BlockId(
|
||||
slot: Slot(2),
|
||||
root: fakeHash(2)),
|
||||
parent_root: GenesisRoot,
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(4),
|
||||
bid: BlockId(
|
||||
slot: Epoch(1).start_slot + 1,
|
||||
root: fakeHash(4)),
|
||||
parent_root: fakeHash(2),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(6),
|
||||
bid: BlockId(
|
||||
slot: Epoch(2).start_slot + 3,
|
||||
root: fakeHash(6)),
|
||||
parent_root: fakeHash(4),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(8),
|
||||
bid: BlockId(
|
||||
slot: Epoch(3).start_slot + 1,
|
||||
root: fakeHash(8)),
|
||||
parent_root: fakeHash(6),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(10),
|
||||
bid: BlockId(
|
||||
slot: Epoch(5).start_slot + 1,
|
||||
root: fakeHash(10)),
|
||||
parent_root: fakeHash(8),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: fakeHash(4), epoch: Epoch(2)),
|
||||
|
|
|
@ -38,7 +38,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
|
|||
# 2
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(2),
|
||||
bid: BlockId(
|
||||
slot: Epoch(3).start_slot + 2,
|
||||
root: fakeHash(2)),
|
||||
parent_root: GenesisRoot,
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
||||
|
@ -64,7 +66,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
|
|||
# 2 1
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(1),
|
||||
bid: BlockId(
|
||||
slot: Epoch(3).start_slot + 1,
|
||||
root: fakeHash(1)),
|
||||
parent_root: GenesisRoot,
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
||||
|
@ -92,7 +96,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
|
|||
# 3
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(3),
|
||||
bid: BlockId(
|
||||
slot: Epoch(3).start_slot + 3,
|
||||
root: fakeHash(3)),
|
||||
parent_root: fakeHash(1),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
||||
|
@ -122,7 +128,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
|
|||
# 4 3
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(4),
|
||||
bid: BlockId(
|
||||
slot: Epoch(3).start_slot + 4,
|
||||
root: fakeHash(4)),
|
||||
parent_root: fakeHash(2),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(5),
|
||||
bid: BlockId(
|
||||
slot: Epoch(4).start_slot,
|
||||
root: fakeHash(5)),
|
||||
parent_root: fakeHash(4),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
|
||||
|
@ -221,7 +231,9 @@ func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]
|
|||
# 6
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(6),
|
||||
bid: BlockId(
|
||||
slot: Epoch(4).start_slot + 1,
|
||||
root: fakeHash(6)),
|
||||
parent_root: fakeHash(5),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
|
||||
|
|
|
@ -38,7 +38,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
|
|||
# 2
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(2),
|
||||
bid: BlockId(
|
||||
slot: Epoch(1).start_slot + 2,
|
||||
root: fakeHash(2)),
|
||||
parent_root: GenesisRoot,
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
||||
|
@ -64,7 +66,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
|
|||
# 2 1
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(1),
|
||||
bid: BlockId(
|
||||
slot: Epoch(1).start_slot + 1,
|
||||
root: fakeHash(1)),
|
||||
parent_root: GenesisRoot,
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
||||
|
@ -140,7 +144,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
|
|||
# 3
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(3),
|
||||
bid: BlockId(
|
||||
slot: Epoch(1).start_slot + 3,
|
||||
root: fakeHash(3)),
|
||||
parent_root: fakeHash(1),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
||||
|
@ -226,7 +232,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
|
|||
# 4
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(4),
|
||||
bid: BlockId(
|
||||
slot: Epoch(1).start_slot + 4,
|
||||
root: fakeHash(4)),
|
||||
parent_root: fakeHash(3),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(5),
|
||||
bid: BlockId(
|
||||
slot: Epoch(2).start_slot,
|
||||
root: fakeHash(5)),
|
||||
parent_root: fakeHash(4),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(6),
|
||||
bid: BlockId(
|
||||
slot: Epoch(2).start_slot + 1,
|
||||
root: fakeHash(6)),
|
||||
parent_root: fakeHash(4),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
||||
|
@ -349,7 +361,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
|
|||
# 9
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(7),
|
||||
bid: BlockId(
|
||||
slot: Epoch(2).start_slot + 2,
|
||||
root: fakeHash(7)),
|
||||
parent_root: fakeHash(5),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(8),
|
||||
bid: BlockId(
|
||||
slot: Epoch(2).start_slot + 3,
|
||||
root: fakeHash(8)),
|
||||
parent_root: fakeHash(7),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
|
||||
|
@ -366,7 +382,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
|
|||
# Finalizes 5
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(9),
|
||||
bid: BlockId(
|
||||
slot: Epoch(2).start_slot + 4,
|
||||
root: fakeHash(9)),
|
||||
parent_root: fakeHash(8),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
|
||||
|
@ -481,7 +499,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
|
|||
# 9 10
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(10),
|
||||
bid: BlockId(
|
||||
slot: Epoch(3).start_slot,
|
||||
root: fakeHash(10)),
|
||||
parent_root: fakeHash(8),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
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
|
||||
result.ops.add Operation(
|
||||
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)
|
||||
|
||||
# Prune shouldn't have changed the head
|
||||
|
@ -651,7 +673,9 @@ func setup_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
|
|||
# 11
|
||||
result.ops.add Operation(
|
||||
kind: ProcessBlock,
|
||||
root: fakeHash(11),
|
||||
bid: BlockId(
|
||||
slot: Epoch(3).start_slot + 1,
|
||||
root: fakeHash(11)),
|
||||
parent_root: fakeHash(9),
|
||||
blk_checkpoints: FinalityCheckpoints(
|
||||
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
|
||||
|
|
Loading…
Reference in New Issue