@lodestar/beacon-node 1.43.0-dev.6b7eebbf6d → 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 (288) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +16 -5
  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/lodestar/index.js +1 -1
  11. package/lib/api/impl/lodestar/index.js.map +1 -1
  12. package/lib/api/impl/validator/index.d.ts.map +1 -1
  13. package/lib/api/impl/validator/index.js +68 -2
  14. package/lib/api/impl/validator/index.js.map +1 -1
  15. package/lib/chain/blocks/blockInput/blockInput.d.ts +3 -0
  16. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  17. package/lib/chain/blocks/blockInput/blockInput.js +4 -1
  18. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  19. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  20. package/lib/chain/blocks/importBlock.js +16 -28
  21. package/lib/chain/blocks/importBlock.js.map +1 -1
  22. package/lib/chain/blocks/importExecutionPayload.d.ts +23 -6
  23. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  24. package/lib/chain/blocks/importExecutionPayload.js +57 -24
  25. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  26. package/lib/chain/blocks/index.d.ts +5 -3
  27. package/lib/chain/blocks/index.d.ts.map +1 -1
  28. package/lib/chain/blocks/index.js +58 -25
  29. package/lib/chain/blocks/index.js.map +1 -1
  30. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +12 -1
  31. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  32. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +28 -2
  33. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  34. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +17 -0
  35. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  36. package/lib/chain/blocks/payloadEnvelopeProcessor.js +2 -2
  37. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  38. package/lib/chain/blocks/types.d.ts +4 -3
  39. package/lib/chain/blocks/types.d.ts.map +1 -1
  40. package/lib/chain/blocks/utils/chainSegment.d.ts +23 -2
  41. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  42. package/lib/chain/blocks/utils/chainSegment.js +89 -12
  43. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  44. package/lib/chain/blocks/verifyBlock.d.ts +5 -3
  45. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  46. package/lib/chain/blocks/verifyBlock.js +50 -7
  47. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  48. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +0 -4
  49. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  50. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -2
  51. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  52. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +2 -1
  53. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  54. package/lib/chain/blocks/verifyBlocksSanityChecks.js +25 -5
  55. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  56. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  57. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  58. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +12 -8
  59. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  60. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  61. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
  62. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  63. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -1
  64. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +1 -10
  65. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -1
  66. package/lib/chain/chain.d.ts +5 -3
  67. package/lib/chain/chain.d.ts.map +1 -1
  68. package/lib/chain/chain.js +42 -12
  69. package/lib/chain/chain.js.map +1 -1
  70. package/lib/chain/emitter.d.ts +0 -11
  71. package/lib/chain/emitter.d.ts.map +1 -1
  72. package/lib/chain/emitter.js +0 -4
  73. package/lib/chain/emitter.js.map +1 -1
  74. package/lib/chain/errors/blockError.d.ts +8 -1
  75. package/lib/chain/errors/blockError.d.ts.map +1 -1
  76. package/lib/chain/errors/blockError.js +2 -0
  77. package/lib/chain/errors/blockError.js.map +1 -1
  78. package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
  79. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  80. package/lib/chain/errors/executionPayloadBid.js +1 -0
  81. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  82. package/lib/chain/errors/index.d.ts +1 -0
  83. package/lib/chain/errors/index.d.ts.map +1 -1
  84. package/lib/chain/errors/index.js +1 -0
  85. package/lib/chain/errors/index.js.map +1 -1
  86. package/lib/chain/errors/proposerPreferences.d.ts +40 -0
  87. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
  88. package/lib/chain/errors/proposerPreferences.js +14 -0
  89. package/lib/chain/errors/proposerPreferences.js.map +1 -0
  90. package/lib/chain/initState.d.ts.map +1 -1
  91. package/lib/chain/initState.js +6 -1
  92. package/lib/chain/initState.js.map +1 -1
  93. package/lib/chain/interface.d.ts +5 -3
  94. package/lib/chain/interface.d.ts.map +1 -1
  95. package/lib/chain/interface.js.map +1 -1
  96. package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
  97. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  98. package/lib/chain/opPools/payloadAttestationPool.js +26 -4
  99. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  100. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  101. package/lib/chain/prepareNextSlot.js +31 -13
  102. package/lib/chain/prepareNextSlot.js.map +1 -1
  103. package/lib/chain/produceBlock/produceBlockBody.d.ts +11 -1
  104. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  105. package/lib/chain/produceBlock/produceBlockBody.js +47 -15
  106. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  107. package/lib/chain/regen/interface.d.ts +1 -0
  108. package/lib/chain/regen/interface.d.ts.map +1 -1
  109. package/lib/chain/regen/interface.js +1 -0
  110. package/lib/chain/regen/interface.js.map +1 -1
  111. package/lib/chain/regen/queued.d.ts.map +1 -1
  112. package/lib/chain/regen/queued.js +1 -4
  113. package/lib/chain/regen/queued.js.map +1 -1
  114. package/lib/chain/regen/regen.d.ts.map +1 -1
  115. package/lib/chain/regen/regen.js +1 -4
  116. package/lib/chain/regen/regen.js.map +1 -1
  117. package/lib/chain/seenCache/index.d.ts +1 -0
  118. package/lib/chain/seenCache/index.d.ts.map +1 -1
  119. package/lib/chain/seenCache/index.js +1 -0
  120. package/lib/chain/seenCache/index.js.map +1 -1
  121. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +24 -7
  122. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  123. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +69 -17
  124. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  125. package/lib/chain/seenCache/seenProposerPreferences.d.ts +16 -0
  126. package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
  127. package/lib/chain/seenCache/seenProposerPreferences.js +26 -0
  128. package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
  129. package/lib/chain/validation/block.d.ts.map +1 -1
  130. package/lib/chain/validation/block.js +1 -0
  131. package/lib/chain/validation/block.js.map +1 -1
  132. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  133. package/lib/chain/validation/executionPayloadBid.js +24 -9
  134. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  135. package/lib/chain/validation/proposerPreferences.d.ts +8 -0
  136. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -0
  137. package/lib/chain/validation/proposerPreferences.js +91 -0
  138. package/lib/chain/validation/proposerPreferences.js.map +1 -0
  139. package/lib/metrics/metrics/lodestar.d.ts +1 -0
  140. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  141. package/lib/metrics/metrics/lodestar.js +4 -0
  142. package/lib/metrics/metrics/lodestar.js.map +1 -1
  143. package/lib/network/gossip/interface.d.ts +7 -1
  144. package/lib/network/gossip/interface.d.ts.map +1 -1
  145. package/lib/network/gossip/interface.js +1 -0
  146. package/lib/network/gossip/interface.js.map +1 -1
  147. package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
  148. package/lib/network/gossip/scoringParameters.js +12 -1
  149. package/lib/network/gossip/scoringParameters.js.map +1 -1
  150. package/lib/network/gossip/topic.d.ts +10 -0
  151. package/lib/network/gossip/topic.d.ts.map +1 -1
  152. package/lib/network/gossip/topic.js +6 -0
  153. package/lib/network/gossip/topic.js.map +1 -1
  154. package/lib/network/interface.d.ts +1 -0
  155. package/lib/network/interface.d.ts.map +1 -1
  156. package/lib/network/network.d.ts +1 -0
  157. package/lib/network/network.d.ts.map +1 -1
  158. package/lib/network/network.js +5 -0
  159. package/lib/network/network.js.map +1 -1
  160. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  161. package/lib/network/processor/gossipHandlers.js +38 -16
  162. package/lib/network/processor/gossipHandlers.js.map +1 -1
  163. package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
  164. package/lib/network/processor/gossipQueues/index.js +5 -0
  165. package/lib/network/processor/gossipQueues/index.js.map +1 -1
  166. package/lib/network/processor/index.d.ts.map +1 -1
  167. package/lib/network/processor/index.js +6 -5
  168. package/lib/network/processor/index.js.map +1 -1
  169. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  170. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -6
  171. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  172. package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
  173. package/lib/network/reqresp/handlers/blobSidecarsByRange.js +11 -5
  174. package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
  175. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  176. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +17 -5
  177. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  178. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  179. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +7 -4
  180. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  181. package/lib/node/nodejs.js +2 -2
  182. package/lib/node/nodejs.js.map +1 -1
  183. package/lib/sync/constants.d.ts +3 -1
  184. package/lib/sync/constants.d.ts.map +1 -1
  185. package/lib/sync/constants.js +3 -4
  186. package/lib/sync/constants.js.map +1 -1
  187. package/lib/sync/range/batch.d.ts +35 -5
  188. package/lib/sync/range/batch.d.ts.map +1 -1
  189. package/lib/sync/range/batch.js +240 -59
  190. package/lib/sync/range/batch.js.map +1 -1
  191. package/lib/sync/range/chain.d.ts +19 -4
  192. package/lib/sync/range/chain.d.ts.map +1 -1
  193. package/lib/sync/range/chain.js +64 -11
  194. package/lib/sync/range/chain.js.map +1 -1
  195. package/lib/sync/range/range.d.ts.map +1 -1
  196. package/lib/sync/range/range.js +31 -9
  197. package/lib/sync/range/range.js.map +1 -1
  198. package/lib/sync/sync.d.ts.map +1 -1
  199. package/lib/sync/sync.js +13 -0
  200. package/lib/sync/sync.js.map +1 -1
  201. package/lib/sync/types.d.ts +34 -0
  202. package/lib/sync/types.d.ts.map +1 -1
  203. package/lib/sync/types.js +34 -0
  204. package/lib/sync/types.js.map +1 -1
  205. package/lib/sync/unknownBlock.d.ts +29 -1
  206. package/lib/sync/unknownBlock.d.ts.map +1 -1
  207. package/lib/sync/unknownBlock.js +738 -61
  208. package/lib/sync/unknownBlock.js.map +1 -1
  209. package/lib/sync/utils/downloadByRange.d.ts +67 -10
  210. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  211. package/lib/sync/utils/downloadByRange.js +211 -26
  212. package/lib/sync/utils/downloadByRange.js.map +1 -1
  213. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  214. package/lib/sync/utils/downloadByRoot.js +16 -2
  215. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  216. package/lib/sync/utils/pendingBlocksTree.d.ts +0 -1
  217. package/lib/sync/utils/pendingBlocksTree.d.ts.map +1 -1
  218. package/lib/sync/utils/pendingBlocksTree.js +0 -9
  219. package/lib/sync/utils/pendingBlocksTree.js.map +1 -1
  220. package/lib/util/sszBytes.d.ts.map +1 -1
  221. package/lib/util/sszBytes.js +8 -6
  222. package/lib/util/sszBytes.js.map +1 -1
  223. package/package.json +16 -15
  224. package/src/api/impl/beacon/blocks/index.ts +21 -5
  225. package/src/api/impl/beacon/pool/index.ts +83 -1
  226. package/src/api/impl/debug/index.ts +0 -1
  227. package/src/api/impl/lodestar/index.ts +1 -1
  228. package/src/api/impl/validator/index.ts +82 -1
  229. package/src/chain/blocks/blockInput/blockInput.ts +4 -1
  230. package/src/chain/blocks/importBlock.ts +16 -48
  231. package/src/chain/blocks/importExecutionPayload.ts +76 -30
  232. package/src/chain/blocks/index.ts +71 -22
  233. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +37 -3
  234. package/src/chain/blocks/payloadEnvelopeInput/types.ts +18 -0
  235. package/src/chain/blocks/payloadEnvelopeProcessor.ts +2 -2
  236. package/src/chain/blocks/types.ts +4 -3
  237. package/src/chain/blocks/utils/chainSegment.ts +114 -17
  238. package/src/chain/blocks/verifyBlock.ts +70 -9
  239. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
  240. package/src/chain/blocks/verifyBlocksSanityChecks.ts +26 -7
  241. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +16 -8
  242. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
  243. package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +8 -17
  244. package/src/chain/chain.ts +55 -10
  245. package/src/chain/emitter.ts +0 -11
  246. package/src/chain/errors/blockError.ts +4 -1
  247. package/src/chain/errors/executionPayloadBid.ts +6 -0
  248. package/src/chain/errors/index.ts +1 -0
  249. package/src/chain/errors/proposerPreferences.ts +47 -0
  250. package/src/chain/initState.ts +9 -1
  251. package/src/chain/interface.ts +9 -1
  252. package/src/chain/opPools/payloadAttestationPool.ts +29 -8
  253. package/src/chain/prepareNextSlot.ts +36 -14
  254. package/src/chain/produceBlock/produceBlockBody.ts +57 -14
  255. package/src/chain/regen/interface.ts +1 -0
  256. package/src/chain/regen/queued.ts +2 -7
  257. package/src/chain/regen/regen.ts +2 -7
  258. package/src/chain/seenCache/index.ts +1 -0
  259. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +89 -21
  260. package/src/chain/seenCache/seenProposerPreferences.ts +32 -0
  261. package/src/chain/validation/block.ts +1 -0
  262. package/src/chain/validation/executionPayloadBid.ts +25 -8
  263. package/src/chain/validation/proposerPreferences.ts +110 -0
  264. package/src/metrics/metrics/lodestar.ts +4 -0
  265. package/src/network/gossip/interface.ts +6 -0
  266. package/src/network/gossip/scoringParameters.ts +14 -1
  267. package/src/network/gossip/topic.ts +6 -0
  268. package/src/network/interface.ts +1 -0
  269. package/src/network/network.ts +11 -0
  270. package/src/network/processor/gossipHandlers.ts +53 -17
  271. package/src/network/processor/gossipQueues/index.ts +5 -0
  272. package/src/network/processor/index.ts +6 -5
  273. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +14 -6
  274. package/src/network/reqresp/handlers/blobSidecarsByRange.ts +11 -5
  275. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -5
  276. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +7 -4
  277. package/src/node/nodejs.ts +2 -2
  278. package/src/sync/constants.ts +4 -4
  279. package/src/sync/range/batch.ts +320 -67
  280. package/src/sync/range/chain.ts +89 -14
  281. package/src/sync/range/range.ts +34 -9
  282. package/src/sync/sync.ts +13 -1
  283. package/src/sync/types.ts +72 -0
  284. package/src/sync/unknownBlock.ts +928 -65
  285. package/src/sync/utils/downloadByRange.ts +378 -39
  286. package/src/sync/utils/downloadByRoot.ts +24 -2
  287. package/src/sync/utils/pendingBlocksTree.ts +0 -15
  288. package/src/util/sszBytes.ts +8 -6
@@ -1,6 +1,24 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {ForkPostDeneb, ForkPostFulu, ForkPreFulu, isForkPostFulu} from "@lodestar/params";
3
- import {SignedBeaconBlock, Slot, deneb, fulu, phase0} from "@lodestar/types";
2
+ import {
3
+ ForkPostDeneb,
4
+ ForkPostFulu,
5
+ ForkPostGloas,
6
+ ForkPreFulu,
7
+ isForkPostFulu,
8
+ isForkPostGloas,
9
+ } from "@lodestar/params";
10
+ import {
11
+ ColumnIndex,
12
+ DataColumnSidecar,
13
+ RootHex,
14
+ SignedBeaconBlock,
15
+ Slot,
16
+ deneb,
17
+ fulu,
18
+ gloas,
19
+ isGloasDataColumnSidecar,
20
+ phase0,
21
+ } from "@lodestar/types";
4
22
  import {LodestarError, Logger, byteArrayEquals, fromHex, prettyPrintIndices, toRootHex} from "@lodestar/utils";
5
23
  import {
6
24
  BlockInputSource,
@@ -9,12 +27,18 @@ import {
9
27
  isBlockInputBlobs,
10
28
  isBlockInputColumns,
11
29
  } from "../../chain/blocks/blockInput/index.js";
30
+ import {PayloadEnvelopeInput} from "../../chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
31
+ import {PayloadEnvelopeInputSource} from "../../chain/blocks/payloadEnvelopeInput/types.js";
12
32
  import {SeenBlockInput} from "../../chain/seenCache/seenGossipBlockInput.js";
33
+ import {SeenPayloadEnvelopeInput} from "../../chain/seenCache/seenPayloadEnvelopeInput.js";
13
34
  import {validateBlockBlobSidecars} from "../../chain/validation/blobSidecar.js";
14
- import {validateFuluBlockDataColumnSidecars} from "../../chain/validation/dataColumnSidecar.js";
35
+ import {
36
+ validateFuluBlockDataColumnSidecars,
37
+ validateGloasBlockDataColumnSidecars,
38
+ } from "../../chain/validation/dataColumnSidecar.js";
15
39
  import {BeaconMetrics} from "../../metrics/metrics/beacon.js";
16
40
  import {INetwork} from "../../network/index.js";
17
- import {getBlobKzgCommitments} from "../../util/dataColumns.js";
41
+ import {CustodyConfig, getBlobKzgCommitments} from "../../util/dataColumns.js";
18
42
  import {PeerIdStr} from "../../util/peerId.js";
19
43
  import {WarnResult} from "../../util/wrapError.js";
20
44
 
@@ -22,12 +46,30 @@ export type DownloadByRangeRequests = {
22
46
  blocksRequest?: phase0.BeaconBlocksByRangeRequest;
23
47
  blobsRequest?: deneb.BlobSidecarsByRangeRequest;
24
48
  columnsRequest?: fulu.DataColumnSidecarsByRangeRequest;
49
+ envelopesRequest?: gloas.ExecutionPayloadEnvelopesByRangeRequest;
50
+ /**
51
+ * Post-Gloas only. Fetches the dangling-parent's payload envelope and/or its missing sampled
52
+ * data columns by-root. Set by `Batch` for the first batch of a `SyncChain` after the first
53
+ * block's parentRoot is known.
54
+ */
55
+ parentPayloadRequest?: {
56
+ blockRoot?: Uint8Array;
57
+ columns?: ColumnIndex[];
58
+ envelopeBlockRoot?: Uint8Array;
59
+ };
60
+ };
61
+
62
+ export type ParentPayloadCommitments = {
63
+ blockRoot: Uint8Array;
64
+ blockRootHex: RootHex;
65
+ kzgCommitments: deneb.BlobKzgCommitments;
25
66
  };
26
67
 
27
68
  export type DownloadByRangeResponses = {
28
69
  blocks?: SignedBeaconBlock[];
29
70
  blobSidecars?: deneb.BlobSidecars;
30
- columnSidecars?: fulu.DataColumnSidecar[];
71
+ columnSidecars?: DataColumnSidecar[];
72
+ payloadEnvelopes?: gloas.SignedExecutionPayloadEnvelope[];
31
73
  };
32
74
 
33
75
  export type DownloadAndCacheByRangeProps = DownloadByRangeRequests & {
@@ -36,14 +78,24 @@ export type DownloadAndCacheByRangeProps = DownloadByRangeRequests & {
36
78
  logger: Logger;
37
79
  peerIdStr: string;
38
80
  batchBlocks?: IBlockInput[];
81
+ /** Required when `parentPayloadRequest` is set; supplies the data needed to validate the parent's columns. */
82
+ parentPayloadCommitments?: ParentPayloadCommitments;
39
83
  peerDasMetrics?: BeaconMetrics["peerDas"] | null;
40
84
  };
41
85
 
42
86
  export type CacheByRangeResponsesProps = {
43
87
  cache: SeenBlockInput;
88
+ seenPayloadEnvelopeInputCache: SeenPayloadEnvelopeInput;
44
89
  peerIdStr: string;
45
90
  responses: ValidatedResponses;
46
91
  batchBlocks: IBlockInput[];
92
+ /** Raw envelopes downloaded in this batch, keyed by slot (from downloadByRange return) */
93
+ downloadedPayloadEnvelopes: Map<Slot, gloas.SignedExecutionPayloadEnvelope> | null;
94
+ /** Envelopes already wrapped from previous partial downloads on this batch */
95
+ existingPayloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
96
+ /** Sampled/custody column indices for building PayloadEnvelopeInputs */
97
+ custodyConfig: Pick<CustodyConfig, "sampledColumns" | "custodyColumns">;
98
+ seenTimestampSec: number;
47
99
  };
48
100
 
49
101
  export type ValidatedBlock = {
@@ -58,7 +110,7 @@ export type ValidatedBlobSidecars = {
58
110
 
59
111
  export type ValidatedColumnSidecars = {
60
112
  blockRoot: Uint8Array;
61
- columnSidecars: fulu.DataColumnSidecar[];
113
+ columnSidecars: DataColumnSidecar[];
62
114
  };
63
115
 
64
116
  export type ValidatedResponses = {
@@ -72,12 +124,16 @@ export type ValidatedResponses = {
72
124
  */
73
125
  export function cacheByRangeResponses({
74
126
  cache,
127
+ seenPayloadEnvelopeInputCache,
75
128
  peerIdStr,
76
129
  responses,
77
130
  batchBlocks,
78
- }: CacheByRangeResponsesProps): IBlockInput[] {
131
+ downloadedPayloadEnvelopes,
132
+ existingPayloadEnvelopes,
133
+ custodyConfig,
134
+ seenTimestampSec,
135
+ }: CacheByRangeResponsesProps): {blocks: IBlockInput[]; payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null} {
79
136
  const source = BlockInputSource.byRange;
80
- const seenTimestampSec = Date.now() / 1000;
81
137
  const updatedBatchBlocks = new Map<Slot, IBlockInput>(batchBlocks.map((block) => [block.slot, block]));
82
138
 
83
139
  const blocks = responses.validatedBlocks ?? [];
@@ -149,16 +205,82 @@ export function cacheByRangeResponses({
149
205
  }
150
206
  }
151
207
 
208
+ // Seed seenPayloadEnvelopeInputCache for every gloas block in the batch, regardless of whether
209
+ // the peer returned its envelope. Without this, a block returned without its envelope would be
210
+ // imported with no cache entry, and later payload-by-root sync would throw
211
+ // "Missing PayloadEnvelopeInput for known block" (see issue #9306).
212
+ for (const blockInput of updatedBatchBlocks.values()) {
213
+ if (!blockInput.hasBlock() || !isForkPostGloas(blockInput.forkName)) continue;
214
+ seenPayloadEnvelopeInputCache.add({
215
+ blockRootHex: blockInput.blockRootHex,
216
+ block: blockInput.getBlock() as SignedBeaconBlock<ForkPostGloas>,
217
+ forkName: blockInput.forkName,
218
+ sampledColumns: custodyConfig.sampledColumns,
219
+ custodyColumns: custodyConfig.custodyColumns,
220
+ timeCreatedSec: seenTimestampSec,
221
+ });
222
+ }
223
+
224
+ let payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null =
225
+ existingPayloadEnvelopes !== null ? new Map(existingPayloadEnvelopes) : null;
226
+ if (downloadedPayloadEnvelopes !== null) {
227
+ payloadEnvelopes ??= new Map();
228
+ for (const [slot, envelope] of downloadedPayloadEnvelopes) {
229
+ const envelopeBlockRootHex = toRootHex(envelope.message.beaconBlockRoot);
230
+ const payloadInput = seenPayloadEnvelopeInputCache.get(envelopeBlockRootHex);
231
+ if (payloadInput === undefined) {
232
+ // Unreachable given the loop above seeded an entry for every gloas block in the batch.
233
+ // for the parent block, it's populated at BeaconChain init
234
+ throw new Error(`Missing PayloadEnvelopeInput for block ${envelopeBlockRootHex}`);
235
+ }
236
+
237
+ if (!payloadInput.hasPayloadEnvelope()) {
238
+ payloadInput.addPayloadEnvelope({
239
+ envelope,
240
+ source: PayloadEnvelopeInputSource.byRange,
241
+ seenTimestampSec,
242
+ peerIdStr,
243
+ });
244
+ }
245
+
246
+ payloadEnvelopes.set(slot, payloadInput);
247
+ }
248
+ }
249
+
152
250
  for (const {blockRoot, columnSidecars} of responses.validatedColumnSidecars ?? []) {
153
- const dataSlot = columnSidecars.at(0)?.signedBlockHeader.message.slot;
154
- if (dataSlot === undefined) {
251
+ const firstColumn = columnSidecars[0];
252
+ if (!firstColumn) {
155
253
  throw new Error(
156
254
  `Coding Error: empty columnSidecars returned for blockRoot=${toRootHex(blockRoot)} from validation functions`
157
255
  );
158
256
  }
159
- const existing = updatedBatchBlocks.get(dataSlot);
257
+
160
258
  const blockRootHex = toRootHex(blockRoot);
161
259
 
260
+ if (isGloasDataColumnSidecar(firstColumn)) {
261
+ // Gloas columns are attached to the matching PayloadEnvelopeInput, NOT to IBlockInput.
262
+ // Gloas DataColumnSidecar has `slot` directly (no signedBlockHeader).
263
+ const dataSlot = firstColumn.slot;
264
+ const payloadInput = payloadEnvelopes?.get(dataSlot);
265
+ if (!payloadInput) {
266
+ // Should not happen: we built payloadInputs for all gloas blocks above
267
+ continue;
268
+ }
269
+ for (const columnSidecar of columnSidecars as gloas.DataColumnSidecar[]) {
270
+ payloadInput.addColumn({
271
+ columnSidecar,
272
+ seenTimestampSec,
273
+ peerIdStr,
274
+ source: PayloadEnvelopeInputSource.byRange,
275
+ });
276
+ }
277
+ continue;
278
+ }
279
+
280
+ const fuluColumns = columnSidecars as fulu.DataColumnSidecar[];
281
+ const dataSlot = fuluColumns[0].signedBlockHeader.message.slot;
282
+ const existing = updatedBatchBlocks.get(dataSlot);
283
+
162
284
  if (!existing) {
163
285
  throw new Error("Coding error: blockInput must exist when adding columns");
164
286
  }
@@ -172,7 +294,7 @@ export function cacheByRangeResponses({
172
294
  actual: existing.type,
173
295
  });
174
296
  }
175
- for (const columnSidecar of columnSidecars) {
297
+ for (const columnSidecar of fuluColumns) {
176
298
  // will throw if root hex does not match (meaning we are following the wrong chain)
177
299
  existing.addColumn(
178
300
  {
@@ -187,7 +309,7 @@ export function cacheByRangeResponses({
187
309
  }
188
310
  }
189
311
 
190
- return Array.from(updatedBatchBlocks.values());
312
+ return {blocks: Array.from(updatedBatchBlocks.values()), payloadEnvelopes};
191
313
  }
192
314
 
193
315
  export async function downloadByRange({
@@ -198,8 +320,16 @@ export async function downloadByRange({
198
320
  blocksRequest,
199
321
  blobsRequest,
200
322
  columnsRequest,
323
+ envelopesRequest,
324
+ parentPayloadRequest,
325
+ parentPayloadCommitments,
201
326
  peerDasMetrics,
202
- }: DownloadAndCacheByRangeProps): Promise<WarnResult<ValidatedResponses, DownloadByRangeError>> {
327
+ }: DownloadAndCacheByRangeProps): Promise<
328
+ WarnResult<
329
+ {responses: ValidatedResponses; payloadEnvelopes: Map<Slot, gloas.SignedExecutionPayloadEnvelope> | null},
330
+ DownloadByRangeError
331
+ >
332
+ > {
203
333
  let response: DownloadByRangeResponses;
204
334
  try {
205
335
  response = await requestByRange({
@@ -208,6 +338,8 @@ export async function downloadByRange({
208
338
  blocksRequest,
209
339
  blobsRequest,
210
340
  columnsRequest,
341
+ envelopesRequest,
342
+ parentPayloadRequest,
211
343
  });
212
344
  } catch (err) {
213
345
  throw new DownloadByRangeError({
@@ -217,17 +349,18 @@ export async function downloadByRange({
217
349
  });
218
350
  }
219
351
 
220
- const validated = await validateResponses({
352
+ return validateResponses({
221
353
  config,
222
354
  batchBlocks,
223
355
  blocksRequest,
224
356
  blobsRequest,
225
357
  columnsRequest,
358
+ envelopesRequest,
359
+ parentPayloadRequest,
360
+ parentPayloadCommitments,
226
361
  peerDasMetrics,
227
362
  ...response,
228
363
  });
229
-
230
- return validated;
231
364
  }
232
365
 
233
366
  /**
@@ -239,13 +372,16 @@ export async function requestByRange({
239
372
  blocksRequest,
240
373
  blobsRequest,
241
374
  columnsRequest,
375
+ envelopesRequest,
376
+ parentPayloadRequest,
242
377
  }: DownloadByRangeRequests & {
243
378
  network: INetwork;
244
379
  peerIdStr: PeerIdStr;
245
380
  }): Promise<DownloadByRangeResponses> {
246
381
  let blocks: undefined | SignedBeaconBlock[];
247
382
  let blobSidecars: undefined | deneb.BlobSidecars;
248
- let columnSidecars: undefined | fulu.DataColumnSidecar[];
383
+ const columnSidecars: DataColumnSidecar[] = [];
384
+ const payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[] = [];
249
385
 
250
386
  const requests: Promise<unknown>[] = [];
251
387
 
@@ -268,17 +404,49 @@ export async function requestByRange({
268
404
  if (columnsRequest) {
269
405
  requests.push(
270
406
  network.sendDataColumnSidecarsByRange(peerIdStr, columnsRequest).then((columnResponse) => {
271
- columnSidecars = columnResponse as fulu.DataColumnSidecar[];
407
+ columnSidecars.push(...columnResponse);
408
+ })
409
+ );
410
+ }
411
+
412
+ if (envelopesRequest) {
413
+ requests.push(
414
+ network.sendExecutionPayloadEnvelopesByRange(peerIdStr, envelopesRequest).then((envelopeResponse) => {
415
+ payloadEnvelopes.push(...envelopeResponse);
272
416
  })
273
417
  );
274
418
  }
275
419
 
420
+ // Only happens on the 1st batch of a SyncChain — fetches whichever pieces of the
421
+ // dangling-parent's payload are still missing from the seen-cache entry.
422
+ if (parentPayloadRequest?.envelopeBlockRoot) {
423
+ requests.push(
424
+ network
425
+ .sendExecutionPayloadEnvelopesByRoot(peerIdStr, [parentPayloadRequest.envelopeBlockRoot])
426
+ .then((envelopeResponse) => {
427
+ payloadEnvelopes.push(...envelopeResponse);
428
+ })
429
+ );
430
+ }
431
+ if (parentPayloadRequest?.blockRoot && parentPayloadRequest.columns && parentPayloadRequest.columns.length > 0) {
432
+ requests.push(
433
+ network
434
+ .sendDataColumnSidecarsByRoot(peerIdStr, [
435
+ {blockRoot: parentPayloadRequest.blockRoot, columns: parentPayloadRequest.columns},
436
+ ])
437
+ .then((columnResponse) => {
438
+ columnSidecars.push(...columnResponse);
439
+ })
440
+ );
441
+ }
442
+
276
443
  await Promise.all(requests);
277
444
 
278
445
  return {
279
446
  blocks,
280
447
  blobSidecars,
281
448
  columnSidecars,
449
+ payloadEnvelopes,
282
450
  };
283
451
  }
284
452
 
@@ -291,16 +459,26 @@ export async function validateResponses({
291
459
  blocksRequest,
292
460
  blobsRequest,
293
461
  columnsRequest,
462
+ envelopesRequest,
463
+ parentPayloadRequest,
464
+ parentPayloadCommitments,
294
465
  blocks,
295
466
  blobSidecars,
296
467
  columnSidecars,
468
+ payloadEnvelopes,
297
469
  peerDasMetrics,
298
470
  }: DownloadByRangeRequests &
299
471
  DownloadByRangeResponses & {
300
472
  config: ChainForkConfig;
301
473
  batchBlocks?: IBlockInput[];
474
+ parentPayloadCommitments?: ParentPayloadCommitments;
302
475
  peerDasMetrics?: BeaconMetrics["peerDas"] | null;
303
- }): Promise<WarnResult<ValidatedResponses, DownloadByRangeError>> {
476
+ }): Promise<
477
+ WarnResult<
478
+ {responses: ValidatedResponses; payloadEnvelopes: Map<Slot, gloas.SignedExecutionPayloadEnvelope> | null},
479
+ DownloadByRangeError
480
+ >
481
+ > {
304
482
  // Blocks are always required for blob/column validation
305
483
  // If a blocksRequest is provided, blocks have just been downloaded
306
484
  // If no blocksRequest is provided, batchBlocks must have been provided from cache
@@ -314,6 +492,11 @@ export async function validateResponses({
314
492
  );
315
493
  }
316
494
 
495
+ // `parentPayloadRequest` and `parentPayloadCommitments` must be supplied together
496
+ if ((parentPayloadRequest === undefined) !== (parentPayloadCommitments === undefined)) {
497
+ throw new Error("Coding error: parentPayloadRequest and parentPayloadCommitments must be both set or both unset");
498
+ }
499
+
317
500
  const validatedResponses: ValidatedResponses = {};
318
501
  let warnings: DownloadByRangeError[] | null = null;
319
502
 
@@ -325,9 +508,31 @@ export async function validateResponses({
325
508
  validatedResponses.validatedBlocks = result.result;
326
509
  }
327
510
 
511
+ const needsEnvelopeValidation = !!envelopesRequest || parentPayloadCommitments !== undefined;
328
512
  const dataRequest = blobsRequest ?? columnsRequest;
513
+ if (!dataRequest && !needsEnvelopeValidation) {
514
+ return {result: {responses: validatedResponses, payloadEnvelopes: null}, warnings};
515
+ }
516
+
329
517
  if (!dataRequest) {
330
- return {result: validatedResponses, warnings};
518
+ // Only envelope and/or parent-by-root validation needed
519
+ if (parentPayloadCommitments !== undefined) {
520
+ const parentValidated = await validateParentPayloadColumns(
521
+ parentPayloadCommitments,
522
+ columnSidecars ?? [],
523
+ peerDasMetrics
524
+ );
525
+ if (parentValidated) {
526
+ validatedResponses.validatedColumnSidecars = [parentValidated];
527
+ }
528
+ }
529
+ const validatedPayloadEnvelopes = validateEnvelopesByRangeResponse(
530
+ validatedResponses.validatedBlocks ?? [],
531
+ batchBlocks,
532
+ payloadEnvelopes ?? [],
533
+ parentPayloadCommitments
534
+ );
535
+ return {result: {responses: validatedResponses, payloadEnvelopes: validatedPayloadEnvelopes}, warnings};
331
536
  }
332
537
 
333
538
  const blocksForDataValidation = getBlocksForDataValidation(
@@ -385,7 +590,62 @@ export async function validateResponses({
385
590
  warnings = validatedColumnSidecarsResult.warnings;
386
591
  }
387
592
 
388
- return {result: validatedResponses, warnings};
593
+ // Parent columns (by-root): KZG-validate against parent's bid commitments and append.
594
+ if (parentPayloadCommitments !== undefined) {
595
+ const parentValidated = await validateParentPayloadColumns(
596
+ parentPayloadCommitments,
597
+ columnSidecars ?? [],
598
+ peerDasMetrics
599
+ );
600
+ if (parentValidated) {
601
+ validatedResponses.validatedColumnSidecars = [
602
+ ...(validatedResponses.validatedColumnSidecars ?? []),
603
+ parentValidated,
604
+ ];
605
+ }
606
+ }
607
+
608
+ let validatedPayloadEnvelopes: Map<Slot, gloas.SignedExecutionPayloadEnvelope> | null = null;
609
+ if (needsEnvelopeValidation) {
610
+ validatedPayloadEnvelopes = validateEnvelopesByRangeResponse(
611
+ validatedResponses.validatedBlocks ?? [],
612
+ batchBlocks,
613
+ payloadEnvelopes ?? [],
614
+ parentPayloadCommitments
615
+ );
616
+ }
617
+
618
+ return {result: {responses: validatedResponses, payloadEnvelopes: validatedPayloadEnvelopes}, warnings};
619
+ }
620
+
621
+ async function validateParentPayloadColumns(
622
+ parentPayloadCommitments: ParentPayloadCommitments,
623
+ columnSidecars: DataColumnSidecar[],
624
+ peerDasMetrics?: BeaconMetrics["peerDas"] | null
625
+ ): Promise<ValidatedColumnSidecars | null> {
626
+ const parentColumns: gloas.DataColumnSidecar[] = [];
627
+ for (const cs of columnSidecars) {
628
+ if (isGloasDataColumnSidecar(cs) && byteArrayEquals(cs.beaconBlockRoot, parentPayloadCommitments.blockRoot)) {
629
+ parentColumns.push(cs);
630
+ }
631
+ }
632
+
633
+ if (parentColumns.length === 0) {
634
+ return null;
635
+ }
636
+
637
+ parentColumns.sort((a, b) => a.index - b.index);
638
+ const parentSlot = parentColumns[0].slot;
639
+
640
+ await validateGloasBlockDataColumnSidecars(
641
+ parentSlot,
642
+ parentPayloadCommitments.blockRoot,
643
+ parentPayloadCommitments.kzgCommitments,
644
+ parentColumns,
645
+ peerDasMetrics
646
+ );
647
+
648
+ return {blockRoot: parentPayloadCommitments.blockRoot, columnSidecars: parentColumns};
389
649
  }
390
650
 
391
651
  /**
@@ -615,19 +875,19 @@ export async function validateColumnsByRangeResponse(
615
875
  config: ChainForkConfig,
616
876
  request: fulu.DataColumnSidecarsByRangeRequest,
617
877
  blocks: ValidatedBlock[],
618
- columnSidecars: fulu.DataColumnSidecar[],
878
+ columnSidecars: DataColumnSidecar[],
619
879
  peerDasMetrics?: BeaconMetrics["peerDas"] | null
620
880
  ): Promise<WarnResult<ValidatedColumnSidecars[], DownloadByRangeError>> {
621
881
  const warnings: DownloadByRangeError[] = [];
622
882
 
623
- // TODO GLOAS: Extend by range column sync to support gloas.DataColumnSidecar and
624
- // validate against the block bid commitments instead of the fulu signed header shape
625
- const seenColumns = new Map<Slot, Map<number, fulu.DataColumnSidecar>>();
883
+ const seenColumns = new Map<Slot, Map<number, DataColumnSidecar>>();
626
884
  let currentSlot = -1;
627
885
  let currentIndex = -1;
628
886
  // Check for duplicates and order
629
887
  for (const columnSidecar of columnSidecars) {
630
- const slot = columnSidecar.signedBlockHeader.message.slot;
888
+ const slot = isGloasDataColumnSidecar(columnSidecar)
889
+ ? columnSidecar.slot
890
+ : columnSidecar.signedBlockHeader.message.slot;
631
891
  let seenSlotColumns = seenColumns.get(slot);
632
892
  if (!seenSlotColumns) {
633
893
  seenSlotColumns = new Map();
@@ -686,20 +946,20 @@ export async function validateColumnsByRangeResponse(
686
946
  const slot = block.message.slot;
687
947
  const rootHex = toRootHex(blockRoot);
688
948
  const forkName = config.getForkName(slot);
689
- const columnSidecarsMap: Map<number, fulu.DataColumnSidecar> = seenColumns.get(slot) ?? new Map();
949
+ const columnSidecarsMap: Map<number, DataColumnSidecar> = seenColumns.get(slot) ?? new Map();
690
950
  const columnSidecars = Array.from(columnSidecarsMap.values()).sort((a, b) => a.index - b.index);
691
951
 
692
952
  let blobCount: number;
693
953
  if (!isForkPostFulu(forkName)) {
694
- const dataSlot = columnSidecars.at(0)?.signedBlockHeader.message.slot;
695
954
  throw new DownloadByRangeError({
696
955
  code: DownloadByRangeErrorCode.MISMATCH_BLOCK_FORK,
697
956
  slot,
698
957
  blockFork: forkName,
699
- dataFork: dataSlot ? config.getForkName(dataSlot) : "unknown",
958
+ dataFork: "unknown",
700
959
  });
701
960
  }
702
- blobCount = getBlobKzgCommitments(forkName, block as SignedBeaconBlock<ForkPostFulu>).length;
961
+ const kzgCommitments = getBlobKzgCommitments(forkName, block as SignedBeaconBlock<ForkPostFulu>);
962
+ blobCount = kzgCommitments.length;
703
963
 
704
964
  if (columnSidecars.length === 0) {
705
965
  if (!blobCount) {
@@ -768,15 +1028,25 @@ export async function validateColumnsByRangeResponse(
768
1028
  );
769
1029
  }
770
1030
 
1031
+ const validatePromise = isForkPostGloas(forkName)
1032
+ ? validateGloasBlockDataColumnSidecars(
1033
+ slot,
1034
+ blockRoot,
1035
+ kzgCommitments,
1036
+ columnSidecars as gloas.DataColumnSidecar[],
1037
+ peerDasMetrics
1038
+ )
1039
+ : validateFuluBlockDataColumnSidecars(
1040
+ null, // do not pass chain here so we do not validate header signature
1041
+ slot,
1042
+ blockRoot,
1043
+ blobCount,
1044
+ columnSidecars as fulu.DataColumnSidecar[],
1045
+ peerDasMetrics
1046
+ );
1047
+
771
1048
  validationPromises.push(
772
- validateFuluBlockDataColumnSidecars(
773
- null, // do not pass chain here so we do not validate header signature
774
- slot,
775
- blockRoot,
776
- blobCount,
777
- columnSidecars,
778
- peerDasMetrics
779
- ).then(() => ({
1049
+ validatePromise.then(() => ({
780
1050
  blockRoot,
781
1051
  columnSidecars,
782
1052
  }))
@@ -882,6 +1152,9 @@ export enum DownloadByRangeErrorCode {
882
1152
  /** Cached block input type mismatches new data */
883
1153
  MISMATCH_BLOCK_FORK = "DOWNLOAD_BY_RANGE_ERROR_MISMATCH_BLOCK_FORK",
884
1154
  MISMATCH_BLOCK_INPUT_TYPE = "DOWNLOAD_BY_RANGE_ERROR_MISMATCH_BLOCK_INPUT_TYPE",
1155
+
1156
+ /** Envelope beaconBlockRoot does not match the block's root */
1157
+ INVALID_ENVELOPE_BEACON_BLOCK_ROOT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_ENVELOPE_BEACON_BLOCK_ROOT",
885
1158
  }
886
1159
 
887
1160
  export type DownloadByRangeErrorType =
@@ -973,6 +1246,72 @@ export type DownloadByRangeErrorType =
973
1246
  blockRoot: string;
974
1247
  expected: DAType;
975
1248
  actual: DAType;
1249
+ }
1250
+ | {
1251
+ code: DownloadByRangeErrorCode.INVALID_ENVELOPE_BEACON_BLOCK_ROOT;
1252
+ slot: Slot;
1253
+ expected: string;
1254
+ actual: string;
976
1255
  };
977
1256
 
978
1257
  export class DownloadByRangeError extends LodestarError<DownloadByRangeErrorType> {}
1258
+
1259
+ /**
1260
+ * Validates SignedExecutionPayloadEnvelopes received for a range request.
1261
+ *
1262
+ * Three categories of envelope slots:
1263
+ * - In-batch slot whose block we have: verify envelope.beaconBlockRoot matches.
1264
+ * - Dangling-parent envelope (only when `parentPayloadCommitments` is set, i.e. the first
1265
+ * batch of a `SyncChain`): keep if `envelope.beaconBlockRoot === parentPayloadCommitments.blockRoot`.
1266
+ * - Other "orphan" envelopes (e.g. unrelated slots): ignored.
1267
+ */
1268
+ export function validateEnvelopesByRangeResponse(
1269
+ validatedBlocks: ValidatedBlock[],
1270
+ batchBlocks: IBlockInput[] | undefined,
1271
+ payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[],
1272
+ parentPayloadCommitments?: ParentPayloadCommitments
1273
+ ): Map<Slot, gloas.SignedExecutionPayloadEnvelope> {
1274
+ // Build a map of slot -> blockRoot for all blocks in the batch
1275
+ const batchBlockRoots = new Map<Slot, Uint8Array>();
1276
+ if (batchBlocks) {
1277
+ for (const blockInput of batchBlocks) {
1278
+ batchBlockRoots.set(blockInput.slot, fromHex(blockInput.blockRootHex));
1279
+ }
1280
+ }
1281
+ for (const {block, blockRoot} of validatedBlocks) {
1282
+ batchBlockRoots.set(block.message.slot, blockRoot);
1283
+ }
1284
+
1285
+ const payloadEnvelopeMap = new Map<Slot, gloas.SignedExecutionPayloadEnvelope>();
1286
+
1287
+ for (const payloadEnvelope of payloadEnvelopes) {
1288
+ const slot = payloadEnvelope.message.payload.slotNumber;
1289
+ const batchBlockRoot = batchBlockRoots.get(slot);
1290
+
1291
+ if (batchBlockRoot === undefined) {
1292
+ // Keep the requested dangling-parent envelope only when its beaconBlockRoot matches
1293
+ // exactly. All other unrelated envelopes are dropped.
1294
+ if (
1295
+ parentPayloadCommitments !== undefined &&
1296
+ byteArrayEquals(payloadEnvelope.message.beaconBlockRoot, parentPayloadCommitments.blockRoot)
1297
+ ) {
1298
+ payloadEnvelopeMap.set(slot, payloadEnvelope);
1299
+ }
1300
+ continue;
1301
+ }
1302
+
1303
+ // Verify beaconBlockRoot matches the block's root
1304
+ if (!byteArrayEquals(payloadEnvelope.message.beaconBlockRoot, batchBlockRoot)) {
1305
+ throw new DownloadByRangeError({
1306
+ code: DownloadByRangeErrorCode.INVALID_ENVELOPE_BEACON_BLOCK_ROOT,
1307
+ slot,
1308
+ expected: toRootHex(batchBlockRoot),
1309
+ actual: toRootHex(payloadEnvelope.message.beaconBlockRoot),
1310
+ });
1311
+ }
1312
+
1313
+ payloadEnvelopeMap.set(slot, payloadEnvelope);
1314
+ }
1315
+
1316
+ return payloadEnvelopeMap;
1317
+ }
@@ -1,6 +1,14 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
- import {ForkPostDeneb, ForkPostFulu, ForkPreFulu, isForkPostDeneb, isForkPostFulu} from "@lodestar/params";
3
+ import {
4
+ ForkPostDeneb,
5
+ ForkPostFulu,
6
+ ForkPostGloas,
7
+ ForkPreFulu,
8
+ isForkPostDeneb,
9
+ isForkPostFulu,
10
+ isForkPostGloas,
11
+ } from "@lodestar/params";
4
12
  import {BlobIndex, ColumnIndex, SignedBeaconBlock, Slot, deneb, fulu} from "@lodestar/types";
5
13
  import {LodestarError, byteArrayEquals, fromHex, prettyPrintIndices, toHex, toRootHex} from "@lodestar/utils";
6
14
  import {isBlockInputBlobs, isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
@@ -107,6 +115,17 @@ export async function downloadByRoot({
107
115
  });
108
116
  }
109
117
 
118
+ if (isForkPostGloas(blockInput.forkName)) {
119
+ chain.seenPayloadEnvelopeInputCache.add({
120
+ blockRootHex: rootHex,
121
+ block: blockInput.getBlock() as SignedBeaconBlock<ForkPostGloas>,
122
+ forkName: blockInput.forkName,
123
+ sampledColumns: chain.custodyConfig.sampledColumns,
124
+ custodyColumns: chain.custodyConfig.custodyColumns,
125
+ timeCreatedSec: Date.now() / 1000,
126
+ });
127
+ }
128
+
110
129
  const hasAllDataPreDownload = blockInput.hasBlockAndAllData();
111
130
 
112
131
  if (isBlockInputBlobs(blockInput) && !hasAllDataPreDownload) {
@@ -263,7 +282,10 @@ export async function fetchByRoot({
263
282
  blockRoot,
264
283
  });
265
284
  const forkName = config.getForkName(block.message.slot);
266
- if (isForkPostFulu(forkName)) {
285
+ if (isForkPostGloas(forkName)) {
286
+ // Post-gloas block sync only needs the block body. Payload columns stay on the
287
+ // payload/envelope path and are queued independently in the network processor.
288
+ } else if (isForkPostFulu(forkName)) {
267
289
  columnSidecarResult = await fetchAndValidateColumns({
268
290
  config,
269
291
  chain,