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