adjust checkpoint tracking for devnets (#4039)
Track checkpoints more defensively on devnets with low participation.
This commit is contained in:
parent
b60456fdf3
commit
994339c7ee
|
@ -97,7 +97,9 @@ proc init*(T: type AttestationPool, dag: ChainDAGRef,
|
|||
## holding a zero_root.
|
||||
let finalizedEpochRef = dag.getFinalizedEpochRef()
|
||||
|
||||
var forkChoice = ForkChoice.init(finalizedEpochRef, dag.finalizedHead.blck)
|
||||
var forkChoice = ForkChoice.init(
|
||||
finalizedEpochRef, dag.finalizedHead.blck,
|
||||
lowParticipation in dag.updateFlags)
|
||||
|
||||
# Feed fork choice with unfinalized history - during startup, block pool only
|
||||
# keeps track of a single history so we just need to follow it
|
||||
|
|
|
@ -775,8 +775,9 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
|
|||
lcDataConfig = default(LightClientDataConfig)): ChainDAGRef =
|
||||
cfg.checkForkConsistency()
|
||||
|
||||
doAssert updateFlags - {strictVerification, enableTestFeatures} == {},
|
||||
"Other flags not supported in ChainDAG"
|
||||
doAssert updateFlags - {
|
||||
strictVerification, enableTestFeatures, lowParticipation
|
||||
} == {}, "Other flags not supported in ChainDAG"
|
||||
|
||||
# TODO we require that the db contains both a head and a tail block -
|
||||
# asserting here doesn't seem like the right way to go about it however..
|
||||
|
@ -803,7 +804,9 @@ 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: {strictVerification, enableTestFeatures} * updateFlags,
|
||||
updateFlags: updateFlags * {
|
||||
strictVerification, enableTestFeatures, lowParticipation
|
||||
},
|
||||
cfg: cfg,
|
||||
|
||||
vanityLogs: vanityLogs,
|
||||
|
|
|
@ -41,5 +41,7 @@ type
|
|||
## should skip calculating that last state root.
|
||||
enableTestFeatures ##\
|
||||
## Whether to enable extra features for testing.
|
||||
lowParticipation ##\
|
||||
## Whether the network is prone to low participation.
|
||||
|
||||
UpdateFlags* = set[UpdateFlag]
|
||||
|
|
|
@ -51,10 +51,14 @@ func compute_deltas(
|
|||
|
||||
logScope: topics = "fork_choice"
|
||||
|
||||
func init*(T: type ForkChoiceBackend, checkpoints: FinalityCheckpoints): T =
|
||||
T(proto_array: ProtoArray.init(checkpoints))
|
||||
func init*(
|
||||
T: type ForkChoiceBackend, checkpoints: FinalityCheckpoints,
|
||||
hasLowParticipation = false): T =
|
||||
T(proto_array: ProtoArray.init(checkpoints, hasLowParticipation))
|
||||
|
||||
proc init*(T: type ForkChoice, epochRef: EpochRef, blck: BlockRef): T =
|
||||
proc init*(
|
||||
T: type ForkChoice, epochRef: EpochRef, blck: BlockRef,
|
||||
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
|
||||
|
@ -66,7 +70,8 @@ proc init*(T: type ForkChoice, epochRef: EpochRef, blck: BlockRef): T =
|
|||
backend: ForkChoiceBackend.init(
|
||||
FinalityCheckpoints(
|
||||
justified: checkpoint,
|
||||
finalized: checkpoint)),
|
||||
finalized: checkpoint),
|
||||
hasLowParticipation),
|
||||
checkpoints: Checkpoints(
|
||||
justified: BalanceCheckpoint(
|
||||
checkpoint: checkpoint,
|
||||
|
@ -369,6 +374,7 @@ proc process_block*(self: var ForkChoice,
|
|||
|
||||
func find_head*(
|
||||
self: var ForkChoiceBackend,
|
||||
current_epoch: Epoch,
|
||||
checkpoints: FinalityCheckpoints,
|
||||
justified_state_balances: seq[Gwei],
|
||||
proposer_boost_root: Eth2Digest
|
||||
|
@ -387,7 +393,8 @@ func find_head*(
|
|||
|
||||
# Apply score changes
|
||||
? self.proto_array.applyScoreChanges(
|
||||
deltas, checkpoints, justified_state_balances, proposer_boost_root)
|
||||
deltas, current_epoch, checkpoints,
|
||||
justified_state_balances, proposer_boost_root)
|
||||
|
||||
self.balances = justified_state_balances
|
||||
|
||||
|
@ -407,6 +414,7 @@ proc get_head*(self: var ForkChoice,
|
|||
? self.update_time(dag, wallTime)
|
||||
|
||||
self.backend.find_head(
|
||||
self.checkpoints.time.slotOrZero.epoch,
|
||||
FinalityCheckpoints(
|
||||
justified: self.checkpoints.justified.checkpoint,
|
||||
finalized: self.checkpoints.finalized),
|
||||
|
|
|
@ -92,6 +92,8 @@ type
|
|||
## to get the physical index
|
||||
|
||||
ProtoArray* = object
|
||||
hasLowParticipation*: bool
|
||||
currentEpoch*: Epoch
|
||||
checkpoints*: FinalityCheckpoints
|
||||
nodes*: ProtoNodes
|
||||
indices*: Table[Eth2Digest, Index]
|
||||
|
|
|
@ -74,6 +74,9 @@ func len*(nodes: ProtoNodes): int =
|
|||
func add(nodes: var ProtoNodes, node: ProtoNode) =
|
||||
nodes.buf.add node
|
||||
|
||||
func isPreviousEpochJustified(self: ProtoArray): bool =
|
||||
self.checkpoints.justified.epoch + 1 == self.currentEpoch
|
||||
|
||||
# Forward declarations
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
|
@ -87,7 +90,9 @@ func nodeLeadsToViableHead(self: ProtoArray, node: ProtoNode): FcResult[bool]
|
|||
# ProtoArray routines
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
func init*(T: type ProtoArray, checkpoints: FinalityCheckpoints): T =
|
||||
func init*(
|
||||
T: type ProtoArray, checkpoints: FinalityCheckpoints,
|
||||
hasLowParticipation: bool): T =
|
||||
let node = ProtoNode(
|
||||
root: checkpoints.finalized.root,
|
||||
parent: none(int),
|
||||
|
@ -96,10 +101,29 @@ func init*(T: type ProtoArray, checkpoints: FinalityCheckpoints): T =
|
|||
bestChild: none(int),
|
||||
bestDescendant: none(int))
|
||||
|
||||
T(checkpoints: checkpoints,
|
||||
T(hasLowParticipation: hasLowParticipation,
|
||||
checkpoints: checkpoints,
|
||||
nodes: ProtoNodes(buf: @[node], offset: 0),
|
||||
indices: {node.root: 0}.toTable())
|
||||
|
||||
iterator realizePendingCheckpoints*(
|
||||
self: var ProtoArray, resetTipTracking = true): FinalityCheckpoints =
|
||||
# Pull-up chain tips from previous epoch
|
||||
for idx, unrealized in self.currentEpochTips.pairs():
|
||||
let physicalIdx = idx - self.nodes.offset
|
||||
if unrealized != self.nodes.buf[physicalIdx].checkpoints:
|
||||
trace "Pulling up chain tip",
|
||||
blck = self.nodes.buf[physicalIdx].root,
|
||||
checkpoints = self.nodes.buf[physicalIdx].checkpoints,
|
||||
unrealized
|
||||
self.nodes.buf[physicalIdx].checkpoints = unrealized
|
||||
|
||||
yield unrealized
|
||||
|
||||
# Reset tip tracking for new epoch
|
||||
if resetTipTracking:
|
||||
self.currentEpochTips.clear()
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/phase0/fork-choice.md#configuration
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/fork-choice.md#get_latest_attesting_balance
|
||||
const PROPOSER_SCORE_BOOST* = 40
|
||||
|
@ -124,6 +148,7 @@ func calculateProposerBoost(validatorBalances: openArray[Gwei]): int64 =
|
|||
|
||||
func applyScoreChanges*(self: var ProtoArray,
|
||||
deltas: var openArray[Delta],
|
||||
currentEpoch: Epoch,
|
||||
checkpoints: FinalityCheckpoints,
|
||||
newBalances: openArray[Gwei],
|
||||
proposerBoostRoot: Eth2Digest): FcResult[void] =
|
||||
|
@ -148,8 +173,14 @@ func applyScoreChanges*(self: var ProtoArray,
|
|||
deltasLen: deltas.len,
|
||||
indicesLen: self.indices.len)
|
||||
|
||||
self.currentEpoch = currentEpoch
|
||||
self.checkpoints = checkpoints
|
||||
|
||||
# If previous epoch is justified, pull up all current tips to previous epoch
|
||||
if self.hasLowParticipation and self.isPreviousEpochJustified:
|
||||
for realized in self.realizePendingCheckpoints(resetTipTracking = false):
|
||||
discard
|
||||
|
||||
## Alias
|
||||
# This cannot raise the IndexError exception, how to tell compiler?
|
||||
template node: untyped {.dirty.} =
|
||||
|
@ -300,21 +331,6 @@ func onBlock*(self: var ProtoArray,
|
|||
|
||||
ok()
|
||||
|
||||
iterator realizePendingCheckpoints*(self: var ProtoArray): FinalityCheckpoints =
|
||||
# Pull-up chain tips from previous epoch
|
||||
for idx, unrealized in self.currentEpochTips.pairs():
|
||||
let physicalIdx = idx - self.nodes.offset
|
||||
trace "Pulling up chain tip",
|
||||
blck = self.nodes.buf[physicalIdx].root,
|
||||
checkpoints = self.nodes.buf[physicalIdx].checkpoints,
|
||||
unrealized
|
||||
self.nodes.buf[physicalIdx].checkpoints = unrealized
|
||||
|
||||
yield unrealized
|
||||
|
||||
# Reset tip tracking for new epoch
|
||||
self.currentEpochTips.clear()
|
||||
|
||||
func findHead*(self: var ProtoArray,
|
||||
head: var Eth2Digest,
|
||||
justifiedRoot: Eth2Digest): FcResult[void] =
|
||||
|
@ -518,7 +534,14 @@ func nodeLeadsToViableHead(self: ProtoArray, node: ProtoNode): FcResult[bool] =
|
|||
func nodeIsViableForHead(self: ProtoArray, node: ProtoNode): bool =
|
||||
## This is the equivalent of `filter_block_tree` function in eth2 spec
|
||||
## https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/phase0/fork-choice.md#filter_block_tree
|
||||
##
|
||||
if self.hasLowParticipation:
|
||||
if node.checkpoints.justified.epoch < self.checkpoints.justified.epoch:
|
||||
return false
|
||||
if self.isPreviousEpochJustified:
|
||||
return true
|
||||
return node.checkpoints.finalized == self.checkpoints.finalized or
|
||||
self.checkpoints.finalized.epoch == GENESIS_EPOCH
|
||||
|
||||
## Any node that has a different finalized or justified epoch
|
||||
## should not be viable for the head.
|
||||
(
|
||||
|
|
|
@ -154,7 +154,7 @@ proc loadChainDag(
|
|||
|
||||
let
|
||||
extraFlags =
|
||||
if shouldEnableTestFeatures: {enableTestFeatures}
|
||||
if shouldEnableTestFeatures: {enableTestFeatures, lowParticipation}
|
||||
else: {enableTestFeatures}
|
||||
chainDagFlags =
|
||||
if config.strictVerification: {strictVerification}
|
||||
|
|
|
@ -65,6 +65,7 @@ func apply(ctx: var ForkChoiceBackend, id: int, op: Operation) =
|
|||
case op.kind
|
||||
of FindHead, InvalidFindHead:
|
||||
let r = ctx.find_head(
|
||||
GENESIS_EPOCH,
|
||||
op.checkpoints,
|
||||
op.justified_state_balances,
|
||||
# Don't use proposer boosting
|
||||
|
|
Loading…
Reference in New Issue