@lodestar/beacon-node 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.
Files changed (177) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +1 -4
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/state/utils.d.ts +2 -2
  5. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  6. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  7. package/lib/api/impl/validator/index.d.ts.map +1 -1
  8. package/lib/api/impl/validator/index.js +1 -4
  9. package/lib/api/impl/validator/index.js.map +1 -1
  10. package/lib/chain/GetBlobsTracker.d.ts +1 -1
  11. package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
  12. package/lib/chain/GetBlobsTracker.js +1 -2
  13. package/lib/chain/GetBlobsTracker.js.map +1 -1
  14. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  16. package/lib/chain/archiveStore/interface.d.ts +4 -4
  17. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  18. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts +4 -4
  19. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
  20. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js +2 -4
  21. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
  22. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +2 -2
  23. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  24. package/lib/chain/archiveStore/utils/archiveBlocks.js +110 -58
  25. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  26. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  27. package/lib/chain/blocks/importBlock.js +23 -31
  28. package/lib/chain/blocks/importBlock.js.map +1 -1
  29. package/lib/chain/blocks/importExecutionPayload.d.ts +1 -1
  30. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  31. package/lib/chain/blocks/importExecutionPayload.js +19 -26
  32. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  33. package/lib/chain/blocks/index.js +1 -1
  34. package/lib/chain/blocks/index.js.map +1 -1
  35. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +3 -0
  36. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  37. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +20 -0
  38. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  39. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -0
  40. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
  41. package/lib/chain/blocks/payloadEnvelopeProcessor.js +6 -4
  42. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  43. package/lib/chain/blocks/types.d.ts +1 -1
  44. package/lib/chain/blocks/types.d.ts.map +1 -1
  45. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +14 -0
  46. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -0
  47. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +25 -0
  48. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -0
  49. package/lib/chain/chain.d.ts +3 -3
  50. package/lib/chain/chain.d.ts.map +1 -1
  51. package/lib/chain/chain.js +16 -32
  52. package/lib/chain/chain.js.map +1 -1
  53. package/lib/chain/emitter.d.ts +16 -4
  54. package/lib/chain/emitter.d.ts.map +1 -1
  55. package/lib/chain/emitter.js +5 -0
  56. package/lib/chain/emitter.js.map +1 -1
  57. package/lib/chain/errors/attestationError.d.ts +8 -1
  58. package/lib/chain/errors/attestationError.d.ts.map +1 -1
  59. package/lib/chain/errors/attestationError.js +4 -0
  60. package/lib/chain/errors/attestationError.js.map +1 -1
  61. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  62. package/lib/chain/forkChoice/index.js +9 -13
  63. package/lib/chain/forkChoice/index.js.map +1 -1
  64. package/lib/chain/interface.d.ts +2 -2
  65. package/lib/chain/interface.d.ts.map +1 -1
  66. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  67. package/lib/chain/prepareNextSlot.js +22 -16
  68. package/lib/chain/prepareNextSlot.js.map +1 -1
  69. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +3 -9
  70. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  71. package/lib/chain/produceBlock/computeNewStateRoot.js +5 -32
  72. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  73. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -8
  74. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  75. package/lib/chain/produceBlock/produceBlockBody.js +29 -19
  76. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  77. package/lib/chain/regen/errors.d.ts +1 -11
  78. package/lib/chain/regen/errors.d.ts.map +1 -1
  79. package/lib/chain/regen/errors.js +0 -2
  80. package/lib/chain/regen/errors.js.map +1 -1
  81. package/lib/chain/regen/interface.d.ts +6 -11
  82. package/lib/chain/regen/interface.d.ts.map +1 -1
  83. package/lib/chain/regen/queued.d.ts +6 -10
  84. package/lib/chain/regen/queued.d.ts.map +1 -1
  85. package/lib/chain/regen/queued.js +3 -10
  86. package/lib/chain/regen/queued.js.map +1 -1
  87. package/lib/chain/regen/regen.d.ts +0 -5
  88. package/lib/chain/regen/regen.d.ts.map +1 -1
  89. package/lib/chain/regen/regen.js +0 -8
  90. package/lib/chain/regen/regen.js.map +1 -1
  91. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -7
  92. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  93. package/lib/chain/stateCache/persistentCheckpointsCache.js +4 -9
  94. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  95. package/lib/chain/stateCache/types.d.ts +0 -6
  96. package/lib/chain/stateCache/types.d.ts.map +1 -1
  97. package/lib/chain/stateCache/types.js.map +1 -1
  98. package/lib/chain/validation/aggregateAndProof.js +12 -0
  99. package/lib/chain/validation/aggregateAndProof.js.map +1 -1
  100. package/lib/chain/validation/attestation.d.ts.map +1 -1
  101. package/lib/chain/validation/attestation.js +12 -0
  102. package/lib/chain/validation/attestation.js.map +1 -1
  103. package/lib/chain/validation/executionPayloadEnvelope.js +10 -10
  104. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  105. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  106. package/lib/chain/validation/payloadAttestationMessage.js +4 -3
  107. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  108. package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
  109. package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
  110. package/lib/execution/engine/http.d.ts.map +1 -1
  111. package/lib/execution/engine/http.js +21 -14
  112. package/lib/execution/engine/http.js.map +1 -1
  113. package/lib/execution/engine/interface.d.ts +1 -0
  114. package/lib/execution/engine/interface.d.ts.map +1 -1
  115. package/lib/execution/engine/mock.d.ts.map +1 -1
  116. package/lib/execution/engine/mock.js +6 -0
  117. package/lib/execution/engine/mock.js.map +1 -1
  118. package/lib/execution/engine/types.d.ts +20 -0
  119. package/lib/execution/engine/types.d.ts.map +1 -1
  120. package/lib/execution/engine/types.js +18 -0
  121. package/lib/execution/engine/types.js.map +1 -1
  122. package/lib/network/gossip/topic.d.ts +3 -2
  123. package/lib/network/gossip/topic.d.ts.map +1 -1
  124. package/lib/network/network.js +1 -1
  125. package/lib/network/network.js.map +1 -1
  126. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  127. package/lib/network/processor/gossipHandlers.js +22 -6
  128. package/lib/network/processor/gossipHandlers.js.map +1 -1
  129. package/lib/node/nodejs.d.ts.map +1 -1
  130. package/lib/node/nodejs.js +4 -2
  131. package/lib/node/nodejs.js.map +1 -1
  132. package/lib/util/sszBytes.d.ts.map +1 -1
  133. package/lib/util/sszBytes.js +16 -3
  134. package/lib/util/sszBytes.js.map +1 -1
  135. package/package.json +16 -16
  136. package/src/api/impl/beacon/blocks/index.ts +1 -4
  137. package/src/api/impl/beacon/state/utils.ts +2 -2
  138. package/src/api/impl/validator/index.ts +3 -6
  139. package/src/chain/GetBlobsTracker.ts +1 -2
  140. package/src/chain/archiveStore/archiveStore.ts +5 -5
  141. package/src/chain/archiveStore/interface.ts +4 -4
  142. package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +6 -8
  143. package/src/chain/archiveStore/utils/archiveBlocks.ts +153 -94
  144. package/src/chain/blocks/importBlock.ts +22 -35
  145. package/src/chain/blocks/importExecutionPayload.ts +20 -26
  146. package/src/chain/blocks/index.ts +1 -1
  147. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -0
  148. package/src/chain/blocks/payloadEnvelopeProcessor.ts +6 -5
  149. package/src/chain/blocks/types.ts +1 -1
  150. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +38 -0
  151. package/src/chain/chain.ts +23 -48
  152. package/src/chain/emitter.ts +15 -3
  153. package/src/chain/errors/attestationError.ts +6 -1
  154. package/src/chain/forkChoice/index.ts +6 -18
  155. package/src/chain/interface.ts +2 -2
  156. package/src/chain/prepareNextSlot.ts +25 -16
  157. package/src/chain/produceBlock/computeNewStateRoot.ts +6 -43
  158. package/src/chain/produceBlock/produceBlockBody.ts +40 -20
  159. package/src/chain/regen/errors.ts +1 -6
  160. package/src/chain/regen/interface.ts +6 -11
  161. package/src/chain/regen/queued.ts +6 -14
  162. package/src/chain/regen/regen.ts +0 -8
  163. package/src/chain/stateCache/persistentCheckpointsCache.ts +5 -15
  164. package/src/chain/stateCache/types.ts +0 -3
  165. package/src/chain/validation/aggregateAndProof.ts +13 -0
  166. package/src/chain/validation/attestation.ts +13 -0
  167. package/src/chain/validation/executionPayloadEnvelope.ts +10 -10
  168. package/src/chain/validation/payloadAttestationMessage.ts +5 -3
  169. package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
  170. package/src/execution/engine/http.ts +21 -14
  171. package/src/execution/engine/interface.ts +1 -0
  172. package/src/execution/engine/mock.ts +8 -1
  173. package/src/execution/engine/types.ts +41 -0
  174. package/src/network/network.ts +1 -1
  175. package/src/network/processor/gossipHandlers.ts +26 -10
  176. package/src/node/nodejs.ts +4 -2
  177. package/src/util/sszBytes.ts +21 -3
@@ -2,13 +2,14 @@ import {routes} from "@lodestar/api";
2
2
  import {ExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
3
3
  import {SLOTS_PER_EPOCH} from "@lodestar/params";
4
4
  import {getExecutionPayloadEnvelopeSignatureSet, isStatePostGloas} from "@lodestar/state-transition";
5
- import {byteArrayEquals, fromHex, toRootHex} from "@lodestar/utils";
5
+ import {fromHex, toRootHex} from "@lodestar/utils";
6
6
  import {ExecutionPayloadStatus} from "../../execution/index.js";
7
7
  import {isQueueErrorAborted} from "../../util/queue/index.js";
8
8
  import {BeaconChain} from "../chain.js";
9
9
  import {RegenCaller} from "../regen/interface.js";
10
10
  import {PayloadEnvelopeInput} from "../seenCache/seenPayloadEnvelopeInput.js";
11
11
  import {ImportPayloadOpts} from "./types.js";
12
+ import {verifyPayloadsDataAvailability} from "./verifyPayloadsDataAvailability.js";
12
13
 
13
14
  const EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS = 64;
14
15
 
@@ -84,21 +85,22 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
84
85
  export async function importExecutionPayload(
85
86
  this: BeaconChain,
86
87
  payloadInput: PayloadEnvelopeInput,
88
+ signal: AbortSignal,
87
89
  opts: ImportPayloadOpts = {}
88
90
  ): Promise<void> {
89
91
  const signedEnvelope = payloadInput.getPayloadEnvelope();
90
92
  const envelope = signedEnvelope.message;
91
93
  const blockRootHex = payloadInput.blockRootHex;
92
94
  const blockHashHex = payloadInput.getBlockHashHex();
93
- const fork = this.config.getForkName(envelope.slot);
95
+ const fork = this.config.getForkName(envelope.payload.slotNumber);
94
96
 
95
97
  // 1. Emit `execution_payload_available` event at the start of import. At this point the payload input
96
98
  // is already complete, so the payload and required data are available for payload attestation.
97
99
  // This event is only about availability, not validity of the execution payload, hence we can emit
98
100
  // it before getting a response from the execution client on whether the payload is valid or not.
99
- if (this.clock.currentSlot - envelope.slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
101
+ if (this.clock.currentSlot - envelope.payload.slotNumber < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
100
102
  this.emitter.emit(routes.events.EventType.executionPayloadAvailable, {
101
- slot: envelope.slot,
103
+ slot: envelope.payload.slotNumber,
102
104
  blockRoot: blockRootHex,
103
105
  });
104
106
  }
@@ -112,11 +114,15 @@ export async function importExecutionPayload(
112
114
  });
113
115
  }
114
116
 
115
- // 3. Apply backpressure from the write queue early, before doing verification work.
117
+ // 3. Wait for data columns to be available before claiming a write-queue slot.
118
+ // The helper is shared with future gloas sync services; take the single-item batch form here.
119
+ await verifyPayloadsDataAvailability([payloadInput], signal);
120
+
121
+ // 4. Apply backpressure from the write queue, before doing verification work.
116
122
  // The actual DB write is deferred until after verification succeeds.
117
123
  await this.unfinalizedPayloadEnvelopeWrites.waitForSpace();
118
124
 
119
- // 4. Get pre-state for processExecutionPayloadEnvelope
125
+ // 5. Get pre-state for processExecutionPayloadEnvelope
120
126
  // We need the block state (post-block, pre-payload) to process the envelope
121
127
  const blockState = await this.regen.getBlockSlotState(
122
128
  protoBlock,
@@ -131,9 +137,7 @@ export async function importExecutionPayload(
131
137
  });
132
138
  }
133
139
 
134
- // 5. Run verification steps in parallel
135
- // Note: No data availability check needed here - importExecutionPayload is only
136
- // called when payloadInput.isComplete() is true, so all data is already available.
140
+ // 6. Run verification steps in parallel
137
141
  const [execResult, signatureValid, postPayloadResult] = await Promise.all([
138
142
  this.executionEngine.notifyNewPayload(
139
143
  fork,
@@ -209,22 +213,16 @@ export async function importExecutionPayload(
209
213
  });
210
214
  }
211
215
 
212
- // 5c. Verify envelope state root matches post-state
216
+ // 5c. Compute post-payload state root
213
217
  const postPayloadState = postPayloadResult.postPayloadState;
214
218
  const postPayloadStateRoot = postPayloadState.hashTreeRoot();
215
- if (!byteArrayEquals(envelope.stateRoot, postPayloadStateRoot)) {
216
- throw new PayloadError({
217
- code: PayloadErrorCode.STATE_TRANSITION_ERROR,
218
- message: `Envelope state root mismatch expected=${toRootHex(envelope.stateRoot)} actual=${toRootHex(postPayloadStateRoot)}`,
219
- });
220
- }
221
219
 
222
220
  // 6. Persist payload envelope to hot DB (performed asynchronously to avoid blocking)
223
221
  this.unfinalizedPayloadEnvelopeWrites.push(payloadInput).catch((e) => {
224
222
  if (!isQueueErrorAborted(e)) {
225
223
  this.logger.error(
226
224
  "Error pushing payload envelope to unfinalized write queue",
227
- {slot: envelope.slot, blockRoot: blockRootHex},
225
+ {slot: envelope.payload.slotNumber, blockRoot: blockRootHex},
228
226
  e as Error
229
227
  );
230
228
  }
@@ -240,10 +238,10 @@ export async function importExecutionPayload(
240
238
  );
241
239
 
242
240
  // 8. Cache payload state
243
- this.regen.processPayloadState(postPayloadState);
241
+ this.regen.processState(blockRootHex, postPayloadState);
244
242
  if (postPayloadState.slot % SLOTS_PER_EPOCH === 0) {
245
243
  const {checkpoint} = postPayloadState.computeAnchorCheckpoint();
246
- this.regen.addCheckpointState(checkpoint, postPayloadState, true);
244
+ this.regen.addCheckpointState(checkpoint, postPayloadState);
247
245
  }
248
246
 
249
247
  // 9. Record metrics for payload envelope and column sources
@@ -252,26 +250,22 @@ export async function importExecutionPayload(
252
250
  this.metrics?.importPayload.columnsBySource.inc({source});
253
251
  }
254
252
 
255
- const stateRootHex = toRootHex(envelope.stateRoot);
256
-
257
253
  // 10. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
258
- if (this.clock.currentSlot - envelope.slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
254
+ if (this.clock.currentSlot - envelope.payload.slotNumber < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
259
255
  this.emitter.emit(routes.events.EventType.executionPayload, {
260
- slot: envelope.slot,
256
+ slot: envelope.payload.slotNumber,
261
257
  builderIndex: envelope.builderIndex,
262
258
  blockHash: blockHashHex,
263
259
  blockRoot: blockRootHex,
264
- stateRoot: stateRootHex,
265
260
  // TODO GLOAS: revisit once we support optimistic import
266
261
  executionOptimistic: false,
267
262
  });
268
263
  }
269
264
 
270
265
  this.logger.verbose("Execution payload imported", {
271
- slot: envelope.slot,
266
+ slot: envelope.payload.slotNumber,
272
267
  builderIndex: envelope.builderIndex,
273
268
  blockRoot: blockRootHex,
274
269
  blockHash: blockHashHex,
275
- stateRoot: stateRootHex,
276
270
  });
277
271
  }
@@ -88,7 +88,7 @@ export async function processBlocks(
88
88
  const fullyVerifiedBlocks = relevantBlocks.map(
89
89
  (block, i): FullyVerifiedBlock => ({
90
90
  blockInput: block,
91
- postBlockState: postStates[i],
91
+ postState: postStates[i],
92
92
  postPayloadState: null,
93
93
  parentBlockSlot: parentSlots[i],
94
94
  executionStatus: executionStatuses[i],
@@ -73,6 +73,7 @@ export class PayloadEnvelopeInput {
73
73
  private timeCreatedSec: number;
74
74
 
75
75
  private readonly payloadEnvelopeDataPromise: PromiseParts<gloas.SignedExecutionPayloadEnvelope>;
76
+ private readonly allDataPromise: PromiseParts<gloas.DataColumnSidecar[]>;
76
77
  private readonly columnsDataPromise: PromiseParts<gloas.DataColumnSidecar[]>;
77
78
 
78
79
  state: PayloadEnvelopeInputState;
@@ -97,6 +98,7 @@ export class PayloadEnvelopeInput {
97
98
  this.custodyColumns = props.custodyColumns;
98
99
  this.timeCreatedSec = props.timeCreatedSec;
99
100
  this.payloadEnvelopeDataPromise = createPromise();
101
+ this.allDataPromise = createPromise();
100
102
  this.columnsDataPromise = createPromise();
101
103
 
102
104
  const noBlobs = props.bid.blobKzgCommitments.length === 0;
@@ -105,6 +107,7 @@ export class PayloadEnvelopeInput {
105
107
 
106
108
  if (hasAllData) {
107
109
  this.state = {hasPayload: false, hasAllData: true, hasComputedAllData: true};
110
+ this.allDataPromise.resolve(this.getSampledColumns());
108
111
  this.columnsDataPromise.resolve(this.getSampledColumns());
109
112
  } else {
110
113
  this.state = {hasPayload: false, hasAllData: false, hasComputedAllData: false};
@@ -203,6 +206,12 @@ export class PayloadEnvelopeInput {
203
206
  return true;
204
207
  }
205
208
 
209
+ // Resolve allDataPromise on the first transition to hasAllData (either sampled-complete or
210
+ // reconstruction-threshold branch). Guarded so it fires exactly once.
211
+ if (!this.state.hasAllData && hasAllData) {
212
+ this.allDataPromise.resolve(sampledColumns);
213
+ }
214
+
206
215
  if (hasComputedAllData) {
207
216
  this.columnsDataPromise.resolve(sampledColumns);
208
217
  }
@@ -315,6 +324,24 @@ export class PayloadEnvelopeInput {
315
324
  return this.state.hasComputedAllData;
316
325
  }
317
326
 
327
+ waitForAllData(timeout: number, signal?: AbortSignal): Promise<gloas.DataColumnSidecar[]> {
328
+ if (this.state.hasAllData) {
329
+ return Promise.resolve(this.getSampledColumns());
330
+ }
331
+ return withTimeout(() => this.allDataPromise.promise, timeout, signal);
332
+ }
333
+
334
+ async waitForEnvelopeAndAllData(timeout: number, signal?: AbortSignal): Promise<this> {
335
+ if (!this.state.hasPayload || !this.state.hasAllData) {
336
+ await withTimeout(
337
+ () => Promise.all([this.payloadEnvelopeDataPromise.promise, this.allDataPromise.promise]),
338
+ timeout,
339
+ signal
340
+ );
341
+ }
342
+ return this;
343
+ }
344
+
318
345
  waitForComputedAllData(timeout: number, signal?: AbortSignal): Promise<gloas.DataColumnSidecar[]> {
319
346
  if (this.state.hasComputedAllData) {
320
347
  return Promise.resolve(this.getSampledColumns());
@@ -16,6 +16,11 @@ enum PayloadEnvelopeImportStatus {
16
16
 
17
17
  /**
18
18
  * PayloadEnvelopeProcessor processes payload envelope jobs in a queued fashion, one after the other.
19
+ *
20
+ * Jobs are enqueued only on envelope arrival (gossip or API). The envelope may reach us before
21
+ * the sampled data columns; importExecutionPayload awaits `verifyPayloadsDataAvailability`
22
+ * internally, so a queued job can pend for up to `PAYLOAD_DATA_AVAILABILITY_TIMEOUT` while
23
+ * waiting for columns. Duplicate triggers for the same payloadInput are deduped via `importStatus`.
19
24
  */
20
25
  export class PayloadEnvelopeProcessor {
21
26
  readonly jobQueue: JobItemQueue<[PayloadEnvelopeInput, ImportPayloadOpts], void>;
@@ -25,7 +30,7 @@ export class PayloadEnvelopeProcessor {
25
30
  this.jobQueue = new JobItemQueue<[PayloadEnvelopeInput, ImportPayloadOpts], void>(
26
31
  (payloadInput, opts) => {
27
32
  this.importStatus.set(payloadInput, PayloadEnvelopeImportStatus.importing);
28
- return importExecutionPayload.call(chain, payloadInput, opts);
33
+ return importExecutionPayload.call(chain, payloadInput, signal, opts);
29
34
  },
30
35
  {maxLength: QUEUE_MAX_LENGTH, noYieldIfOneItem: true, signal},
31
36
  metrics?.payloadEnvelopeProcessorQueue ?? undefined
@@ -33,10 +38,6 @@ export class PayloadEnvelopeProcessor {
33
38
  }
34
39
 
35
40
  async processPayloadEnvelopeJob(payloadInput: PayloadEnvelopeInput, opts: ImportPayloadOpts = {}): Promise<void> {
36
- if (!payloadInput.isComplete()) {
37
- return;
38
- }
39
-
40
41
  if (this.importStatus.get(payloadInput) !== undefined) {
41
42
  return;
42
43
  }
@@ -90,7 +90,7 @@ export type ImportBlockOpts = {
90
90
 
91
91
  type FullyVerifiedBlockBase = {
92
92
  blockInput: IBlockInput;
93
- postBlockState: IBeaconStateView;
93
+ postState: IBeaconStateView;
94
94
  parentBlockSlot: Slot;
95
95
  proposerBalanceDelta: number;
96
96
  dataAvailabilityStatus: DataAvailabilityStatus;
@@ -0,0 +1,38 @@
1
+ import {DataAvailabilityStatus} from "@lodestar/state-transition";
2
+ import {gloas} from "@lodestar/types";
3
+ import {PayloadEnvelopeInput} from "../seenCache/seenPayloadEnvelopeInput.js";
4
+
5
+ // we can now wait for full 12 seconds because sync and reconstruction will try pulling
6
+ // the data columns from the network anyway while the envelope is being processed
7
+ export const PAYLOAD_DATA_AVAILABILITY_TIMEOUT = 12_000;
8
+
9
+ /**
10
+ * Verifies that all payload envelope inputs have their data columns available.
11
+ * - Waits a max of PAYLOAD_DATA_AVAILABILITY_TIMEOUT for all data to be available
12
+ * - Returns the time at which all data was available
13
+ * - Returns the data availability status for each payload input
14
+ */
15
+ export async function verifyPayloadsDataAvailability(
16
+ payloadInputs: PayloadEnvelopeInput[],
17
+ signal: AbortSignal
18
+ ): Promise<{
19
+ dataAvailabilityStatuses: DataAvailabilityStatus[];
20
+ availableTime: number;
21
+ }> {
22
+ const promises: Promise<gloas.DataColumnSidecar[]>[] = [];
23
+ for (const payloadInput of payloadInputs) {
24
+ if (!payloadInput.hasAllData()) {
25
+ promises.push(payloadInput.waitForAllData(PAYLOAD_DATA_AVAILABILITY_TIMEOUT, signal));
26
+ }
27
+ }
28
+ await Promise.all(promises);
29
+
30
+ const availableTime = Math.max(0, Math.max(...payloadInputs.map((payloadInput) => payloadInput.getTimeComplete())));
31
+ const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) =>
32
+ payloadInput.getBlobKzgCommitments().length === 0
33
+ ? DataAvailabilityStatus.NotRequired
34
+ : DataAvailabilityStatus.Available
35
+ );
36
+
37
+ return {dataAvailabilityStatuses, availableTime};
38
+ }
@@ -2,10 +2,9 @@ import path from "node:path";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Type} from "@chainsafe/ssz";
4
4
  import {BeaconConfig} from "@lodestar/config";
5
- import {CheckpointWithPayloadStatus, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
5
+ import {CheckpointWithHex, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
6
6
  import {LoggerNode} from "@lodestar/logger/node";
7
7
  import {
8
- BUILDER_INDEX_SELF_BUILD,
9
8
  EFFECTIVE_BALANCE_INCREMENT,
10
9
  type ForkPostFulu,
11
10
  type ForkPostGloas,
@@ -24,7 +23,6 @@ import {
24
23
  getEffectiveBalancesFromStateBytes,
25
24
  isStatePostAltair,
26
25
  isStatePostElectra,
27
- isStatePostGloas,
28
26
  } from "@lodestar/state-transition";
29
27
  import {
30
28
  BeaconBlock,
@@ -93,8 +91,8 @@ import {
93
91
  } from "./opPools/index.js";
94
92
  import {IChainOptions} from "./options.js";
95
93
  import {PrepareNextSlotScheduler} from "./prepareNextSlot.js";
96
- import {computeNewStateRoot, computePayloadEnvelopeStateRoot} from "./produceBlock/computeNewStateRoot.js";
97
- import {AssembledBlockType, BlockType, ProduceFullGloas, ProduceResult} from "./produceBlock/index.js";
94
+ import {computeNewStateRoot} from "./produceBlock/computeNewStateRoot.js";
95
+ import {AssembledBlockType, BlockType, ProduceResult} from "./produceBlock/index.js";
98
96
  import {BlockAttributes, produceBlockBody, produceCommonBlockBody} from "./produceBlock/produceBlockBody.js";
99
97
  import {QueuedStateRegenerator, RegenCaller} from "./regen/index.js";
100
98
  import {ReprocessController} from "./reprocess.js";
@@ -118,7 +116,7 @@ import {DbCPStateDatastore, checkpointToDatastoreKey} from "./stateCache/datasto
118
116
  import {FileCPStateDatastore} from "./stateCache/datastore/file.js";
119
117
  import {CPStateDatastore} from "./stateCache/datastore/types.js";
120
118
  import {FIFOBlockStateCache} from "./stateCache/fifoBlockStateCache.js";
121
- import {PersistentCheckpointStateCache, fcCheckpointToHexPayload} from "./stateCache/persistentCheckpointsCache.js";
119
+ import {PersistentCheckpointStateCache} from "./stateCache/persistentCheckpointsCache.js";
122
120
  import {CheckpointStateCache} from "./stateCache/types.js";
123
121
  import {ValidatorMonitor} from "./validatorMonitor.js";
124
122
 
@@ -682,11 +680,11 @@ export class BeaconChain implements IBeaconChain {
682
680
  }
683
681
 
684
682
  getStateByCheckpoint(
685
- checkpoint: CheckpointWithPayloadStatus
683
+ checkpoint: CheckpointWithHex
686
684
  ): {state: IBeaconStateView; executionOptimistic: boolean; finalized: boolean} | null {
687
685
  // finalized or justified checkpoint states maynot be available with PersistentCheckpointStateCache, use getCheckpointStateOrBytes() api to get Uint8Array
688
- const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint);
689
- const cachedStateCtx = this.regen.getCheckpointStateSync(checkpointHexPayload);
686
+ const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
687
+ const cachedStateCtx = this.regen.getCheckpointStateSync(checkpointHex);
690
688
  if (cachedStateCtx) {
691
689
  const block = this.forkChoice.getBlockDefaultStatus(
692
690
  ssz.phase0.BeaconBlockHeader.hashTreeRoot(cachedStateCtx.latestBlockHeader)
@@ -703,10 +701,10 @@ export class BeaconChain implements IBeaconChain {
703
701
  }
704
702
 
705
703
  async getStateOrBytesByCheckpoint(
706
- checkpoint: CheckpointWithPayloadStatus
704
+ checkpoint: CheckpointWithHex
707
705
  ): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
708
- const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint);
709
- const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHexPayload);
706
+ const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
707
+ const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHex);
710
708
  if (cachedStateCtx) {
711
709
  const block = this.forkChoice.getBlockDefaultStatus(checkpoint.root);
712
710
  const finalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
@@ -1062,7 +1060,7 @@ export class BeaconChain implements IBeaconChain {
1062
1060
  body,
1063
1061
  } as AssembledBlockType<T>;
1064
1062
 
1065
- const {newStateRoot, proposerReward, postBlockState} = computeNewStateRoot(this.metrics, state, block);
1063
+ const {newStateRoot, proposerReward} = computeNewStateRoot(this.metrics, state, block);
1066
1064
  block.stateRoot = newStateRoot;
1067
1065
  const blockRoot =
1068
1066
  produceResult.type === BlockType.Full
@@ -1071,26 +1069,9 @@ export class BeaconChain implements IBeaconChain {
1071
1069
  const blockRootHex = toRootHex(blockRoot);
1072
1070
 
1073
1071
  const fork = this.config.getForkName(slot);
1074
- if (isForkPostGloas(fork)) {
1075
- // TODO GLOAS: we should retire BlockType post-gloas, may need a new enum for self vs non-self built
1076
- if (produceResult.type !== BlockType.Full) {
1077
- throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`);
1078
- }
1079
-
1080
- const gloasResult = produceResult as ProduceFullGloas;
1081
- const envelope: gloas.ExecutionPayloadEnvelope = {
1082
- payload: gloasResult.executionPayload,
1083
- executionRequests: gloasResult.executionRequests,
1084
- builderIndex: BUILDER_INDEX_SELF_BUILD,
1085
- beaconBlockRoot: blockRoot,
1086
- slot,
1087
- stateRoot: ZERO_HASH,
1088
- };
1089
- if (!isStatePostGloas(postBlockState)) {
1090
- throw Error(`Expected gloas+ post-state for execution payload envelope, got fork=${postBlockState.forkName}`);
1091
- }
1092
- const payloadEnvelopeStateRoot = computePayloadEnvelopeStateRoot(this.metrics, postBlockState, envelope);
1093
- gloasResult.payloadEnvelopeStateRoot = payloadEnvelopeStateRoot;
1072
+ // TODO GLOAS: we should retire BlockType post-gloas, may need a new enum for self vs non-self built
1073
+ if (isForkPostGloas(fork) && produceResult.type !== BlockType.Full) {
1074
+ throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`);
1094
1075
  }
1095
1076
 
1096
1077
  // Track the produced block for consensus broadcast validations, later validation, etc.
@@ -1296,7 +1277,7 @@ export class BeaconChain implements IBeaconChain {
1296
1277
  * @param blockState state that declares justified checkpoint `checkpoint`
1297
1278
  */
1298
1279
  private justifiedBalancesGetter(
1299
- checkpoint: CheckpointWithPayloadStatus,
1280
+ checkpoint: CheckpointWithHex,
1300
1281
  blockState: IBeaconStateView
1301
1282
  ): EffectiveBalanceIncrements {
1302
1283
  this.metrics?.balancesCache.requests.inc();
@@ -1335,11 +1316,11 @@ export class BeaconChain implements IBeaconChain {
1335
1316
  * @param blockState state that declares justified checkpoint `checkpoint`
1336
1317
  */
1337
1318
  private closestJustifiedBalancesStateToCheckpoint(
1338
- checkpoint: CheckpointWithPayloadStatus,
1319
+ checkpoint: CheckpointWithHex,
1339
1320
  blockState: IBeaconStateView
1340
1321
  ): {state: IBeaconStateView; stateId: string; shouldWarn: boolean} {
1341
- const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint);
1342
- const state = this.regen.getCheckpointStateSync(checkpointHexPayload);
1322
+ const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
1323
+ const state = this.regen.getCheckpointStateSync(checkpointHex);
1343
1324
  if (state) {
1344
1325
  return {state, stateId: "checkpoint_state", shouldWarn: false};
1345
1326
  }
@@ -1350,10 +1331,7 @@ export class BeaconChain implements IBeaconChain {
1350
1331
  }
1351
1332
 
1352
1333
  // Find a state in the same branch of checkpoint at same epoch. Balances should exactly the same
1353
- for (const descendantBlock of this.forkChoice.forwardIterateDescendants(
1354
- checkpoint.rootHex,
1355
- checkpoint.payloadStatus
1356
- )) {
1334
+ for (const descendantBlock of this.forkChoice.forwardIterateDescendantsDefaultStatus(checkpoint.rootHex)) {
1357
1335
  if (computeEpochAtSlot(descendantBlock.slot) === checkpoint.epoch) {
1358
1336
  const descendantBlockState = this.regen.getStateSync(descendantBlock.stateRoot);
1359
1337
  if (descendantBlockState) {
@@ -1369,10 +1347,7 @@ export class BeaconChain implements IBeaconChain {
1369
1347
 
1370
1348
  // Find a state in the same branch of checkpoint at a latter epoch. Balances are not the same, but should be close
1371
1349
  // Note: must call .forwardIterateDescendants() again since nodes are not sorted
1372
- for (const descendantBlock of this.forkChoice.forwardIterateDescendants(
1373
- checkpoint.rootHex,
1374
- checkpoint.payloadStatus
1375
- )) {
1350
+ for (const descendantBlock of this.forkChoice.forwardIterateDescendantsDefaultStatus(checkpoint.rootHex)) {
1376
1351
  if (computeEpochAtSlot(descendantBlock.slot) > checkpoint.epoch) {
1377
1352
  const descendantBlockState = this.regen.getStateSync(descendantBlock.stateRoot);
1378
1353
  if (descendantBlockState) {
@@ -1476,7 +1451,7 @@ export class BeaconChain implements IBeaconChain {
1476
1451
  this.seenContributionAndProof.prune(head.slot);
1477
1452
  }
1478
1453
 
1479
- private onForkChoiceJustified(this: BeaconChain, cp: CheckpointWithPayloadStatus): void {
1454
+ private onForkChoiceJustified(this: BeaconChain, cp: CheckpointWithHex): void {
1480
1455
  this.logger.verbose("Fork choice justified", {epoch: cp.epoch, root: cp.rootHex});
1481
1456
  }
1482
1457
 
@@ -1487,7 +1462,7 @@ export class BeaconChain implements IBeaconChain {
1487
1462
  });
1488
1463
  }
1489
1464
 
1490
- private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithPayloadStatus): Promise<void> {
1465
+ private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithHex): Promise<void> {
1491
1466
  this.logger.verbose("Fork choice finalized", {epoch: cp.epoch, root: cp.rootHex});
1492
1467
  const finalizedSlot = computeStartSlotAtEpoch(cp.epoch);
1493
1468
  this.seenBlockProposers.prune(finalizedSlot);
@@ -1528,7 +1503,7 @@ export class BeaconChain implements IBeaconChain {
1528
1503
  }
1529
1504
  }
1530
1505
 
1531
- private async updateValidatorsCustodyRequirement(finalizedCheckpoint: CheckpointWithPayloadStatus): Promise<void> {
1506
+ private async updateValidatorsCustodyRequirement(finalizedCheckpoint: CheckpointWithHex): Promise<void> {
1532
1507
  if (this.custodyConfig.targetCustodyGroupCount === this.config.NUMBER_OF_CUSTODY_GROUPS) {
1533
1508
  // Custody requirements can only be increased, we can disable dynamic custody updates
1534
1509
  // if the node already maintains custody of all custody groups in case it is configured
@@ -1,12 +1,13 @@
1
1
  import {EventEmitter} from "node:events";
2
2
  import {StrictEventEmitter} from "strict-event-emitter-types";
3
3
  import {routes} from "@lodestar/api";
4
- import {CheckpointWithPayloadStatus} from "@lodestar/fork-choice";
4
+ import {CheckpointWithHex} from "@lodestar/fork-choice";
5
5
  import {IBeaconStateView} from "@lodestar/state-transition";
6
6
  import {DataColumnSidecar, RootHex, deneb, phase0} from "@lodestar/types";
7
7
  import {SignedExecutionPayloadEnvelope} from "@lodestar/types/gloas";
8
8
  import {PeerIdStr} from "../util/peerId.js";
9
9
  import {BlockInputSource, IBlockInput} from "./blocks/blockInput/types.js";
10
+ import {PayloadEnvelopeInput} from "./blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
10
11
 
11
12
  /**
12
13
  * Important chain events that occur during normal chain operation.
@@ -76,6 +77,11 @@ export enum ChainEvent {
76
77
  * cut-off window passes for waiting on gossip
77
78
  */
78
79
  incompleteBlockInput = "incompleteBlockInput",
80
+ /**
81
+ * Post-gloas: trigger BlockInputSync for payload envelopes whose envelope and/or sampled columns are partially
82
+ * received via gossip but are not complete by time the cut-off window passes for waiting on gossip
83
+ */
84
+ incompletePayloadEnvelope = "incompletePayloadEnvelope",
79
85
  }
80
86
 
81
87
  export type HeadEventData = routes.events.EventData[routes.events.EventType.head];
@@ -93,14 +99,19 @@ export type ChainEventData = {
93
99
  };
94
100
  [ChainEvent.unknownBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
95
101
  [ChainEvent.incompleteBlockInput]: {blockInput: IBlockInput; peer: PeerIdStr; source: BlockInputSource};
102
+ [ChainEvent.incompletePayloadEnvelope]: {
103
+ payloadInput: PayloadEnvelopeInput;
104
+ peer: PeerIdStr;
105
+ source: BlockInputSource;
106
+ };
96
107
  [ChainEvent.unknownEnvelopeBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
97
108
  };
98
109
 
99
110
  export type IChainEvents = ApiEvents & {
100
111
  [ChainEvent.checkpoint]: (checkpoint: phase0.Checkpoint, state: IBeaconStateView) => void;
101
112
 
102
- [ChainEvent.forkChoiceJustified]: (checkpoint: CheckpointWithPayloadStatus) => void;
103
- [ChainEvent.forkChoiceFinalized]: (checkpoint: CheckpointWithPayloadStatus) => void;
113
+ [ChainEvent.forkChoiceJustified]: (checkpoint: CheckpointWithHex) => void;
114
+ [ChainEvent.forkChoiceFinalized]: (checkpoint: CheckpointWithHex) => void;
104
115
 
105
116
  [ChainEvent.updateTargetCustodyGroupCount]: (targetGroupCount: number) => void;
106
117
 
@@ -116,6 +127,7 @@ export type IChainEvents = ApiEvents & {
116
127
  [ChainEvent.envelopeUnknownBlock]: (data: ChainEventData[ChainEvent.envelopeUnknownBlock]) => void;
117
128
  [ChainEvent.unknownBlockRoot]: (data: ChainEventData[ChainEvent.unknownBlockRoot]) => void;
118
129
  [ChainEvent.incompleteBlockInput]: (data: ChainEventData[ChainEvent.incompleteBlockInput]) => void;
130
+ [ChainEvent.incompletePayloadEnvelope]: (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]) => void;
119
131
  [ChainEvent.unknownEnvelopeBlockRoot]: (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRoot]) => void;
120
132
  };
121
133
 
@@ -147,6 +147,10 @@ export enum AttestationErrorCode {
147
147
  * Gloas: Current slot attestation is marking payload as present
148
148
  */
149
149
  PREMATURELY_INDICATED_PAYLOAD_PRESENT = "ATTESTATION_ERROR_PREMATURELY_INDICATED_PAYLOAD_PRESENT",
150
+ /**
151
+ * Gloas: index-1 attestation but the execution payload has not been seen yet
152
+ */
153
+ EXECUTION_PAYLOAD_NOT_SEEN = "ATTESTATION_ERROR_EXECUTION_PAYLOAD_NOT_SEEN",
150
154
  }
151
155
 
152
156
  export type AttestationErrorType =
@@ -185,7 +189,8 @@ export type AttestationErrorType =
185
189
  | {code: AttestationErrorCode.NON_ZERO_ATTESTATION_DATA_INDEX}
186
190
  | {code: AttestationErrorCode.ATTESTER_NOT_IN_COMMITTEE}
187
191
  | {code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE; attDataIndex: number}
188
- | {code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT};
192
+ | {code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT}
193
+ | {code: AttestationErrorCode.EXECUTION_PAYLOAD_NOT_SEEN; beaconBlockRoot: RootHex};
189
194
 
190
195
  export class AttestationError extends GossipActionError<AttestationErrorType> {
191
196
  getMetadata(): Record<string, string | number | null> {
@@ -8,7 +8,6 @@ import {
8
8
  ProtoArray,
9
9
  ProtoBlock,
10
10
  ForkChoiceOpts as RawForkChoiceOpts,
11
- getCheckpointPayloadStatus,
12
11
  } from "@lodestar/fork-choice";
13
12
  import {ZERO_HASH_HEX} from "@lodestar/params";
14
13
  import {
@@ -104,12 +103,6 @@ export function initializeForkChoiceFromFinalizedState(
104
103
 
105
104
  const isForkPostGloas = computeEpochAtSlot(state.slot) >= config.GLOAS_FORK_EPOCH;
106
105
 
107
- // Determine justified checkpoint payload status
108
- const justifiedPayloadStatus = getCheckpointPayloadStatus(config, state, justifiedCheckpoint.epoch);
109
-
110
- // Determine finalized checkpoint payload status
111
- const finalizedPayloadStatus = getCheckpointPayloadStatus(config, state, finalizedCheckpoint.epoch);
112
-
113
106
  return new forkchoiceConstructor(
114
107
  config,
115
108
 
@@ -119,8 +112,6 @@ export function initializeForkChoiceFromFinalizedState(
119
112
  finalizedCheckpoint,
120
113
  justifiedBalances,
121
114
  justifiedBalancesGetter,
122
- justifiedPayloadStatus,
123
- finalizedPayloadStatus,
124
115
  {
125
116
  onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
126
117
  onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
@@ -146,7 +137,9 @@ export function initializeForkChoiceFromFinalizedState(
146
137
 
147
138
  ...(isStatePostBellatrix(state) && state.isExecutionStateType && state.isMergeTransitionComplete
148
139
  ? {
149
- executionPayloadBlockHash: toRootHex(state.latestBlockHash),
140
+ executionPayloadBlockHash: isStatePostGloas(state)
141
+ ? toRootHex(state.latestBlockHash)
142
+ : toRootHex(state.latestExecutionPayloadHeader.blockHash),
150
143
  // TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed
151
144
  // latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found.
152
145
  executionPayloadNumber: isStatePostGloas(state) ? 0 : state.payloadBlockNumber,
@@ -202,19 +195,12 @@ export function initializeForkChoiceFromUnfinalizedState(
202
195
 
203
196
  const isForkPostGloas = computeEpochAtSlot(unfinalizedState.slot) >= config.GLOAS_FORK_EPOCH;
204
197
 
205
- // For unfinalized state, use getCheckpointPayloadStatus to determine the correct status.
206
- // It checks state.execution_payload_availability to determine EMPTY vs FULL.
207
- const justifiedPayloadStatus = getCheckpointPayloadStatus(config, unfinalizedState, justifiedCheckpoint.epoch);
208
- const finalizedPayloadStatus = getCheckpointPayloadStatus(config, unfinalizedState, finalizedCheckpoint.epoch);
209
-
210
198
  const store = new ForkChoiceStore(
211
199
  currentSlot,
212
200
  justifiedCheckpoint,
213
201
  finalizedCheckpoint,
214
202
  justifiedBalances,
215
203
  justifiedBalancesGetter,
216
- justifiedPayloadStatus,
217
- finalizedPayloadStatus,
218
204
  {
219
205
  onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
220
206
  onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
@@ -243,7 +229,9 @@ export function initializeForkChoiceFromUnfinalizedState(
243
229
  unfinalizedState.isExecutionStateType &&
244
230
  unfinalizedState.isMergeTransitionComplete
245
231
  ? {
246
- executionPayloadBlockHash: toRootHex(unfinalizedState.latestBlockHash),
232
+ executionPayloadBlockHash: isStatePostGloas(unfinalizedState)
233
+ ? toRootHex(unfinalizedState.latestBlockHash)
234
+ : toRootHex(unfinalizedState.latestExecutionPayloadHeader.blockHash),
247
235
  // TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed
248
236
  // latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found.
249
237
  executionPayloadNumber: isStatePostGloas(unfinalizedState) ? 0 : unfinalizedState.payloadBlockNumber,
@@ -1,6 +1,6 @@
1
1
  import {Type} from "@chainsafe/ssz";
2
2
  import {BeaconConfig} from "@lodestar/config";
3
- import {CheckpointWithHex, CheckpointWithPayloadStatus, IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
3
+ import {CheckpointWithHex, IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
4
4
  import {EpochShuffling, IBeaconStateView, PubkeyCache} from "@lodestar/state-transition";
5
5
  import {
6
6
  BeaconBlock,
@@ -195,7 +195,7 @@ export interface IBeaconChain {
195
195
  ): {state: IBeaconStateView; executionOptimistic: boolean; finalized: boolean} | null;
196
196
  /** Return state bytes by checkpoint */
197
197
  getStateOrBytesByCheckpoint(
198
- checkpoint: CheckpointWithPayloadStatus
198
+ checkpoint: CheckpointWithHex
199
199
  ): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null>;
200
200
 
201
201
  /**