@lodestar/fork-choice 1.51.0-dev.fe53cec080 → 1.51.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -86,8 +86,8 @@ export enum UpdateHeadOpt {
86
86
 
87
87
  export type UpdateAndGetHeadOpt =
88
88
  | {mode: UpdateHeadOpt.GetCanonicalHead}
89
- | {mode: UpdateHeadOpt.GetProposerHead; secFromSlot: number; slot: Slot}
90
- | {mode: UpdateHeadOpt.GetPredictedProposerHead; secFromSlot: number; slot: Slot};
89
+ | {mode: UpdateHeadOpt.GetProposerHead; msFromSlot: number; slot: Slot}
90
+ | {mode: UpdateHeadOpt.GetPredictedProposerHead; msFromSlot: number; slot: Slot};
91
91
 
92
92
  // the initial vote epoch for all validators
93
93
  const INIT_VOTE_SLOT: Slot = 0;
@@ -315,13 +315,13 @@ export class ForkChoice implements IForkChoice {
315
315
  const canonicalHeadBlock = mode === UpdateHeadOpt.GetPredictedProposerHead ? this.getHead() : this.updateHead();
316
316
  switch (mode) {
317
317
  case UpdateHeadOpt.GetPredictedProposerHead:
318
- return {head: this.predictProposerHead(canonicalHeadBlock, opt.secFromSlot, opt.slot)};
318
+ return {head: this.predictProposerHead(canonicalHeadBlock, opt.msFromSlot, opt.slot)};
319
319
  case UpdateHeadOpt.GetProposerHead: {
320
320
  const {
321
321
  proposerHead: head,
322
322
  isHeadTimely,
323
323
  notReorgedReason,
324
- } = this.getProposerHead(canonicalHeadBlock, opt.secFromSlot, opt.slot);
324
+ } = this.getProposerHead(canonicalHeadBlock, opt.msFromSlot, opt.slot);
325
325
  return {head, isHeadTimely, notReorgedReason};
326
326
  }
327
327
  case UpdateHeadOpt.GetCanonicalHead:
@@ -338,7 +338,7 @@ export class ForkChoice implements IForkChoice {
338
338
  // Note when proposer boost reorg is disabled, it always returns false
339
339
  shouldOverrideForkChoiceUpdate(
340
340
  headBlock: ProtoBlock,
341
- secFromSlot: number,
341
+ msFromSlot: number,
342
342
  currentSlot: Slot
343
343
  ): ShouldOverrideForkChoiceUpdateResult {
344
344
  if (headBlock === null) {
@@ -369,7 +369,7 @@ export class ForkChoice implements IForkChoice {
369
369
 
370
370
  const currentTimeOk =
371
371
  headBlock.slot === currentSlot ||
372
- (proposalSlot === currentSlot && this.isProposingOnTime(secFromSlot, currentSlot));
372
+ (proposalSlot === currentSlot && this.isProposingOnTime(msFromSlot, currentSlot));
373
373
 
374
374
  // Mirror the proposer equivocation branch of getProposerHead(). The head slot's attestations are
375
375
  // still queued at this point so the head is assumed weak, same as for the regular branch below.
@@ -455,7 +455,7 @@ export class ForkChoice implements IForkChoice {
455
455
  * By calling this function, we assume we are the proposer of next slot
456
456
  *
457
457
  */
458
- predictProposerHead(headBlock: ProtoBlock, secFromSlot: number, currentSlot: Slot): ProtoBlock {
458
+ predictProposerHead(headBlock: ProtoBlock, msFromSlot: number, currentSlot: Slot): ProtoBlock {
459
459
  const {proposerBoost, proposerBoostReorg} = this.opts ?? {};
460
460
  // Skip re-org attempt if proposer boost (reorg) are disabled
461
461
  if (!proposerBoost || !proposerBoostReorg) {
@@ -468,7 +468,7 @@ export class ForkChoice implements IForkChoice {
468
468
  }
469
469
 
470
470
  const blockRoot = headBlock.blockRoot;
471
- const result = this.shouldOverrideForkChoiceUpdate(headBlock, secFromSlot, currentSlot);
471
+ const result = this.shouldOverrideForkChoiceUpdate(headBlock, msFromSlot, currentSlot);
472
472
 
473
473
  if (result.shouldOverrideFcu) {
474
474
  this.logger?.verbose("Current head is weak. Predicting next block to be built on parent of head.", {
@@ -499,7 +499,7 @@ export class ForkChoice implements IForkChoice {
499
499
  */
500
500
  getProposerHead(
501
501
  headBlock: ProtoBlock,
502
- secFromSlot: number,
502
+ msFromSlot: number,
503
503
  slot: Slot
504
504
  ): {proposerHead: ProtoBlock; isHeadTimely: boolean; notReorgedReason?: NotReorgedReason} {
505
505
  const isHeadTimely = headBlock.timeliness;
@@ -556,7 +556,7 @@ export class ForkChoice implements IForkChoice {
556
556
  }
557
557
 
558
558
  // Only re-org if we are proposing on-time
559
- if (!this.isProposingOnTime(secFromSlot, slot)) {
559
+ if (!this.isProposingOnTime(msFromSlot, slot)) {
560
560
  return {proposerHead, isHeadTimely, notReorgedReason: NotReorgedReason.NotProposingOnTime};
561
561
  }
562
562
 
@@ -757,8 +757,8 @@ export class ForkChoice implements IForkChoice {
757
757
  onBlock(
758
758
  block: BeaconBlock,
759
759
  state: IBeaconStateView,
760
- receiveDelaySec: number,
761
- importDelaySec: number,
760
+ receiveDelayMs: number,
761
+ importDelayMs: number,
762
762
  currentSlot: Slot,
763
763
  executionStatus: BlockExecutionStatus,
764
764
  dataAvailabilityStatus: DataAvailabilityStatus
@@ -830,7 +830,7 @@ export class ForkChoice implements IForkChoice {
830
830
  // Decide whether this block should receive proposer boost, and whether the block is timely.
831
831
  // The store field `this.proposerBoostRoot` and `updateCheckpoints()` are mutated only after
832
832
  // `protoArray.onBlock()` succeeds
833
- const isTimely = this.isBlockReceivedTimely(block, receiveDelaySec);
833
+ const isTimely = this.isBlockReceivedTimely(block, receiveDelayMs);
834
834
  const isProposerBoostBlock =
835
835
  this.opts?.proposerBoost === true &&
836
836
  isTimely &&
@@ -849,14 +849,17 @@ export class ForkChoice implements IForkChoice {
849
849
  const blockEpoch = computeEpochAtSlot(slot);
850
850
 
851
851
  // same logic to compute_pulled_up_tip in the spec, making it inline because of reusing variables
852
- // If the parent checkpoints are already at the same epoch as the block being imported,
853
- // it's impossible for the unrealized checkpoints to differ from the parent's. This
854
- // holds true because:
852
+ // If the block has no slashings and the parent checkpoints are already at the same epoch as the
853
+ // block being imported, it's impossible for the unrealized checkpoints to differ from the parent's.
854
+ // This holds true because:
855
855
  //
856
856
  // 1. A child block cannot have lower FFG checkpoints than its parent.
857
857
  // 2. A block in epoch `N` cannot contain attestations which would justify an epoch higher than `N`.
858
858
  // 3. A block in epoch `N` cannot contain attestations which would finalize an epoch higher than `N - 1`.
859
859
  //
860
+ // Slashings are excluded because they can reduce the unslashed target balance of the child state
861
+ // and therefore lower its unrealized checkpoints.
862
+ //
860
863
  // This is an optimization. It should reduce the amount of times we run
861
864
  // `process_justification_and_finalization` by approximately 1/3rd when the chain is
862
865
  // performing optimally.
@@ -864,6 +867,8 @@ export class ForkChoice implements IForkChoice {
864
867
  let unrealizedFinalizedCheckpoint: CheckpointWithHex;
865
868
  if (this.opts?.computeUnrealized) {
866
869
  if (
870
+ block.body.proposerSlashings.length === 0 &&
871
+ block.body.attesterSlashings.length === 0 &&
867
872
  parentBlock.unrealizedJustifiedEpoch === blockEpoch &&
868
873
  parentBlock.unrealizedFinalizedEpoch + 1 >= blockEpoch
869
874
  ) {
@@ -901,8 +906,8 @@ export class ForkChoice implements IForkChoice {
901
906
  targetRoot: toRootHex(targetRoot),
902
907
  stateRoot: toRootHex(block.stateRoot),
903
908
  timeliness: isTimely,
904
- ptcTimeliness: this.isBlockPtcTimely(block, receiveDelaySec),
905
- importedTimely: this.isBlockImportedTimely(block, importDelaySec),
909
+ ptcTimeliness: this.isBlockPtcTimely(block, receiveDelayMs),
910
+ importedTimely: this.isBlockImportedTimely(block, importDelayMs),
906
911
  proposerIndex: block.proposerIndex,
907
912
 
908
913
  justifiedEpoch: stateJustifiedEpoch,
@@ -1432,7 +1437,9 @@ export class ForkChoice implements IForkChoice {
1432
1437
  *
1433
1438
  * `ancestors` is the raw walk and includes the previous finalized block as its last element —
1434
1439
  * callers that don't want the boundary should slice it off themselves.
1435
- * Post-gloas for each block root, it returns exactly one variant of it.
1440
+ * For Gloas parent blocks, `ancestors` contains the selected EMPTY or FULL variant.
1441
+ * The other variant, if present in the traversed range, goes into `nonAncestors` with the same root.
1442
+ * Exclude roots present in `ancestors` before pruning by block root.
1436
1443
  */
1437
1444
  getAllAncestorAndNonAncestorBlocks(
1438
1445
  blockRoot: RootHex,
@@ -1775,9 +1782,9 @@ export class ForkChoice implements IForkChoice {
1775
1782
  * Return true if the block is timely for the current slot.
1776
1783
  * Child class can overwrite this for testing purpose.
1777
1784
  */
1778
- protected isBlockReceivedTimely(block: BeaconBlock, blockDelaySec: number): boolean {
1785
+ protected isBlockReceivedTimely(block: BeaconBlock, blockDelayMs: number): boolean {
1779
1786
  const fork = this.config.getForkName(block.slot);
1780
- const isBeforeLateBlockCutoff = blockDelaySec * 1000 < this.config.getAttestationDueMs(fork);
1787
+ const isBeforeLateBlockCutoff = blockDelayMs < this.config.getAttestationDueMs(fork);
1781
1788
  return this.fcStore.currentSlot === block.slot && isBeforeLateBlockCutoff;
1782
1789
  }
1783
1790
 
@@ -1791,12 +1798,12 @@ export class ForkChoice implements IForkChoice {
1791
1798
  *
1792
1799
  * Child class can overwrite this for testing purpose.
1793
1800
  */
1794
- protected isBlockPtcTimely(block: BeaconBlock, blockDelaySec: number): boolean {
1801
+ protected isBlockPtcTimely(block: BeaconBlock, blockDelayMs: number): boolean {
1795
1802
  const ptcThresholdMs = this.config.getSlotComponentDurationMs(
1796
1803
  this.config.getForkName(block.slot),
1797
1804
  this.config.PAYLOAD_ATTESTATION_DUE_BPS
1798
1805
  );
1799
- return this.fcStore.currentSlot === block.slot && blockDelaySec * 1000 < ptcThresholdMs;
1806
+ return this.fcStore.currentSlot === block.slot && blockDelayMs < ptcThresholdMs;
1800
1807
  }
1801
1808
 
1802
1809
  /**
@@ -1804,9 +1811,9 @@ export class ForkChoice implements IForkChoice {
1804
1811
  * block was imported in a timely manner for its own slot.
1805
1812
  * This is not part of the spec, we use this to determine late canonical blocks.
1806
1813
  */
1807
- protected isBlockImportedTimely(block: BeaconBlock, importDelaySec: number): boolean {
1814
+ protected isBlockImportedTimely(block: BeaconBlock, importDelayMs: number): boolean {
1808
1815
  const fork = this.config.getForkName(block.slot);
1809
- const isBeforeLateBlockCutoff = importDelaySec * 1000 < this.config.getAttestationDueMs(fork);
1816
+ const isBeforeLateBlockCutoff = importDelayMs < this.config.getAttestationDueMs(fork);
1810
1817
  return this.fcStore.currentSlot === block.slot && isBeforeLateBlockCutoff;
1811
1818
  }
1812
1819
 
@@ -1891,10 +1898,10 @@ export class ForkChoice implements IForkChoice {
1891
1898
  /**
1892
1899
  * https://github.com/ethereum/consensus-specs/blob/v1.5.0/specs/phase0/fork-choice.md#is_proposing_on_time
1893
1900
  */
1894
- private isProposingOnTime(secFromSlot: number, slot: Slot): boolean {
1901
+ private isProposingOnTime(msFromSlot: number, slot: Slot): boolean {
1895
1902
  const fork = this.config.getForkName(slot);
1896
1903
  const proposerReorgCutoff = this.config.getProposerReorgCutoffMs(fork);
1897
- return secFromSlot * 1000 <= proposerReorgCutoff;
1904
+ return msFromSlot <= proposerReorgCutoff;
1898
1905
  }
1899
1906
 
1900
1907
  private getPreMergeExecStatus(executionStatus: BlockExecutionStatus): ExecutionStatus.PreMerge {
@@ -123,7 +123,7 @@ export interface IForkChoice {
123
123
  */
124
124
  shouldOverrideForkChoiceUpdate(
125
125
  headBlock: ProtoBlock,
126
- secFromSlot: number,
126
+ msFromSlot: number,
127
127
  currentSlot: Slot
128
128
  ): ShouldOverrideForkChoiceUpdateResult;
129
129
  /**
@@ -159,8 +159,8 @@ export interface IForkChoice {
159
159
  onBlock(
160
160
  block: BeaconBlock,
161
161
  state: IBeaconStateView,
162
- receiveDelaySec: number,
163
- importDelaySec: number,
162
+ receiveDelayMs: number,
163
+ importDelayMs: number,
164
164
  currentSlot: Slot,
165
165
  executionStatus: BlockExecutionStatus,
166
166
  dataAvailabilityStatus: DataAvailabilityStatus