@lodestar/fork-choice 1.43.0-dev.ade910fc78 → 1.43.0-dev.ca1fc40294

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.
@@ -1,5 +1,5 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {ForkSeq, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
2
+ import {SLOTS_PER_EPOCH} from "@lodestar/params";
3
3
  import {
4
4
  DataAvailabilityStatus,
5
5
  EffectiveBalanceIncrements,
@@ -11,7 +11,6 @@ import {
11
11
  getAttesterSlashableIndices,
12
12
  isExecutionBlockBodyType,
13
13
  isStatePostBellatrix,
14
- isStatePostGloas,
15
14
  } from "@lodestar/state-transition";
16
15
  import {
17
16
  AttesterSlashing,
@@ -53,7 +52,7 @@ import {
53
52
  NotReorgedReason,
54
53
  ShouldOverrideForkChoiceUpdateResult,
55
54
  } from "./interface.js";
56
- import {CheckpointWithPayloadStatus, IForkChoiceStore, JustifiedBalances, toCheckpointWithPayload} from "./store.js";
55
+ import {CheckpointWithHex, IForkChoiceStore, JustifiedBalances, toCheckpointWithHex} from "./store.js";
57
56
 
58
57
  export type ForkChoiceOpts = {
59
58
  proposerBoost?: boolean;
@@ -311,6 +310,14 @@ export class ForkChoice implements IForkChoice {
311
310
  return this.proposerBoostRoot ?? HEX_ZERO_HASH;
312
311
  }
313
312
 
313
+ /**
314
+ * Decides whether to extend an available payload from the previous slot,
315
+ * corresponding to the beacon block `blockRoot`.
316
+ */
317
+ shouldExtendPayload(blockRoot: RootHex): boolean {
318
+ return this.protoArray.shouldExtendPayload(blockRoot, this.proposerBoostRoot);
319
+ }
320
+
314
321
  /**
315
322
  * To predict the proposer head of the next slot. That is, to predict if proposer-boost-reorg could happen.
316
323
  * Reason why we can't be certain is because information of the head block is not fully available yet
@@ -556,11 +563,11 @@ export class ForkChoice implements IForkChoice {
556
563
  return this.protoArray.nodes;
557
564
  }
558
565
 
559
- getFinalizedCheckpoint(): CheckpointWithPayloadStatus {
566
+ getFinalizedCheckpoint(): CheckpointWithHex {
560
567
  return this.fcStore.finalizedCheckpoint;
561
568
  }
562
569
 
563
- getJustifiedCheckpoint(): CheckpointWithPayloadStatus {
570
+ getJustifiedCheckpoint(): CheckpointWithHex {
564
571
  return this.fcStore.justified.checkpoint;
565
572
  }
566
573
 
@@ -641,10 +648,7 @@ export class ForkChoice implements IForkChoice {
641
648
  // Check block is a descendant of the finalized block at the checkpoint finalized slot.
642
649
  const blockAncestorNode = this.getAncestor(parentRootHex, finalizedSlot);
643
650
  const fcStoreFinalized = this.fcStore.finalizedCheckpoint;
644
- if (
645
- blockAncestorNode.blockRoot !== fcStoreFinalized.rootHex ||
646
- blockAncestorNode.payloadStatus !== fcStoreFinalized.payloadStatus
647
- ) {
651
+ if (blockAncestorNode.blockRoot !== fcStoreFinalized.rootHex) {
648
652
  throw new ForkChoiceError({
649
653
  code: ForkChoiceErrorCode.INVALID_BLOCK,
650
654
  err: {
@@ -670,18 +674,10 @@ export class ForkChoice implements IForkChoice {
670
674
  this.proposerBoostRoot = blockRootHex;
671
675
  }
672
676
 
673
- // Get justified checkpoint with payload status for Gloas
674
- const justifiedPayloadStatus = getCheckpointPayloadStatus(
675
- this.config,
676
- state,
677
- state.currentJustifiedCheckpoint.epoch
678
- );
679
- const justifiedCheckpoint = toCheckpointWithPayload(state.currentJustifiedCheckpoint, justifiedPayloadStatus);
677
+ const justifiedCheckpoint = toCheckpointWithHex(state.currentJustifiedCheckpoint);
680
678
  const stateJustifiedEpoch = justifiedCheckpoint.epoch;
681
679
 
682
- // Get finalized checkpoint with payload status for Gloas
683
- const finalizedPayloadStatus = getCheckpointPayloadStatus(this.config, state, state.finalizedCheckpoint.epoch);
684
- const finalizedCheckpoint = toCheckpointWithPayload(state.finalizedCheckpoint, finalizedPayloadStatus);
680
+ const finalizedCheckpoint = toCheckpointWithHex(state.finalizedCheckpoint);
685
681
 
686
682
  // Justified balances for `justifiedCheckpoint` are new to the fork-choice. Compute them on demand only if
687
683
  // the justified checkpoint changes
@@ -703,61 +699,29 @@ export class ForkChoice implements IForkChoice {
703
699
  // This is an optimization. It should reduce the amount of times we run
704
700
  // `process_justification_and_finalization` by approximately 1/3rd when the chain is
705
701
  // performing optimally.
706
- let unrealizedJustifiedCheckpoint: CheckpointWithPayloadStatus;
707
- let unrealizedFinalizedCheckpoint: CheckpointWithPayloadStatus;
702
+ let unrealizedJustifiedCheckpoint: CheckpointWithHex;
703
+ let unrealizedFinalizedCheckpoint: CheckpointWithHex;
708
704
  if (this.opts?.computeUnrealized) {
709
705
  if (
710
706
  parentBlock.unrealizedJustifiedEpoch === blockEpoch &&
711
707
  parentBlock.unrealizedFinalizedEpoch + 1 >= blockEpoch
712
708
  ) {
713
709
  // reuse from parent, happens at 1/3 last blocks of epoch as monitored in mainnet
714
- // Get payload status for unrealized justified checkpoint
715
- const unrealizedJustifiedPayloadStatus = getCheckpointPayloadStatus(
716
- this.config,
717
- state,
718
- parentBlock.unrealizedJustifiedEpoch
719
- );
720
710
  unrealizedJustifiedCheckpoint = {
721
711
  epoch: parentBlock.unrealizedJustifiedEpoch,
722
712
  root: fromHex(parentBlock.unrealizedJustifiedRoot),
723
713
  rootHex: parentBlock.unrealizedJustifiedRoot,
724
- payloadStatus: unrealizedJustifiedPayloadStatus,
725
714
  };
726
- // Get payload status for unrealized finalized checkpoint
727
- const unrealizedFinalizedPayloadStatus = getCheckpointPayloadStatus(
728
- this.config,
729
- state,
730
- parentBlock.unrealizedFinalizedEpoch
731
- );
732
715
  unrealizedFinalizedCheckpoint = {
733
716
  epoch: parentBlock.unrealizedFinalizedEpoch,
734
717
  root: fromHex(parentBlock.unrealizedFinalizedRoot),
735
718
  rootHex: parentBlock.unrealizedFinalizedRoot,
736
- payloadStatus: unrealizedFinalizedPayloadStatus,
737
719
  };
738
720
  } else {
739
721
  // compute new, happens 2/3 first blocks of epoch as monitored in mainnet
740
722
  const unrealized = state.computeUnrealizedCheckpoints();
741
- // Get payload status for unrealized justified checkpoint
742
- const unrealizedJustifiedPayloadStatus = getCheckpointPayloadStatus(
743
- this.config,
744
- state,
745
- unrealized.justifiedCheckpoint.epoch
746
- );
747
- unrealizedJustifiedCheckpoint = toCheckpointWithPayload(
748
- unrealized.justifiedCheckpoint,
749
- unrealizedJustifiedPayloadStatus
750
- );
751
- // Get payload status for unrealized finalized checkpoint
752
- const unrealizedFinalizedPayloadStatus = getCheckpointPayloadStatus(
753
- this.config,
754
- state,
755
- unrealized.finalizedCheckpoint.epoch
756
- );
757
- unrealizedFinalizedCheckpoint = toCheckpointWithPayload(
758
- unrealized.finalizedCheckpoint,
759
- unrealizedFinalizedPayloadStatus
760
- );
723
+ unrealizedJustifiedCheckpoint = toCheckpointWithHex(unrealized.justifiedCheckpoint);
724
+ unrealizedFinalizedCheckpoint = toCheckpointWithHex(unrealized.finalizedCheckpoint);
761
725
  }
762
726
  } else {
763
727
  unrealizedJustifiedCheckpoint = justifiedCheckpoint;
@@ -1122,8 +1086,8 @@ export class ForkChoice implements IForkChoice {
1122
1086
  }
1123
1087
 
1124
1088
  getJustifiedBlock(): ProtoBlock {
1125
- const {rootHex, payloadStatus} = this.fcStore.justified.checkpoint;
1126
- const block = this.getBlockHex(rootHex, payloadStatus);
1089
+ const {rootHex} = this.fcStore.justified.checkpoint;
1090
+ const block = this.getBlockHexDefaultStatus(rootHex);
1127
1091
  if (!block) {
1128
1092
  throw new ForkChoiceError({
1129
1093
  code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
@@ -1134,8 +1098,8 @@ export class ForkChoice implements IForkChoice {
1134
1098
  }
1135
1099
 
1136
1100
  getFinalizedBlock(): ProtoBlock {
1137
- const {rootHex, payloadStatus} = this.fcStore.finalizedCheckpoint;
1138
- const block = this.getBlockHex(rootHex, payloadStatus);
1101
+ const {rootHex} = this.fcStore.finalizedCheckpoint;
1102
+ const block = this.getBlockHexDefaultStatus(rootHex);
1139
1103
  if (!block) {
1140
1104
  throw new ForkChoiceError({
1141
1105
  code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
@@ -1228,18 +1192,33 @@ export class ForkChoice implements IForkChoice {
1228
1192
 
1229
1193
  /**
1230
1194
  * Returns both ancestor and non-ancestor blocks in a single traversal.
1195
+ *
1196
+ * `ancestors` is the raw walk and includes the previous finalized block as its last element —
1197
+ * callers that don't want the boundary should slice it off themselves.
1198
+ * Post-gloas for each block root, it returns exactly one variant of it.
1231
1199
  */
1232
1200
  getAllAncestorAndNonAncestorBlocks(
1233
1201
  blockRoot: RootHex,
1234
1202
  payloadStatus: PayloadStatus
1235
1203
  ): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]} {
1236
- const {ancestors, nonAncestors} = this.protoArray.getAllAncestorAndNonAncestorNodes(blockRoot, payloadStatus);
1204
+ return this.protoArray.getAllAncestorAndNonAncestorNodes(blockRoot, payloadStatus);
1205
+ }
1237
1206
 
1238
- return {
1239
- // the last node is the previous finalized one, it's there to check onBlock finalized checkpoint only.
1240
- ancestors: ancestors.slice(0, ancestors.length - 1),
1241
- nonAncestors,
1242
- };
1207
+ /**
1208
+ * Same to getAllAncestorAndNonAncestorBlocks with default variant of ${blockRoot} to start with
1209
+ */
1210
+ getAllAncestorAndNonAncestorBlocksDefaultStatus(blockRoot: RootHex): {
1211
+ ancestors: ProtoBlock[];
1212
+ nonAncestors: ProtoBlock[];
1213
+ } {
1214
+ const defaultStatus = this.protoArray.getDefaultVariant(blockRoot);
1215
+ if (defaultStatus === undefined) {
1216
+ throw new ForkChoiceError({
1217
+ code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
1218
+ root: blockRoot,
1219
+ });
1220
+ }
1221
+ return this.getAllAncestorAndNonAncestorBlocks(blockRoot, defaultStatus);
1243
1222
  }
1244
1223
 
1245
1224
  getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null {
@@ -1311,6 +1290,17 @@ export class ForkChoice implements IForkChoice {
1311
1290
  }
1312
1291
  }
1313
1292
 
1293
+ forwardIterateDescendantsDefaultStatus(blockRoot: RootHex): IterableIterator<ProtoBlock> {
1294
+ const defaultStatus = this.protoArray.getDefaultVariant(blockRoot);
1295
+ if (defaultStatus === undefined) {
1296
+ throw new ForkChoiceError({
1297
+ code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
1298
+ root: blockRoot,
1299
+ });
1300
+ }
1301
+ return this.forwardIterateDescendants(blockRoot, defaultStatus);
1302
+ }
1303
+
1314
1304
  /** Very expensive function, iterates the entire ProtoArray. TODO: Is this function even necessary? */
1315
1305
  getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[] {
1316
1306
  return this.protoArray.nodes.filter((node) => node.parentRoot === parentRoot);
@@ -1497,12 +1487,12 @@ export class ForkChoice implements IForkChoice {
1497
1487
  *
1498
1488
  * **`on_tick`**
1499
1489
  * May need the justified balances of:
1500
- * - unrealizedJustified: Already available in `CheckpointWithPayloadAndBalance`
1490
+ * - unrealizedJustified: Already available in `CheckpointWithBalance`
1501
1491
  * Since this balances are already available the getter is just `() => balances`, without cache interaction
1502
1492
  */
1503
1493
  private updateCheckpoints(
1504
- justifiedCheckpoint: CheckpointWithPayloadStatus,
1505
- finalizedCheckpoint: CheckpointWithPayloadStatus,
1494
+ justifiedCheckpoint: CheckpointWithHex,
1495
+ finalizedCheckpoint: CheckpointWithHex,
1506
1496
  getJustifiedBalances: () => JustifiedBalances
1507
1497
  ): void {
1508
1498
  // Update justified checkpoint.
@@ -1522,8 +1512,8 @@ export class ForkChoice implements IForkChoice {
1522
1512
  * Update unrealized checkpoints in store if necessary
1523
1513
  */
1524
1514
  private updateUnrealizedCheckpoints(
1525
- unrealizedJustifiedCheckpoint: CheckpointWithPayloadStatus,
1526
- unrealizedFinalizedCheckpoint: CheckpointWithPayloadStatus,
1515
+ unrealizedJustifiedCheckpoint: CheckpointWithHex,
1516
+ unrealizedFinalizedCheckpoint: CheckpointWithHex,
1527
1517
  getJustifiedBalances: () => JustifiedBalances
1528
1518
  ): void {
1529
1519
  if (unrealizedJustifiedCheckpoint.epoch > this.fcStore.unrealizedJustified.checkpoint.epoch) {
@@ -1898,38 +1888,3 @@ export function getCommitteeFraction(
1898
1888
  const committeeWeight = Math.floor(justifiedTotalActiveBalanceByIncrement / config.slotsPerEpoch);
1899
1889
  return Math.floor((committeeWeight * config.committeePercent) / 100);
1900
1890
  }
1901
-
1902
- /**
1903
- * Get the payload status for a checkpoint.
1904
- *
1905
- * Pre-Gloas: always FULL (payload embedded in block)
1906
- * Gloas: determined by state.execution_payload_availability
1907
- *
1908
- * @param config - The chain fork config to determine fork at checkpoint slot
1909
- * @param state - The state to check execution_payload_availability
1910
- * @param checkpointEpoch - The epoch of the checkpoint
1911
- */
1912
- export function getCheckpointPayloadStatus(
1913
- config: ChainForkConfig,
1914
- state: IBeaconStateView,
1915
- checkpointEpoch: number
1916
- ): PayloadStatus {
1917
- // Compute checkpoint slot first to determine the correct fork
1918
- const checkpointSlot = computeStartSlotAtEpoch(checkpointEpoch);
1919
- const fork = config.getForkSeq(checkpointSlot);
1920
-
1921
- // Pre-Gloas: always FULL
1922
- if (fork < ForkSeq.gloas) {
1923
- return PayloadStatus.FULL;
1924
- }
1925
- if (!isStatePostGloas(state)) {
1926
- throw new Error(`Expected gloas+ state for checkpoint payload status, got fork=${state.forkName}`);
1927
- }
1928
-
1929
- // For Gloas, check state.execution_payload_availability
1930
- // - For non-skipped slots at checkpoint: returns false (EMPTY) since payload hasn't arrived yet
1931
- // - For skipped slots at checkpoint: returns the actual availability status from state
1932
- const payloadAvailable = state.executionPayloadAvailability.get(checkpointSlot % SLOTS_PER_HISTORICAL_ROOT);
1933
-
1934
- return payloadAvailable ? PayloadStatus.FULL : PayloadStatus.EMPTY;
1935
- }
@@ -9,7 +9,7 @@ import {
9
9
  ProtoNode,
10
10
  } from "../protoArray/interface.js";
11
11
  import {UpdateAndGetHeadOpt} from "./forkChoice.js";
12
- import {CheckpointWithHex, CheckpointWithPayloadStatus} from "./store.js";
12
+ import {CheckpointWithHex} from "./store.js";
13
13
 
14
14
  export type CheckpointHex = {
15
15
  epoch: Epoch;
@@ -21,12 +21,12 @@ export type CheckpointsWithHex = {
21
21
  finalizedCheckpoint: CheckpointWithHex;
22
22
  };
23
23
 
24
- export type CheckpointWithPayloadAndBalance = {
25
- checkpoint: CheckpointWithPayloadStatus;
24
+ export type CheckpointWithBalance = {
25
+ checkpoint: CheckpointWithHex;
26
26
  balances: EffectiveBalanceIncrements;
27
27
  };
28
28
 
29
- export type CheckpointWithPayloadAndTotalBalance = CheckpointWithPayloadAndBalance & {
29
+ export type CheckpointWithTotalBalance = CheckpointWithBalance & {
30
30
  totalBalance: number;
31
31
  };
32
32
 
@@ -121,8 +121,8 @@ export interface IForkChoice {
121
121
  * Retrieve all nodes for the debug API.
122
122
  */
123
123
  getAllNodes(): ProtoNode[];
124
- getFinalizedCheckpoint(): CheckpointWithPayloadStatus;
125
- getJustifiedCheckpoint(): CheckpointWithPayloadStatus;
124
+ getFinalizedCheckpoint(): CheckpointWithHex;
125
+ getJustifiedCheckpoint(): CheckpointWithHex;
126
126
  /**
127
127
  * Add `block` to the fork choice DAG.
128
128
  *
@@ -241,6 +241,7 @@ export interface IForkChoice {
241
241
  getBlockDefaultStatus(blockRoot: Root): ProtoBlock | null;
242
242
  getBlockHexDefaultStatus(blockRoot: RootHex): ProtoBlock | null;
243
243
  getBlockHexAndBlockHash(blockRoot: RootHex, blockHash: RootHex): ProtoBlock | null;
244
+ shouldExtendPayload(blockRoot: RootHex): boolean;
244
245
  getFinalizedBlock(): ProtoBlock;
245
246
  getJustifiedBlock(): ProtoBlock;
246
247
  getFinalizedCheckpointSlot(): Slot;
@@ -272,11 +273,23 @@ export interface IForkChoice {
272
273
  getAllNonAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[];
273
274
  /**
274
275
  * Returns both ancestor and non-ancestor blocks in a single traversal.
276
+ *
277
+ * `ancestors` is the raw walk and includes the previous finalized block as its last element —
278
+ * callers that don't want the boundary should slice it off themselves.
275
279
  */
276
280
  getAllAncestorAndNonAncestorBlocks(
277
281
  blockRoot: RootHex,
278
282
  payloadStatus: PayloadStatus
279
283
  ): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]};
284
+ /**
285
+ * Same as `getAllAncestorAndNonAncestorBlocks` but resolves the default payload-status variant
286
+ * (FULL pre-Gloas, PENDING for Gloas) for the given root. Use when the caller holds a
287
+ * `CheckpointWithHex` / finalized root without a specific payload-status variant in mind.
288
+ */
289
+ getAllAncestorAndNonAncestorBlocksDefaultStatus(blockRoot: RootHex): {
290
+ ancestors: ProtoBlock[];
291
+ nonAncestors: ProtoBlock[];
292
+ };
280
293
  getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null;
281
294
  getCanonicalBlockAtSlot(slot: Slot): ProtoBlock | null;
282
295
  getCanonicalBlockClosestLteSlot(slot: Slot): ProtoBlock | null;
@@ -288,6 +301,12 @@ export interface IForkChoice {
288
301
  * Iterates forward descendants of blockRoot. Does not yield blockRoot itself
289
302
  */
290
303
  forwardIterateDescendants(blockRoot: RootHex, payloadStatus: PayloadStatus): IterableIterator<ProtoBlock>;
304
+ /**
305
+ * Same as `forwardIterateDescendants` but resolves the default payload-status variant
306
+ * (FULL pre-Gloas, PENDING for Gloas) for the given root. Use when the caller holds a
307
+ * `CheckpointWithHex` / finalized root without a specific payload-status variant in mind.
308
+ */
309
+ forwardIterateDescendantsDefaultStatus(blockRoot: RootHex): IterableIterator<ProtoBlock>;
291
310
  getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[];
292
311
  getBlockSummariesAtSlot(slot: Slot): ProtoBlock[];
293
312
  /** Returns the distance of common ancestor of nodes to the max of the newNode and the prevNode. */
@@ -1,8 +1,7 @@
1
1
  import {EffectiveBalanceIncrements, IBeaconStateView} from "@lodestar/state-transition";
2
2
  import {RootHex, Slot, ValidatorIndex, phase0} from "@lodestar/types";
3
3
  import {toRootHex} from "@lodestar/utils";
4
- import {PayloadStatus} from "../protoArray/interface.js";
5
- import {CheckpointWithPayloadAndBalance, CheckpointWithPayloadAndTotalBalance} from "./interface.js";
4
+ import {CheckpointWithBalance, CheckpointWithTotalBalance} from "./interface.js";
6
5
 
7
6
  /**
8
7
  * Stores checkpoints in a hybrid format:
@@ -11,15 +10,6 @@ import {CheckpointWithPayloadAndBalance, CheckpointWithPayloadAndTotalBalance} f
11
10
  */
12
11
  export type CheckpointWithHex = phase0.Checkpoint & {rootHex: RootHex};
13
12
 
14
- /**
15
- * Checkpoint with payload status for Gloas fork choice.
16
- * Used to track which variant (EMPTY or FULL) of the finalized/justified block to use.
17
- *
18
- * Pre-Gloas: payloadStatus is always FULL (payload embedded in block)
19
- * Gloas: determined by state.execution_payload_availability
20
- */
21
- export type CheckpointWithPayloadStatus = CheckpointWithHex & {payloadStatus: PayloadStatus};
22
-
23
13
  export type JustifiedBalances = EffectiveBalanceIncrements;
24
14
 
25
15
  /**
@@ -29,7 +19,7 @@ export type JustifiedBalances = EffectiveBalanceIncrements;
29
19
  * @param blockState state that declares justified checkpoint `checkpoint`
30
20
  */
31
21
  export type JustifiedBalancesGetter = (
32
- checkpoint: CheckpointWithPayloadStatus,
22
+ checkpoint: CheckpointWithHex,
33
23
  blockState: IBeaconStateView
34
24
  ) => JustifiedBalances;
35
25
 
@@ -47,11 +37,11 @@ export type JustifiedBalancesGetter = (
47
37
  */
48
38
  export interface IForkChoiceStore {
49
39
  currentSlot: Slot;
50
- get justified(): CheckpointWithPayloadAndTotalBalance;
51
- set justified(justified: CheckpointWithPayloadAndBalance);
52
- unrealizedJustified: CheckpointWithPayloadAndBalance;
53
- finalizedCheckpoint: CheckpointWithPayloadStatus;
54
- unrealizedFinalizedCheckpoint: CheckpointWithPayloadStatus;
40
+ get justified(): CheckpointWithTotalBalance;
41
+ set justified(justified: CheckpointWithBalance);
42
+ unrealizedJustified: CheckpointWithBalance;
43
+ finalizedCheckpoint: CheckpointWithHex;
44
+ unrealizedFinalizedCheckpoint: CheckpointWithHex;
55
45
  justifiedBalancesGetter: JustifiedBalancesGetter;
56
46
  equivocatingIndices: Set<ValidatorIndex>;
57
47
  }
@@ -60,10 +50,10 @@ export interface IForkChoiceStore {
60
50
  * IForkChoiceStore implementer which emits forkChoice events on updated justified and finalized checkpoints.
61
51
  */
62
52
  export class ForkChoiceStore implements IForkChoiceStore {
63
- private _justified: CheckpointWithPayloadAndTotalBalance;
64
- unrealizedJustified: CheckpointWithPayloadAndBalance;
65
- private _finalizedCheckpoint: CheckpointWithPayloadStatus;
66
- unrealizedFinalizedCheckpoint: CheckpointWithPayloadStatus;
53
+ private _justified: CheckpointWithTotalBalance;
54
+ unrealizedJustified: CheckpointWithBalance;
55
+ private _finalizedCheckpoint: CheckpointWithHex;
56
+ unrealizedFinalizedCheckpoint: CheckpointWithHex;
67
57
  equivocatingIndices = new Set<ValidatorIndex>();
68
58
  justifiedBalancesGetter: JustifiedBalancesGetter;
69
59
  currentSlot: Slot;
@@ -74,49 +64,37 @@ export class ForkChoiceStore implements IForkChoiceStore {
74
64
  finalizedCheckpoint: phase0.Checkpoint,
75
65
  justifiedBalances: EffectiveBalanceIncrements,
76
66
  justifiedBalancesGetter: JustifiedBalancesGetter,
77
- /**
78
- * Payload status for justified checkpoint.
79
- * Pre-Gloas: always FULL
80
- * Gloas: determined by state.execution_payload_availability
81
- */
82
- justifiedPayloadStatus: PayloadStatus,
83
- /**
84
- * Payload status for finalized checkpoint.
85
- * Pre-Gloas: always FULL
86
- * Gloas: determined by state.execution_payload_availability
87
- */
88
- finalizedPayloadStatus: PayloadStatus,
89
67
  private readonly events?: {
90
- onJustified: (cp: CheckpointWithPayloadStatus) => void;
91
- onFinalized: (cp: CheckpointWithPayloadStatus) => void;
68
+ onJustified: (cp: CheckpointWithHex) => void;
69
+ onFinalized: (cp: CheckpointWithHex) => void;
92
70
  }
93
71
  ) {
94
72
  this.justifiedBalancesGetter = justifiedBalancesGetter;
95
73
  this.currentSlot = currentSlot;
96
74
  const justified = {
97
- checkpoint: toCheckpointWithPayload(justifiedCheckpoint, justifiedPayloadStatus),
75
+ checkpoint: toCheckpointWithHex(justifiedCheckpoint),
98
76
  balances: justifiedBalances,
99
77
  totalBalance: computeTotalBalance(justifiedBalances),
100
78
  };
101
79
  this._justified = justified;
102
80
  this.unrealizedJustified = justified;
103
- this._finalizedCheckpoint = toCheckpointWithPayload(finalizedCheckpoint, finalizedPayloadStatus);
81
+ this._finalizedCheckpoint = toCheckpointWithHex(finalizedCheckpoint);
104
82
  this.unrealizedFinalizedCheckpoint = this._finalizedCheckpoint;
105
83
  }
106
84
 
107
- get justified(): CheckpointWithPayloadAndTotalBalance {
85
+ get justified(): CheckpointWithTotalBalance {
108
86
  return this._justified;
109
87
  }
110
- set justified(justified: CheckpointWithPayloadAndBalance) {
88
+ set justified(justified: CheckpointWithBalance) {
111
89
  this._justified = {...justified, totalBalance: computeTotalBalance(justified.balances)};
112
90
  this.events?.onJustified(justified.checkpoint);
113
91
  }
114
92
 
115
- get finalizedCheckpoint(): CheckpointWithPayloadStatus {
93
+ get finalizedCheckpoint(): CheckpointWithHex {
116
94
  return this._finalizedCheckpoint;
117
95
  }
118
- set finalizedCheckpoint(checkpoint: CheckpointWithPayloadStatus) {
119
- const cp = toCheckpointWithPayload(checkpoint, checkpoint.payloadStatus);
96
+ set finalizedCheckpoint(checkpoint: CheckpointWithHex) {
97
+ const cp = toCheckpointWithHex(checkpoint);
120
98
  this._finalizedCheckpoint = cp;
121
99
  this.events?.onFinalized(cp);
122
100
  }
@@ -133,16 +111,6 @@ export function toCheckpointWithHex(checkpoint: phase0.Checkpoint): CheckpointWi
133
111
  };
134
112
  }
135
113
 
136
- export function toCheckpointWithPayload(
137
- checkpoint: phase0.Checkpoint,
138
- payloadStatus: PayloadStatus
139
- ): CheckpointWithPayloadStatus {
140
- return {
141
- ...toCheckpointWithHex(checkpoint),
142
- payloadStatus,
143
- };
144
- }
145
-
146
114
  export function equalCheckpointWithHex(a: CheckpointWithHex, b: CheckpointWithHex): boolean {
147
115
  return a.epoch === b.epoch && a.rootHex === b.rootHex;
148
116
  }
package/src/index.ts CHANGED
@@ -6,17 +6,12 @@ export {
6
6
  type InvalidBlock,
7
7
  InvalidBlockCode,
8
8
  } from "./forkChoice/errors.js";
9
- export {
10
- ForkChoice,
11
- type ForkChoiceOpts,
12
- UpdateHeadOpt,
13
- getCheckpointPayloadStatus,
14
- } from "./forkChoice/forkChoice.js";
9
+ export {ForkChoice, type ForkChoiceOpts, UpdateHeadOpt} from "./forkChoice/forkChoice.js";
15
10
  export {
16
11
  type AncestorResult,
17
12
  AncestorStatus,
18
- type CheckpointWithPayloadAndBalance,
19
- type CheckpointWithPayloadAndTotalBalance,
13
+ type CheckpointWithBalance,
14
+ type CheckpointWithTotalBalance,
20
15
  EpochDifference,
21
16
  type IForkChoice,
22
17
  NotReorgedReason,
@@ -24,7 +19,6 @@ export {
24
19
  export * from "./forkChoice/safeBlocks.js";
25
20
  export {
26
21
  type CheckpointWithHex,
27
- type CheckpointWithPayloadStatus,
28
22
  ForkChoiceStore,
29
23
  type IForkChoiceStore,
30
24
  type JustifiedBalancesGetter,
@@ -114,6 +114,23 @@ export class ProtoArray {
114
114
  // Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.4/specs/gloas/fork-choice.md#modified-get_forkchoice_store
115
115
  if (protoArray.ptcVotes.has(block.blockRoot)) {
116
116
  protoArray.ptcVotes.set(block.blockRoot, BitArray.fromBoolArray(Array.from({length: PTC_SIZE}, () => true)));
117
+
118
+ // In the spec, we have payload_states = {anchor_root: anchor_state.copy()}
119
+ // which means the anchor's "payload" is considered received
120
+ // Without FULL, blocks extending FULL from the anchor would be orphaned.
121
+ // TODO GLOAS: This is a bug in the spec. Keep this to pass the current spec test
122
+ // for now. Need to remove this when we work on v1.7.0-alpha.5
123
+ if (block.executionPayloadBlockHash !== null) {
124
+ protoArray.onExecutionPayload(
125
+ block.blockRoot,
126
+ currentSlot,
127
+ block.executionPayloadBlockHash,
128
+ (block as {executionPayloadNumber?: number}).executionPayloadNumber ?? 0,
129
+ block.stateRoot,
130
+ null,
131
+ ExecutionStatus.Valid
132
+ );
133
+ }
117
134
  }
118
135
 
119
136
  return protoArray;
@@ -690,7 +707,7 @@ export class ProtoArray {
690
707
  * Determine if we should extend the payload (prefer FULL over EMPTY)
691
708
  * Spec: gloas/fork-choice.md#new-should_extend_payload
692
709
  *
693
- * Returns true if:
710
+ * Returns true if payload is verified (FULL variant exists) AND:
694
711
  * 1. Payload is timely, OR
695
712
  * 2. No proposer boost root (empty/zero hash), OR
696
713
  * 3. Proposer boost root's parent is not this block, OR
@@ -700,6 +717,10 @@ export class ProtoArray {
700
717
  * @param proposerBoostRoot - Current proposer boost root (from ForkChoice)
701
718
  */
702
719
  shouldExtendPayload(blockRoot: RootHex, proposerBoostRoot: RootHex | null): boolean {
720
+ if (!this.hasPayload(blockRoot)) {
721
+ return false;
722
+ }
723
+
703
724
  // Condition 1: Payload is timely
704
725
  if (this.isPayloadTimely(blockRoot)) {
705
726
  return true;
@@ -1655,10 +1676,9 @@ export class ProtoArray {
1655
1676
  const ancestors: ProtoNode[] = [];
1656
1677
  const nonAncestors: ProtoNode[] = [];
1657
1678
 
1658
- // Include starting node if it's not PENDING (i.e., pre-Gloas or EMPTY/FULL variant post-Gloas)
1659
- if (node.payloadStatus !== PayloadStatus.PENDING) {
1660
- ancestors.push(node);
1661
- }
1679
+ // caller of this method may pass default status
1680
+ // this is the only node that we accept PENDING
1681
+ ancestors.push(node);
1662
1682
 
1663
1683
  let nodeIndex = startIndex;
1664
1684
  while (node.parent !== undefined) {