@@ -71,7 +71,7 @@ export enum UpdateHeadOpt {
71
71
export type UpdateAndGetHeadOpt =
72
72
| { mode : UpdateHeadOpt . GetCanonicialHead }
73
73
| { mode : UpdateHeadOpt . GetProposerHead ; secFromSlot : number ; slot : Slot }
74
- | { mode : UpdateHeadOpt . GetPredictedProposerHead ; slot : Slot } ;
74
+ | { mode : UpdateHeadOpt . GetPredictedProposerHead ; secFromSlot : number ; slot : Slot } ;
75
75
76
76
/**
77
77
* Provides an implementation of "Ethereum Consensus -- Beacon Chain Fork Choice":
@@ -208,7 +208,7 @@ export class ForkChoice implements IForkChoice {
208
208
const canonicialHeadBlock = mode === UpdateHeadOpt . GetPredictedProposerHead ? this . getHead ( ) : this . updateHead ( ) ;
209
209
switch ( mode ) {
210
210
case UpdateHeadOpt . GetPredictedProposerHead :
211
- return { head : this . predictProposerHead ( canonicialHeadBlock , opt . slot ) } ;
211
+ return { head : this . predictProposerHead ( canonicialHeadBlock , opt . secFromSlot , opt . slot ) } ;
212
212
case UpdateHeadOpt . GetProposerHead : {
213
213
const {
214
214
proposerHead : head ,
@@ -229,7 +229,11 @@ export class ForkChoice implements IForkChoice {
229
229
// Return true if the given block passes all criteria to be re-orged out
230
230
// Return false otherwise.
231
231
// Note when proposer boost reorg is disabled, it always returns false
232
- shouldOverrideForkChoiceUpdate ( currentSlot : Slot , blockRoot : RootHex ) : ShouldOverrideForkChoiceUpdateResult {
232
+ shouldOverrideForkChoiceUpdate (
233
+ blockRoot : RootHex ,
234
+ secFromSlot : number ,
235
+ currentSlot : Slot
236
+ ) : ShouldOverrideForkChoiceUpdateResult {
233
237
const headBlock = this . getBlockHex ( blockRoot ) ;
234
238
if ( headBlock === null ) {
235
239
// should not happen beacause this block just got imported. Fall back to no-reorg.
@@ -261,7 +265,8 @@ export class ForkChoice implements IForkChoice {
261
265
return { shouldOverrideFcu : false , reason : prelimNotReorgedReason ?? NotReorgedReason . Unknown } ;
262
266
}
263
267
264
- const currentTimeOk = headBlock . slot === currentSlot ;
268
+ const currentTimeOk =
269
+ headBlock . slot === currentSlot || ( proposalSlot === currentSlot && this . isProposingOnTime ( secFromSlot ) ) ;
265
270
if ( ! currentTimeOk ) {
266
271
return { shouldOverrideFcu : false , reason : NotReorgedReason . ReorgMoreThanOneSlot } ;
267
272
}
@@ -287,7 +292,7 @@ export class ForkChoice implements IForkChoice {
287
292
* By calling this function, we assume we are the proposer of next slot
288
293
*
289
294
*/
290
- predictProposerHead ( headBlock : ProtoBlock , currentSlot ? : Slot ) : ProtoBlock {
295
+ predictProposerHead ( headBlock : ProtoBlock , secFromSlot : number , currentSlot : Slot ) : ProtoBlock {
291
296
// Skip re-org attempt if proposer boost (reorg) are disabled
292
297
if ( ! this . opts ?. proposerBoost || ! this . opts ?. proposerBoostReorg ) {
293
298
this . logger ?. verbose ( "No proposer boot reorg prediction since the related flags are disabled" ) ;
@@ -300,10 +305,8 @@ export class ForkChoice implements IForkChoice {
300
305
return headBlock ;
301
306
}
302
307
303
- currentSlot = currentSlot ?? this . fcStore . currentSlot ;
304
-
305
308
const blockRoot = headBlock . blockRoot ;
306
- const result = this . shouldOverrideForkChoiceUpdate ( currentSlot , blockRoot ) ;
309
+ const result = this . shouldOverrideForkChoiceUpdate ( blockRoot , secFromSlot , currentSlot ) ;
307
310
308
311
if ( result . shouldOverrideFcu ) {
309
312
this . logger ?. verbose ( "Current head is weak. Predicting next block to be built on parent of head." , {
@@ -351,10 +354,8 @@ export class ForkChoice implements IForkChoice {
351
354
return { proposerHead, isHeadTimely, notReorgedReason : prelimNotReorgedReason } ;
352
355
}
353
356
354
- // https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.4/specs/phase0/fork-choice.md#is_proposing_on_time
355
- const proposerReorgCutoff = this . config . SECONDS_PER_SLOT / INTERVALS_PER_SLOT / 2 ;
356
- const isProposingOnTime = secFromSlot <= proposerReorgCutoff ;
357
- if ( ! isProposingOnTime ) {
357
+ // Only re-org if we are proposing on-time
358
+ if ( ! this . isProposingOnTime ( secFromSlot ) ) {
358
359
return { proposerHead, isHeadTimely, notReorgedReason : NotReorgedReason . NotProposingOnTime } ;
359
360
}
360
361
@@ -1177,6 +1178,14 @@ export class ForkChoice implements IForkChoice {
1177
1178
return this . fcStore . currentSlot === block . slot && isBeforeAttestingInterval ;
1178
1179
}
1179
1180
1181
+ /**
1182
+ * https://github.com/ethereum/consensus-specs/blob/v1.5.0/specs/phase0/fork-choice.md#is_proposing_on_time
1183
+ */
1184
+ private isProposingOnTime ( secFromSlot : number ) : boolean {
1185
+ const proposerReorgCutoff = this . config . SECONDS_PER_SLOT / INTERVALS_PER_SLOT / 2 ;
1186
+ return secFromSlot <= proposerReorgCutoff ;
1187
+ }
1188
+
1180
1189
private getPreMergeExecStatus ( executionStatus : MaybeValidExecutionStatus ) : ExecutionStatus . PreMerge {
1181
1190
if ( executionStatus !== ExecutionStatus . PreMerge )
1182
1191
throw Error ( `Invalid pre-merge execution status: expected: ${ ExecutionStatus . PreMerge } , got ${ executionStatus } ` ) ;
0 commit comments