@lodestar/beacon-node 1.42.0 → 1.43.0-dev.0bc48d3b54

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 (268) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +20 -0
  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 +4 -0
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  8. package/lib/api/impl/beacon/state/index.js +13 -10
  9. package/lib/api/impl/beacon/state/index.js.map +1 -1
  10. package/lib/api/impl/lodestar/attesterSlashing.d.ts +8 -0
  11. package/lib/api/impl/lodestar/attesterSlashing.d.ts.map +1 -0
  12. package/lib/api/impl/lodestar/attesterSlashing.js +29 -0
  13. package/lib/api/impl/lodestar/attesterSlashing.js.map +1 -0
  14. package/lib/api/impl/lodestar/index.d.ts.map +1 -1
  15. package/lib/api/impl/lodestar/index.js +39 -0
  16. package/lib/api/impl/lodestar/index.js.map +1 -1
  17. package/lib/api/impl/validator/index.d.ts.map +1 -1
  18. package/lib/api/impl/validator/index.js +11 -4
  19. package/lib/api/impl/validator/index.js.map +1 -1
  20. package/lib/chain/GetBlobsTracker.d.ts +1 -1
  21. package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
  22. package/lib/chain/GetBlobsTracker.js +1 -2
  23. package/lib/chain/GetBlobsTracker.js.map +1 -1
  24. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
  25. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js +2 -4
  26. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
  27. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  28. package/lib/chain/blocks/importBlock.js +29 -35
  29. package/lib/chain/blocks/importBlock.js.map +1 -1
  30. package/lib/chain/blocks/importExecutionPayload.d.ts +1 -1
  31. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  32. package/lib/chain/blocks/importExecutionPayload.js +24 -13
  33. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  34. package/lib/chain/blocks/index.js +2 -2
  35. package/lib/chain/blocks/index.js.map +1 -1
  36. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +3 -0
  37. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  38. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +20 -0
  39. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  40. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -0
  41. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
  42. package/lib/chain/blocks/payloadEnvelopeProcessor.js +6 -4
  43. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  44. package/lib/chain/blocks/types.d.ts +4 -4
  45. package/lib/chain/blocks/types.d.ts.map +1 -1
  46. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  47. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +3 -2
  48. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  49. package/lib/chain/blocks/verifyBlocksSignatures.d.ts.map +1 -1
  50. package/lib/chain/blocks/verifyBlocksSignatures.js +4 -2
  51. package/lib/chain/blocks/verifyBlocksSignatures.js.map +1 -1
  52. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +14 -0
  53. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -0
  54. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +25 -0
  55. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -0
  56. package/lib/chain/chain.d.ts +2 -1
  57. package/lib/chain/chain.d.ts.map +1 -1
  58. package/lib/chain/chain.js +30 -36
  59. package/lib/chain/chain.js.map +1 -1
  60. package/lib/chain/emitter.d.ts +13 -1
  61. package/lib/chain/emitter.d.ts.map +1 -1
  62. package/lib/chain/emitter.js +5 -0
  63. package/lib/chain/emitter.js.map +1 -1
  64. package/lib/chain/errors/attestationError.d.ts +8 -1
  65. package/lib/chain/errors/attestationError.d.ts.map +1 -1
  66. package/lib/chain/errors/attestationError.js +4 -0
  67. package/lib/chain/errors/attestationError.js.map +1 -1
  68. package/lib/chain/errors/blockError.d.ts +11 -1
  69. package/lib/chain/errors/blockError.d.ts.map +1 -1
  70. package/lib/chain/errors/blockError.js +4 -0
  71. package/lib/chain/errors/blockError.js.map +1 -1
  72. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  73. package/lib/chain/forkChoice/index.js +22 -12
  74. package/lib/chain/forkChoice/index.js.map +1 -1
  75. package/lib/chain/interface.d.ts +2 -1
  76. package/lib/chain/interface.d.ts.map +1 -1
  77. package/lib/chain/interface.js.map +1 -1
  78. package/lib/chain/lightClient/index.d.ts +2 -2
  79. package/lib/chain/lightClient/index.d.ts.map +1 -1
  80. package/lib/chain/lightClient/index.js +7 -0
  81. package/lib/chain/lightClient/index.js.map +1 -1
  82. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  83. package/lib/chain/opPools/aggregatedAttestationPool.js +5 -2
  84. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  85. package/lib/chain/opPools/executionPayloadBidPool.d.ts +2 -2
  86. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
  87. package/lib/chain/opPools/executionPayloadBidPool.js +2 -2
  88. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
  89. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  90. package/lib/chain/prepareNextSlot.js +22 -10
  91. package/lib/chain/prepareNextSlot.js.map +1 -1
  92. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +1 -7
  93. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  94. package/lib/chain/produceBlock/computeNewStateRoot.js +1 -28
  95. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  96. package/lib/chain/produceBlock/produceBlockBody.d.ts +5 -10
  97. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  98. package/lib/chain/produceBlock/produceBlockBody.js +46 -19
  99. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  100. package/lib/chain/regen/errors.d.ts +1 -11
  101. package/lib/chain/regen/errors.d.ts.map +1 -1
  102. package/lib/chain/regen/errors.js +0 -2
  103. package/lib/chain/regen/errors.js.map +1 -1
  104. package/lib/chain/regen/interface.d.ts +6 -12
  105. package/lib/chain/regen/interface.d.ts.map +1 -1
  106. package/lib/chain/regen/queued.d.ts +6 -11
  107. package/lib/chain/regen/queued.d.ts.map +1 -1
  108. package/lib/chain/regen/queued.js +8 -40
  109. package/lib/chain/regen/queued.js.map +1 -1
  110. package/lib/chain/regen/regen.d.ts +0 -5
  111. package/lib/chain/regen/regen.d.ts.map +1 -1
  112. package/lib/chain/regen/regen.js +7 -34
  113. package/lib/chain/regen/regen.js.map +1 -1
  114. package/lib/chain/stateCache/datastore/db.d.ts +5 -4
  115. package/lib/chain/stateCache/datastore/db.d.ts.map +1 -1
  116. package/lib/chain/stateCache/datastore/db.js +10 -32
  117. package/lib/chain/stateCache/datastore/db.js.map +1 -1
  118. package/lib/chain/stateCache/datastore/file.d.ts +1 -1
  119. package/lib/chain/stateCache/datastore/file.d.ts.map +1 -1
  120. package/lib/chain/stateCache/datastore/file.js +5 -5
  121. package/lib/chain/stateCache/datastore/file.js.map +1 -1
  122. package/lib/chain/stateCache/datastore/types.d.ts +1 -1
  123. package/lib/chain/stateCache/datastore/types.d.ts.map +1 -1
  124. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +1 -7
  125. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  126. package/lib/chain/stateCache/fifoBlockStateCache.js +0 -8
  127. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  128. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +13 -30
  129. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  130. package/lib/chain/stateCache/persistentCheckpointsCache.js +120 -216
  131. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  132. package/lib/chain/stateCache/types.d.ts +8 -15
  133. package/lib/chain/stateCache/types.d.ts.map +1 -1
  134. package/lib/chain/stateCache/types.js.map +1 -1
  135. package/lib/chain/validation/aggregateAndProof.js +12 -0
  136. package/lib/chain/validation/aggregateAndProof.js.map +1 -1
  137. package/lib/chain/validation/attestation.d.ts.map +1 -1
  138. package/lib/chain/validation/attestation.js +12 -0
  139. package/lib/chain/validation/attestation.js.map +1 -1
  140. package/lib/chain/validation/block.d.ts.map +1 -1
  141. package/lib/chain/validation/block.js +27 -5
  142. package/lib/chain/validation/block.js.map +1 -1
  143. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  144. package/lib/chain/validation/executionPayloadBid.js +7 -4
  145. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  146. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  147. package/lib/chain/validation/executionPayloadEnvelope.js +8 -3
  148. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  149. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  150. package/lib/chain/validation/payloadAttestationMessage.js +8 -4
  151. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  152. package/lib/chain/validation/syncCommittee.d.ts.map +1 -1
  153. package/lib/chain/validation/syncCommittee.js +4 -0
  154. package/lib/chain/validation/syncCommittee.js.map +1 -1
  155. package/lib/chain/validation/syncCommitteeContributionAndProof.js +4 -1
  156. package/lib/chain/validation/syncCommitteeContributionAndProof.js.map +1 -1
  157. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  158. package/lib/chain/validatorMonitor.js +3 -3
  159. package/lib/chain/validatorMonitor.js.map +1 -1
  160. package/lib/execution/engine/http.d.ts.map +1 -1
  161. package/lib/execution/engine/http.js +21 -14
  162. package/lib/execution/engine/http.js.map +1 -1
  163. package/lib/execution/engine/interface.d.ts +1 -0
  164. package/lib/execution/engine/interface.d.ts.map +1 -1
  165. package/lib/execution/engine/mock.d.ts.map +1 -1
  166. package/lib/execution/engine/mock.js +3 -0
  167. package/lib/execution/engine/mock.js.map +1 -1
  168. package/lib/execution/engine/types.d.ts +20 -0
  169. package/lib/execution/engine/types.d.ts.map +1 -1
  170. package/lib/execution/engine/types.js +18 -0
  171. package/lib/execution/engine/types.js.map +1 -1
  172. package/lib/network/gossip/topic.d.ts +2 -0
  173. package/lib/network/gossip/topic.d.ts.map +1 -1
  174. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  175. package/lib/network/processor/gossipHandlers.js +23 -3
  176. package/lib/network/processor/gossipHandlers.js.map +1 -1
  177. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  178. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +2 -1
  179. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  180. package/lib/network/reqresp/handlers/beaconBlocksByRoot.d.ts.map +1 -1
  181. package/lib/network/reqresp/handlers/beaconBlocksByRoot.js +2 -0
  182. package/lib/network/reqresp/handlers/beaconBlocksByRoot.js.map +1 -1
  183. package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts +2 -2
  184. package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
  185. package/lib/network/reqresp/handlers/blobSidecarsByRange.js +7 -3
  186. package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
  187. package/lib/network/reqresp/handlers/blobSidecarsByRoot.d.ts.map +1 -1
  188. package/lib/network/reqresp/handlers/blobSidecarsByRoot.js +6 -0
  189. package/lib/network/reqresp/handlers/blobSidecarsByRoot.js.map +1 -1
  190. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts +2 -2
  191. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  192. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +7 -3
  193. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  194. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  195. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +2 -1
  196. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  197. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  198. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +3 -8
  199. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  200. package/lib/node/nodejs.d.ts.map +1 -1
  201. package/lib/node/nodejs.js +6 -1
  202. package/lib/node/nodejs.js.map +1 -1
  203. package/lib/node/notifier.d.ts.map +1 -1
  204. package/lib/node/notifier.js +2 -2
  205. package/lib/node/notifier.js.map +1 -1
  206. package/package.json +16 -16
  207. package/src/api/impl/beacon/blocks/index.ts +29 -0
  208. package/src/api/impl/beacon/pool/index.ts +4 -0
  209. package/src/api/impl/beacon/state/index.ts +15 -15
  210. package/src/api/impl/lodestar/attesterSlashing.ts +43 -0
  211. package/src/api/impl/lodestar/index.ts +51 -1
  212. package/src/api/impl/validator/index.ts +13 -5
  213. package/src/chain/GetBlobsTracker.ts +1 -2
  214. package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +2 -4
  215. package/src/chain/blocks/importBlock.ts +33 -42
  216. package/src/chain/blocks/importExecutionPayload.ts +26 -12
  217. package/src/chain/blocks/index.ts +2 -2
  218. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -0
  219. package/src/chain/blocks/payloadEnvelopeProcessor.ts +6 -5
  220. package/src/chain/blocks/types.ts +4 -4
  221. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +2 -1
  222. package/src/chain/blocks/verifyBlocksSignatures.ts +9 -2
  223. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +38 -0
  224. package/src/chain/chain.ts +38 -47
  225. package/src/chain/emitter.ts +12 -0
  226. package/src/chain/errors/attestationError.ts +6 -1
  227. package/src/chain/errors/blockError.ts +7 -1
  228. package/src/chain/forkChoice/index.ts +23 -12
  229. package/src/chain/interface.ts +5 -0
  230. package/src/chain/lightClient/index.ts +15 -3
  231. package/src/chain/opPools/aggregatedAttestationPool.ts +6 -1
  232. package/src/chain/opPools/executionPayloadBidPool.ts +3 -3
  233. package/src/chain/prepareNextSlot.ts +26 -9
  234. package/src/chain/produceBlock/computeNewStateRoot.ts +1 -37
  235. package/src/chain/produceBlock/produceBlockBody.ts +71 -23
  236. package/src/chain/regen/errors.ts +1 -6
  237. package/src/chain/regen/interface.ts +6 -12
  238. package/src/chain/regen/queued.ts +12 -48
  239. package/src/chain/regen/regen.ts +8 -36
  240. package/src/chain/stateCache/datastore/db.ts +10 -33
  241. package/src/chain/stateCache/datastore/file.ts +5 -6
  242. package/src/chain/stateCache/datastore/types.ts +2 -3
  243. package/src/chain/stateCache/fifoBlockStateCache.ts +1 -10
  244. package/src/chain/stateCache/persistentCheckpointsCache.ts +139 -247
  245. package/src/chain/stateCache/types.ts +8 -14
  246. package/src/chain/validation/aggregateAndProof.ts +13 -0
  247. package/src/chain/validation/attestation.ts +13 -0
  248. package/src/chain/validation/block.ts +30 -7
  249. package/src/chain/validation/executionPayloadBid.ts +7 -3
  250. package/src/chain/validation/executionPayloadEnvelope.ts +12 -3
  251. package/src/chain/validation/payloadAttestationMessage.ts +9 -3
  252. package/src/chain/validation/syncCommittee.ts +5 -1
  253. package/src/chain/validation/syncCommitteeContributionAndProof.ts +5 -1
  254. package/src/chain/validatorMonitor.ts +3 -2
  255. package/src/execution/engine/http.ts +21 -14
  256. package/src/execution/engine/interface.ts +1 -0
  257. package/src/execution/engine/mock.ts +3 -0
  258. package/src/execution/engine/types.ts +41 -0
  259. package/src/network/processor/gossipHandlers.ts +28 -7
  260. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +3 -1
  261. package/src/network/reqresp/handlers/beaconBlocksByRoot.ts +3 -0
  262. package/src/network/reqresp/handlers/blobSidecarsByRange.ts +15 -3
  263. package/src/network/reqresp/handlers/blobSidecarsByRoot.ts +11 -0
  264. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +19 -3
  265. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +3 -1
  266. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +3 -12
  267. package/src/node/nodejs.ts +7 -2
  268. package/src/node/notifier.ts +7 -2
@@ -25,6 +25,7 @@ import {
25
25
  computeStartSlotAtEpoch,
26
26
  computeTimeAtSlot,
27
27
  getCurrentSlot,
28
+ isStatePostAltair,
28
29
  proposerShufflingDecisionRoot,
29
30
  } from "@lodestar/state-transition";
30
31
  import {
@@ -69,7 +70,7 @@ import {ChainEvent, CommonBlockBody} from "../../../chain/index.js";
69
70
  import {PREPARE_NEXT_SLOT_BPS} from "../../../chain/prepareNextSlot.js";
70
71
  import {BlockType, ProduceFullDeneb, ProduceFullGloas} from "../../../chain/produceBlock/index.js";
71
72
  import {RegenCaller} from "../../../chain/regen/index.js";
72
- import {CheckpointHexPayload} from "../../../chain/stateCache/types.js";
73
+ import {CheckpointHex} from "../../../chain/stateCache/types.js";
73
74
  import {validateApiAggregateAndProof} from "../../../chain/validation/index.js";
74
75
  import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js";
75
76
  import {ZERO_HASH} from "../../../constants/index.js";
@@ -300,7 +301,7 @@ export function getValidatorApi(
300
301
  * |
301
302
  * prepareNextSlot (4s before next slot)
302
303
  */
303
- async function waitForCheckpointState(cpHex: CheckpointHexPayload): Promise<IBeaconStateView | null> {
304
+ async function waitForCheckpointState(cpHex: CheckpointHex): Promise<IBeaconStateView | null> {
304
305
  const cpState = chain.regen.getCheckpointStateSync(cpHex);
305
306
  if (cpState) {
306
307
  return cpState;
@@ -1112,7 +1113,6 @@ export function getValidatorApi(
1112
1113
  const cpState = await waitForCheckpointState({
1113
1114
  rootHex: head.blockRoot,
1114
1115
  epoch,
1115
- payloadPresent: head.payloadStatus === PayloadStatus.FULL,
1116
1116
  });
1117
1117
  if (cpState) {
1118
1118
  state = cpState;
@@ -1282,6 +1282,9 @@ export function getValidatorApi(
1282
1282
  if (indices.length === 0) {
1283
1283
  throw new ApiError(400, "No validator to get attester duties");
1284
1284
  }
1285
+ if (epoch < config.ALTAIR_FORK_EPOCH) {
1286
+ throw new ApiError(400, "Sync committee duties are not supported before Altair");
1287
+ }
1285
1288
 
1286
1289
  // May request for an epoch that's in the future
1287
1290
  await waitForNextClosestEpoch();
@@ -1291,6 +1294,9 @@ export function getValidatorApi(
1291
1294
  // Note: does not support requesting past duties
1292
1295
  const head = chain.forkChoice.getHead();
1293
1296
  const state = chain.getHeadState();
1297
+ if (!isStatePostAltair(state)) {
1298
+ throw new ApiError(400, "Sync committee duties are not available before Altair");
1299
+ }
1294
1300
 
1295
1301
  // Check that all validatorIndex belong to the state before calling getCommitteeAssignments()
1296
1302
  const pubkeys = getPubkeysForIndices(state, indices);
@@ -1635,7 +1641,7 @@ export function getValidatorApi(
1635
1641
  throw Error("Cached block production result is not full block");
1636
1642
  }
1637
1643
 
1638
- const {executionPayload, executionRequests, envelopeStateRoot} = produceResult as ProduceFullGloas;
1644
+ const {executionPayload, executionRequests} = produceResult as ProduceFullGloas;
1639
1645
 
1640
1646
  const envelope: gloas.ExecutionPayloadEnvelope = {
1641
1647
  payload: executionPayload,
@@ -1643,7 +1649,9 @@ export function getValidatorApi(
1643
1649
  builderIndex: BUILDER_INDEX_SELF_BUILD,
1644
1650
  beaconBlockRoot,
1645
1651
  slot,
1646
- stateRoot: envelopeStateRoot,
1652
+ // TODO GLOAS: stateRoot is no longer computed during block production.
1653
+ // This field will be removed when we implement defer payload processing
1654
+ stateRoot: ZERO_HASH,
1647
1655
  };
1648
1656
 
1649
1657
  logger.info("Produced execution payload envelope", {
@@ -44,7 +44,7 @@ export class GetBlobsTracker {
44
44
  this.config = init.config;
45
45
  }
46
46
 
47
- triggerGetBlobs(input: IBlockInput | PayloadEnvelopeInput, onComplete?: () => void): void {
47
+ triggerGetBlobs(input: IBlockInput | PayloadEnvelopeInput): void {
48
48
  if (this.activeReconstructions.has(input.blockRootHex)) {
49
49
  return;
50
50
  }
@@ -101,7 +101,6 @@ export class GetBlobsTracker {
101
101
  .then((result) => {
102
102
  this.logger.debug("getBlobsV2 result for block", {...logCtx, result});
103
103
  this.metrics?.dataColumns.dataColumnEngineResult.inc({result});
104
- onComplete?.();
105
104
  })
106
105
  .catch((error) => {
107
106
  this.logger.debug("Error during getBlobsV2 for block", logCtx, error as Error);
@@ -9,7 +9,6 @@ import {AllocSource, BufferPool} from "../../../util/bufferPool.js";
9
9
  import {getStateSlotFromBytes} from "../../../util/multifork.js";
10
10
  import {IStateRegenerator} from "../../regen/interface.js";
11
11
  import {serializeState} from "../../serializeState.js";
12
- import {fcCheckpointToHexPayload} from "../../stateCache/persistentCheckpointsCache.js";
13
12
  import {StateArchiveStrategy, StatesArchiveOpts} from "../interface.js";
14
13
 
15
14
  /**
@@ -108,9 +107,8 @@ export class FrequencyStateArchiveStrategy implements StateArchiveStrategy {
108
107
  async archiveState(finalized: CheckpointWithPayloadStatus, metrics?: Metrics | null): Promise<void> {
109
108
  // starting from Mar 2024, the finalized state could be from disk or in memory
110
109
  let timer = metrics?.processFinalizedCheckpoint.frequencyStateArchive.startTimer();
111
- // Convert fork-choice checkpoint to beacon-node checkpoint with payloadPresent
112
- const finalizedHexPayload = fcCheckpointToHexPayload(finalized);
113
- const finalizedStateOrBytes = await this.regen.getCheckpointStateOrBytes(finalizedHexPayload);
110
+ const finalizedHex = {epoch: finalized.epoch, rootHex: finalized.rootHex};
111
+ const finalizedStateOrBytes = await this.regen.getCheckpointStateOrBytes(finalizedHex);
114
112
  timer?.({step: FrequencyStateArchiveStep.GetFinalizedState});
115
113
 
116
114
  const {rootHex} = finalized;
@@ -8,7 +8,6 @@ import {
8
8
  ForkChoiceErrorCode,
9
9
  NotReorgedReason,
10
10
  getSafeExecutionBlockHash,
11
- isGloasBlock,
12
11
  } from "@lodestar/fork-choice";
13
12
  import {
14
13
  ForkPostAltair,
@@ -25,6 +24,8 @@ import {
25
24
  computeStartSlotAtEpoch,
26
25
  computeTimeAtSlot,
27
26
  isStartSlotOfEpoch,
27
+ isStatePostAltair,
28
+ isStatePostBellatrix,
28
29
  } from "@lodestar/state-transition";
29
30
  import {
30
31
  Attestation,
@@ -46,7 +47,7 @@ import type {BeaconChain} from "../chain.js";
46
47
  import {ChainEvent, ReorgEventData} from "../emitter.js";
47
48
  import {ForkchoiceCaller} from "../forkChoice/index.js";
48
49
  import {REPROCESS_MIN_TIME_TO_NEXT_SLOT_SEC} from "../reprocess.js";
49
- import {toCheckpointHexPayload} from "../stateCache/persistentCheckpointsCache.js";
50
+ import {toCheckpointHex} from "../stateCache/persistentCheckpointsCache.js";
50
51
  import {isBlockInputBlobs, isBlockInputColumns} from "./blockInput/blockInput.js";
51
52
  import {AttestationImportOpt, FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
52
53
  import {getCheckpointFromState} from "./utils/checkpoint.js";
@@ -85,7 +86,7 @@ export async function importBlock(
85
86
  fullyVerifiedBlock: FullyVerifiedBlock,
86
87
  opts: ImportBlockOpts
87
88
  ): Promise<void> {
88
- const {blockInput, postBlockState, parentBlockSlot, executionStatus, dataAvailabilityStatus, indexedAttestations} =
89
+ const {blockInput, postState, parentBlockSlot, executionStatus, dataAvailabilityStatus, indexedAttestations} =
89
90
  fullyVerifiedBlock;
90
91
  const block = blockInput.getBlock();
91
92
  const source = blockInput.getBlockSource();
@@ -97,7 +98,7 @@ export async function importBlock(
97
98
  const blockEpoch = computeEpochAtSlot(blockSlot);
98
99
  const prevFinalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
99
100
  const blockDelaySec =
100
- fullyVerifiedBlock.seenTimestampSec - computeTimeAtSlot(this.config, blockSlot, postBlockState.genesisTime);
101
+ fullyVerifiedBlock.seenTimestampSec - computeTimeAtSlot(this.config, blockSlot, postState.genesisTime);
101
102
  const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000);
102
103
  const fork = this.config.getForkSeq(blockSlot);
103
104
 
@@ -120,10 +121,10 @@ export async function importBlock(
120
121
  // 2. Import block to fork choice
121
122
 
122
123
  // Should compute checkpoint balances before forkchoice.onBlock
123
- this.checkpointBalancesCache.processState(blockRootHex, postBlockState);
124
+ this.checkpointBalancesCache.processState(blockRootHex, postState);
124
125
  const blockSummary = this.forkChoice.onBlock(
125
126
  block.message,
126
- postBlockState,
127
+ postState,
127
128
  blockDelaySec,
128
129
  currentSlot,
129
130
  fork >= ForkSeq.gloas ? ExecutionStatus.PayloadSeparated : executionStatus,
@@ -132,11 +133,7 @@ export async function importBlock(
132
133
 
133
134
  // This adds the state necessary to process the next block
134
135
  // Some block event handlers require state being in state cache so need to do this before emitting EventType.block
135
- // Pre-Gloas: blockSummary.payloadStatus is always FULL, payloadPresent = true
136
- // Post-Gloas: blockSummary.payloadStatus is always PENDING, so payloadPresent = false (block state only, no payload processing yet)
137
- const payloadPresent = !isGloasBlock(blockSummary);
138
- // processState manages both block state and payload state variants together for memory/disk management
139
- this.regen.processBlockState(blockRootHex, postBlockState);
136
+ this.regen.processState(blockRootHex, postState);
140
137
 
141
138
  // For Gloas blocks, create PayloadEnvelopeInput so it's available for later payload import
142
139
  if (fork >= ForkSeq.gloas) {
@@ -159,17 +156,9 @@ export async function importBlock(
159
156
  // which is all the information we need so there is no reason to delay until execution payload arrives
160
157
  // TODO GLOAS: If we want EL retries after this initial attempt, add an explicit retry policy here
161
158
  // (for example later in the slot). Do not couple retries to incoming gossip columns.
162
- this.getBlobsTracker.triggerGetBlobs(payloadInput, () => {
163
- // TODO GLOAS: come up with a better mechanism to trigger processExecutionPayload after data becomes available,
164
- // similar to how pre-gloas uses waitForBlockAndAllData with a cutoff timeout and incompleteBlockInput event
165
- this.processExecutionPayload(payloadInput, {validSignature: true}).catch((e) => {
166
- this.logger.debug(
167
- "Error processing execution payload after getBlobs",
168
- {slot: blockSlot, root: blockRootHex},
169
- e as Error
170
- );
171
- });
172
- });
159
+ // Columns fetched here feed payloadInput.addColumn, which resolves waitForAllData for any
160
+ // in-flight importExecutionPayload. No processExecutionPayload trigger needed from this path.
161
+ this.getBlobsTracker.triggerGetBlobs(payloadInput);
173
162
  }
174
163
 
175
164
  this.metrics?.importBlock.bySource.inc({source: source.source});
@@ -189,7 +178,7 @@ export async function importBlock(
189
178
  (opts.importAttestations !== AttestationImportOpt.Skip && blockEpoch >= currentEpoch - FORK_CHOICE_ATT_EPOCH_LIMIT)
190
179
  ) {
191
180
  const attestations = block.message.body.attestations;
192
- const rootCache = new RootCache(postBlockState);
181
+ const rootCache = new RootCache(postState);
193
182
  const invalidAttestationErrorsByCode = new Map<string, {error: Error; count: number}>();
194
183
 
195
184
  const addAttestation = fork >= ForkSeq.electra ? addAttestationPostElectra : addAttestationPreElectra;
@@ -203,7 +192,7 @@ export async function importBlock(
203
192
  const attDataRoot = toRootHex(ssz.phase0.AttestationData.hashTreeRoot(indexedAttestation.data));
204
193
  addAttestation.call(
205
194
  this,
206
- postBlockState,
195
+ postState,
207
196
  target,
208
197
  attDataRoot,
209
198
  attestation as Attestation<ForkPostElectra>,
@@ -318,7 +307,7 @@ export async function importBlock(
318
307
 
319
308
  if (newHead.blockRoot !== oldHead.blockRoot) {
320
309
  // Set head state as strong reference
321
- this.regen.updateHeadState(newHead, postBlockState);
310
+ this.regen.updateHeadState(newHead, postState);
322
311
 
323
312
  try {
324
313
  this.emitter.emit(routes.events.EventType.head, {
@@ -388,11 +377,13 @@ export async function importBlock(
388
377
  // we want to import block asap so do this in the next event loop
389
378
  callInNextEventLoop(() => {
390
379
  try {
391
- this.lightClientServer?.onImportBlockHead(
392
- block.message as BeaconBlock<ForkPostAltair>,
393
- postBlockState,
394
- parentBlockSlot
395
- );
380
+ if (isStatePostAltair(postState)) {
381
+ this.lightClientServer?.onImportBlockHead(
382
+ block.message as BeaconBlock<ForkPostAltair>,
383
+ postState,
384
+ parentBlockSlot
385
+ );
386
+ }
396
387
  } catch (e) {
397
388
  this.logger.verbose("Error lightClientServer.onImportBlock", {slot: blockSlot}, e as Error);
398
389
  }
@@ -411,11 +402,11 @@ export async function importBlock(
411
402
  // and the block is weak and can potentially be reorged out.
412
403
  let shouldOverrideFcu = false;
413
404
 
414
- if (blockSlot >= currentSlot && postBlockState.isExecutionStateType) {
405
+ if (blockSlot >= currentSlot && isStatePostBellatrix(postState) && postState.isExecutionStateType) {
415
406
  let notOverrideFcuReason = NotReorgedReason.Unknown;
416
407
  const proposalSlot = blockSlot + 1;
417
408
  try {
418
- const proposerIndex = postBlockState.getBeaconProposer(proposalSlot);
409
+ const proposerIndex = postState.getBeaconProposer(proposalSlot);
419
410
  const feeRecipient = this.beaconProposerCache.get(proposerIndex);
420
411
 
421
412
  if (feeRecipient) {
@@ -495,27 +486,27 @@ export async function importBlock(
495
486
  }
496
487
  }
497
488
 
498
- if (!postBlockState.isStateValidatorsNodesPopulated()) {
499
- this.logger.verbose("After importBlock caching postState without SSZ cache", {slot: postBlockState.slot});
489
+ if (!postState.isStateValidatorsNodesPopulated()) {
490
+ this.logger.verbose("After importBlock caching postState without SSZ cache", {slot: postState.slot});
500
491
  }
501
492
 
502
493
  // Cache shufflings when crossing an epoch boundary
503
494
  const parentEpoch = computeEpochAtSlot(parentBlockSlot);
504
495
  if (parentEpoch < blockEpoch) {
505
- this.shufflingCache.processState(postBlockState);
496
+ this.shufflingCache.processState(postState);
506
497
  this.logger.verbose("Processed shuffling for next epoch", {parentEpoch, blockEpoch, slot: blockSlot});
507
498
  }
508
499
 
509
500
  if (blockSlot % SLOTS_PER_EPOCH === 0) {
510
501
  // Cache state to preserve epoch transition work
511
- const checkpointState = postBlockState;
502
+ const checkpointState = postState;
512
503
  const cp = getCheckpointFromState(checkpointState);
513
- this.regen.addCheckpointState(cp, checkpointState, payloadPresent);
504
+ this.regen.addCheckpointState(cp, checkpointState);
514
505
  // consumers should not mutate state ever
515
506
  this.emitter.emit(ChainEvent.checkpoint, cp, checkpointState);
516
507
 
517
508
  // Note: in-lined code from previos handler of ChainEvent.checkpoint
518
- this.logger.verbose("Checkpoint processed", toCheckpointHexPayload(cp, payloadPresent));
509
+ this.logger.verbose("Checkpoint processed", toCheckpointHex(cp));
519
510
 
520
511
  const activeValidatorsCount = checkpointState.activeValidatorCount;
521
512
  this.metrics?.currentActiveValidators.set(activeValidatorsCount);
@@ -533,7 +524,7 @@ export async function importBlock(
533
524
  const justifiedEpoch = justifiedCheckpoint.epoch;
534
525
  const preJustifiedEpoch = parentBlockSummary.justifiedEpoch;
535
526
  if (justifiedEpoch > preJustifiedEpoch) {
536
- this.logger.verbose("Checkpoint justified", toCheckpointHexPayload(justifiedCheckpoint, payloadPresent));
527
+ this.logger.verbose("Checkpoint justified", toCheckpointHex(justifiedCheckpoint));
537
528
  this.metrics?.previousJustifiedEpoch.set(checkpointState.previousJustifiedCheckpoint.epoch);
538
529
  this.metrics?.currentJustifiedEpoch.set(justifiedCheckpoint.epoch);
539
530
  }
@@ -547,7 +538,7 @@ export async function importBlock(
547
538
  state: toRootHex(checkpointState.hashTreeRoot()),
548
539
  executionOptimistic: false,
549
540
  });
550
- this.logger.verbose("Checkpoint finalized", toCheckpointHexPayload(finalizedCheckpoint, payloadPresent));
541
+ this.logger.verbose("Checkpoint finalized", toCheckpointHex(finalizedCheckpoint));
551
542
  this.metrics?.finalizedEpoch.set(finalizedCheckpoint.epoch);
552
543
  }
553
544
  }
@@ -598,11 +589,11 @@ export async function importBlock(
598
589
  this.metrics?.parentBlockDistance.observe(blockSlot - parentBlockSlot);
599
590
  this.metrics?.proposerBalanceDeltaAny.observe(fullyVerifiedBlock.proposerBalanceDelta);
600
591
  this.validatorMonitor?.registerImportedBlock(block.message, fullyVerifiedBlock);
601
- if (this.config.getForkSeq(blockSlot) >= ForkSeq.altair) {
592
+ if (isStatePostAltair(fullyVerifiedBlock.postState)) {
602
593
  this.validatorMonitor?.registerSyncAggregateInBlock(
603
594
  blockEpoch,
604
595
  (block as altair.SignedBeaconBlock).message.body.syncAggregate,
605
- fullyVerifiedBlock.postBlockState.currentSyncCommitteeIndexed.validatorIndices
596
+ fullyVerifiedBlock.postState.currentSyncCommitteeIndexed.validatorIndices
606
597
  );
607
598
  }
608
599
 
@@ -1,7 +1,7 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {ExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
3
3
  import {SLOTS_PER_EPOCH} from "@lodestar/params";
4
- import {getExecutionPayloadEnvelopeSignatureSet} from "@lodestar/state-transition";
4
+ import {getExecutionPayloadEnvelopeSignatureSet, isStatePostGloas} from "@lodestar/state-transition";
5
5
  import {byteArrayEquals, fromHex, toRootHex} from "@lodestar/utils";
6
6
  import {ExecutionPayloadStatus} from "../../execution/index.js";
7
7
  import {isQueueErrorAborted} from "../../util/queue/index.js";
@@ -9,6 +9,7 @@ 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,6 +85,7 @@ 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();
@@ -96,7 +98,7 @@ export async function importExecutionPayload(
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) {
101
+ if (this.clock.currentSlot - envelope.slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
100
102
  this.emitter.emit(routes.events.EventType.executionPayloadAvailable, {
101
103
  slot: envelope.slot,
102
104
  blockRoot: blockRootHex,
@@ -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,
@@ -124,10 +130,14 @@ export async function importExecutionPayload(
124
130
  {dontTransferCache: true},
125
131
  RegenCaller.processBlock
126
132
  );
133
+ if (!isStatePostGloas(blockState)) {
134
+ throw new PayloadError({
135
+ code: PayloadErrorCode.STATE_TRANSITION_ERROR,
136
+ message: `Expected gloas+ block state for payload import, got fork=${blockState.forkName}`,
137
+ });
138
+ }
127
139
 
128
- // 5. Run verification steps in parallel
129
- // Note: No data availability check needed here - importExecutionPayload is only
130
- // called when payloadInput.isComplete() is true, so all data is already available.
140
+ // 6. Run verification steps in parallel
131
141
  const [execResult, signatureValid, postPayloadResult] = await Promise.all([
132
142
  this.executionEngine.notifyNewPayload(
133
143
  fork,
@@ -218,7 +228,7 @@ export async function importExecutionPayload(
218
228
  if (!isQueueErrorAborted(e)) {
219
229
  this.logger.error(
220
230
  "Error pushing payload envelope to unfinalized write queue",
221
- {slot: envelope.slot, root: blockRootHex},
231
+ {slot: envelope.slot, blockRoot: blockRootHex},
222
232
  e as Error
223
233
  );
224
234
  }
@@ -234,10 +244,10 @@ export async function importExecutionPayload(
234
244
  );
235
245
 
236
246
  // 8. Cache payload state
237
- this.regen.processPayloadState(postPayloadState);
247
+ this.regen.processState(blockRootHex, postPayloadState);
238
248
  if (postPayloadState.slot % SLOTS_PER_EPOCH === 0) {
239
249
  const {checkpoint} = postPayloadState.computeAnchorCheckpoint();
240
- this.regen.addCheckpointState(checkpoint, postPayloadState, true);
250
+ this.regen.addCheckpointState(checkpoint, postPayloadState);
241
251
  }
242
252
 
243
253
  // 9. Record metrics for payload envelope and column sources
@@ -246,6 +256,8 @@ export async function importExecutionPayload(
246
256
  this.metrics?.importPayload.columnsBySource.inc({source});
247
257
  }
248
258
 
259
+ const stateRootHex = toRootHex(envelope.stateRoot);
260
+
249
261
  // 10. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
250
262
  if (this.clock.currentSlot - envelope.slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
251
263
  this.emitter.emit(routes.events.EventType.executionPayload, {
@@ -253,7 +265,7 @@ export async function importExecutionPayload(
253
265
  builderIndex: envelope.builderIndex,
254
266
  blockHash: blockHashHex,
255
267
  blockRoot: blockRootHex,
256
- stateRoot: toRootHex(envelope.stateRoot),
268
+ stateRoot: stateRootHex,
257
269
  // TODO GLOAS: revisit once we support optimistic import
258
270
  executionOptimistic: false,
259
271
  });
@@ -261,7 +273,9 @@ export async function importExecutionPayload(
261
273
 
262
274
  this.logger.verbose("Execution payload imported", {
263
275
  slot: envelope.slot,
264
- root: blockRootHex,
276
+ builderIndex: envelope.builderIndex,
277
+ blockRoot: blockRootHex,
265
278
  blockHash: blockHashHex,
279
+ stateRoot: stateRootHex,
266
280
  });
267
281
  }
@@ -88,8 +88,8 @@ export async function processBlocks(
88
88
  const fullyVerifiedBlocks = relevantBlocks.map(
89
89
  (block, i): FullyVerifiedBlock => ({
90
90
  blockInput: block,
91
- postBlockState: postStates[i],
92
- postEnvelopeState: null,
91
+ postState: postStates[i],
92
+ postPayloadState: null,
93
93
  parentBlockSlot: parentSlots[i],
94
94
  executionStatus: executionStatuses[i],
95
95
  // start supporting optimistic syncing/processing
@@ -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;
@@ -103,7 +103,7 @@ type FullyVerifiedBlockBase = {
103
103
  /**
104
104
  * A wrapper around a `SignedBeaconBlock` that indicates that this block is fully verified and ready to import.
105
105
  *
106
- * Discriminated union on `postEnvelopeState`:
106
+ * Discriminated union on `postPayloadState`:
107
107
  * - `null` → block has no pre-verified envelope; `executionStatus` is any `BlockExecutionStatus`
108
108
  * - non-null → envelope was pre-verified during state transition; `executionStatus` is narrowed to
109
109
  * `Valid | Syncing` (matching what `forkChoice.onExecutionPayload` expects)
@@ -111,12 +111,12 @@ type FullyVerifiedBlockBase = {
111
111
  export type FullyVerifiedBlock = FullyVerifiedBlockBase &
112
112
  (
113
113
  | {
114
- postEnvelopeState: null;
114
+ postPayloadState: null;
115
115
  /** If the execution payload couldn't be verified because of EL syncing status, used in optimistic sync or for merge block */
116
116
  executionStatus: BlockExecutionStatus;
117
117
  }
118
118
  | {
119
- postEnvelopeState: IBeaconStateView;
119
+ postPayloadState: IBeaconStateView;
120
120
  executionStatus: PayloadExecutionStatus;
121
121
  }
122
122
  );
@@ -8,7 +8,7 @@ import {
8
8
  ProtoBlock,
9
9
  } from "@lodestar/fork-choice";
10
10
  import {ForkSeq} from "@lodestar/params";
11
- import {IBeaconStateView, isExecutionBlockBodyType} from "@lodestar/state-transition";
11
+ import {IBeaconStateView, isExecutionBlockBodyType, isStatePostBellatrix} from "@lodestar/state-transition";
12
12
  import {bellatrix, electra} from "@lodestar/types";
13
13
  import {ErrorAborted, Logger, toRootHex} from "@lodestar/utils";
14
14
  import {ExecutionPayloadStatus, IExecutionEngine} from "../../execution/engine/interface.js";
@@ -152,6 +152,7 @@ export async function verifyBlockExecutionPayload(
152
152
 
153
153
  /** Not null if execution is enabled */
154
154
  const executionPayloadEnabled =
155
+ isStatePostBellatrix(preState0) &&
155
156
  preState0.isExecutionStateType &&
156
157
  isExecutionBlockBodyType(block.message.body) &&
157
158
  preState0.isExecutionEnabled(block.message)
@@ -1,5 +1,10 @@
1
1
  import {BeaconConfig} from "@lodestar/config";
2
- import {IBeaconStateView, getBlockSignatureSets} from "@lodestar/state-transition";
2
+ import {
3
+ IBeaconStateView,
4
+ SyncCommitteeCacheEmpty,
5
+ getBlockSignatureSets,
6
+ isStatePostAltair,
7
+ } from "@lodestar/state-transition";
3
8
  import {IndexedAttestation, SignedBeaconBlock} from "@lodestar/types";
4
9
  import {Logger} from "@lodestar/utils";
5
10
  import {Metrics} from "../../metrics/metrics.js";
@@ -27,7 +32,9 @@ export async function verifyBlocksSignatures(
27
32
  ): Promise<{verifySignaturesTime: number}> {
28
33
  const isValidPromises: Promise<boolean>[] = [];
29
34
  const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000);
30
- const currentSyncCommitteeIndexed = preState0.currentSyncCommitteeIndexed;
35
+ const currentSyncCommitteeIndexed = isStatePostAltair(preState0)
36
+ ? preState0.currentSyncCommitteeIndexed
37
+ : new SyncCommitteeCacheEmpty();
31
38
 
32
39
  // Verifies signatures after running state transition, so all SyncCommittee signed roots are known at this point.
33
40
  // We must ensure block.slot <= state.slot before running getAllBlockSignatureSets().
@@ -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
+ }