@lodestar/beacon-node 1.43.0-dev.66d2c102e3 → 1.43.0-dev.6f485b1b61

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 (195) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +13 -3
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +45 -2
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/debug/index.d.ts.map +1 -1
  8. package/lib/api/impl/debug/index.js +0 -1
  9. package/lib/api/impl/debug/index.js.map +1 -1
  10. package/lib/api/impl/validator/index.d.ts.map +1 -1
  11. package/lib/api/impl/validator/index.js +68 -2
  12. package/lib/api/impl/validator/index.js.map +1 -1
  13. package/lib/chain/blocks/blockInput/blockInput.d.ts +3 -0
  14. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  15. package/lib/chain/blocks/blockInput/blockInput.js +4 -1
  16. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  17. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  18. package/lib/chain/blocks/importBlock.js +16 -31
  19. package/lib/chain/blocks/importBlock.js.map +1 -1
  20. package/lib/chain/blocks/importExecutionPayload.d.ts +9 -3
  21. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  22. package/lib/chain/blocks/importExecutionPayload.js +37 -15
  23. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  24. package/lib/chain/blocks/index.d.ts.map +1 -1
  25. package/lib/chain/blocks/index.js +35 -21
  26. package/lib/chain/blocks/index.js.map +1 -1
  27. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +12 -1
  28. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  29. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +28 -2
  30. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  31. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +17 -0
  32. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  33. package/lib/chain/blocks/types.d.ts +2 -1
  34. package/lib/chain/blocks/types.d.ts.map +1 -1
  35. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  36. package/lib/chain/blocks/utils/chainSegment.js +8 -0
  37. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  38. package/lib/chain/blocks/verifyBlock.d.ts +2 -1
  39. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  40. package/lib/chain/blocks/verifyBlock.js +30 -12
  41. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  42. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +0 -4
  43. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  44. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -2
  45. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  46. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +2 -1
  47. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  48. package/lib/chain/blocks/verifyBlocksSanityChecks.js +16 -7
  49. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  50. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  51. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  52. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +10 -6
  53. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  54. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  55. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
  56. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  57. package/lib/chain/chain.d.ts.map +1 -1
  58. package/lib/chain/chain.js +25 -8
  59. package/lib/chain/chain.js.map +1 -1
  60. package/lib/chain/emitter.d.ts +0 -11
  61. package/lib/chain/emitter.d.ts.map +1 -1
  62. package/lib/chain/emitter.js +0 -4
  63. package/lib/chain/emitter.js.map +1 -1
  64. package/lib/chain/errors/proposerPreferences.d.ts +8 -1
  65. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
  66. package/lib/chain/errors/proposerPreferences.js +1 -0
  67. package/lib/chain/errors/proposerPreferences.js.map +1 -1
  68. package/lib/chain/initState.d.ts.map +1 -1
  69. package/lib/chain/initState.js +6 -1
  70. package/lib/chain/initState.js.map +1 -1
  71. package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
  72. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  73. package/lib/chain/opPools/payloadAttestationPool.js +26 -4
  74. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  75. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  76. package/lib/chain/prepareNextSlot.js +16 -18
  77. package/lib/chain/prepareNextSlot.js.map +1 -1
  78. package/lib/chain/produceBlock/produceBlockBody.d.ts +12 -3
  79. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  80. package/lib/chain/produceBlock/produceBlockBody.js +34 -22
  81. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  82. package/lib/chain/regen/queued.d.ts.map +1 -1
  83. package/lib/chain/regen/queued.js +1 -4
  84. package/lib/chain/regen/queued.js.map +1 -1
  85. package/lib/chain/regen/regen.d.ts.map +1 -1
  86. package/lib/chain/regen/regen.js +1 -4
  87. package/lib/chain/regen/regen.js.map +1 -1
  88. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +21 -11
  89. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  90. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +70 -20
  91. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  92. package/lib/chain/seenCache/seenProposerPreferences.d.ts +8 -7
  93. package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -1
  94. package/lib/chain/seenCache/seenProposerPreferences.js +11 -10
  95. package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -1
  96. package/lib/chain/validation/executionPayloadBid.js +11 -8
  97. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  98. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  99. package/lib/chain/validation/proposerPreferences.js +39 -17
  100. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  101. package/lib/network/gossip/topic.d.ts +2 -0
  102. package/lib/network/gossip/topic.d.ts.map +1 -1
  103. package/lib/network/interface.d.ts +1 -0
  104. package/lib/network/interface.d.ts.map +1 -1
  105. package/lib/network/network.d.ts +1 -0
  106. package/lib/network/network.d.ts.map +1 -1
  107. package/lib/network/network.js +5 -0
  108. package/lib/network/network.js.map +1 -1
  109. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  110. package/lib/network/processor/gossipHandlers.js +28 -10
  111. package/lib/network/processor/gossipHandlers.js.map +1 -1
  112. package/lib/network/processor/index.js +5 -5
  113. package/lib/network/processor/index.js.map +1 -1
  114. package/lib/node/nodejs.js +2 -2
  115. package/lib/node/nodejs.js.map +1 -1
  116. package/lib/node/notifier.js +1 -7
  117. package/lib/node/notifier.js.map +1 -1
  118. package/lib/sync/constants.d.ts +3 -1
  119. package/lib/sync/constants.d.ts.map +1 -1
  120. package/lib/sync/constants.js +3 -4
  121. package/lib/sync/constants.js.map +1 -1
  122. package/lib/sync/range/batch.d.ts +23 -3
  123. package/lib/sync/range/batch.d.ts.map +1 -1
  124. package/lib/sync/range/batch.js +191 -36
  125. package/lib/sync/range/batch.js.map +1 -1
  126. package/lib/sync/range/chain.d.ts +13 -2
  127. package/lib/sync/range/chain.d.ts.map +1 -1
  128. package/lib/sync/range/chain.js +61 -9
  129. package/lib/sync/range/chain.js.map +1 -1
  130. package/lib/sync/range/range.d.ts.map +1 -1
  131. package/lib/sync/range/range.js +14 -3
  132. package/lib/sync/range/range.js.map +1 -1
  133. package/lib/sync/sync.d.ts.map +1 -1
  134. package/lib/sync/sync.js +13 -0
  135. package/lib/sync/sync.js.map +1 -1
  136. package/lib/sync/unknownBlock.d.ts +7 -2
  137. package/lib/sync/unknownBlock.d.ts.map +1 -1
  138. package/lib/sync/unknownBlock.js +138 -57
  139. package/lib/sync/unknownBlock.js.map +1 -1
  140. package/lib/sync/utils/downloadByRange.d.ts +29 -8
  141. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  142. package/lib/sync/utils/downloadByRange.js +104 -42
  143. package/lib/sync/utils/downloadByRange.js.map +1 -1
  144. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  145. package/lib/sync/utils/downloadByRoot.js +10 -0
  146. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  147. package/lib/util/sszBytes.d.ts.map +1 -1
  148. package/lib/util/sszBytes.js +8 -6
  149. package/lib/util/sszBytes.js.map +1 -1
  150. package/package.json +15 -15
  151. package/src/api/impl/beacon/blocks/index.ts +16 -3
  152. package/src/api/impl/beacon/pool/index.ts +83 -1
  153. package/src/api/impl/debug/index.ts +0 -1
  154. package/src/api/impl/validator/index.ts +82 -1
  155. package/src/chain/blocks/blockInput/blockInput.ts +4 -1
  156. package/src/chain/blocks/importBlock.ts +16 -50
  157. package/src/chain/blocks/importExecutionPayload.ts +51 -20
  158. package/src/chain/blocks/index.ts +32 -15
  159. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +37 -3
  160. package/src/chain/blocks/payloadEnvelopeInput/types.ts +18 -0
  161. package/src/chain/blocks/types.ts +2 -1
  162. package/src/chain/blocks/utils/chainSegment.ts +8 -0
  163. package/src/chain/blocks/verifyBlock.ts +45 -13
  164. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
  165. package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -6
  166. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +14 -6
  167. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
  168. package/src/chain/chain.ts +29 -7
  169. package/src/chain/emitter.ts +0 -11
  170. package/src/chain/errors/proposerPreferences.ts +9 -1
  171. package/src/chain/initState.ts +9 -1
  172. package/src/chain/opPools/payloadAttestationPool.ts +29 -8
  173. package/src/chain/prepareNextSlot.ts +21 -29
  174. package/src/chain/produceBlock/produceBlockBody.ts +45 -27
  175. package/src/chain/regen/queued.ts +2 -7
  176. package/src/chain/regen/regen.ts +2 -7
  177. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +90 -24
  178. package/src/chain/seenCache/seenProposerPreferences.ts +14 -11
  179. package/src/chain/validation/executionPayloadBid.ts +11 -8
  180. package/src/chain/validation/proposerPreferences.ts +37 -18
  181. package/src/network/interface.ts +1 -0
  182. package/src/network/network.ts +11 -0
  183. package/src/network/processor/gossipHandlers.ts +38 -11
  184. package/src/network/processor/index.ts +5 -5
  185. package/src/node/nodejs.ts +2 -2
  186. package/src/node/notifier.ts +1 -8
  187. package/src/sync/constants.ts +4 -4
  188. package/src/sync/range/batch.ts +240 -42
  189. package/src/sync/range/chain.ts +77 -10
  190. package/src/sync/range/range.ts +16 -3
  191. package/src/sync/sync.ts +13 -1
  192. package/src/sync/unknownBlock.ts +170 -60
  193. package/src/sync/utils/downloadByRange.ts +166 -44
  194. package/src/sync/utils/downloadByRoot.ts +12 -0
  195. package/src/util/sszBytes.ts +8 -6
@@ -1,7 +1,8 @@
1
1
  import {routes} from "@lodestar/api";
2
- import {ExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
3
- import {isStatePostGloas} from "@lodestar/state-transition";
4
- import {fromHex} from "@lodestar/utils";
2
+ import {ExecutionStatus, PayloadExecutionStatus, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
3
+ import {DataAvailabilityStatus, isStatePostGloas} from "@lodestar/state-transition";
4
+ import {isErrorAborted} from "@lodestar/utils";
5
+ import {ZERO_HASH_HEX} from "../../constants/index.js";
5
6
  import {ExecutionPayloadStatus} from "../../execution/index.js";
6
7
  import {isQueueErrorAborted} from "../../util/queue/index.js";
7
8
  import {BeaconChain} from "../chain.js";
@@ -20,6 +21,7 @@ export enum PayloadErrorCode {
20
21
  EXECUTION_ENGINE_INVALID = "PAYLOAD_ERROR_EXECUTION_ENGINE_INVALID",
21
22
  EXECUTION_ENGINE_ERROR = "PAYLOAD_ERROR_EXECUTION_ENGINE_ERROR",
22
23
  BLOCK_NOT_IN_FORK_CHOICE = "PAYLOAD_ERROR_BLOCK_NOT_IN_FORK_CHOICE",
24
+ MISS_BLOCK_STATE = "PAYLOAD_ERROR_MISS_BLOCK_STATE",
23
25
  ENVELOPE_VERIFICATION_ERROR = "PAYLOAD_ERROR_ENVELOPE_VERIFICATION_ERROR",
24
26
  INVALID_SIGNATURE = "PAYLOAD_ERROR_INVALID_SIGNATURE",
25
27
  }
@@ -39,6 +41,10 @@ export type PayloadErrorType =
39
41
  code: PayloadErrorCode.BLOCK_NOT_IN_FORK_CHOICE;
40
42
  blockRootHex: string;
41
43
  }
44
+ | {
45
+ code: PayloadErrorCode.MISS_BLOCK_STATE;
46
+ blockRootHex: string;
47
+ }
42
48
  | {
43
49
  code: PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR;
44
50
  message: string;
@@ -60,7 +66,6 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
60
66
  switch (status) {
61
67
  case ExecutionPayloadStatus.VALID:
62
68
  return ExecutionStatus.Valid;
63
- // TODO GLOAS: Handle optimistic import for payload
64
69
  case ExecutionPayloadStatus.SYNCING:
65
70
  case ExecutionPayloadStatus.ACCEPTED:
66
71
  return ExecutionStatus.Syncing;
@@ -85,12 +90,14 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
85
90
  * 4. Verify envelope (fields against state, signature, and EL in parallel where possible)
86
91
  * 5. Persist verified payload envelope to hot DB (waits for write-queue space for backpressure)
87
92
  * 6. Update fork choice (transitions the block's PENDING variant to FULL)
88
- * 7. Record metrics for payload envelope and column sources
89
- * 8. Emit `execution_payload` event
93
+ * 7. Queue notifyForkchoiceUpdate to engine api
94
+ * 8. Record metrics for payload envelope and column sources
95
+ * 9. Emit `execution_payload` event
90
96
  */
91
97
  export async function importExecutionPayload(
92
98
  this: BeaconChain,
93
99
  payloadInput: PayloadEnvelopeInput,
100
+ dataAvailabilityStatus: DataAvailabilityStatus,
94
101
  opts: ImportPayloadOpts = {}
95
102
  ): Promise<void> {
96
103
  const signedEnvelope = payloadInput.getPayloadEnvelope();
@@ -121,12 +128,19 @@ export async function importExecutionPayload(
121
128
  }
122
129
 
123
130
  // 3. Regenerate state for envelope verification
124
- const blockState = await this.regen.getBlockSlotState(
125
- protoBlock,
126
- protoBlock.slot,
127
- {dontTransferCache: true},
128
- RegenCaller.processBlock
129
- );
131
+ const blockState = await this.regen
132
+ .getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.processBlock)
133
+ .catch(() =>
134
+ // only happen at the 1st batch of skipped slot checkpoint sync
135
+ this.regen.getClosestHeadState(protoBlock)
136
+ );
137
+
138
+ if (blockState == null) {
139
+ throw new PayloadError({
140
+ code: PayloadErrorCode.MISS_BLOCK_STATE,
141
+ blockRootHex: protoBlock.blockRoot,
142
+ });
143
+ }
130
144
  if (!isStatePostGloas(blockState)) {
131
145
  throw new PayloadError({
132
146
  code: PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR,
@@ -157,7 +171,7 @@ export async function importExecutionPayload(
157
171
  fork,
158
172
  envelope.payload,
159
173
  payloadInput.getVersionedHashes(),
160
- fromHex(protoBlock.parentRoot),
174
+ envelope.parentBeaconBlockRoot,
161
175
  envelope.executionRequests
162
176
  ),
163
177
 
@@ -219,23 +233,40 @@ export async function importExecutionPayload(
219
233
 
220
234
  // 6. Update fork choice, transitions the block's PENDING variant to FULL
221
235
  const execStatus = toForkChoiceExecutionStatus(execResult.status);
222
- this.forkChoice.onExecutionPayload(blockRootHex, blockHashHex, envelope.payload.blockNumber, execStatus);
236
+ this.forkChoice.onExecutionPayload(
237
+ blockRootHex,
238
+ blockHashHex,
239
+ envelope.payload.blockNumber,
240
+ execStatus,
241
+ dataAvailabilityStatus
242
+ );
243
+
244
+ // 7. Queue notifyForkchoiceUpdate to engine api
245
+ const head = this.forkChoice.getHead();
246
+ if (!this.opts.disableImportExecutionFcU && blockRootHex === head.blockRoot) {
247
+ const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
248
+ const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
249
+ this.executionEngine.notifyForkchoiceUpdate(fork, blockHashHex, safeBlockHash, finalizedBlockHash).catch((e) => {
250
+ if (!isErrorAborted(e) && !isQueueErrorAborted(e)) {
251
+ this.logger.error("Error pushing notifyForkchoiceUpdate()", {blockHashHex, finalizedBlockHash}, e);
252
+ }
253
+ });
254
+ }
223
255
 
224
- // 7. Record metrics for payload envelope and column sources
256
+ // 8. Record metrics for payload envelope and column sources
225
257
  this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
226
258
  for (const {source} of payloadInput.getSampledColumnsWithSource()) {
227
259
  this.metrics?.importPayload.columnsBySource.inc({source});
228
260
  }
229
261
 
230
- // 8. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
262
+ // 9. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
231
263
  if (this.clock.currentSlot - slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
232
264
  this.emitter.emit(routes.events.EventType.executionPayload, {
233
265
  slot,
234
266
  builderIndex: envelope.builderIndex,
235
267
  blockHash: blockHashHex,
236
268
  blockRoot: blockRootHex,
237
- // TODO GLOAS: revisit once we support optimistic import
238
- executionOptimistic: false,
269
+ executionOptimistic: execStatus === ExecutionStatus.Syncing,
239
270
  });
240
271
  }
241
272
 
@@ -261,6 +292,6 @@ export async function processExecutionPayload(
261
292
  signal: AbortSignal,
262
293
  opts: ImportPayloadOpts = {}
263
294
  ): Promise<void> {
264
- await verifyPayloadsDataAvailability([payloadInput], signal);
265
- await importExecutionPayload.call(this, payloadInput, opts);
295
+ const {dataAvailabilityStatuses} = await verifyPayloadsDataAvailability([payloadInput], signal);
296
+ await importExecutionPayload.call(this, payloadInput, dataAvailabilityStatuses[0], opts);
266
297
  }
@@ -65,7 +65,7 @@ export async function processBlocks(
65
65
  }
66
66
 
67
67
  try {
68
- const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, opts);
68
+ const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, payloadEnvelopes, opts);
69
69
 
70
70
  // No relevant blocks, skip verifyBlocksInEpoch()
71
71
  if (relevantBlocks.length === 0 || parentBlock === null) {
@@ -90,8 +90,14 @@ export async function processBlocks(
90
90
 
91
91
  // Fully verify a block to be imported immediately after. Does not produce any side-effects besides adding intermediate
92
92
  // states in the state cache through regen.
93
- const {postStates, dataAvailabilityStatuses, proposerBalanceDeltas, segmentExecStatus, indexedAttestationsByBlock} =
94
- await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, payloadEnvelopes, opts);
93
+ const {
94
+ postStates,
95
+ blockDAStatuses,
96
+ payloadDAStatuses,
97
+ proposerBalanceDeltas,
98
+ segmentExecStatus,
99
+ indexedAttestationsByBlock,
100
+ } = await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, payloadEnvelopes, opts);
95
101
 
96
102
  // If segmentExecStatus has lvhForkchoice then, the entire segment should be invalid
97
103
  // and we need to further propagate
@@ -103,26 +109,35 @@ export async function processBlocks(
103
109
  }
104
110
 
105
111
  const {executionStatuses} = segmentExecStatus;
106
- const fullyVerifiedBlocks = relevantBlocks.map(
107
- (block, i): FullyVerifiedBlock => ({
112
+ const verifiedBlocksBySlot = new Map<Slot, FullyVerifiedBlock>();
113
+ for (let i = 0; i < relevantBlocks.length; i++) {
114
+ const block = relevantBlocks[i];
115
+ verifiedBlocksBySlot.set(block.getBlock().message.slot, {
108
116
  blockInput: block,
109
117
  postState: postStates[i],
110
118
  parentBlockSlot: parentSlots[i],
111
119
  executionStatus: executionStatuses[i],
112
120
  // start supporting optimistic syncing/processing
113
- dataAvailabilityStatus: dataAvailabilityStatuses[i],
121
+ dataAvailabilityStatus: blockDAStatuses[i],
114
122
  proposerBalanceDelta: proposerBalanceDeltas[i],
115
123
  indexedAttestations: indexedAttestationsByBlock[i],
116
124
  // TODO: Make this param mandatory and capture in gossip
117
125
  seenTimestampSec: opts.seenTimestampSec ?? Math.floor(Date.now() / 1000),
118
- })
119
- );
126
+ });
127
+ }
120
128
 
121
- for (const fullyVerifiedBlock of fullyVerifiedBlocks) {
122
- // TODO: Consider batching importBlock too if it takes significant time
123
- await importBlock.call(this, fullyVerifiedBlock, opts);
129
+ const slotSet = new Set<Slot>(blocks.map((b) => b.getBlock().message.slot));
130
+ if (payloadEnvelopes) {
131
+ for (const slot of payloadEnvelopes.keys()) slotSet.add(slot);
132
+ }
133
+ const slots = Array.from(slotSet).sort((a, b) => a - b);
134
+ for (const slot of slots) {
135
+ const fullyVerifiedBlock = verifiedBlocksBySlot.get(slot);
136
+ if (fullyVerifiedBlock !== undefined) {
137
+ // TODO: Consider batching importBlock too if it takes significant time
138
+ await importBlock.call(this, fullyVerifiedBlock, opts);
139
+ }
124
140
 
125
- const slot = fullyVerifiedBlock.blockInput.getBlock().message.slot;
126
141
  const payloadInput = payloadEnvelopes?.get(slot);
127
142
  if (payloadInput?.hasPayloadEnvelope()) {
128
143
  if (!payloadInput.isComplete()) {
@@ -130,9 +145,11 @@ export async function processBlocks(
130
145
  throw new Error(`Payload envelope for slot ${slot} not complete after DA verification`);
131
146
  }
132
147
  // we already awaited DA in verifyBlocksInEpoch for this segment
133
- // TODO GLOAS: may need FullyVerifiedPayload here with DatAvailabilityStatus added from here
134
- // the current flow use that data from the forkchoice pending node which is not correct
135
- await importExecutionPayload.call(this, payloadInput, {validSignature: false});
148
+ const payloadDA = payloadDAStatuses.get(slot);
149
+ if (payloadDA === undefined) {
150
+ throw new Error(`Missing payload DA status for slot ${slot}`);
151
+ }
152
+ await importExecutionPayload.call(this, payloadInput, payloadDA, {validSignature: false});
136
153
  }
137
154
 
138
155
  await nextEventLoop();
@@ -4,7 +4,13 @@ import {toRootHex, withTimeout} from "@lodestar/utils";
4
4
  import {VersionedHashes} from "../../../execution/index.js";
5
5
  import {kzgCommitmentToVersionedHash} from "../../../util/blobs.js";
6
6
  import {MissingColumnMeta} from "../blockInput/types.js";
7
- import {AddPayloadEnvelopeProps, ColumnWithSource, CreateFromBlockProps, SourceMeta} from "./types.js";
7
+ import {
8
+ AddPayloadEnvelopeProps,
9
+ ColumnWithSource,
10
+ CreateFromBidProps,
11
+ CreateFromBlockProps,
12
+ SourceMeta,
13
+ } from "./types.js";
8
14
 
9
15
  export type PayloadEnvelopeInputState =
10
16
  | {
@@ -64,6 +70,7 @@ export class PayloadEnvelopeInput {
64
70
  readonly proposerIndex: ValidatorIndex;
65
71
  readonly bid: gloas.ExecutionPayloadBid;
66
72
  readonly versionedHashes: VersionedHashes;
73
+ readonly daOutOfRange: boolean;
67
74
 
68
75
  private columnsCache = new Map<ColumnIndex, ColumnWithSource>();
69
76
 
@@ -87,6 +94,7 @@ export class PayloadEnvelopeInput {
87
94
  sampledColumns: ColumnIndex[];
88
95
  custodyColumns: ColumnIndex[];
89
96
  timeCreatedSec: number;
97
+ daOutOfRange: boolean;
90
98
  }) {
91
99
  this.blockRootHex = props.blockRootHex;
92
100
  this.slot = props.slot;
@@ -97,13 +105,14 @@ export class PayloadEnvelopeInput {
97
105
  this.sampledColumns = props.sampledColumns;
98
106
  this.custodyColumns = props.custodyColumns;
99
107
  this.timeCreatedSec = props.timeCreatedSec;
108
+ this.daOutOfRange = props.daOutOfRange;
100
109
  this.payloadEnvelopeDataPromise = createPromise();
101
110
  this.allDataPromise = createPromise();
102
111
  this.columnsDataPromise = createPromise();
103
112
 
104
113
  const noBlobs = props.bid.blobKzgCommitments.length === 0;
105
114
  const noSampledColumns = props.sampledColumns.length === 0;
106
- const hasAllData = noBlobs || noSampledColumns;
115
+ const hasAllData = props.daOutOfRange || noBlobs || noSampledColumns;
107
116
 
108
117
  if (hasAllData) {
109
118
  this.state = {hasPayload: false, hasAllData: true, hasComputedAllData: true};
@@ -125,6 +134,27 @@ export class PayloadEnvelopeInput {
125
134
  sampledColumns: props.sampledColumns,
126
135
  custodyColumns: props.custodyColumns,
127
136
  timeCreatedSec: props.timeCreatedSec,
137
+ daOutOfRange: props.daOutOfRange,
138
+ });
139
+ }
140
+
141
+ /**
142
+ * Create a `PayloadEnvelopeInput` from a state's `latestExecutionPayloadBid` (the bid
143
+ * recorded in beacon state for the latest imported block). Used when seeding the cache
144
+ * for a checkpoint anchor block — we have the bid via state but not the full
145
+ * SignedBeaconBlock body.
146
+ */
147
+ static createFromBid(props: CreateFromBidProps): PayloadEnvelopeInput {
148
+ return new PayloadEnvelopeInput({
149
+ blockRootHex: props.blockRootHex,
150
+ slot: props.slot,
151
+ forkName: props.forkName,
152
+ proposerIndex: props.proposerIndex,
153
+ bid: props.bid,
154
+ sampledColumns: props.sampledColumns,
155
+ custodyColumns: props.custodyColumns,
156
+ timeCreatedSec: props.timeCreatedSec,
157
+ daOutOfRange: props.daOutOfRange,
128
158
  });
129
159
  }
130
160
 
@@ -152,6 +182,7 @@ export class PayloadEnvelopeInput {
152
182
  throw new Error("Payload envelope beacon_block_root mismatch");
153
183
  }
154
184
 
185
+ // TODO GLOAS: track source by metrics, maybe inside the seen cache
155
186
  const source: SourceMeta = {
156
187
  source: props.source,
157
188
  seenTimestampSec: props.seenTimestampSec,
@@ -306,8 +337,11 @@ export class PayloadEnvelopeInput {
306
337
  return this.state.hasAllData;
307
338
  }
308
339
 
340
+ /**
341
+ * Strictly checks missing sampled columns. Does NOT short-circuit on `state.hasAllData`.
342
+ */
309
343
  getMissingSampledColumnMeta(): MissingColumnMeta {
310
- if (this.state.hasAllData) {
344
+ if (this.state.hasComputedAllData) {
311
345
  return {missing: [], versionedHashes: this.versionedHashes};
312
346
  }
313
347
 
@@ -27,6 +27,24 @@ export type CreateFromBlockProps = {
27
27
  sampledColumns: ColumnIndex[];
28
28
  custodyColumns: ColumnIndex[];
29
29
  timeCreatedSec: number;
30
+ daOutOfRange: boolean;
31
+ };
32
+
33
+ /**
34
+ * Used to seed an entry from a state's `latestExecutionPayloadBid` (e.g., when initializing
35
+ * the chain from a checkpoint anchor state — we have the bid via the state but not the
36
+ * full SignedBeaconBlock).
37
+ */
38
+ export type CreateFromBidProps = {
39
+ blockRootHex: RootHex;
40
+ slot: number;
41
+ forkName: ForkName;
42
+ proposerIndex: number;
43
+ bid: gloas.ExecutionPayloadBid;
44
+ sampledColumns: ColumnIndex[];
45
+ custodyColumns: ColumnIndex[];
46
+ timeCreatedSec: number;
47
+ daOutOfRange: boolean;
30
48
  };
31
49
 
32
50
  export type AddPayloadEnvelopeProps = SourceMeta & {
@@ -94,7 +94,8 @@ export type ImportBlockOpts = {
94
94
  *
95
95
  * `executionStatus` reflects the outcome of execution payload verification at block-import time:
96
96
  * - pre-gloas: Valid | Syncing | PreMerge (from EL notifyNewPayload against the in-block payload)
97
- * - post-gloas: PayloadSeparated (payload arrives separately as an envelope and is imported later)
97
+ * - post-gloas: inherited from parent's chain (Valid/Syncing) by importBlock; payload arrives
98
+ * separately as an envelope and creates the FULL variant later via onExecutionPayload
98
99
  */
99
100
  export type FullyVerifiedBlock = {
100
101
  blockInput: IBlockInput;
@@ -41,6 +41,14 @@ export function assertLinearChainSegment(
41
41
  // - EMPTY variant (no envelope for slot): execution hash is unchanged
42
42
  // null only for pre-merge parents, which cannot precede gloas blocks.
43
43
  let currentExecHash: string | null = parentBlock.executionPayloadBlockHash;
44
+ // Checkpoint sync first batch: parent is the anchor PENDING whose executionPayloadBlockHash
45
+ // is the inherited parentBlockHash semantic (= grandparent's payload), not its own payload.
46
+ // If parent's own payload envelope arrives in this batch, advance currentExecHash to that
47
+ // payload's blockHash so the segment validation sees the true EL chain head.
48
+ const parentPayloadInput = payloadEnvelopes?.get(parentBlock.slot);
49
+ if (parentPayloadInput?.hasPayloadEnvelope()) {
50
+ currentExecHash = parentPayloadInput.getBlockHashHex();
51
+ }
44
52
  // Track the execution hash before the last FULL advancement so we can recover
45
53
  // if the next block reveals that envelope was orphaned.
46
54
  let prevExecHash: string | null = currentExecHash;
@@ -41,7 +41,8 @@ export async function verifyBlocksInEpoch(
41
41
  postStates: IBeaconStateView[];
42
42
  proposerBalanceDeltas: number[];
43
43
  segmentExecStatus: SegmentExecStatus;
44
- dataAvailabilityStatuses: DataAvailabilityStatus[];
44
+ blockDAStatuses: DataAvailabilityStatus[];
45
+ payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
45
46
  indexedAttestationsByBlock: IndexedAttestation[][];
46
47
  }> {
47
48
  const blocks = blockInputs.map((blockInput) => blockInput.getBlock());
@@ -116,28 +117,52 @@ export async function verifyBlocksInEpoch(
116
117
 
117
118
  // Pick the data-availability source by fork:
118
119
  // - Pre-Gloas: blob/Fulu-column data lives in IBlockInput → verifyBlocksDataAvailability.
119
- // - Post-Gloas: verifyPayloadsDataAvailability
120
- const daAvailabilityPromise =
120
+ // - Post-Gloas: verifyPayloadsDataAvailability (payload-level DA, keyed by slot).
121
+ const daAvailabilityPromise: Promise<{
122
+ blockDAStatuses: DataAvailabilityStatus[];
123
+ payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
124
+ availableTime: number;
125
+ }> =
121
126
  fork >= ForkSeq.gloas
122
127
  ? (async () => {
123
- const payloadInputsForDa: PayloadEnvelopeInput[] = [];
124
- for (const input of blockInputs) {
125
- const pi = payloadEnvelopes?.get(input.slot);
126
- if (pi !== undefined) payloadInputsForDa.push(pi);
128
+ // Validate DA for ALL payloads in the Map, not just those paired with blockInputs.
129
+ // A checkpoint-sync batch may include a payload for a slot whose block was filtered
130
+ // out of relevantBlocks (e.g., the anchor at the finalized slot); that payload still
131
+ // needs DA validation so it can be imported in processBlocks.
132
+ const payloadInputsForDa: PayloadEnvelopeInput[] =
133
+ payloadEnvelopes !== null ? Array.from(payloadEnvelopes.values()) : [];
134
+ const {dataAvailabilityStatuses, availableTime} = await verifyPayloadsDataAvailability(
135
+ payloadInputsForDa,
136
+ abortController.signal
137
+ );
138
+
139
+ const payloadDAStatuses = new Map<Slot, DataAvailabilityStatus>();
140
+ for (let i = 0; i < payloadInputsForDa.length; i++) {
141
+ payloadDAStatuses.set(payloadInputsForDa[i].slot, dataAvailabilityStatuses[i]);
127
142
  }
128
- await verifyPayloadsDataAvailability(payloadInputsForDa, abortController.signal);
129
143
  return {
130
144
  // post-gloas, DataAvailabilityStatus is NotRequired for forkChoice.onBlock() ProtoBlock
131
- dataAvailabilityStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
132
- availableTime: Date.now(),
145
+ blockDAStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
146
+ payloadDAStatuses,
147
+ availableTime,
133
148
  };
134
149
  })()
135
- : verifyBlocksDataAvailability(blockInputs, abortController.signal);
150
+ : (async () => {
151
+ const {dataAvailabilityStatuses, availableTime} = await verifyBlocksDataAvailability(
152
+ blockInputs,
153
+ abortController.signal
154
+ );
155
+ return {
156
+ blockDAStatuses: dataAvailabilityStatuses,
157
+ payloadDAStatuses: new Map<Slot, DataAvailabilityStatus>(),
158
+ availableTime,
159
+ };
160
+ })();
136
161
 
137
162
  // batch all I/O operations to reduce overhead
138
163
  const [
139
164
  segmentExecStatus,
140
- {dataAvailabilityStatuses, availableTime},
165
+ {blockDAStatuses, payloadDAStatuses, availableTime},
141
166
  {postStates, proposerBalanceDeltas, verifyStateTime},
142
167
  {verifySignaturesTime},
143
168
  ] = await Promise.all([
@@ -258,7 +283,14 @@ export async function verifyBlocksInEpoch(
258
283
  );
259
284
  }
260
285
 
261
- return {postStates, dataAvailabilityStatuses, proposerBalanceDeltas, segmentExecStatus, indexedAttestationsByBlock};
286
+ return {
287
+ postStates,
288
+ blockDAStatuses,
289
+ payloadDAStatuses,
290
+ proposerBalanceDeltas,
291
+ segmentExecStatus,
292
+ indexedAttestationsByBlock,
293
+ };
262
294
  } finally {
263
295
  abortController.abort();
264
296
  }
@@ -46,8 +46,7 @@ type VerifyBlockExecutionResponse =
46
46
  | VerifyExecutionErrorResponse
47
47
  | {executionStatus: ExecutionStatus.Valid; lvhResponse: LVHValidResponse; execError: null}
48
48
  | {executionStatus: ExecutionStatus.Syncing; lvhResponse?: LVHValidResponse; execError: null}
49
- | {executionStatus: ExecutionStatus.PreMerge; lvhResponse: undefined; execError: null}
50
- | {executionStatus: ExecutionStatus.PayloadSeparated; lvhResponse: undefined; execError: null};
49
+ | {executionStatus: ExecutionStatus.PreMerge; lvhResponse: undefined; execError: null};
51
50
 
52
51
  /**
53
52
  * Verifies 1 or more execution payloads from a linear sequence of blocks.
@@ -145,9 +144,10 @@ export async function verifyBlockExecutionPayload(
145
144
  ): Promise<VerifyBlockExecutionResponse> {
146
145
  const block = blockInput.getBlock();
147
146
 
148
- // Gloas block doesn't have execution payload. Return right away
147
+ // Gloas block doesn't have execution payload. Return Syncing as a placeholder; the actual
148
+ // status for gloas PENDING/EMPTY is derived from parent's chain in importBlock.
149
149
  if (isBlockInputNoData(blockInput)) {
150
- return {executionStatus: ExecutionStatus.PayloadSeparated, lvhResponse: undefined, execError: null};
150
+ return {executionStatus: ExecutionStatus.Syncing, lvhResponse: undefined, execError: null};
151
151
  }
152
152
 
153
153
  /** Not null if execution is enabled */
@@ -198,6 +198,7 @@ export async function verifyBlockExecutionPayload(
198
198
  executionStatus,
199
199
  latestValidExecHash: execResult.latestValidHash,
200
200
  invalidateFromParentBlockRoot: blockInput.parentRootHex,
201
+ invalidateFromParentBlockHash: toRootHex(executionPayloadEnabled.parentHash),
201
202
  };
202
203
  const execError = new BlockError(block, {
203
204
  code: BlockErrorCode.EXECUTION_ENGINE_ERROR,
@@ -281,6 +282,7 @@ function getSegmentErrorResponse(
281
282
  executionStatus: ExecutionStatus.Invalid,
282
283
  latestValidExecHash: lvhResponse.latestValidExecHash,
283
284
  invalidateFromParentBlockRoot: parentBlock.blockRoot,
285
+ invalidateFromParentBlockHash: parentBlock.executionPayloadBlockHash,
284
286
  };
285
287
  }
286
288
  }
@@ -7,6 +7,7 @@ import {IClock} from "../../util/clock.js";
7
7
  import {BlockError, BlockErrorCode} from "../errors/index.js";
8
8
  import {IChainOptions} from "../options.js";
9
9
  import {IBlockInput} from "./blockInput/types.js";
10
+ import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
10
11
  import {ImportBlockOpts} from "./types.js";
11
12
 
12
13
  /**
@@ -30,6 +31,7 @@ export function verifyBlocksSanityChecks(
30
31
  blacklistedBlocks: Map<RootHex, Slot | null>;
31
32
  },
32
33
  blocks: IBlockInput[],
34
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
33
35
  opts: ImportBlockOpts
34
36
  ): {
35
37
  relevantBlocks: IBlockInput[];
@@ -100,13 +102,21 @@ export function verifyBlocksSanityChecks(
100
102
  const parentBlockHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
101
103
  const parentBlockWithPayload = chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash);
102
104
  if (!parentBlockWithPayload) {
103
- throw new BlockError(block, {
104
- code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
105
- parentRoot,
106
- parentBlockHash,
107
- });
105
+ // Checkpoint sync: parent's FULL variant may not be in fork-choice yet because the
106
+ // anchor block is initialized with PENDING+EMPTY only. The parent's payload arrives
107
+ // in the same batch via payloadEnvelopes and will be imported by processBlocks. If
108
+ // a matching payload is in the Map, accept the parent as known.
109
+ const parentPayloadInput = payloadEnvelopes?.get(parentBlockDefaultStatus.slot);
110
+ if (parentPayloadInput?.getBlockHashHex() !== parentBlockHash) {
111
+ throw new BlockError(block, {
112
+ code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
113
+ parentRoot,
114
+ parentBlockHash,
115
+ });
116
+ }
117
+ } else {
118
+ parentBlock = parentBlockWithPayload;
108
119
  }
109
- parentBlock = parentBlockWithPayload;
110
120
  }
111
121
  // Parent is known to the fork-choice
112
122
  parentBlockSlot = parentBlock.slot;
@@ -20,7 +20,7 @@ export type VerifyExecutionPayloadEnvelopeOpts = {
20
20
  * performed outside this function, see `verifyExecutionPayloadEnvelopeSignature` and
21
21
  * `importExecutionPayload` which run both in parallel with this check.
22
22
  *
23
- * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/fork-choice.md#new-verify_execution_payload_envelope
23
+ * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.6/specs/gloas/fork-choice.md#new-verify_execution_payload_envelope
24
24
  */
25
25
  export function verifyExecutionPayloadEnvelope(
26
26
  config: BeaconConfig,
@@ -43,6 +43,11 @@ export function verifyExecutionPayloadEnvelope(
43
43
  `Envelope's block is not the latest block header envelope=${toRootHex(envelope.beaconBlockRoot)} latestBlockHeader=${toRootHex(headerRoot)}`
44
44
  );
45
45
  }
46
+ if (!byteArrayEquals(envelope.parentBeaconBlockRoot, state.latestBlockHeader.parentRoot)) {
47
+ throw new Error(
48
+ `Envelope's parent_beacon_block_root mismatch envelope=${toRootHex(envelope.parentBeaconBlockRoot)} state=${toRootHex(state.latestBlockHeader.parentRoot)}`
49
+ );
50
+ }
46
51
 
47
52
  // Verify consistency with the committed bid
48
53
  const bid = state.latestExecutionPayloadBid;
@@ -77,16 +82,19 @@ export function verifyExecutionPayloadEnvelope(
77
82
  }
78
83
  }
79
84
 
80
- // Verify the execution payload is valid
81
- if (payload.slotNumber !== state.slot) {
82
- throw new Error(`Slot mismatch between payload and state payload=${payload.slotNumber} state=${state.slot}`);
85
+ // should not use state.slot, it does not work for skipped slot checkpoint sync
86
+ const blockSlot = state.latestBlockHeader.slot;
87
+ if (payload.slotNumber !== blockSlot) {
88
+ throw new Error(
89
+ `Slot mismatch between payload and latest block header payload=${payload.slotNumber} latestBlockHeader=${blockSlot}`
90
+ );
83
91
  }
84
92
  if (!byteArrayEquals(payload.parentHash, state.latestBlockHash)) {
85
93
  throw new Error(
86
94
  `Parent hash mismatch between payload and state payload=${toRootHex(payload.parentHash)} state=${toRootHex(state.latestBlockHash)}`
87
95
  );
88
96
  }
89
- const expectedTimestamp = computeTimeAtSlot(config, state.slot, state.genesisTime);
97
+ const expectedTimestamp = computeTimeAtSlot(config, blockSlot, state.genesisTime);
90
98
  if (payload.timestamp !== expectedTimestamp) {
91
99
  throw new Error(
92
100
  `Timestamp mismatch between payload and state payload=${payload.timestamp} state=${expectedTimestamp}`
@@ -108,7 +116,7 @@ export function verifyExecutionPayloadEnvelope(
108
116
  /**
109
117
  * Verify the BLS signature of an execution payload envelope.
110
118
  *
111
- * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/fork-choice.md#new-verify_execution_payload_envelope_signature
119
+ * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.6/specs/gloas/fork-choice.md#new-verify_execution_payload_envelope_signature
112
120
  */
113
121
  export async function verifyExecutionPayloadEnvelopeSignature(
114
122
  config: BeaconConfig,
@@ -28,11 +28,14 @@ export async function verifyPayloadsDataAvailability(
28
28
  await Promise.all(promises);
29
29
 
30
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
31
+ const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) => {
32
+ if (payloadInput.daOutOfRange) {
33
+ return DataAvailabilityStatus.OutOfRange;
34
+ }
35
+ return payloadInput.getBlobKzgCommitments().length === 0
33
36
  ? DataAvailabilityStatus.NotRequired
34
- : DataAvailabilityStatus.Available
35
- );
37
+ : DataAvailabilityStatus.Available;
38
+ });
36
39
 
37
40
  return {dataAvailabilityStatuses, availableTime};
38
41
  }