@@ -74,6 +74,9 @@ func len*(nodes: ProtoNodes): int =
7474func add (nodes: var ProtoNodes , node: ProtoNode ) =
7575 nodes.buf.add node
7676
77+ func isPreviousEpochJustified (self: ProtoArray ): bool =
78+ self.checkpoints.justified.epoch + 1 == self.currentEpoch
79+
7780# Forward declarations
7881# ----------------------------------------------------------------------
7982
@@ -87,7 +90,9 @@ func nodeLeadsToViableHead(self: ProtoArray, node: ProtoNode): FcResult[bool]
8790# ProtoArray routines
8891# ----------------------------------------------------------------------
8992
90- func init * (T: type ProtoArray , checkpoints: FinalityCheckpoints ): T =
93+ func init * (
94+ T: type ProtoArray , checkpoints: FinalityCheckpoints ,
95+ hasLowParticipation: bool ): T =
9196 let node = ProtoNode (
9297 root: checkpoints.finalized.root,
9398 parent: none (int ),
@@ -96,10 +101,29 @@ func init*(T: type ProtoArray, checkpoints: FinalityCheckpoints): T =
96101 bestChild: none (int ),
97102 bestDescendant: none (int ))
98103
99- T (checkpoints: checkpoints,
104+ T (hasLowParticipation: hasLowParticipation,
105+ checkpoints: checkpoints,
100106 nodes: ProtoNodes (buf: @ [node], offset: 0 ),
101107 indices: {node.root: 0 }.toTable ())
102108
109+ iterator realizePendingCheckpoints * (
110+ self: var ProtoArray , resetTipTracking = true ): FinalityCheckpoints =
111+ # Pull-up chain tips from previous epoch
112+ for idx, unrealized in self.currentEpochTips.pairs ():
113+ let physicalIdx = idx - self.nodes.offset
114+ if unrealized != self.nodes.buf[physicalIdx].checkpoints:
115+ trace " Pulling up chain tip" ,
116+ blck = self.nodes.buf[physicalIdx].root,
117+ checkpoints = self.nodes.buf[physicalIdx].checkpoints,
118+ unrealized
119+ self.nodes.buf[physicalIdx].checkpoints = unrealized
120+
121+ yield unrealized
122+
123+ # Reset tip tracking for new epoch
124+ if resetTipTracking:
125+ self.currentEpochTips.clear ()
126+
103127# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/phase0/fork-choice.md#configuration
104128# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/fork-choice.md#get_latest_attesting_balance
105129const PROPOSER_SCORE_BOOST * = 40
@@ -124,6 +148,7 @@ func calculateProposerBoost(validatorBalances: openArray[Gwei]): int64 =
124148
125149func applyScoreChanges * (self: var ProtoArray ,
126150 deltas: var openArray [Delta ],
151+ currentEpoch: Epoch ,
127152 checkpoints: FinalityCheckpoints ,
128153 newBalances: openArray [Gwei ],
129154 proposerBoostRoot: Eth2Digest ): FcResult [void ] =
@@ -148,8 +173,14 @@ func applyScoreChanges*(self: var ProtoArray,
148173 deltasLen: deltas.len,
149174 indicesLen: self.indices.len)
150175
176+ self.currentEpoch = currentEpoch
151177 self.checkpoints = checkpoints
152178
179+ # If previous epoch is justified, pull up all current tips to previous epoch
180+ if self.hasLowParticipation and self.isPreviousEpochJustified:
181+ for realized in self.realizePendingCheckpoints (resetTipTracking = false ):
182+ discard
183+
153184 # # Alias
154185 # This cannot raise the IndexError exception, how to tell compiler?
155186 template node : untyped {.dirty .} =
@@ -300,21 +331,6 @@ func onBlock*(self: var ProtoArray,
300331
301332 ok ()
302333
303- iterator realizePendingCheckpoints * (self: var ProtoArray ): FinalityCheckpoints =
304- # Pull-up chain tips from previous epoch
305- for idx, unrealized in self.currentEpochTips.pairs ():
306- let physicalIdx = idx - self.nodes.offset
307- trace " Pulling up chain tip" ,
308- blck = self.nodes.buf[physicalIdx].root,
309- checkpoints = self.nodes.buf[physicalIdx].checkpoints,
310- unrealized
311- self.nodes.buf[physicalIdx].checkpoints = unrealized
312-
313- yield unrealized
314-
315- # Reset tip tracking for new epoch
316- self.currentEpochTips.clear ()
317-
318334func findHead * (self: var ProtoArray ,
319335 head: var Eth2Digest ,
320336 justifiedRoot: Eth2Digest ): FcResult [void ] =
@@ -518,7 +534,14 @@ func nodeLeadsToViableHead(self: ProtoArray, node: ProtoNode): FcResult[bool] =
518534func nodeIsViableForHead (self: ProtoArray , node: ProtoNode ): bool =
519535 # # This is the equivalent of `filter_block_tree` function in eth2 spec
520536 # # https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/phase0/fork-choice.md#filter_block_tree
521- # #
537+ if self.hasLowParticipation:
538+ if node.checkpoints.justified.epoch < self.checkpoints.justified.epoch:
539+ return false
540+ if self.isPreviousEpochJustified:
541+ return true
542+ return node.checkpoints.finalized == self.checkpoints.finalized or
543+ self.checkpoints.finalized.epoch == GENESIS_EPOCH
544+
522545 # # Any node that has a different finalized or justified epoch
523546 # # should not be viable for the head.
524547 (
0 commit comments