@lodestar/fork-choice 1.41.0-dev.f36ec31497 → 1.41.0-dev.f7a5f4ddda

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.
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/ChainSafe/lodestar/issues"
13
13
  },
14
- "version": "1.41.0-dev.f36ec31497",
14
+ "version": "1.41.0-dev.f7a5f4ddda",
15
15
  "type": "module",
16
16
  "exports": {
17
17
  ".": {
@@ -27,11 +27,11 @@
27
27
  ],
28
28
  "scripts": {
29
29
  "clean": "rm -rf lib && rm -f *.tsbuildinfo",
30
- "build": "tsc -p tsconfig.build.json",
30
+ "build": "tsgo -p tsconfig.build.json",
31
31
  "build:watch": "pnpm run build --watch",
32
32
  "build:release": "pnpm clean && pnpm run build",
33
33
  "check-build": "node -e \"(async function() { await import('./lib/index.js') })()\"",
34
- "check-types": "tsc",
34
+ "check-types": "tsgo",
35
35
  "lint": "biome check src/ test/",
36
36
  "lint:fix": "pnpm run lint --write",
37
37
  "test": "pnpm test:unit",
@@ -40,11 +40,11 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@chainsafe/ssz": "^1.2.2",
43
- "@lodestar/config": "^1.41.0-dev.f36ec31497",
44
- "@lodestar/params": "^1.41.0-dev.f36ec31497",
45
- "@lodestar/state-transition": "^1.41.0-dev.f36ec31497",
46
- "@lodestar/types": "^1.41.0-dev.f36ec31497",
47
- "@lodestar/utils": "^1.41.0-dev.f36ec31497"
43
+ "@lodestar/config": "^1.41.0-dev.f7a5f4ddda",
44
+ "@lodestar/params": "^1.41.0-dev.f7a5f4ddda",
45
+ "@lodestar/state-transition": "^1.41.0-dev.f7a5f4ddda",
46
+ "@lodestar/types": "^1.41.0-dev.f7a5f4ddda",
47
+ "@lodestar/utils": "^1.41.0-dev.f7a5f4ddda"
48
48
  },
49
49
  "keywords": [
50
50
  "ethereum",
@@ -52,5 +52,5 @@
52
52
  "beacon",
53
53
  "blockchain"
54
54
  ],
55
- "gitHead": "9c929ad4b7b1b1ba94960007698cce0dd6536c17"
55
+ "gitHead": "4e275225295e0987e05ef15f8c48633c8e5e5960"
56
56
  }
@@ -54,7 +54,7 @@ import {
54
54
  NotReorgedReason,
55
55
  ShouldOverrideForkChoiceUpdateResult,
56
56
  } from "./interface.js";
57
- import {CheckpointWithPayload, IForkChoiceStore, JustifiedBalances, toCheckpointWithPayload} from "./store.js";
57
+ import {CheckpointWithPayloadStatus, IForkChoiceStore, JustifiedBalances, toCheckpointWithPayload} from "./store.js";
58
58
 
59
59
  export type ForkChoiceOpts = {
60
60
  proposerBoost?: boolean;
@@ -557,11 +557,11 @@ export class ForkChoice implements IForkChoice {
557
557
  return this.protoArray.nodes;
558
558
  }
559
559
 
560
- getFinalizedCheckpoint(): CheckpointWithPayload {
560
+ getFinalizedCheckpoint(): CheckpointWithPayloadStatus {
561
561
  return this.fcStore.finalizedCheckpoint;
562
562
  }
563
563
 
564
- getJustifiedCheckpoint(): CheckpointWithPayload {
564
+ getJustifiedCheckpoint(): CheckpointWithPayloadStatus {
565
565
  return this.fcStore.justified.checkpoint;
566
566
  }
567
567
 
@@ -700,8 +700,8 @@ export class ForkChoice implements IForkChoice {
700
700
  // This is an optimization. It should reduce the amount of times we run
701
701
  // `process_justification_and_finalization` by approximately 1/3rd when the chain is
702
702
  // performing optimally.
703
- let unrealizedJustifiedCheckpoint: CheckpointWithPayload;
704
- let unrealizedFinalizedCheckpoint: CheckpointWithPayload;
703
+ let unrealizedJustifiedCheckpoint: CheckpointWithPayloadStatus;
704
+ let unrealizedFinalizedCheckpoint: CheckpointWithPayloadStatus;
705
705
  if (this.opts?.computeUnrealized) {
706
706
  if (
707
707
  parentBlock.unrealizedJustifiedEpoch === blockEpoch &&
@@ -1133,8 +1133,13 @@ export class ForkChoice implements IForkChoice {
1133
1133
  * Always returns `false` if either input roots are unknown.
1134
1134
  * Still returns `true` if `ancestorRoot===descendantRoot` (and the roots are known)
1135
1135
  */
1136
- isDescendant(ancestorRoot: RootHex, descendantRoot: RootHex): boolean {
1137
- return this.protoArray.isDescendant(ancestorRoot, descendantRoot);
1136
+ isDescendant(
1137
+ ancestorRoot: RootHex,
1138
+ ancestorPayloadStatus: PayloadStatus,
1139
+ descendantRoot: RootHex,
1140
+ descendantPayloadStatus: PayloadStatus
1141
+ ): boolean {
1142
+ return this.protoArray.isDescendant(ancestorRoot, ancestorPayloadStatus, descendantRoot, descendantPayloadStatus);
1138
1143
  }
1139
1144
 
1140
1145
  /**
@@ -1177,16 +1182,16 @@ export class ForkChoice implements IForkChoice {
1177
1182
  * Iterates backwards through block summaries, starting from a block root.
1178
1183
  * Return only the non-finalized blocks.
1179
1184
  */
1180
- iterateAncestorBlocks(blockRoot: RootHex): IterableIterator<ProtoBlock> {
1181
- return this.protoArray.iterateAncestorNodes(blockRoot);
1185
+ iterateAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): IterableIterator<ProtoBlock> {
1186
+ return this.protoArray.iterateAncestorNodes(blockRoot, payloadStatus);
1182
1187
  }
1183
1188
 
1184
1189
  /**
1185
1190
  * Returns all blocks backwards starting from a block root.
1186
1191
  * Return only the non-finalized blocks.
1187
1192
  */
1188
- getAllAncestorBlocks(blockRoot: RootHex): ProtoBlock[] {
1189
- const blocks = this.protoArray.getAllAncestorNodes(blockRoot);
1193
+ getAllAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[] {
1194
+ const blocks = this.protoArray.getAllAncestorNodes(blockRoot, payloadStatus);
1190
1195
  // the last node is the previous finalized one, it's there to check onBlock finalized checkpoint only.
1191
1196
  return blocks.slice(0, blocks.length - 1);
1192
1197
  }
@@ -1194,15 +1199,18 @@ export class ForkChoice implements IForkChoice {
1194
1199
  /**
1195
1200
  * The same to iterateAncestorBlocks but this gets non-ancestor nodes instead of ancestor nodes.
1196
1201
  */
1197
- getAllNonAncestorBlocks(blockRoot: RootHex): ProtoBlock[] {
1198
- return this.protoArray.getAllNonAncestorNodes(blockRoot);
1202
+ getAllNonAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[] {
1203
+ return this.protoArray.getAllNonAncestorNodes(blockRoot, payloadStatus);
1199
1204
  }
1200
1205
 
1201
1206
  /**
1202
1207
  * Returns both ancestor and non-ancestor blocks in a single traversal.
1203
1208
  */
1204
- getAllAncestorAndNonAncestorBlocks(blockRoot: RootHex): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]} {
1205
- const {ancestors, nonAncestors} = this.protoArray.getAllAncestorAndNonAncestorNodes(blockRoot);
1209
+ getAllAncestorAndNonAncestorBlocks(
1210
+ blockRoot: RootHex,
1211
+ payloadStatus: PayloadStatus
1212
+ ): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]} {
1213
+ const {ancestors, nonAncestors} = this.protoArray.getAllAncestorAndNonAncestorNodes(blockRoot, payloadStatus);
1206
1214
 
1207
1215
  return {
1208
1216
  // the last node is the previous finalized one, it's there to check onBlock finalized checkpoint only.
@@ -1217,7 +1225,7 @@ export class ForkChoice implements IForkChoice {
1217
1225
  return this.head;
1218
1226
  }
1219
1227
 
1220
- for (const block of this.protoArray.iterateAncestorNodes(this.head.blockRoot)) {
1228
+ for (const block of this.protoArray.iterateAncestorNodes(this.head.blockRoot, this.head.payloadStatus)) {
1221
1229
  if (block.blockRoot === blockRootHex) {
1222
1230
  return block;
1223
1231
  }
@@ -1235,7 +1243,7 @@ export class ForkChoice implements IForkChoice {
1235
1243
  return this.head;
1236
1244
  }
1237
1245
 
1238
- for (const block of this.protoArray.iterateAncestorNodes(this.head.blockRoot)) {
1246
+ for (const block of this.protoArray.iterateAncestorNodes(this.head.blockRoot, this.head.payloadStatus)) {
1239
1247
  if (block.slot === slot) {
1240
1248
  return block;
1241
1249
  }
@@ -1248,7 +1256,7 @@ export class ForkChoice implements IForkChoice {
1248
1256
  return this.head;
1249
1257
  }
1250
1258
 
1251
- for (const block of this.protoArray.iterateAncestorNodes(this.head.blockRoot)) {
1259
+ for (const block of this.protoArray.iterateAncestorNodes(this.head.blockRoot, this.head.payloadStatus)) {
1252
1260
  if (slot >= block.slot) {
1253
1261
  return block;
1254
1262
  }
@@ -1261,24 +1269,16 @@ export class ForkChoice implements IForkChoice {
1261
1269
  return this.protoArray.nodes;
1262
1270
  }
1263
1271
 
1264
- // TODO GLOAS: this function is ambiguous, consumer should also provide payload, or it should accept a ProtoBlock instead
1265
- // also consumer may want PENDING or EMPTY only
1266
- *forwardIterateDescendants(blockRoot: RootHex): IterableIterator<ProtoBlock> {
1272
+ *forwardIterateDescendants(blockRoot: RootHex, payloadStatus: PayloadStatus): IterableIterator<ProtoBlock> {
1267
1273
  const rootsInChain = new Set([blockRoot]);
1268
-
1269
- const blockVariants = this.protoArray.indices.get(blockRoot);
1270
- if (blockVariants === undefined) {
1274
+ const blockIndex = this.protoArray.getNodeIndexByRootAndStatus(blockRoot, payloadStatus);
1275
+ if (blockIndex === undefined) {
1271
1276
  throw new ForkChoiceError({
1272
1277
  code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
1273
1278
  root: blockRoot,
1274
1279
  });
1275
1280
  }
1276
1281
 
1277
- // Find the minimum index among all variants to start iteration
1278
- const blockIndex = Array.isArray(blockVariants)
1279
- ? Math.min(...blockVariants.filter((idx) => idx !== undefined))
1280
- : blockVariants;
1281
-
1282
1282
  for (let i = blockIndex + 1; i < this.protoArray.nodes.length; i++) {
1283
1283
  const node = this.protoArray.nodes[i];
1284
1284
  if (rootsInChain.has(node.parentRoot)) {
@@ -1478,8 +1478,8 @@ export class ForkChoice implements IForkChoice {
1478
1478
  * Since this balances are already available the getter is just `() => balances`, without cache interaction
1479
1479
  */
1480
1480
  private updateCheckpoints(
1481
- justifiedCheckpoint: CheckpointWithPayload,
1482
- finalizedCheckpoint: CheckpointWithPayload,
1481
+ justifiedCheckpoint: CheckpointWithPayloadStatus,
1482
+ finalizedCheckpoint: CheckpointWithPayloadStatus,
1483
1483
  getJustifiedBalances: () => JustifiedBalances
1484
1484
  ): void {
1485
1485
  // Update justified checkpoint.
@@ -1499,8 +1499,8 @@ export class ForkChoice implements IForkChoice {
1499
1499
  * Update unrealized checkpoints in store if necessary
1500
1500
  */
1501
1501
  private updateUnrealizedCheckpoints(
1502
- unrealizedJustifiedCheckpoint: CheckpointWithPayload,
1503
- unrealizedFinalizedCheckpoint: CheckpointWithPayload,
1502
+ unrealizedJustifiedCheckpoint: CheckpointWithPayloadStatus,
1503
+ unrealizedFinalizedCheckpoint: CheckpointWithPayloadStatus,
1504
1504
  getJustifiedBalances: () => JustifiedBalances
1505
1505
  ): void {
1506
1506
  if (unrealizedJustifiedCheckpoint.epoch > this.fcStore.unrealizedJustified.checkpoint.epoch) {
@@ -12,7 +12,7 @@ import {
12
12
  ProtoNode,
13
13
  } from "../protoArray/interface.js";
14
14
  import {UpdateAndGetHeadOpt} from "./forkChoice.js";
15
- import {CheckpointWithHex, CheckpointWithPayload} from "./store.js";
15
+ import {CheckpointWithHex, CheckpointWithPayloadStatus} from "./store.js";
16
16
 
17
17
  export type CheckpointHex = {
18
18
  epoch: Epoch;
@@ -25,7 +25,7 @@ export type CheckpointsWithHex = {
25
25
  };
26
26
 
27
27
  export type CheckpointWithPayloadAndBalance = {
28
- checkpoint: CheckpointWithPayload;
28
+ checkpoint: CheckpointWithPayloadStatus;
29
29
  balances: EffectiveBalanceIncrements;
30
30
  };
31
31
 
@@ -124,8 +124,8 @@ export interface IForkChoice {
124
124
  * Retrieve all nodes for the debug API.
125
125
  */
126
126
  getAllNodes(): ProtoNode[];
127
- getFinalizedCheckpoint(): CheckpointWithPayload;
128
- getJustifiedCheckpoint(): CheckpointWithPayload;
127
+ getFinalizedCheckpoint(): CheckpointWithPayloadStatus;
128
+ getJustifiedCheckpoint(): CheckpointWithPayloadStatus;
129
129
  /**
130
130
  * Add `block` to the fork choice DAG.
131
131
  *
@@ -246,7 +246,12 @@ export interface IForkChoice {
246
246
  * Always returns `false` if either input roots are unknown.
247
247
  * Still returns `true` if `ancestorRoot===descendantRoot` (and the roots are known)
248
248
  */
249
- isDescendant(ancestorRoot: RootHex, descendantRoot: RootHex): boolean;
249
+ isDescendant(
250
+ ancestorRoot: RootHex,
251
+ ancestorPayloadStatus: PayloadStatus,
252
+ descendantRoot: RootHex,
253
+ descendantPayloadStatus: PayloadStatus
254
+ ): boolean;
250
255
  /**
251
256
  * Prune items up to a finalized root.
252
257
  */
@@ -255,16 +260,19 @@ export interface IForkChoice {
255
260
  /**
256
261
  * Iterates backwards through ancestor block summaries, starting from a block root
257
262
  */
258
- iterateAncestorBlocks(blockRoot: RootHex): IterableIterator<ProtoBlock>;
259
- getAllAncestorBlocks(blockRoot: RootHex): ProtoBlock[];
263
+ iterateAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): IterableIterator<ProtoBlock>;
264
+ getAllAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[];
260
265
  /**
261
266
  * The same to iterateAncestorBlocks but this gets non-ancestor nodes instead of ancestor nodes.
262
267
  */
263
- getAllNonAncestorBlocks(blockRoot: RootHex): ProtoBlock[];
268
+ getAllNonAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[];
264
269
  /**
265
270
  * Returns both ancestor and non-ancestor blocks in a single traversal.
266
271
  */
267
- getAllAncestorAndNonAncestorBlocks(blockRoot: RootHex): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]};
272
+ getAllAncestorAndNonAncestorBlocks(
273
+ blockRoot: RootHex,
274
+ payloadStatus: PayloadStatus
275
+ ): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]};
268
276
  getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null;
269
277
  getCanonicalBlockAtSlot(slot: Slot): ProtoBlock | null;
270
278
  getCanonicalBlockClosestLteSlot(slot: Slot): ProtoBlock | null;
@@ -275,7 +283,7 @@ export interface IForkChoice {
275
283
  /**
276
284
  * Iterates forward descendants of blockRoot. Does not yield blockRoot itself
277
285
  */
278
- forwardIterateDescendants(blockRoot: RootHex): IterableIterator<ProtoBlock>;
286
+ forwardIterateDescendants(blockRoot: RootHex, payloadStatus: PayloadStatus): IterableIterator<ProtoBlock>;
279
287
  getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[];
280
288
  getBlockSummariesAtSlot(slot: Slot): ProtoBlock[];
281
289
  /** Returns the distance of common ancestor of nodes to the max of the newNode and the prevNode. */
@@ -18,7 +18,7 @@ export type CheckpointWithHex = phase0.Checkpoint & {rootHex: RootHex};
18
18
  * Pre-Gloas: payloadStatus is always FULL (payload embedded in block)
19
19
  * Gloas: determined by state.execution_payload_availability
20
20
  */
21
- export type CheckpointWithPayload = CheckpointWithHex & {payloadStatus: PayloadStatus};
21
+ export type CheckpointWithPayloadStatus = CheckpointWithHex & {payloadStatus: PayloadStatus};
22
22
 
23
23
  export type JustifiedBalances = EffectiveBalanceIncrements;
24
24
 
@@ -29,7 +29,7 @@ export type JustifiedBalances = EffectiveBalanceIncrements;
29
29
  * @param blockState state that declares justified checkpoint `checkpoint`
30
30
  */
31
31
  export type JustifiedBalancesGetter = (
32
- checkpoint: CheckpointWithPayload,
32
+ checkpoint: CheckpointWithPayloadStatus,
33
33
  blockState: CachedBeaconStateAllForks
34
34
  ) => JustifiedBalances;
35
35
 
@@ -50,8 +50,8 @@ export interface IForkChoiceStore {
50
50
  get justified(): CheckpointWithPayloadAndTotalBalance;
51
51
  set justified(justified: CheckpointWithPayloadAndBalance);
52
52
  unrealizedJustified: CheckpointWithPayloadAndBalance;
53
- finalizedCheckpoint: CheckpointWithPayload;
54
- unrealizedFinalizedCheckpoint: CheckpointWithPayload;
53
+ finalizedCheckpoint: CheckpointWithPayloadStatus;
54
+ unrealizedFinalizedCheckpoint: CheckpointWithPayloadStatus;
55
55
  justifiedBalancesGetter: JustifiedBalancesGetter;
56
56
  equivocatingIndices: Set<ValidatorIndex>;
57
57
  }
@@ -62,8 +62,8 @@ export interface IForkChoiceStore {
62
62
  export class ForkChoiceStore implements IForkChoiceStore {
63
63
  private _justified: CheckpointWithPayloadAndTotalBalance;
64
64
  unrealizedJustified: CheckpointWithPayloadAndBalance;
65
- private _finalizedCheckpoint: CheckpointWithPayload;
66
- unrealizedFinalizedCheckpoint: CheckpointWithPayload;
65
+ private _finalizedCheckpoint: CheckpointWithPayloadStatus;
66
+ unrealizedFinalizedCheckpoint: CheckpointWithPayloadStatus;
67
67
  equivocatingIndices = new Set<ValidatorIndex>();
68
68
  justifiedBalancesGetter: JustifiedBalancesGetter;
69
69
  currentSlot: Slot;
@@ -87,8 +87,8 @@ export class ForkChoiceStore implements IForkChoiceStore {
87
87
  */
88
88
  finalizedPayloadStatus: PayloadStatus,
89
89
  private readonly events?: {
90
- onJustified: (cp: CheckpointWithPayload) => void;
91
- onFinalized: (cp: CheckpointWithPayload) => void;
90
+ onJustified: (cp: CheckpointWithPayloadStatus) => void;
91
+ onFinalized: (cp: CheckpointWithPayloadStatus) => void;
92
92
  }
93
93
  ) {
94
94
  this.justifiedBalancesGetter = justifiedBalancesGetter;
@@ -112,10 +112,10 @@ export class ForkChoiceStore implements IForkChoiceStore {
112
112
  this.events?.onJustified(justified.checkpoint);
113
113
  }
114
114
 
115
- get finalizedCheckpoint(): CheckpointWithPayload {
115
+ get finalizedCheckpoint(): CheckpointWithPayloadStatus {
116
116
  return this._finalizedCheckpoint;
117
117
  }
118
- set finalizedCheckpoint(checkpoint: CheckpointWithPayload) {
118
+ set finalizedCheckpoint(checkpoint: CheckpointWithPayloadStatus) {
119
119
  const cp = toCheckpointWithPayload(checkpoint, checkpoint.payloadStatus);
120
120
  this._finalizedCheckpoint = cp;
121
121
  this.events?.onFinalized(cp);
@@ -136,7 +136,7 @@ export function toCheckpointWithHex(checkpoint: phase0.Checkpoint): CheckpointWi
136
136
  export function toCheckpointWithPayload(
137
137
  checkpoint: phase0.Checkpoint,
138
138
  payloadStatus: PayloadStatus
139
- ): CheckpointWithPayload {
139
+ ): CheckpointWithPayloadStatus {
140
140
  return {
141
141
  ...toCheckpointWithHex(checkpoint),
142
142
  payloadStatus,
package/src/index.ts CHANGED
@@ -24,7 +24,7 @@ export {
24
24
  export * from "./forkChoice/safeBlocks.js";
25
25
  export {
26
26
  type CheckpointWithHex,
27
- type CheckpointWithPayload,
27
+ type CheckpointWithPayloadStatus,
28
28
  ForkChoiceStore,
29
29
  type IForkChoiceStore,
30
30
  type JustifiedBalancesGetter,
@@ -1477,11 +1477,8 @@ export class ProtoArray {
1477
1477
  * For Gloas blocks: returns EMPTY/FULL variants (not PENDING) based on parent payload status
1478
1478
  * For pre-Gloas blocks: returns FULL variants
1479
1479
  */
1480
- *iterateAncestorNodes(blockRoot: RootHex): IterableIterator<ProtoNode> {
1481
- // Get canonical node: FULL for pre-Gloas, PENDING for Gloas
1482
- const defaultStatus = this.getDefaultVariant(blockRoot);
1483
- const startIndex =
1484
- defaultStatus !== undefined ? this.getNodeIndexByRootAndStatus(blockRoot, defaultStatus) : undefined;
1480
+ *iterateAncestorNodes(blockRoot: RootHex, payloadStatus: PayloadStatus): IterableIterator<ProtoNode> {
1481
+ const startIndex = this.getNodeIndexByRootAndStatus(blockRoot, payloadStatus);
1485
1482
  if (startIndex === undefined) {
1486
1483
  return;
1487
1484
  }
@@ -1520,11 +1517,8 @@ export class ProtoArray {
1520
1517
  * For Gloas blocks: returns EMPTY/FULL variants (not PENDING) based on parent payload status
1521
1518
  * For pre-Gloas blocks: returns FULL variants
1522
1519
  */
1523
- getAllAncestorNodes(blockRoot: RootHex): ProtoNode[] {
1524
- // Get canonical node: FULL for pre-Gloas, PENDING for Gloas
1525
- const defaultStatus = this.getDefaultVariant(blockRoot);
1526
- const startIndex =
1527
- defaultStatus !== undefined ? this.getNodeIndexByRootAndStatus(blockRoot, defaultStatus) : undefined;
1520
+ getAllAncestorNodes(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoNode[] {
1521
+ const startIndex = this.getNodeIndexByRootAndStatus(blockRoot, payloadStatus);
1528
1522
  if (startIndex === undefined) {
1529
1523
  return [];
1530
1524
  }
@@ -1537,12 +1531,10 @@ export class ProtoArray {
1537
1531
  });
1538
1532
  }
1539
1533
 
1540
- // Include starting node if node is pre-gloas
1541
- // Reason why we exclude post-gloas is because node is always default variant (PENDING)
1542
- // which we want to exclude.
1534
+ // Exclude PENDING variant from returned ancestors.
1543
1535
  const nodes: ProtoNode[] = [];
1544
1536
 
1545
- if (!isGloasBlock(node)) {
1537
+ if (node.payloadStatus !== PayloadStatus.PENDING) {
1546
1538
  nodes.push(node);
1547
1539
  }
1548
1540
 
@@ -1567,13 +1559,8 @@ export class ProtoArray {
1567
1559
  * For Gloas blocks: returns EMPTY/FULL variants (not PENDING) based on parent payload status
1568
1560
  * For pre-Gloas blocks: returns FULL variants
1569
1561
  */
1570
- getAllNonAncestorNodes(blockRoot: RootHex): ProtoNode[] {
1571
- // Get canonical node: FULL for pre-Gloas, PENDING for Gloas
1572
- const defaultStatus = this.getDefaultVariant(blockRoot);
1573
- if (defaultStatus === undefined) {
1574
- return [];
1575
- }
1576
- const startIndex = this.getNodeIndexByRootAndStatus(blockRoot, defaultStatus);
1562
+ getAllNonAncestorNodes(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoNode[] {
1563
+ const startIndex = this.getNodeIndexByRootAndStatus(blockRoot, payloadStatus);
1577
1564
  if (startIndex === undefined) {
1578
1565
  return [];
1579
1566
  }
@@ -1613,11 +1600,11 @@ export class ProtoArray {
1613
1600
  * For Gloas blocks: returns EMPTY/FULL variants (not PENDING) based on parent payload status
1614
1601
  * For pre-Gloas blocks: returns FULL variants
1615
1602
  */
1616
- getAllAncestorAndNonAncestorNodes(blockRoot: RootHex): {ancestors: ProtoNode[]; nonAncestors: ProtoNode[]} {
1617
- // Get canonical node: FULL for pre-Gloas, PENDING for Gloas
1618
- const defaultStatus = this.getDefaultVariant(blockRoot);
1619
- const startIndex =
1620
- defaultStatus !== undefined ? this.getNodeIndexByRootAndStatus(blockRoot, defaultStatus) : undefined;
1603
+ getAllAncestorAndNonAncestorNodes(
1604
+ blockRoot: RootHex,
1605
+ payloadStatus: PayloadStatus
1606
+ ): {ancestors: ProtoNode[]; nonAncestors: ProtoNode[]} {
1607
+ const startIndex = this.getNodeIndexByRootAndStatus(blockRoot, payloadStatus);
1621
1608
  if (startIndex === undefined) {
1622
1609
  return {ancestors: [], nonAncestors: []};
1623
1610
  }
@@ -1735,26 +1722,28 @@ export class ProtoArray {
1735
1722
  /**
1736
1723
  * Returns `true` if the `descendantRoot` has an ancestor with `ancestorRoot`.
1737
1724
  * Always returns `false` if either input roots are unknown.
1738
- * Still returns `true` if `ancestorRoot` === `descendantRoot` (and the roots are known)
1725
+ * Still returns `true` if `ancestorRoot` === `descendantRoot` and payload statuses match.
1739
1726
  */
1740
- isDescendant(ancestorRoot: RootHex, descendantRoot: RootHex): boolean {
1741
- // We use the default variant (PENDING for Gloas, FULL for pre-Gloas)
1742
- // We cannot use FULL/EMPTY variants for Gloas because they may not be canonical
1743
- const defaultStatus = this.getDefaultVariant(ancestorRoot);
1744
- const ancestorNode = defaultStatus !== undefined ? this.getNode(ancestorRoot, defaultStatus) : undefined;
1727
+ isDescendant(
1728
+ ancestorRoot: RootHex,
1729
+ ancestorPayloadStatus: PayloadStatus,
1730
+ descendantRoot: RootHex,
1731
+ descendantPayloadStatus: PayloadStatus
1732
+ ): boolean {
1733
+ const ancestorNode = this.getNode(ancestorRoot, ancestorPayloadStatus);
1745
1734
  if (!ancestorNode) {
1746
1735
  return false;
1747
1736
  }
1748
1737
 
1749
- if (ancestorRoot === descendantRoot) {
1738
+ if (ancestorRoot === descendantRoot && ancestorPayloadStatus === descendantPayloadStatus) {
1750
1739
  return true;
1751
1740
  }
1752
1741
 
1753
- for (const node of this.iterateAncestorNodes(descendantRoot)) {
1742
+ for (const node of this.iterateAncestorNodes(descendantRoot, descendantPayloadStatus)) {
1754
1743
  if (node.slot < ancestorNode.slot) {
1755
1744
  return false;
1756
1745
  }
1757
- if (node.blockRoot === ancestorNode.blockRoot) {
1746
+ if (node.blockRoot === ancestorNode.blockRoot && node.payloadStatus === ancestorNode.payloadStatus) {
1758
1747
  return true;
1759
1748
  }
1760
1749
  }