@lodestar/fork-choice 1.51.0-dev.83da04fef3 → 1.51.0-dev.8d5cdd33be

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.
@@ -422,6 +422,24 @@ export class ForkChoice implements IForkChoice {
422
422
  return this.protoArray.shouldExtendPayload(blockRoot, this.proposerBoostRoot);
423
423
  }
424
424
 
425
+ /** Inclusion list verdict for the payload of `blockRoot`, as reported by the execution engine */
426
+ recordPayloadInclusionListSatisfaction(blockRoot: RootHex, satisfied: boolean): boolean {
427
+ const changed = this.protoArray.recordPayloadInclusionListSatisfaction(blockRoot, satisfied);
428
+ if (changed && !satisfied) {
429
+ this.metrics?.forkChoice.unsatisfiedInclusionListBlocks.inc();
430
+ this.logger?.verbose("Payload does not satisfy the inclusion list constraints, not extending it", {blockRoot});
431
+ }
432
+ return changed;
433
+ }
434
+
435
+ isPayloadInclusionListSatisfied(blockRoot: RootHex): boolean {
436
+ return this.protoArray.isPayloadInclusionListSatisfied(blockRoot);
437
+ }
438
+
439
+ getPayloadInclusionListSatisfaction(blockRoot: RootHex): boolean | null {
440
+ return this.protoArray.getPayloadInclusionListSatisfaction(blockRoot);
441
+ }
442
+
425
443
  /** Spec: should_build_on_full(store, head, slot) */
426
444
  shouldBuildOnFull(head: ProtoBlock, slot: Slot): boolean {
427
445
  return this.protoArray.shouldBuildOnFull(head, slot);
@@ -437,7 +455,7 @@ export class ForkChoice implements IForkChoice {
437
455
  * By calling this function, we assume we are the proposer of next slot
438
456
  *
439
457
  */
440
- predictProposerHead(headBlock: ProtoBlock, secFromSlot: number, currentSlot: Slot): ProtoBlock {
458
+ predictProposerHead(headBlock: ProtoBlock, msFromSlot: number, currentSlot: Slot): ProtoBlock {
441
459
  const {proposerBoost, proposerBoostReorg} = this.opts ?? {};
442
460
  // Skip re-org attempt if proposer boost (reorg) are disabled
443
461
  if (!proposerBoost || !proposerBoostReorg) {
@@ -450,7 +468,7 @@ export class ForkChoice implements IForkChoice {
450
468
  }
451
469
 
452
470
  const blockRoot = headBlock.blockRoot;
453
- const result = this.shouldOverrideForkChoiceUpdate(headBlock, secFromSlot, currentSlot);
471
+ const result = this.shouldOverrideForkChoiceUpdate(headBlock, msFromSlot, currentSlot);
454
472
 
455
473
  if (result.shouldOverrideFcu) {
456
474
  this.logger?.verbose("Current head is weak. Predicting next block to be built on parent of head.", {
@@ -481,7 +499,7 @@ export class ForkChoice implements IForkChoice {
481
499
  */
482
500
  getProposerHead(
483
501
  headBlock: ProtoBlock,
484
- secFromSlot: number,
502
+ msFromSlot: number,
485
503
  slot: Slot
486
504
  ): {proposerHead: ProtoBlock; isHeadTimely: boolean; notReorgedReason?: NotReorgedReason} {
487
505
  const isHeadTimely = headBlock.timeliness;
@@ -538,7 +556,7 @@ export class ForkChoice implements IForkChoice {
538
556
  }
539
557
 
540
558
  // Only re-org if we are proposing on-time
541
- if (!this.isProposingOnTime(secFromSlot, slot)) {
559
+ if (!this.isProposingOnTime(msFromSlot, slot)) {
542
560
  return {proposerHead, isHeadTimely, notReorgedReason: NotReorgedReason.NotProposingOnTime};
543
561
  }
544
562
 
@@ -675,7 +693,20 @@ export class ForkChoice implements IForkChoice {
675
693
  }
676
694
 
677
695
  // findHead returns the ProtoNode representing the head
678
- const head = this.protoArray.findHead(this.fcStore.justified.checkpoint.rootHex, currentSlot);
696
+ let head: ProtoNode;
697
+ try {
698
+ head = this.protoArray.findHead(this.fcStore.justified.checkpoint.rootHex, currentSlot);
699
+ } catch (e) {
700
+ // A poisoned tree never recovers, shut the node down instead of failing every head computation
701
+ if (
702
+ e instanceof ProtoArrayError &&
703
+ (e.type.code === ProtoArrayErrorCode.INVALID_LVH_EXECUTION_RESPONSE ||
704
+ e.type.code === ProtoArrayErrorCode.INVALID_JUSTIFIED_EXECUTION_STATUS)
705
+ ) {
706
+ this.irrecoverableError = e;
707
+ }
708
+ throw e;
709
+ }
679
710
 
680
711
  this.head = head;
681
712
  return this.head;
@@ -735,12 +766,15 @@ export class ForkChoice implements IForkChoice {
735
766
  *
736
767
  * `justifiedBalances` balances of justified state which is updated synchronously.
737
768
  * This ensures that the forkchoice is never out of sync.
769
+ *
770
+ * A block whose parent payload chain was invalidated is rejected. `executionStatus` is ignored for a gloas
771
+ * block: it has no payload of its own and inherits the status of the parent variant it builds on.
738
772
  */
739
773
  onBlock(
740
774
  block: BeaconBlock,
741
775
  state: IBeaconStateView,
742
- receiveDelaySec: number,
743
- importDelaySec: number,
776
+ receiveDelayMs: number,
777
+ importDelayMs: number,
744
778
  currentSlot: Slot,
745
779
  executionStatus: BlockExecutionStatus,
746
780
  dataAvailabilityStatus: DataAvailabilityStatus
@@ -763,6 +797,19 @@ export class ForkChoice implements IForkChoice {
763
797
  });
764
798
  }
765
799
 
800
+ // An execution engine verdict can invalidate the parent while this block is being verified. Inserting the
801
+ // block would put a viable node below an invalidated one, or poison the tree if this block is VALID
802
+ if (parentBlock.executionStatus === ExecutionStatus.Invalid) {
803
+ throw new ForkChoiceError({
804
+ code: ForkChoiceErrorCode.INVALID_BLOCK,
805
+ err: {
806
+ code: InvalidBlockCode.PARENT_EXECUTION_INVALID,
807
+ root: parentRootHex,
808
+ hash: parentHashHex,
809
+ },
810
+ });
811
+ }
812
+
766
813
  // Blocks cannot be in the future. If they are, their consideration must be delayed until
767
814
  // the are in the past.
768
815
  //
@@ -812,7 +859,7 @@ export class ForkChoice implements IForkChoice {
812
859
  // Decide whether this block should receive proposer boost, and whether the block is timely.
813
860
  // The store field `this.proposerBoostRoot` and `updateCheckpoints()` are mutated only after
814
861
  // `protoArray.onBlock()` succeeds
815
- const isTimely = this.isBlockReceivedTimely(block, receiveDelaySec);
862
+ const isTimely = this.isBlockReceivedTimely(block, receiveDelayMs);
816
863
  const isProposerBoostBlock =
817
864
  this.opts?.proposerBoost === true &&
818
865
  isTimely &&
@@ -831,14 +878,17 @@ export class ForkChoice implements IForkChoice {
831
878
  const blockEpoch = computeEpochAtSlot(slot);
832
879
 
833
880
  // same logic to compute_pulled_up_tip in the spec, making it inline because of reusing variables
834
- // If the parent checkpoints are already at the same epoch as the block being imported,
835
- // it's impossible for the unrealized checkpoints to differ from the parent's. This
836
- // holds true because:
881
+ // If the block has no slashings and the parent checkpoints are already at the same epoch as the
882
+ // block being imported, it's impossible for the unrealized checkpoints to differ from the parent's.
883
+ // This holds true because:
837
884
  //
838
885
  // 1. A child block cannot have lower FFG checkpoints than its parent.
839
886
  // 2. A block in epoch `N` cannot contain attestations which would justify an epoch higher than `N`.
840
887
  // 3. A block in epoch `N` cannot contain attestations which would finalize an epoch higher than `N - 1`.
841
888
  //
889
+ // Slashings are excluded because they can reduce the unslashed target balance of the child state
890
+ // and therefore lower its unrealized checkpoints.
891
+ //
842
892
  // This is an optimization. It should reduce the amount of times we run
843
893
  // `process_justification_and_finalization` by approximately 1/3rd when the chain is
844
894
  // performing optimally.
@@ -846,6 +896,8 @@ export class ForkChoice implements IForkChoice {
846
896
  let unrealizedFinalizedCheckpoint: CheckpointWithHex;
847
897
  if (this.opts?.computeUnrealized) {
848
898
  if (
899
+ block.body.proposerSlashings.length === 0 &&
900
+ block.body.attesterSlashings.length === 0 &&
849
901
  parentBlock.unrealizedJustifiedEpoch === blockEpoch &&
850
902
  parentBlock.unrealizedFinalizedEpoch + 1 >= blockEpoch
851
903
  ) {
@@ -883,8 +935,8 @@ export class ForkChoice implements IForkChoice {
883
935
  targetRoot: toRootHex(targetRoot),
884
936
  stateRoot: toRootHex(block.stateRoot),
885
937
  timeliness: isTimely,
886
- ptcTimeliness: this.isBlockPtcTimely(block, receiveDelaySec),
887
- importedTimely: this.isBlockImportedTimely(block, importDelaySec),
938
+ ptcTimeliness: this.isBlockPtcTimely(block, receiveDelayMs),
939
+ importedTimely: this.isBlockImportedTimely(block, importDelayMs),
888
940
  proposerIndex: block.proposerIndex,
889
941
 
890
942
  justifiedEpoch: stateJustifiedEpoch,
@@ -919,7 +971,9 @@ export class ForkChoice implements IForkChoice {
919
971
  executionPayloadBlockHash: parentBlockHashFromBid,
920
972
  executionPayloadNumber: parentMeta.number,
921
973
  executionPayloadGasLimit: parentMeta.gasLimit,
922
- executionStatus: this.getPostMergeExecStatus(executionStatus),
974
+ // The parent's default (PENDING) variant does not reflect a SYNCING parent payload, inheriting
975
+ // from the variant matching the bid's parent block hash keeps that payload from being marked VALID
976
+ executionStatus: this.getPostMergeExecStatus(parentBlock.executionStatus),
923
977
  dataAvailabilityStatus,
924
978
  };
925
979
  })()
@@ -980,14 +1034,8 @@ export class ForkChoice implements IForkChoice {
980
1034
  *
981
1035
  * https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/fork-choice.md#on_attestation
982
1036
  *
983
- * It only approximates the specification since it does not perform
984
- * `is_valid_indexed_attestation` since that should already have been called upstream and it's
985
- * too expensive to call again.
986
- *
987
- * ## Notes:
988
- *
989
- * The supplied `attestation` **must** pass the `in_valid_indexed_attestation` function as it
990
- * will not be run here.
1037
+ * Does not run `is_valid_indexed_attestation`. The caller must ensure all of its
1038
+ * validity requirements except index ordering: `attestingIndices` may be unsorted.
991
1039
  */
992
1040
  onAttestation(attestation: IndexedAttestation, attDataRoot: string, forceImport?: boolean): void {
993
1041
  // Ignore any attestations to the zero hash.
@@ -1126,6 +1174,19 @@ export class ForkChoice implements IForkChoice {
1126
1174
  executionStatus: PayloadExecutionStatus,
1127
1175
  dataAvailabilityStatus: DataAvailabilityStatus
1128
1176
  ): void {
1177
+ // Same guard as onBlock: the PENDING variant carries the status of the payload chain this block builds on
1178
+ const pendingBlock = this.getBlockHexDefaultStatus(blockRoot);
1179
+ if (pendingBlock?.executionStatus === ExecutionStatus.Invalid) {
1180
+ throw new ForkChoiceError({
1181
+ code: ForkChoiceErrorCode.INVALID_BLOCK,
1182
+ err: {
1183
+ code: InvalidBlockCode.PARENT_EXECUTION_INVALID,
1184
+ root: pendingBlock.parentRoot,
1185
+ hash: pendingBlock.executionPayloadBlockHash,
1186
+ },
1187
+ });
1188
+ }
1189
+
1129
1190
  this.protoArray.onExecutionPayload(
1130
1191
  blockRoot,
1131
1192
  this.fcStore.currentSlot,
@@ -1414,7 +1475,9 @@ export class ForkChoice implements IForkChoice {
1414
1475
  *
1415
1476
  * `ancestors` is the raw walk and includes the previous finalized block as its last element —
1416
1477
  * callers that don't want the boundary should slice it off themselves.
1417
- * Post-gloas for each block root, it returns exactly one variant of it.
1478
+ * For Gloas parent blocks, `ancestors` contains the selected EMPTY or FULL variant.
1479
+ * The other variant, if present in the traversed range, goes into `nonAncestors` with the same root.
1480
+ * Exclude roots present in `ancestors` before pruning by block root.
1418
1481
  */
1419
1482
  getAllAncestorAndNonAncestorBlocks(
1420
1483
  blockRoot: RootHex,
@@ -1522,7 +1585,9 @@ export class ForkChoice implements IForkChoice {
1522
1585
 
1523
1586
  /** Very expensive function, iterates the entire ProtoArray. TODO: Is this function even necessary? */
1524
1587
  getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[] {
1525
- return this.protoArray.nodes.filter((node) => node.parentRoot === parentRoot);
1588
+ return this.protoArray.nodes.filter(
1589
+ (node) => node.parentRoot === parentRoot && (!isGloasBlock(node) || node.payloadStatus === PayloadStatus.PENDING)
1590
+ );
1526
1591
  }
1527
1592
 
1528
1593
  /** Very expensive function, iterates the entire ProtoArray. TODO: Is this function even necessary? */
@@ -1531,7 +1596,7 @@ export class ForkChoice implements IForkChoice {
1531
1596
  const blocksAtSlot: ProtoBlock[] = [];
1532
1597
  for (let i = 0, len = nodes.length; i < len; i++) {
1533
1598
  const node = nodes[i];
1534
- if (node.slot === slot) {
1599
+ if (node.slot === slot && (!isGloasBlock(node) || node.payloadStatus === PayloadStatus.PENDING)) {
1535
1600
  blocksAtSlot.push(node);
1536
1601
  }
1537
1602
  }
@@ -1568,13 +1633,14 @@ export class ForkChoice implements IForkChoice {
1568
1633
  * justified's status as well as validate the terminal conditions if terminal block
1569
1634
  * becomes valid
1570
1635
  */
1571
- validateLatestHash(execResponse: LVHExecResponse): void {
1636
+ validateLatestHash(execResponse: LVHExecResponse, opts?: {strict?: boolean}): boolean {
1572
1637
  try {
1573
- this.protoArray.validateLatestHash(execResponse, this.fcStore.currentSlot);
1638
+ return this.protoArray.validateLatestHash(execResponse, this.fcStore.currentSlot, opts);
1574
1639
  } catch (e) {
1575
1640
  if (e instanceof ProtoArrayError && e.type.code === ProtoArrayErrorCode.INVALID_LVH_EXECUTION_RESPONSE) {
1576
1641
  this.irrecoverableError = e;
1577
1642
  }
1643
+ return false;
1578
1644
  }
1579
1645
  }
1580
1646
 
@@ -1757,9 +1823,9 @@ export class ForkChoice implements IForkChoice {
1757
1823
  * Return true if the block is timely for the current slot.
1758
1824
  * Child class can overwrite this for testing purpose.
1759
1825
  */
1760
- protected isBlockReceivedTimely(block: BeaconBlock, blockDelaySec: number): boolean {
1826
+ protected isBlockReceivedTimely(block: BeaconBlock, blockDelayMs: number): boolean {
1761
1827
  const fork = this.config.getForkName(block.slot);
1762
- const isBeforeLateBlockCutoff = blockDelaySec * 1000 < this.config.getAttestationDueMs(fork);
1828
+ const isBeforeLateBlockCutoff = blockDelayMs < this.config.getAttestationDueMs(fork);
1763
1829
  return this.fcStore.currentSlot === block.slot && isBeforeLateBlockCutoff;
1764
1830
  }
1765
1831
 
@@ -1773,12 +1839,12 @@ export class ForkChoice implements IForkChoice {
1773
1839
  *
1774
1840
  * Child class can overwrite this for testing purpose.
1775
1841
  */
1776
- protected isBlockPtcTimely(block: BeaconBlock, blockDelaySec: number): boolean {
1842
+ protected isBlockPtcTimely(block: BeaconBlock, blockDelayMs: number): boolean {
1777
1843
  const ptcThresholdMs = this.config.getSlotComponentDurationMs(
1778
1844
  this.config.getForkName(block.slot),
1779
1845
  this.config.PAYLOAD_ATTESTATION_DUE_BPS
1780
1846
  );
1781
- return this.fcStore.currentSlot === block.slot && blockDelaySec * 1000 < ptcThresholdMs;
1847
+ return this.fcStore.currentSlot === block.slot && blockDelayMs < ptcThresholdMs;
1782
1848
  }
1783
1849
 
1784
1850
  /**
@@ -1786,9 +1852,9 @@ export class ForkChoice implements IForkChoice {
1786
1852
  * block was imported in a timely manner for its own slot.
1787
1853
  * This is not part of the spec, we use this to determine late canonical blocks.
1788
1854
  */
1789
- protected isBlockImportedTimely(block: BeaconBlock, importDelaySec: number): boolean {
1855
+ protected isBlockImportedTimely(block: BeaconBlock, importDelayMs: number): boolean {
1790
1856
  const fork = this.config.getForkName(block.slot);
1791
- const isBeforeLateBlockCutoff = importDelaySec * 1000 < this.config.getAttestationDueMs(fork);
1857
+ const isBeforeLateBlockCutoff = importDelayMs < this.config.getAttestationDueMs(fork);
1792
1858
  return this.fcStore.currentSlot === block.slot && isBeforeLateBlockCutoff;
1793
1859
  }
1794
1860
 
@@ -1873,10 +1939,10 @@ export class ForkChoice implements IForkChoice {
1873
1939
  /**
1874
1940
  * https://github.com/ethereum/consensus-specs/blob/v1.5.0/specs/phase0/fork-choice.md#is_proposing_on_time
1875
1941
  */
1876
- private isProposingOnTime(secFromSlot: number, slot: Slot): boolean {
1942
+ private isProposingOnTime(msFromSlot: number, slot: Slot): boolean {
1877
1943
  const fork = this.config.getForkName(slot);
1878
1944
  const proposerReorgCutoff = this.config.getProposerReorgCutoffMs(fork);
1879
- return secFromSlot * 1000 <= proposerReorgCutoff;
1945
+ return msFromSlot <= proposerReorgCutoff;
1880
1946
  }
1881
1947
 
1882
1948
  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
  /**
@@ -155,12 +155,15 @@ export interface IForkChoice {
155
155
  * ## Notes:
156
156
  *
157
157
  * The supplied block **must** pass the `state_transition` function as it will not be run here.
158
+ *
159
+ * Throws `InvalidBlockCode.PARENT_EXECUTION_INVALID` if the parent payload chain was invalidated.
160
+ * `executionStatus` is ignored for a gloas block, it inherits the status of the parent variant it builds on.
158
161
  */
159
162
  onBlock(
160
163
  block: BeaconBlock,
161
164
  state: IBeaconStateView,
162
- receiveDelaySec: number,
163
- importDelaySec: number,
165
+ receiveDelayMs: number,
166
+ importDelayMs: number,
164
167
  currentSlot: Slot,
165
168
  executionStatus: BlockExecutionStatus,
166
169
  dataAvailabilityStatus: DataAvailabilityStatus
@@ -279,6 +282,12 @@ export interface IForkChoice {
279
282
  shouldExtendPayload(blockRoot: RootHex): boolean;
280
283
  /** Spec: should_build_on_full(store, head, slot) */
281
284
  shouldBuildOnFull(head: ProtoBlock, slot: Slot): boolean;
285
+ /** Inclusion list verdict for the payload of `blockRoot`, as reported by the execution engine. True if it changed */
286
+ recordPayloadInclusionListSatisfaction(blockRoot: RootHex, satisfied: boolean): boolean;
287
+ /** True once the payload is delivered and not recorded unsatisfied */
288
+ isPayloadInclusionListSatisfied(blockRoot: RootHex): boolean;
289
+ /** Engine verdict recorded for the payload of `blockRoot`, null when none was recorded */
290
+ getPayloadInclusionListSatisfaction(blockRoot: RootHex): boolean | null;
282
291
  getFinalizedBlock(): ProtoBlock;
283
292
  getJustifiedBlock(): ProtoBlock;
284
293
  getFinalizedCheckpointSlot(): Slot;
@@ -344,14 +353,17 @@ export interface IForkChoice {
344
353
  * `CheckpointWithHex` / finalized root without a specific payload-status variant in mind.
345
354
  */
346
355
  forwardIterateDescendantsDefaultStatus(blockRoot: RootHex): IterableIterator<ProtoBlock>;
356
+ /** One summary per block, its default variant (FULL pre-Gloas, PENDING for Gloas) */
347
357
  getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[];
358
+ /** One summary per block, its default variant (FULL pre-Gloas, PENDING for Gloas) */
348
359
  getBlockSummariesAtSlot(slot: Slot): ProtoBlock[];
349
360
  /** Returns the distance of common ancestor of nodes to the max of the newNode and the prevNode. */
350
361
  getCommonAncestorDepth(prevBlock: ProtoBlock, newBlock: ProtoBlock): AncestorResult;
351
362
  /**
352
- * Optimistic sync validate till validated latest hash, invalidate any decendant branch if invalidated branch decendant provided
363
+ * Apply an execution engine verdict with a latest valid hash, see `ProtoArray.validateLatestHash`.
364
+ * Returns whether any execution status changed. A consensus failure is recorded in `irrecoverableError`.
353
365
  */
354
- validateLatestHash(execResponse: LVHExecResponse): void;
366
+ validateLatestHash(execResponse: LVHExecResponse, opts?: {strict?: boolean}): boolean;
355
367
 
356
368
  /**
357
369
  * A dependent root is the block root of the last block before the state transition that decided a specific shuffling
package/src/metrics.ts CHANGED
@@ -19,6 +19,10 @@ export function getForkChoiceMetrics(register: MetricsRegisterExtra) {
19
19
  name: "beacon_fork_choice_requests_total",
20
20
  help: "Count of occasions where fork choice has tried to find a head",
21
21
  }),
22
+ unsatisfiedInclusionListBlocks: register.gauge({
23
+ name: "beacon_fork_choice_unsatisfied_inclusion_list_blocks_total",
24
+ help: "Count of payloads not extended because they did not satisfy the inclusion list constraints",
25
+ }),
22
26
  errors: register.gauge<{entrypoint: UpdateHeadOpt}>({
23
27
  name: "beacon_fork_choice_errors_total",
24
28
  help: "Count of occasions where fork choice has returned an error when trying to find a head",
@@ -28,6 +32,11 @@ export function getForkChoiceMetrics(register: MetricsRegisterExtra) {
28
32
  name: "beacon_fork_choice_changed_head_total",
29
33
  help: "Count of occasions fork choice has found a new head",
30
34
  }),
35
+ executionInvalidations: register.gauge<{source: string}>({
36
+ name: "beacon_fork_choice_execution_invalidations_total",
37
+ help: "Count of execution engine INVALID verdicts applied to fork choice, by verdict source",
38
+ labelNames: ["source"],
39
+ }),
31
40
  reorg: register.gauge({
32
41
  name: "beacon_fork_choice_reorg_total",
33
42
  help: "Count of occasions fork choice has switched to a different chain",
@@ -55,6 +55,8 @@ export function isGloasBlock(block: ProtoBlock): block is ProtoBlock & {parentBl
55
55
  export type LVHValidResponse = {
56
56
  executionStatus: ExecutionStatus.Valid;
57
57
  latestValidExecHash: RootHex;
58
+ /** Resolves the exact node to validate when several nodes carry the same payload hash (gloas variants) */
59
+ blockRoot?: RootHex;
58
60
  };
59
61
  export type LVHInvalidResponse = {
60
62
  executionStatus: ExecutionStatus.Invalid;