@lodestar/fork-choice 1.41.0-dev.f2caa915ab → 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.
Files changed (48) hide show
  1. package/lib/forkChoice/errors.d.ts +9 -1
  2. package/lib/forkChoice/errors.d.ts.map +1 -1
  3. package/lib/forkChoice/errors.js +10 -3
  4. package/lib/forkChoice/errors.js.map +1 -1
  5. package/lib/forkChoice/forkChoice.d.ts +75 -19
  6. package/lib/forkChoice/forkChoice.d.ts.map +1 -1
  7. package/lib/forkChoice/forkChoice.js +301 -117
  8. package/lib/forkChoice/forkChoice.js.map +1 -1
  9. package/lib/forkChoice/interface.d.ts +54 -21
  10. package/lib/forkChoice/interface.d.ts.map +1 -1
  11. package/lib/forkChoice/interface.js +6 -3
  12. package/lib/forkChoice/interface.js.map +1 -1
  13. package/lib/forkChoice/safeBlocks.js.map +1 -1
  14. package/lib/forkChoice/store.d.ts +40 -16
  15. package/lib/forkChoice/store.d.ts.map +1 -1
  16. package/lib/forkChoice/store.js +22 -4
  17. package/lib/forkChoice/store.js.map +1 -1
  18. package/lib/index.d.ts +4 -4
  19. package/lib/index.d.ts.map +1 -1
  20. package/lib/index.js +2 -2
  21. package/lib/index.js.map +1 -1
  22. package/lib/metrics.d.ts.map +1 -1
  23. package/lib/metrics.js.map +1 -1
  24. package/lib/protoArray/computeDeltas.d.ts.map +1 -1
  25. package/lib/protoArray/computeDeltas.js +3 -0
  26. package/lib/protoArray/computeDeltas.js.map +1 -1
  27. package/lib/protoArray/errors.d.ts +15 -2
  28. package/lib/protoArray/errors.d.ts.map +1 -1
  29. package/lib/protoArray/errors.js +7 -2
  30. package/lib/protoArray/errors.js.map +1 -1
  31. package/lib/protoArray/interface.d.ts +20 -2
  32. package/lib/protoArray/interface.d.ts.map +1 -1
  33. package/lib/protoArray/interface.js +19 -1
  34. package/lib/protoArray/interface.js.map +1 -1
  35. package/lib/protoArray/protoArray.d.ts +219 -24
  36. package/lib/protoArray/protoArray.d.ts.map +1 -1
  37. package/lib/protoArray/protoArray.js +748 -133
  38. package/lib/protoArray/protoArray.js.map +1 -1
  39. package/package.json +9 -9
  40. package/src/forkChoice/errors.ts +7 -2
  41. package/src/forkChoice/forkChoice.ts +384 -126
  42. package/src/forkChoice/interface.ts +72 -20
  43. package/src/forkChoice/store.ts +52 -20
  44. package/src/index.ts +10 -2
  45. package/src/protoArray/computeDeltas.ts +6 -0
  46. package/src/protoArray/errors.ts +7 -1
  47. package/src/protoArray/interface.ts +36 -3
  48. package/src/protoArray/protoArray.ts +880 -134
@@ -4,9 +4,15 @@ import {
4
4
  EffectiveBalanceIncrements,
5
5
  } from "@lodestar/state-transition";
6
6
  import {AttesterSlashing, BeaconBlock, Epoch, IndexedAttestation, Root, RootHex, Slot} from "@lodestar/types";
7
- import {LVHExecResponse, MaybeValidExecutionStatus, ProtoBlock, ProtoNode} from "../protoArray/interface.js";
7
+ import {
8
+ LVHExecResponse,
9
+ MaybeValidExecutionStatus,
10
+ PayloadStatus,
11
+ ProtoBlock,
12
+ ProtoNode,
13
+ } from "../protoArray/interface.js";
8
14
  import {UpdateAndGetHeadOpt} from "./forkChoice.js";
9
- import {CheckpointWithHex} from "./store.js";
15
+ import {CheckpointWithHex, CheckpointWithPayloadStatus} from "./store.js";
10
16
 
11
17
  export type CheckpointHex = {
12
18
  epoch: Epoch;
@@ -18,12 +24,12 @@ export type CheckpointsWithHex = {
18
24
  finalizedCheckpoint: CheckpointWithHex;
19
25
  };
20
26
 
21
- export type CheckpointHexWithBalance = {
22
- checkpoint: CheckpointWithHex;
27
+ export type CheckpointWithPayloadAndBalance = {
28
+ checkpoint: CheckpointWithPayloadStatus;
23
29
  balances: EffectiveBalanceIncrements;
24
30
  };
25
31
 
26
- export type CheckpointHexWithTotalBalance = CheckpointHexWithBalance & {
32
+ export type CheckpointWithPayloadAndTotalBalance = CheckpointWithPayloadAndBalance & {
27
33
  totalBalance: number;
28
34
  };
29
35
 
@@ -72,16 +78,18 @@ export interface IForkChoice {
72
78
  irrecoverableError?: Error;
73
79
 
74
80
  /**
75
- * Returns the block root of an ancestor of `block_root` at the given `slot`. (Note: `slot` refers
81
+ * Returns the ancestor node of `block_root` at the given `slot`. (Note: `slot` refers
76
82
  * to the block that is *returned*, not the one that is supplied.)
77
83
  *
78
84
  * ## Specification
79
85
  *
80
- * Equivalent to:
86
+ * Modified for Gloas to return ProtoNode instead of just root:
87
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.1/specs/gloas/fork-choice.md#modified-get_ancestor
81
88
  *
82
- * https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/fork-choice.md#get_ancestor
89
+ * Pre-Gloas: Returns (root, PAYLOAD_STATUS_FULL)
90
+ * Gloas: Returns (root, payloadStatus) based on actual node state
83
91
  */
84
- getAncestor(blockRoot: RootHex, ancestorSlot: Slot): RootHex;
92
+ getAncestor(blockRoot: RootHex, ancestorSlot: Slot): ProtoNode;
85
93
  /**
86
94
  * Run the fork choice rule to determine the head.
87
95
  *
@@ -104,7 +112,7 @@ export interface IForkChoice {
104
112
  * called by `predictProposerHead()` during `prepareNextSlot()`.
105
113
  */
106
114
  shouldOverrideForkChoiceUpdate(
107
- blockRoot: RootHex,
115
+ headBlock: ProtoBlock,
108
116
  secFromSlot: number,
109
117
  currentSlot: Slot
110
118
  ): ShouldOverrideForkChoiceUpdateResult;
@@ -116,8 +124,8 @@ export interface IForkChoice {
116
124
  * Retrieve all nodes for the debug API.
117
125
  */
118
126
  getAllNodes(): ProtoNode[];
119
- getFinalizedCheckpoint(): CheckpointWithHex;
120
- getJustifiedCheckpoint(): CheckpointWithHex;
127
+ getFinalizedCheckpoint(): CheckpointWithPayloadStatus;
128
+ getJustifiedCheckpoint(): CheckpointWithPayloadStatus;
121
129
  /**
122
130
  * Add `block` to the fork choice DAG.
123
131
  *
@@ -169,6 +177,38 @@ export interface IForkChoice {
169
177
  * https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/phase0/fork-choice.md#on_attester_slashing
170
178
  */
171
179
  onAttesterSlashing(slashing: AttesterSlashing): void;
180
+ /**
181
+ * Process PTC (Payload Timeliness Committee) messages from a block
182
+ * Updates the PTC votes for the attested beacon block
183
+ *
184
+ * ## Specification
185
+ *
186
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.0/specs/gloas/fork-choice.md#new-notify_ptc_messages
187
+ *
188
+ * @param blockRoot - The beacon block root being attested
189
+ * @param ptcIndices - Array of PTC committee indices that voted
190
+ * @param payloadPresent - Whether validators attest the payload is present
191
+ */
192
+ notifyPtcMessages(blockRoot: RootHex, ptcIndices: number[], payloadPresent: boolean): void;
193
+ /**
194
+ * Notify fork choice that an execution payload has arrived (Gloas fork)
195
+ * Creates the FULL variant of a Gloas block when the payload becomes available
196
+ *
197
+ * ## Specification
198
+ *
199
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.1/specs/gloas/fork-choice.md#new-on_execution_payload
200
+ *
201
+ * @param blockRoot - The beacon block root for which the payload arrived
202
+ * @param executionPayloadBlockHash - The block hash of the execution payload
203
+ * @param executionPayloadNumber - The block number of the execution payload
204
+ * @param executionPayloadStateRoot - The execution payload state root ie. the root of post-state after processExecutionPayloadEnvelope()
205
+ */
206
+ onExecutionPayload(
207
+ blockRoot: RootHex,
208
+ executionPayloadBlockHash: RootHex,
209
+ executionPayloadNumber: number,
210
+ executionPayloadStateRoot: RootHex
211
+ ): void;
172
212
  /**
173
213
  * Call `onTick` for all slots between `fcStore.getCurrentSlot()` and the provided `currentSlot`.
174
214
  */
@@ -192,8 +232,11 @@ export interface IForkChoice {
192
232
  /**
193
233
  * Returns a `ProtoBlock` if the block is known **and** a descendant of the finalized root.
194
234
  */
195
- getBlock(blockRoot: Root): ProtoBlock | null;
196
- getBlockHex(blockRoot: RootHex): ProtoBlock | null;
235
+ getBlock(blockRoot: Root, payloadStatus: PayloadStatus): ProtoBlock | null;
236
+ getBlockHex(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock | null;
237
+ getBlockDefaultStatus(blockRoot: Root): ProtoBlock | null;
238
+ getBlockHexDefaultStatus(blockRoot: RootHex): ProtoBlock | null;
239
+ getBlockHexAndBlockHash(blockRoot: RootHex, blockHash: RootHex): ProtoBlock | null;
197
240
  getFinalizedBlock(): ProtoBlock;
198
241
  getJustifiedBlock(): ProtoBlock;
199
242
  getFinalizedCheckpointSlot(): Slot;
@@ -203,7 +246,12 @@ export interface IForkChoice {
203
246
  * Always returns `false` if either input roots are unknown.
204
247
  * Still returns `true` if `ancestorRoot===descendantRoot` (and the roots are known)
205
248
  */
206
- isDescendant(ancestorRoot: RootHex, descendantRoot: RootHex): boolean;
249
+ isDescendant(
250
+ ancestorRoot: RootHex,
251
+ ancestorPayloadStatus: PayloadStatus,
252
+ descendantRoot: RootHex,
253
+ descendantPayloadStatus: PayloadStatus
254
+ ): boolean;
207
255
  /**
208
256
  * Prune items up to a finalized root.
209
257
  */
@@ -212,16 +260,20 @@ export interface IForkChoice {
212
260
  /**
213
261
  * Iterates backwards through ancestor block summaries, starting from a block root
214
262
  */
215
- iterateAncestorBlocks(blockRoot: RootHex): IterableIterator<ProtoBlock>;
216
- getAllAncestorBlocks(blockRoot: RootHex): ProtoBlock[];
263
+ iterateAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): IterableIterator<ProtoBlock>;
264
+ getAllAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[];
217
265
  /**
218
266
  * The same to iterateAncestorBlocks but this gets non-ancestor nodes instead of ancestor nodes.
219
267
  */
220
- getAllNonAncestorBlocks(blockRoot: RootHex): ProtoBlock[];
268
+ getAllNonAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[];
221
269
  /**
222
270
  * Returns both ancestor and non-ancestor blocks in a single traversal.
223
271
  */
224
- getAllAncestorAndNonAncestorBlocks(blockRoot: RootHex): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]};
272
+ getAllAncestorAndNonAncestorBlocks(
273
+ blockRoot: RootHex,
274
+ payloadStatus: PayloadStatus
275
+ ): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]};
276
+ getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null;
225
277
  getCanonicalBlockAtSlot(slot: Slot): ProtoBlock | null;
226
278
  getCanonicalBlockClosestLteSlot(slot: Slot): ProtoBlock | null;
227
279
  /**
@@ -231,7 +283,7 @@ export interface IForkChoice {
231
283
  /**
232
284
  * Iterates forward descendants of blockRoot. Does not yield blockRoot itself
233
285
  */
234
- forwardIterateDescendants(blockRoot: RootHex): IterableIterator<ProtoBlock>;
286
+ forwardIterateDescendants(blockRoot: RootHex, payloadStatus: PayloadStatus): IterableIterator<ProtoBlock>;
235
287
  getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[];
236
288
  getBlockSummariesAtSlot(slot: Slot): ProtoBlock[];
237
289
  /** Returns the distance of common ancestor of nodes to the max of the newNode and the prevNode. */
@@ -1,7 +1,8 @@
1
1
  import {CachedBeaconStateAllForks, EffectiveBalanceIncrements} from "@lodestar/state-transition";
2
2
  import {RootHex, Slot, ValidatorIndex, phase0} from "@lodestar/types";
3
3
  import {toRootHex} from "@lodestar/utils";
4
- import {CheckpointHexWithBalance, CheckpointHexWithTotalBalance} from "./interface.js";
4
+ import {PayloadStatus} from "../protoArray/interface.js";
5
+ import {CheckpointWithPayloadAndBalance, CheckpointWithPayloadAndTotalBalance} from "./interface.js";
5
6
 
6
7
  /**
7
8
  * Stores checkpoints in a hybrid format:
@@ -10,6 +11,15 @@ import {CheckpointHexWithBalance, CheckpointHexWithTotalBalance} from "./interfa
10
11
  */
11
12
  export type CheckpointWithHex = phase0.Checkpoint & {rootHex: RootHex};
12
13
 
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
+
13
23
  export type JustifiedBalances = EffectiveBalanceIncrements;
14
24
 
15
25
  /**
@@ -19,7 +29,7 @@ export type JustifiedBalances = EffectiveBalanceIncrements;
19
29
  * @param blockState state that declares justified checkpoint `checkpoint`
20
30
  */
21
31
  export type JustifiedBalancesGetter = (
22
- checkpoint: CheckpointWithHex,
32
+ checkpoint: CheckpointWithPayloadStatus,
23
33
  blockState: CachedBeaconStateAllForks
24
34
  ) => JustifiedBalances;
25
35
 
@@ -37,11 +47,11 @@ export type JustifiedBalancesGetter = (
37
47
  */
38
48
  export interface IForkChoiceStore {
39
49
  currentSlot: Slot;
40
- get justified(): CheckpointHexWithTotalBalance;
41
- set justified(justified: CheckpointHexWithBalance);
42
- unrealizedJustified: CheckpointHexWithBalance;
43
- finalizedCheckpoint: CheckpointWithHex;
44
- unrealizedFinalizedCheckpoint: CheckpointWithHex;
50
+ get justified(): CheckpointWithPayloadAndTotalBalance;
51
+ set justified(justified: CheckpointWithPayloadAndBalance);
52
+ unrealizedJustified: CheckpointWithPayloadAndBalance;
53
+ finalizedCheckpoint: CheckpointWithPayloadStatus;
54
+ unrealizedFinalizedCheckpoint: CheckpointWithPayloadStatus;
45
55
  justifiedBalancesGetter: JustifiedBalancesGetter;
46
56
  equivocatingIndices: Set<ValidatorIndex>;
47
57
  }
@@ -50,10 +60,10 @@ export interface IForkChoiceStore {
50
60
  * IForkChoiceStore implementer which emits forkChoice events on updated justified and finalized checkpoints.
51
61
  */
52
62
  export class ForkChoiceStore implements IForkChoiceStore {
53
- private _justified: CheckpointHexWithTotalBalance;
54
- unrealizedJustified: CheckpointHexWithBalance;
55
- private _finalizedCheckpoint: CheckpointWithHex;
56
- unrealizedFinalizedCheckpoint: CheckpointWithHex;
63
+ private _justified: CheckpointWithPayloadAndTotalBalance;
64
+ unrealizedJustified: CheckpointWithPayloadAndBalance;
65
+ private _finalizedCheckpoint: CheckpointWithPayloadStatus;
66
+ unrealizedFinalizedCheckpoint: CheckpointWithPayloadStatus;
57
67
  equivocatingIndices = new Set<ValidatorIndex>();
58
68
  justifiedBalancesGetter: JustifiedBalancesGetter;
59
69
  currentSlot: Slot;
@@ -64,37 +74,49 @@ export class ForkChoiceStore implements IForkChoiceStore {
64
74
  finalizedCheckpoint: phase0.Checkpoint,
65
75
  justifiedBalances: EffectiveBalanceIncrements,
66
76
  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,
67
89
  private readonly events?: {
68
- onJustified: (cp: CheckpointWithHex) => void;
69
- onFinalized: (cp: CheckpointWithHex) => void;
90
+ onJustified: (cp: CheckpointWithPayloadStatus) => void;
91
+ onFinalized: (cp: CheckpointWithPayloadStatus) => void;
70
92
  }
71
93
  ) {
72
94
  this.justifiedBalancesGetter = justifiedBalancesGetter;
73
95
  this.currentSlot = currentSlot;
74
96
  const justified = {
75
- checkpoint: toCheckpointWithHex(justifiedCheckpoint),
97
+ checkpoint: toCheckpointWithPayload(justifiedCheckpoint, justifiedPayloadStatus),
76
98
  balances: justifiedBalances,
77
99
  totalBalance: computeTotalBalance(justifiedBalances),
78
100
  };
79
101
  this._justified = justified;
80
102
  this.unrealizedJustified = justified;
81
- this._finalizedCheckpoint = toCheckpointWithHex(finalizedCheckpoint);
103
+ this._finalizedCheckpoint = toCheckpointWithPayload(finalizedCheckpoint, finalizedPayloadStatus);
82
104
  this.unrealizedFinalizedCheckpoint = this._finalizedCheckpoint;
83
105
  }
84
106
 
85
- get justified(): CheckpointHexWithTotalBalance {
107
+ get justified(): CheckpointWithPayloadAndTotalBalance {
86
108
  return this._justified;
87
109
  }
88
- set justified(justified: CheckpointHexWithBalance) {
110
+ set justified(justified: CheckpointWithPayloadAndBalance) {
89
111
  this._justified = {...justified, totalBalance: computeTotalBalance(justified.balances)};
90
112
  this.events?.onJustified(justified.checkpoint);
91
113
  }
92
114
 
93
- get finalizedCheckpoint(): CheckpointWithHex {
115
+ get finalizedCheckpoint(): CheckpointWithPayloadStatus {
94
116
  return this._finalizedCheckpoint;
95
117
  }
96
- set finalizedCheckpoint(checkpoint: CheckpointWithHex) {
97
- const cp = toCheckpointWithHex(checkpoint);
118
+ set finalizedCheckpoint(checkpoint: CheckpointWithPayloadStatus) {
119
+ const cp = toCheckpointWithPayload(checkpoint, checkpoint.payloadStatus);
98
120
  this._finalizedCheckpoint = cp;
99
121
  this.events?.onFinalized(cp);
100
122
  }
@@ -111,6 +133,16 @@ export function toCheckpointWithHex(checkpoint: phase0.Checkpoint): CheckpointWi
111
133
  };
112
134
  }
113
135
 
136
+ export function toCheckpointWithPayload(
137
+ checkpoint: phase0.Checkpoint,
138
+ payloadStatus: PayloadStatus
139
+ ): CheckpointWithPayloadStatus {
140
+ return {
141
+ ...toCheckpointWithHex(checkpoint),
142
+ payloadStatus,
143
+ };
144
+ }
145
+
114
146
  export function equalCheckpointWithHex(a: CheckpointWithHex, b: CheckpointWithHex): boolean {
115
147
  return a.epoch === b.epoch && a.rootHex === b.rootHex;
116
148
  }
package/src/index.ts CHANGED
@@ -6,10 +6,17 @@ export {
6
6
  type InvalidBlock,
7
7
  InvalidBlockCode,
8
8
  } from "./forkChoice/errors.js";
9
- export {ForkChoice, type ForkChoiceOpts, UpdateHeadOpt} from "./forkChoice/forkChoice.js";
9
+ export {
10
+ ForkChoice,
11
+ type ForkChoiceOpts,
12
+ UpdateHeadOpt,
13
+ getCheckpointPayloadStatus,
14
+ } from "./forkChoice/forkChoice.js";
10
15
  export {
11
16
  type AncestorResult,
12
17
  AncestorStatus,
18
+ type CheckpointWithPayloadAndBalance,
19
+ type CheckpointWithPayloadAndTotalBalance,
13
20
  EpochDifference,
14
21
  type IForkChoice,
15
22
  NotReorgedReason,
@@ -17,6 +24,7 @@ export {
17
24
  export * from "./forkChoice/safeBlocks.js";
18
25
  export {
19
26
  type CheckpointWithHex,
27
+ type CheckpointWithPayloadStatus,
20
28
  ForkChoiceStore,
21
29
  type IForkChoiceStore,
22
30
  type JustifiedBalancesGetter,
@@ -30,5 +38,5 @@ export type {
30
38
  ProtoBlock,
31
39
  ProtoNode,
32
40
  } from "./protoArray/interface.js";
33
- export {ExecutionStatus} from "./protoArray/interface.js";
41
+ export {ExecutionStatus, PayloadStatus} from "./protoArray/interface.js";
34
42
  export {ProtoArray} from "./protoArray/protoArray.js";
@@ -66,6 +66,7 @@ export function computeDeltas(
66
66
  for (let vIndex = 0; vIndex < voteNextIndices.length; vIndex++) {
67
67
  currentIndex = voteCurrentIndices[vIndex];
68
68
  nextIndex = voteNextIndices[vIndex];
69
+
69
70
  // There is no need to create a score change if the validator has never voted or both of their
70
71
  // votes are for the zero hash (genesis block)
71
72
  if (currentIndex === NULL_VOTE_INDEX && nextIndex === NULL_VOTE_INDEX) {
@@ -106,6 +107,9 @@ export function computeDeltas(
106
107
  continue;
107
108
  }
108
109
 
110
+ // Deduct old balance from current index, add new balance to next index
111
+ // currentIndex and nextIndex already point to the correct node variants
112
+ // Note: If a validator changes from EMPTY to FULL variant of the same block, indexChanged will be true
109
113
  if (currentIndex !== nextIndex || oldBalance !== newBalance) {
110
114
  // We ignore the vote if it is not known in `indices .
111
115
  // We assume that it is outside of our tree (ie: pre-finalization) and therefore not interesting
@@ -116,6 +120,7 @@ export function computeDeltas(
116
120
  index: currentIndex,
117
121
  });
118
122
  }
123
+
119
124
  deltas[currentIndex] -= oldBalance;
120
125
  }
121
126
 
@@ -128,6 +133,7 @@ export function computeDeltas(
128
133
  index: nextIndex,
129
134
  });
130
135
  }
136
+
131
137
  deltas[nextIndex] += newBalance;
132
138
  }
133
139
  voteCurrentIndices[vIndex] = nextIndex;
@@ -12,6 +12,8 @@ export type LVHExecError = {lvhCode: LVHExecErrorCode; blockRoot: RootHex; execH
12
12
  export enum ProtoArrayErrorCode {
13
13
  FINALIZED_NODE_UNKNOWN = "PROTO_ARRAY_ERROR_FINALIZED_NODE_UNKNOWN",
14
14
  JUSTIFIED_NODE_UNKNOWN = "PROTO_ARRAY_ERROR_JUSTIFIED_NODE_UNKNOWN",
15
+ UNKNOWN_BLOCK = "PROTO_ARRAY_ERROR_UNKNOWN_BLOCK",
16
+ UNKNOWN_PARENT_BLOCK = "PROTO_ARRAY_ERROR_UNKNOWN_PARENT_BLOCK",
15
17
  INVALID_FINALIZED_ROOT_CHANGE = "PROTO_ARRAY_ERROR_INVALID_FINALIZED_ROOT_CHANGE",
16
18
  INVALID_NODE_INDEX = "PROTO_ARRAY_ERROR_INVALID_NODE_INDEX",
17
19
  INVALID_PARENT_INDEX = "PROTO_ARRAY_ERROR_INVALID_PARENT_INDEX",
@@ -27,11 +29,14 @@ export enum ProtoArrayErrorCode {
27
29
  INVALID_BLOCK_EXECUTION_STATUS = "PROTO_ARRAY_INVALID_BLOCK_EXECUTION_STATUS",
28
30
  INVALID_JUSTIFIED_EXECUTION_STATUS = "PROTO_ARRAY_INVALID_JUSTIFIED_EXECUTION_STATUS",
29
31
  INVALID_LVH_EXECUTION_RESPONSE = "PROTO_ARRAY_INVALID_LVH_EXECUTION_RESPONSE",
32
+ PRE_GLOAS_BLOCK = "PROTO_ARRAY_ERROR_PRE_GLOAS_BLOCK",
30
33
  }
31
34
 
32
35
  export type ProtoArrayErrorType =
33
36
  | {code: ProtoArrayErrorCode.FINALIZED_NODE_UNKNOWN; root: RootHex}
34
37
  | {code: ProtoArrayErrorCode.JUSTIFIED_NODE_UNKNOWN; root: RootHex}
38
+ | {code: ProtoArrayErrorCode.UNKNOWN_BLOCK; root: RootHex}
39
+ | {code: ProtoArrayErrorCode.UNKNOWN_PARENT_BLOCK; parentRoot: RootHex; parentHash: RootHex | null}
35
40
  | {code: ProtoArrayErrorCode.INVALID_FINALIZED_ROOT_CHANGE}
36
41
  | {code: ProtoArrayErrorCode.INVALID_NODE_INDEX; index: number}
37
42
  | {code: ProtoArrayErrorCode.INVALID_PARENT_INDEX; index: number}
@@ -54,6 +59,7 @@ export type ProtoArrayErrorType =
54
59
  }
55
60
  | {code: ProtoArrayErrorCode.INVALID_BLOCK_EXECUTION_STATUS; root: RootHex}
56
61
  | {code: ProtoArrayErrorCode.INVALID_JUSTIFIED_EXECUTION_STATUS; root: RootHex}
57
- | ({code: ProtoArrayErrorCode.INVALID_LVH_EXECUTION_RESPONSE} & LVHExecError);
62
+ | ({code: ProtoArrayErrorCode.INVALID_LVH_EXECUTION_RESPONSE} & LVHExecError)
63
+ | {code: ProtoArrayErrorCode.PRE_GLOAS_BLOCK; root: RootHex};
58
64
 
59
65
  export class ProtoArrayError extends LodestarError<ProtoArrayErrorType> {}
@@ -36,6 +36,23 @@ export enum ExecutionStatus {
36
36
  PayloadSeparated = "PayloadSeparated",
37
37
  }
38
38
 
39
+ /**
40
+ * Payload status for ePBS (Gloas fork)
41
+ * Spec: gloas/fork-choice.md#constants
42
+ */
43
+ export enum PayloadStatus {
44
+ PENDING = 0,
45
+ EMPTY = 1,
46
+ FULL = 2,
47
+ }
48
+
49
+ /**
50
+ * Check if a block is in the Gloas fork (ePBS enabled)
51
+ */
52
+ export function isGloasBlock(block: ProtoBlock): boolean {
53
+ return block.parentBlockHash !== null;
54
+ }
55
+
39
56
  export type LVHValidResponse = {
40
57
  executionStatus: ExecutionStatus.Valid;
41
58
  latestValidExecHash: RootHex;
@@ -51,6 +68,11 @@ export type MaybeValidExecutionStatus = Exclude<ExecutionStatus, ExecutionStatus
51
68
 
52
69
  export type BlockExtraMeta =
53
70
  | {
71
+ // Pre-gloas:
72
+ // - block hash of payload of the block
73
+ // Post-gloas:
74
+ // - this is parentBlockHash of block bid because payload is only received later
75
+ // - payload block hash for FULL variant
54
76
  executionPayloadBlockHash: RootHex;
55
77
  executionPayloadNumber: UintNum64;
56
78
  executionStatus: Exclude<ExecutionStatus, ExecutionStatus.PreMerge>;
@@ -102,14 +124,25 @@ export type ProtoBlock = BlockExtraMeta & {
102
124
  // Indicate whether block arrives in a timely manner ie. before the 4 second mark
103
125
  timeliness: boolean;
104
126
 
105
- // GLOAS: The followings are from bids. Used for execution payload gossip validation
106
- builderIndex?: number;
107
- blockHashHex?: RootHex;
127
+ /** Payload status for this node (Gloas fork). Always FULL in pre-gloas */
128
+ payloadStatus: PayloadStatus;
129
+
130
+ // GLOAS: The followings are from bids. They are null in pre-gloas
131
+ // Used for execution payload gossip validation
132
+ builderIndex: number | null;
133
+ // Used for execution payload gossip validation. Not to be confused with executionPayloadBlockHash
134
+ blockHashFromBid: RootHex | null;
135
+
136
+ // Used to determine if this block extends EMPTY or FULL parent variant
137
+ // Spec: gloas/fork-choice.md#new-get_parent_payload_status
138
+ parentBlockHash: RootHex | null;
108
139
  };
109
140
 
110
141
  /**
111
142
  * A block root with additional metadata required to form a DAG
112
143
  * with vote weights and best blocks stored as metadata
144
+ *
145
+ * It is also used as ForkChoiceNode in fork choice spec
113
146
  */
114
147
  export type ProtoNode = ProtoBlock & {
115
148
  parent?: number;