@lodestar/beacon-node 1.43.0-dev.9c8becae00 → 1.43.0-dev.9f5db5b9c7

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 (331) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +17 -9
  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/beacon/state/utils.d.ts +2 -2
  8. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  9. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  10. package/lib/api/impl/debug/index.d.ts.map +1 -1
  11. package/lib/api/impl/debug/index.js +0 -1
  12. package/lib/api/impl/debug/index.js.map +1 -1
  13. package/lib/api/impl/lodestar/index.js +1 -1
  14. package/lib/api/impl/lodestar/index.js.map +1 -1
  15. package/lib/api/impl/validator/index.d.ts.map +1 -1
  16. package/lib/api/impl/validator/index.js +68 -6
  17. package/lib/api/impl/validator/index.js.map +1 -1
  18. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  19. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  20. package/lib/chain/archiveStore/interface.d.ts +4 -4
  21. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  22. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts +4 -4
  23. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
  24. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
  25. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +2 -2
  26. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  27. package/lib/chain/archiveStore/utils/archiveBlocks.js +110 -58
  28. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  29. package/lib/chain/blocks/blockInput/blockInput.d.ts +3 -0
  30. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  31. package/lib/chain/blocks/blockInput/blockInput.js +4 -1
  32. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  33. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  34. package/lib/chain/blocks/importBlock.js +24 -20
  35. package/lib/chain/blocks/importBlock.js.map +1 -1
  36. package/lib/chain/blocks/importExecutionPayload.d.ts +28 -14
  37. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  38. package/lib/chain/blocks/importExecutionPayload.js +88 -90
  39. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  40. package/lib/chain/blocks/index.d.ts +5 -3
  41. package/lib/chain/blocks/index.d.ts.map +1 -1
  42. package/lib/chain/blocks/index.js +59 -26
  43. package/lib/chain/blocks/index.js.map +1 -1
  44. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +4 -0
  45. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  46. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +9 -2
  47. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  48. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -0
  49. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  50. package/lib/chain/blocks/payloadEnvelopeProcessor.js +2 -2
  51. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  52. package/lib/chain/blocks/types.d.ts +15 -20
  53. package/lib/chain/blocks/types.d.ts.map +1 -1
  54. package/lib/chain/blocks/utils/chainSegment.d.ts +23 -2
  55. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  56. package/lib/chain/blocks/utils/chainSegment.js +89 -12
  57. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  58. package/lib/chain/blocks/verifyBlock.d.ts +5 -3
  59. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  60. package/lib/chain/blocks/verifyBlock.js +50 -7
  61. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  62. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +0 -4
  63. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  64. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -2
  65. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  66. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +2 -1
  67. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  68. package/lib/chain/blocks/verifyBlocksSanityChecks.js +25 -5
  69. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  70. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +24 -0
  71. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -0
  72. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +79 -0
  73. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -0
  74. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  75. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
  76. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  77. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts +1 -1
  78. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -1
  79. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +2 -11
  80. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -1
  81. package/lib/chain/chain.d.ts +8 -6
  82. package/lib/chain/chain.d.ts.map +1 -1
  83. package/lib/chain/chain.js +21 -6
  84. package/lib/chain/chain.js.map +1 -1
  85. package/lib/chain/emitter.d.ts +3 -14
  86. package/lib/chain/emitter.d.ts.map +1 -1
  87. package/lib/chain/emitter.js +0 -4
  88. package/lib/chain/emitter.js.map +1 -1
  89. package/lib/chain/errors/blockError.d.ts +8 -1
  90. package/lib/chain/errors/blockError.d.ts.map +1 -1
  91. package/lib/chain/errors/blockError.js +2 -0
  92. package/lib/chain/errors/blockError.js.map +1 -1
  93. package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
  94. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  95. package/lib/chain/errors/executionPayloadBid.js +1 -0
  96. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  97. package/lib/chain/errors/executionPayloadEnvelope.d.ts +5 -0
  98. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  99. package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
  100. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  101. package/lib/chain/errors/index.d.ts +1 -0
  102. package/lib/chain/errors/index.d.ts.map +1 -1
  103. package/lib/chain/errors/index.js +1 -0
  104. package/lib/chain/errors/index.js.map +1 -1
  105. package/lib/chain/errors/proposerPreferences.d.ts +40 -0
  106. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
  107. package/lib/chain/errors/proposerPreferences.js +14 -0
  108. package/lib/chain/errors/proposerPreferences.js.map +1 -0
  109. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  110. package/lib/chain/forkChoice/index.js +5 -17
  111. package/lib/chain/forkChoice/index.js.map +1 -1
  112. package/lib/chain/interface.d.ts +7 -5
  113. package/lib/chain/interface.d.ts.map +1 -1
  114. package/lib/chain/interface.js.map +1 -1
  115. package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
  116. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  117. package/lib/chain/opPools/payloadAttestationPool.js +26 -4
  118. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  119. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  120. package/lib/chain/prepareNextSlot.js +31 -13
  121. package/lib/chain/prepareNextSlot.js.map +1 -1
  122. package/lib/chain/produceBlock/produceBlockBody.d.ts +11 -1
  123. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  124. package/lib/chain/produceBlock/produceBlockBody.js +52 -14
  125. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  126. package/lib/chain/regen/interface.d.ts +1 -0
  127. package/lib/chain/regen/interface.d.ts.map +1 -1
  128. package/lib/chain/regen/interface.js +1 -0
  129. package/lib/chain/regen/interface.js.map +1 -1
  130. package/lib/chain/regen/queued.d.ts.map +1 -1
  131. package/lib/chain/regen/queued.js +1 -4
  132. package/lib/chain/regen/queued.js.map +1 -1
  133. package/lib/chain/regen/regen.d.ts.map +1 -1
  134. package/lib/chain/regen/regen.js +1 -4
  135. package/lib/chain/regen/regen.js.map +1 -1
  136. package/lib/chain/seenCache/index.d.ts +1 -0
  137. package/lib/chain/seenCache/index.d.ts.map +1 -1
  138. package/lib/chain/seenCache/index.js +1 -0
  139. package/lib/chain/seenCache/index.js.map +1 -1
  140. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +19 -6
  141. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  142. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +40 -22
  143. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  144. package/lib/chain/seenCache/seenProposerPreferences.d.ts +16 -0
  145. package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
  146. package/lib/chain/seenCache/seenProposerPreferences.js +26 -0
  147. package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
  148. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  149. package/lib/chain/stateCache/persistentCheckpointsCache.js +4 -1
  150. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  151. package/lib/chain/validation/block.d.ts.map +1 -1
  152. package/lib/chain/validation/block.js +1 -0
  153. package/lib/chain/validation/block.js.map +1 -1
  154. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  155. package/lib/chain/validation/executionPayloadBid.js +24 -9
  156. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  157. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  158. package/lib/chain/validation/executionPayloadEnvelope.js +19 -9
  159. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  160. package/lib/chain/validation/proposerPreferences.d.ts +8 -0
  161. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -0
  162. package/lib/chain/validation/proposerPreferences.js +91 -0
  163. package/lib/chain/validation/proposerPreferences.js.map +1 -0
  164. package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
  165. package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
  166. package/lib/execution/engine/http.d.ts.map +1 -1
  167. package/lib/execution/engine/http.js +21 -14
  168. package/lib/execution/engine/http.js.map +1 -1
  169. package/lib/execution/engine/interface.d.ts +1 -0
  170. package/lib/execution/engine/interface.d.ts.map +1 -1
  171. package/lib/execution/engine/mock.d.ts.map +1 -1
  172. package/lib/execution/engine/mock.js +6 -0
  173. package/lib/execution/engine/mock.js.map +1 -1
  174. package/lib/execution/engine/types.d.ts +20 -0
  175. package/lib/execution/engine/types.d.ts.map +1 -1
  176. package/lib/execution/engine/types.js +18 -0
  177. package/lib/execution/engine/types.js.map +1 -1
  178. package/lib/metrics/metrics/lodestar.d.ts +1 -0
  179. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  180. package/lib/metrics/metrics/lodestar.js +4 -0
  181. package/lib/metrics/metrics/lodestar.js.map +1 -1
  182. package/lib/network/gossip/interface.d.ts +7 -1
  183. package/lib/network/gossip/interface.d.ts.map +1 -1
  184. package/lib/network/gossip/interface.js +1 -0
  185. package/lib/network/gossip/interface.js.map +1 -1
  186. package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
  187. package/lib/network/gossip/scoringParameters.js +12 -1
  188. package/lib/network/gossip/scoringParameters.js.map +1 -1
  189. package/lib/network/gossip/topic.d.ts +13 -2
  190. package/lib/network/gossip/topic.d.ts.map +1 -1
  191. package/lib/network/gossip/topic.js +6 -0
  192. package/lib/network/gossip/topic.js.map +1 -1
  193. package/lib/network/interface.d.ts +1 -0
  194. package/lib/network/interface.d.ts.map +1 -1
  195. package/lib/network/network.d.ts +1 -0
  196. package/lib/network/network.d.ts.map +1 -1
  197. package/lib/network/network.js +6 -1
  198. package/lib/network/network.js.map +1 -1
  199. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  200. package/lib/network/processor/gossipHandlers.js +27 -19
  201. package/lib/network/processor/gossipHandlers.js.map +1 -1
  202. package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
  203. package/lib/network/processor/gossipQueues/index.js +5 -0
  204. package/lib/network/processor/gossipQueues/index.js.map +1 -1
  205. package/lib/network/processor/index.d.ts.map +1 -1
  206. package/lib/network/processor/index.js +6 -5
  207. package/lib/network/processor/index.js.map +1 -1
  208. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  209. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -6
  210. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  211. package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
  212. package/lib/network/reqresp/handlers/blobSidecarsByRange.js +11 -5
  213. package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
  214. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  215. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +17 -5
  216. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  217. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  218. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +7 -4
  219. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  220. package/lib/node/nodejs.js +2 -2
  221. package/lib/node/nodejs.js.map +1 -1
  222. package/lib/sync/range/batch.d.ts +23 -2
  223. package/lib/sync/range/batch.d.ts.map +1 -1
  224. package/lib/sync/range/batch.js +132 -44
  225. package/lib/sync/range/batch.js.map +1 -1
  226. package/lib/sync/range/chain.d.ts +6 -2
  227. package/lib/sync/range/chain.d.ts.map +1 -1
  228. package/lib/sync/range/chain.js +26 -7
  229. package/lib/sync/range/chain.js.map +1 -1
  230. package/lib/sync/range/range.d.ts.map +1 -1
  231. package/lib/sync/range/range.js +17 -6
  232. package/lib/sync/range/range.js.map +1 -1
  233. package/lib/sync/types.d.ts +34 -0
  234. package/lib/sync/types.d.ts.map +1 -1
  235. package/lib/sync/types.js +34 -0
  236. package/lib/sync/types.js.map +1 -1
  237. package/lib/sync/unknownBlock.d.ts +22 -1
  238. package/lib/sync/unknownBlock.d.ts.map +1 -1
  239. package/lib/sync/unknownBlock.js +602 -53
  240. package/lib/sync/unknownBlock.js.map +1 -1
  241. package/lib/sync/utils/downloadByRange.d.ts +46 -10
  242. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  243. package/lib/sync/utils/downloadByRange.js +162 -24
  244. package/lib/sync/utils/downloadByRange.js.map +1 -1
  245. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  246. package/lib/sync/utils/downloadByRoot.js +16 -2
  247. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  248. package/lib/sync/utils/pendingBlocksTree.d.ts +0 -1
  249. package/lib/sync/utils/pendingBlocksTree.d.ts.map +1 -1
  250. package/lib/sync/utils/pendingBlocksTree.js +0 -9
  251. package/lib/sync/utils/pendingBlocksTree.js.map +1 -1
  252. package/lib/util/sszBytes.d.ts.map +1 -1
  253. package/lib/util/sszBytes.js +20 -5
  254. package/lib/util/sszBytes.js.map +1 -1
  255. package/package.json +16 -15
  256. package/src/api/impl/beacon/blocks/index.ts +22 -9
  257. package/src/api/impl/beacon/pool/index.ts +83 -1
  258. package/src/api/impl/beacon/state/utils.ts +2 -2
  259. package/src/api/impl/debug/index.ts +0 -1
  260. package/src/api/impl/lodestar/index.ts +1 -1
  261. package/src/api/impl/validator/index.ts +82 -5
  262. package/src/chain/archiveStore/archiveStore.ts +5 -5
  263. package/src/chain/archiveStore/interface.ts +4 -4
  264. package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +4 -4
  265. package/src/chain/archiveStore/utils/archiveBlocks.ts +153 -94
  266. package/src/chain/blocks/blockInput/blockInput.ts +4 -1
  267. package/src/chain/blocks/importBlock.ts +24 -38
  268. package/src/chain/blocks/importExecutionPayload.ts +109 -105
  269. package/src/chain/blocks/index.ts +73 -23
  270. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +10 -2
  271. package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -0
  272. package/src/chain/blocks/payloadEnvelopeProcessor.ts +2 -2
  273. package/src/chain/blocks/types.ts +15 -25
  274. package/src/chain/blocks/utils/chainSegment.ts +114 -17
  275. package/src/chain/blocks/verifyBlock.ts +70 -9
  276. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
  277. package/src/chain/blocks/verifyBlocksSanityChecks.ts +26 -7
  278. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +134 -0
  279. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
  280. package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +9 -18
  281. package/src/chain/chain.ts +38 -19
  282. package/src/chain/emitter.ts +3 -14
  283. package/src/chain/errors/blockError.ts +4 -1
  284. package/src/chain/errors/executionPayloadBid.ts +6 -0
  285. package/src/chain/errors/executionPayloadEnvelope.ts +6 -0
  286. package/src/chain/errors/index.ts +1 -0
  287. package/src/chain/errors/proposerPreferences.ts +47 -0
  288. package/src/chain/forkChoice/index.ts +2 -22
  289. package/src/chain/interface.ts +11 -3
  290. package/src/chain/opPools/payloadAttestationPool.ts +29 -8
  291. package/src/chain/prepareNextSlot.ts +36 -14
  292. package/src/chain/produceBlock/produceBlockBody.ts +63 -13
  293. package/src/chain/regen/interface.ts +1 -0
  294. package/src/chain/regen/queued.ts +2 -7
  295. package/src/chain/regen/regen.ts +2 -7
  296. package/src/chain/seenCache/index.ts +1 -0
  297. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +47 -25
  298. package/src/chain/seenCache/seenProposerPreferences.ts +32 -0
  299. package/src/chain/stateCache/persistentCheckpointsCache.ts +4 -1
  300. package/src/chain/validation/block.ts +1 -0
  301. package/src/chain/validation/executionPayloadBid.ts +25 -8
  302. package/src/chain/validation/executionPayloadEnvelope.ts +20 -10
  303. package/src/chain/validation/proposerPreferences.ts +110 -0
  304. package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
  305. package/src/execution/engine/http.ts +21 -14
  306. package/src/execution/engine/interface.ts +1 -0
  307. package/src/execution/engine/mock.ts +8 -1
  308. package/src/execution/engine/types.ts +41 -0
  309. package/src/metrics/metrics/lodestar.ts +4 -0
  310. package/src/network/gossip/interface.ts +6 -0
  311. package/src/network/gossip/scoringParameters.ts +14 -1
  312. package/src/network/gossip/topic.ts +6 -0
  313. package/src/network/interface.ts +1 -0
  314. package/src/network/network.ts +12 -1
  315. package/src/network/processor/gossipHandlers.ts +38 -20
  316. package/src/network/processor/gossipQueues/index.ts +5 -0
  317. package/src/network/processor/index.ts +6 -5
  318. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +14 -6
  319. package/src/network/reqresp/handlers/blobSidecarsByRange.ts +11 -5
  320. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -5
  321. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +7 -4
  322. package/src/node/nodejs.ts +2 -2
  323. package/src/sync/range/batch.ts +188 -49
  324. package/src/sync/range/chain.ts +37 -9
  325. package/src/sync/range/range.ts +18 -6
  326. package/src/sync/types.ts +72 -0
  327. package/src/sync/unknownBlock.ts +760 -57
  328. package/src/sync/utils/downloadByRange.ts +272 -39
  329. package/src/sync/utils/downloadByRoot.ts +24 -2
  330. package/src/sync/utils/pendingBlocksTree.ts +0 -15
  331. package/src/util/sszBytes.ts +25 -5
@@ -46,6 +46,7 @@ import {
46
46
  electra,
47
47
  fulu,
48
48
  gloas,
49
+ ssz,
49
50
  } from "@lodestar/types";
50
51
  import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
51
52
  import {ZERO_HASH_HEX} from "../../constants/index.js";
@@ -110,6 +111,7 @@ export type ProduceFullGloas = {
110
111
  executionRequests: electra.ExecutionRequests;
111
112
  blobsBundle: BlobsBundle<ForkPostGloas>;
112
113
  cells: fulu.Cell[][];
114
+ parentBlockRoot: Root;
113
115
  };
114
116
  export type ProduceFullFulu = {
115
117
  type: BlockType.Full;
@@ -213,9 +215,19 @@ export async function produceBlockBody<T extends BlockType>(
213
215
  });
214
216
 
215
217
  // Get execution payload from EL
216
- const parentBlockHash = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot))
217
- ? currentState.latestExecutionPayloadBid.blockHash
218
- : currentState.latestExecutionPayloadBid.parentBlockHash;
218
+ let parentBlockHash: Bytes32;
219
+ let parentExecutionRequests: electra.ExecutionRequests;
220
+ // Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
221
+ let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
222
+ const isExtendingPayload = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot));
223
+ if (isExtendingPayload) {
224
+ parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
225
+ parentExecutionRequests = await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot);
226
+ stateAfterParentPayload = currentState.withParentPayloadApplied(parentExecutionRequests);
227
+ } else {
228
+ parentBlockHash = currentState.latestExecutionPayloadBid.parentBlockHash;
229
+ parentExecutionRequests = ssz.electra.ExecutionRequests.defaultValue();
230
+ }
219
231
  const prepareRes = await prepareExecutionPayload(
220
232
  this,
221
233
  this.logger,
@@ -224,7 +236,7 @@ export async function produceBlockBody<T extends BlockType>(
224
236
  parentBlockHash,
225
237
  safeBlockHash,
226
238
  finalizedBlockHash ?? ZERO_HASH_HEX,
227
- currentState,
239
+ stateAfterParentPayload,
228
240
  feeRecipient
229
241
  );
230
242
 
@@ -269,6 +281,7 @@ export async function produceBlockBody<T extends BlockType>(
269
281
  value: 0,
270
282
  executionPayment: 0,
271
283
  blobKzgCommitments: blobsBundle.commitments,
284
+ executionRequestsRoot: ssz.electra.ExecutionRequests.hashTreeRoot(executionRequests),
272
285
  };
273
286
  const signedBid: gloas.SignedExecutionPayloadBid = {
274
287
  message: bid,
@@ -278,8 +291,19 @@ export async function produceBlockBody<T extends BlockType>(
278
291
  const commonBlockBody = await commonBlockBodyPromise;
279
292
  const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
280
293
  gloasBody.signedExecutionPayloadBid = signedBid;
281
- // TODO GLOAS: Get payload attestations from pool for previous slot
282
- gloasBody.payloadAttestations = [];
294
+ gloasBody.payloadAttestations = this.payloadAttestationPool.getPayloadAttestationsForBlock(
295
+ parentBlock.blockRoot,
296
+ blockSlot - 1
297
+ );
298
+ gloasBody.parentExecutionRequests = parentExecutionRequests;
299
+ // Drop voluntary exits that parent_execution_requests have invalidated (e.g. a withdrawal
300
+ // request initiating an exit on the same validator). Op pool selected against the unapplied
301
+ // state, so re-validate against the post-apply state to avoid producing an invalid block.
302
+ if (isExtendingPayload && commonBlockBody.voluntaryExits.length > 0) {
303
+ gloasBody.voluntaryExits = commonBlockBody.voluntaryExits.filter((signedVoluntaryExit) =>
304
+ stateAfterParentPayload.isValidVoluntaryExit(signedVoluntaryExit, false)
305
+ );
306
+ }
283
307
  blockBody = gloasBody as AssembledBodyType<T>;
284
308
 
285
309
  // Store execution payload data required to construct execution payload envelope later
@@ -288,6 +312,7 @@ export async function produceBlockBody<T extends BlockType>(
288
312
  gloasResult.executionRequests = executionRequests;
289
313
  gloasResult.blobsBundle = blobsBundle;
290
314
  gloasResult.cells = cells;
315
+ gloasResult.parentBlockRoot = fromHex(parentBlock.blockRoot);
291
316
 
292
317
  const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime);
293
318
  this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime);
@@ -615,6 +640,10 @@ export async function prepareExecutionPayload(
615
640
  parentBlockHash: Bytes32,
616
641
  safeBlockHash: RootHex,
617
642
  finalizedBlockHash: RootHex,
643
+ /**
644
+ * Post-gloas, when extending a full parent, callers must apply
645
+ * parent execution payload first (see `withParentPayloadApplied`).
646
+ */
618
647
  state: IBeaconStateViewBellatrix,
619
648
  suggestedFeeRecipient: string
620
649
  ): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
@@ -712,6 +741,10 @@ export function getPayloadAttributesForSSE(
712
741
  parentBlockHash,
713
742
  feeRecipient,
714
743
  }: {
744
+ /**
745
+ * Post-gloas, when extending a full parent, callers must apply
746
+ * parent execution payload first (see `withParentPayloadApplied`).
747
+ */
715
748
  prepareState: IBeaconStateViewBellatrix;
716
749
  prepareSlot: Slot;
717
750
  parentBlockRoot: Root;
@@ -764,6 +797,10 @@ function preparePayloadAttributes(
764
797
  parentBlockHash,
765
798
  feeRecipient,
766
799
  }: {
800
+ /**
801
+ * Post-gloas, when extending a full parent, callers must apply
802
+ * parent execution payload first (see `withParentPayloadApplied`).
803
+ */
767
804
  prepareState: IBeaconStateViewBellatrix;
768
805
  prepareSlot: Slot;
769
806
  parentBlockRoot: Root;
@@ -786,13 +823,22 @@ function preparePayloadAttributes(
786
823
 
787
824
  if (isStatePostGloas(prepareState)) {
788
825
  const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
789
- // When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
790
- // already deducted from CL balances but never credited on the EL (the envelope
791
- // was not delivered). The next payload must carry those same withdrawals to
792
- // restore CL/EL consistency, otherwise validators permanently lose that balance.
793
- (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals = isExtendingPayload
794
- ? prepareState.getExpectedWithdrawals().expectedWithdrawals
795
- : prepareState.payloadExpectedWithdrawals;
826
+ if (isExtendingPayload) {
827
+ // applyParentExecutionPayload sets latestBlockHash = parentBid.blockHash, so a mismatch
828
+ // here means the caller did not apply parent payload to prepareState
829
+ if (!byteArrayEquals(prepareState.latestBlockHash, prepareState.latestExecutionPayloadBid.blockHash)) {
830
+ throw new Error("Expected state with parent execution payload applied for withdrawals");
831
+ }
832
+ (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
833
+ prepareState.getExpectedWithdrawals().expectedWithdrawals;
834
+ } else {
835
+ // When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
836
+ // already deducted from CL balances but never credited on the EL (the envelope
837
+ // was not delivered). The next payload must carry those same withdrawals to
838
+ // restore CL/EL consistency, otherwise validators permanently lose that balance.
839
+ (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
840
+ prepareState.payloadExpectedWithdrawals;
841
+ }
796
842
  } else {
797
843
  // withdrawals logic is now fork aware as it changes on electra fork post capella
798
844
  (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
@@ -804,6 +850,10 @@ function preparePayloadAttributes(
804
850
  (payloadAttributes as deneb.SSEPayloadAttributes["payloadAttributes"]).parentBeaconBlockRoot = parentBlockRoot;
805
851
  }
806
852
 
853
+ if (ForkSeq[fork] >= ForkSeq.gloas) {
854
+ (payloadAttributes as gloas.SSEPayloadAttributes["payloadAttributes"]).slotNumber = prepareSlot;
855
+ }
856
+
807
857
  return payloadAttributes;
808
858
  }
809
859
 
@@ -21,6 +21,7 @@ export enum RegenCaller {
21
21
  validateGossipAttestation = "validateGossipAttestation",
22
22
  validateGossipVoluntaryExit = "validateGossipVoluntaryExit",
23
23
  validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid",
24
+ validateGossipProposerPreferences = "validateGossipProposerPreferences",
24
25
  onForkChoiceFinalized = "onForkChoiceFinalized",
25
26
  restApi = "restApi",
26
27
  }
@@ -1,7 +1,7 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
3
3
  import {IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
4
- import {BeaconBlock, Epoch, RootHex, Slot, isGloasBeaconBlock, phase0} from "@lodestar/types";
4
+ import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
5
5
  import {Logger, toRootHex} from "@lodestar/utils";
6
6
  import {Metrics} from "../../metrics/index.js";
7
7
  import {JobItemQueue} from "../../util/queue/index.js";
@@ -88,12 +88,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
88
88
  */
89
89
  getPreStateSync(block: BeaconBlock): IBeaconStateView | null {
90
90
  const parentRoot = toRootHex(block.parentRoot);
91
- const parentBlock = isGloasBeaconBlock(block)
92
- ? this.forkChoice.getBlockHexAndBlockHash(
93
- parentRoot,
94
- toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
95
- )
96
- : this.forkChoice.getBlockHexDefaultStatus(parentRoot);
91
+ const parentBlock = this.forkChoice.getBlockHexDefaultStatus(parentRoot);
97
92
  if (!parentBlock) {
98
93
  throw new RegenError({
99
94
  code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,
@@ -9,7 +9,7 @@ import {
9
9
  computeEpochAtSlot,
10
10
  computeStartSlotAtEpoch,
11
11
  } from "@lodestar/state-transition";
12
- import {BeaconBlock, RootHex, SignedBeaconBlock, Slot, isGloasBeaconBlock} from "@lodestar/types";
12
+ import {BeaconBlock, RootHex, SignedBeaconBlock, Slot} from "@lodestar/types";
13
13
  import {Logger, fromHex, toRootHex} from "@lodestar/utils";
14
14
  import {IBeaconDb} from "../../db/index.js";
15
15
  import {Metrics} from "../../metrics/index.js";
@@ -57,12 +57,7 @@ export class StateRegenerator implements IStateRegeneratorInternal {
57
57
  regenCaller: RegenCaller
58
58
  ): Promise<IBeaconStateView> {
59
59
  const parentRoot = toRootHex(block.parentRoot);
60
- const parentBlock = isGloasBeaconBlock(block)
61
- ? this.modules.forkChoice.getBlockHexAndBlockHash(
62
- parentRoot,
63
- toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
64
- )
65
- : this.modules.forkChoice.getBlockHexDefaultStatus(parentRoot);
60
+ const parentBlock = this.modules.forkChoice.getBlockHexDefaultStatus(parentRoot);
66
61
  if (!parentBlock) {
67
62
  throw new RegenError({
68
63
  code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,
@@ -5,3 +5,4 @@ export {SeenContributionAndProof} from "./seenCommitteeContribution.js";
5
5
  export {SeenExecutionPayloadBids} from "./seenExecutionPayloadBids.js";
6
6
  export {SeenBlockInput} from "./seenGossipBlockInput.js";
7
7
  export {PayloadEnvelopeInput, SeenPayloadEnvelopeInput} from "./seenPayloadEnvelopeInput.js";
8
+ export {SeenProposerPreferences} from "./seenProposerPreferences.js";
@@ -1,9 +1,12 @@
1
+ import {ChainForkConfig} from "@lodestar/config";
1
2
  import {CheckpointWithHex} from "@lodestar/fork-choice";
2
3
  import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
3
- import {RootHex} from "@lodestar/types";
4
+ import {RootHex, Slot} from "@lodestar/types";
4
5
  import {Logger} from "@lodestar/utils";
5
6
  import {Metrics} from "../../metrics/metrics.js";
7
+ import {IClock} from "../../util/clock.js";
6
8
  import {SerializedCache} from "../../util/serializedCache.js";
9
+ import {isDaOutOfRange} from "../blocks/blockInput/index.js";
7
10
  import {CreateFromBlockProps, PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
8
11
  import {ChainEvent, ChainEventEmitter} from "../emitter.js";
9
12
 
@@ -11,6 +14,8 @@ export type {PayloadEnvelopeInputState} from "../blocks/payloadEnvelopeInput/ind
11
14
  export {PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
12
15
 
13
16
  export type SeenPayloadEnvelopeInputModules = {
17
+ config: ChainForkConfig;
18
+ clock: IClock;
14
19
  chainEvents: ChainEventEmitter;
15
20
  signal: AbortSignal;
16
21
  serializedCache: SerializedCache;
@@ -21,10 +26,19 @@ export type SeenPayloadEnvelopeInputModules = {
21
26
  /**
22
27
  * Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root.
23
28
  *
24
- * Created during block import when a block is processed.
25
- * Pruned on finalization and after payload is written to DB.
29
+ * Created during block import when a block is processed. Two pruning paths:
30
+ * - `prepareNextSlot` calls `pruneBelow(headParentSlot)` every slot once the head we'll build
31
+ * on is known.
32
+ * - `onFinalized` calls `pruneBelow(finalizedSlot)` on every finalization for bulk cleanup.
33
+ *
34
+ * Steady state (linear chain, healthy progression): the cache holds ~2 entries — the head
35
+ * (parent for next-slot production) and its parent (proposer-boost-reorg fallback). It can
36
+ * transiently hold more during forks, range-sync bursts, or when `prepareNextSlot` skips
37
+ * ticks; subsequent ticks settle it back.
26
38
  */
27
39
  export class SeenPayloadEnvelopeInput {
40
+ private readonly config: ChainForkConfig;
41
+ private readonly clock: IClock;
28
42
  private readonly chainEvents: ChainEventEmitter;
29
43
  private readonly signal: AbortSignal;
30
44
  private readonly serializedCache: SerializedCache;
@@ -32,7 +46,9 @@ export class SeenPayloadEnvelopeInput {
32
46
  private readonly logger?: Logger;
33
47
  private payloadInputs = new Map<RootHex, PayloadEnvelopeInput>();
34
48
 
35
- constructor({chainEvents, signal, serializedCache, metrics, logger}: SeenPayloadEnvelopeInputModules) {
49
+ constructor({config, clock, chainEvents, signal, serializedCache, metrics, logger}: SeenPayloadEnvelopeInputModules) {
50
+ this.config = config;
51
+ this.clock = clock;
36
52
  this.chainEvents = chainEvents;
37
53
  this.signal = signal;
38
54
  this.serializedCache = serializedCache;
@@ -58,25 +74,27 @@ export class SeenPayloadEnvelopeInput {
58
74
  }
59
75
 
60
76
  private onFinalized = (checkpoint: CheckpointWithHex): void => {
61
- // Prune all entries with slot < finalized slot
62
- const finalizedSlot = computeStartSlotAtEpoch(checkpoint.epoch);
63
- let deletedCount = 0;
64
- for (const [, input] of this.payloadInputs) {
65
- if (input.slot < finalizedSlot) {
66
- this.evictPayloadInput(input);
67
- deletedCount++;
68
- }
69
- }
70
- this.logger?.debug("SeenPayloadEnvelopeInput.onFinalized deleted cached entries", {deletedCount});
77
+ this.pruneBelow(computeStartSlotAtEpoch(checkpoint.epoch));
71
78
  };
72
79
 
73
- add(props: CreateFromBlockProps): PayloadEnvelopeInput {
74
- if (this.payloadInputs.has(props.blockRootHex)) {
75
- throw new Error(`PayloadEnvelopeInput already exists for block ${props.blockRootHex}`);
80
+ add(props: Omit<CreateFromBlockProps, "daOutOfRange">): PayloadEnvelopeInput {
81
+ const existing = this.payloadInputs.get(props.blockRootHex);
82
+ if (existing !== undefined) {
83
+ this.logger?.verbose("SeenPayloadEnvelopeInput.add reused existing entry", {
84
+ slot: existing.slot,
85
+ root: props.blockRootHex,
86
+ });
87
+ return existing;
76
88
  }
77
- const input = PayloadEnvelopeInput.createFromBlock(props);
89
+ const daOutOfRange = isDaOutOfRange(this.config, props.forkName, props.block.message.slot, this.clock.currentEpoch);
90
+ const input = PayloadEnvelopeInput.createFromBlock({...props, daOutOfRange});
78
91
  this.payloadInputs.set(props.blockRootHex, input);
79
92
  this.metrics?.seenCache.payloadEnvelopeInput.created.inc();
93
+ this.logger?.verbose("SeenPayloadEnvelopeInput.add created new entry", {
94
+ slot: input.slot,
95
+ root: props.blockRootHex,
96
+ daOutOfRange,
97
+ });
80
98
  return input;
81
99
  }
82
100
 
@@ -88,17 +106,21 @@ export class SeenPayloadEnvelopeInput {
88
106
  return this.payloadInputs.get(blockRootHex)?.hasPayloadEnvelope() ?? false;
89
107
  }
90
108
 
91
- prune(blockRootHex: RootHex): void {
92
- const payloadInput = this.payloadInputs.get(blockRootHex);
93
- if (payloadInput) {
94
- this.evictPayloadInput(payloadInput);
95
- }
96
- }
97
-
98
109
  size(): number {
99
110
  return this.payloadInputs.size;
100
111
  }
101
112
 
113
+ pruneBelow(slot: Slot): void {
114
+ let deletedCount = 0;
115
+ for (const [, input] of this.payloadInputs) {
116
+ if (input.slot < slot) {
117
+ this.evictPayloadInput(input);
118
+ deletedCount++;
119
+ }
120
+ }
121
+ this.logger?.debug("SeenPayloadEnvelopeInput.pruneBelow deleted entries", {slot, deletedCount});
122
+ }
123
+
102
124
  private evictPayloadInput(payloadInput: PayloadEnvelopeInput): void {
103
125
  this.serializedCache.delete(payloadInput.getSerializedCacheKeys());
104
126
  this.payloadInputs.delete(payloadInput.blockRootHex);
@@ -0,0 +1,32 @@
1
+ import {RootHex, Slot, ValidatorIndex} from "@lodestar/types";
2
+ import {MapDef} from "@lodestar/utils";
3
+
4
+ /**
5
+ * Tracks signed proposer preferences we've already seen per (dependent_root, proposal_slot, validator_index).
6
+ */
7
+ export class SeenProposerPreferences {
8
+ private readonly validatorByDependentRootBySlot = new MapDef<Slot, Map<RootHex, ValidatorIndex>>(
9
+ () => new Map<RootHex, ValidatorIndex>()
10
+ );
11
+
12
+ isKnown(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): boolean {
13
+ return this.validatorByDependentRootBySlot.get(proposalSlot)?.get(dependentRoot) === validatorIndex;
14
+ }
15
+
16
+ add(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): void {
17
+ this.validatorByDependentRootBySlot.getOrDefault(proposalSlot).set(dependentRoot, validatorIndex);
18
+ }
19
+
20
+ /**
21
+ * Entries are only load-bearing while `proposal_slot > current_slot`. Once the slot has
22
+ * passed the `[IGNORE] proposal_slot > current_slot` gossip rule takes over, so drop them
23
+ * on each slot tick.
24
+ */
25
+ prune(currentSlot: Slot): void {
26
+ for (const slot of this.validatorByDependentRootBySlot.keys()) {
27
+ if (slot < currentSlot) {
28
+ this.validatorByDependentRootBySlot.delete(slot);
29
+ }
30
+ }
31
+ }
32
+ }
@@ -226,7 +226,10 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
226
226
  }
227
227
  sszTimer?.();
228
228
  const timer = this.metrics?.cpStateCache.stateReloadDuration.startTimer();
229
- const newCachedState = seedState.loadOtherState(stateBytes, validatorsBytes);
229
+ // preload validators and balances for faster state transition
230
+ const newCachedState = seedState.loadOtherState(stateBytes, validatorsBytes, {
231
+ preloadValidatorsAndBalances: true,
232
+ });
230
233
  // hashTreeRoot() calls the commit() inside
231
234
  // there is no modification inside the state, it's just that we want to compute and cache all roots
232
235
  const stateRoot = toRootHex(newCachedState.hashTreeRoot());
@@ -103,6 +103,7 @@ export async function validateGossipBlock(
103
103
  if (chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHashHex) === null) {
104
104
  throw new BlockGossipError(GossipAction.IGNORE, {
105
105
  code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
106
+ parentRoot,
106
107
  parentBlockHash: parentBlockHashHex,
107
108
  });
108
109
  }
@@ -1,5 +1,6 @@
1
1
  import {PublicKey} from "@chainsafe/blst";
2
2
  import {
3
+ computeEpochAtSlot,
3
4
  createSingleSignatureSetFromComponents,
4
5
  getExecutionPayloadBidSigningRoot,
5
6
  isActiveBuilder,
@@ -47,9 +48,12 @@ async function validateExecutionPayloadBid(
47
48
  });
48
49
  }
49
50
 
50
- // [IGNORE] the `SignedProposerPreferences` where `preferences.proposal_slot`
51
- // is equal to `bid.slot` has been seen.
52
- // TODO GLOAS: Implement this along with proposer preference
51
+ // [IGNORE] A `SignedProposerPreferences` matching `bid.slot` and the bid's branch has been
52
+ // seen i.e. `proposal_slot == bid.slot` AND `dependent_root ==
53
+ // get_proposer_dependent_root(parent_state, compute_epoch_at_slot(bid.slot))`,
54
+ // where `parent_state` is the post-state of `bid.parent_block_root`.
55
+ // This is the message referenced as `proposer_preferences` in the following REJECT rules.
56
+ // TODO GLOAS: Implement once a ProposerPreferencesPool exists.
53
57
 
54
58
  // [REJECT] `bid.builder_index` is a valid/active builder index -- i.e.
55
59
  // `is_active_builder(state, bid.builder_index)` returns `True`.
@@ -70,11 +74,24 @@ async function validateExecutionPayloadBid(
70
74
  });
71
75
  }
72
76
 
73
- // [REJECT] `bid.fee_recipient` matches the `fee_recipient` from the proposer's
74
- // `SignedProposerPreferences` associated with `bid.slot`.
75
- // [REJECT] `bid.gas_limit` matches the `gas_limit` from the proposer's
76
- // `SignedProposerPreferences` associated with `bid.slot`.
77
- // TODO GLOAS: Implement this along with proposer preference
77
+ // [REJECT] `bid.fee_recipient == proposer_preferences.fee_recipient`.
78
+ // [REJECT] `bid.gas_limit == proposer_preferences.gas_limit`.
79
+ // Both compared against the matching `proposer_preferences` defined above (same branch
80
+ // via dependent_root, same proposal_slot).
81
+ // TODO GLOAS: Implement once a ProposerPreferencesPool exists.
82
+
83
+ // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
84
+ // consensus layer -- i.e. validate that
85
+ // `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
86
+ const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
87
+ const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
88
+ if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
89
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
90
+ code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
91
+ blobKzgCommitmentsLen,
92
+ commitmentLimit: maxBlobsPerBlock,
93
+ });
94
+ }
78
95
 
79
96
  // [IGNORE] this is the first signed bid seen with a valid signature from the given builder for this slot.
80
97
  if (chain.seenExecutionPayloadBids.isKnown(bid.slot, bid.builderIndex)) {
@@ -4,8 +4,8 @@ import {
4
4
  getExecutionPayloadEnvelopeSignatureSet,
5
5
  isStatePostGloas,
6
6
  } from "@lodestar/state-transition";
7
- import {gloas} from "@lodestar/types";
8
- import {toRootHex} from "@lodestar/utils";
7
+ import {gloas, ssz} from "@lodestar/types";
8
+ import {byteArrayEquals, toRootHex} from "@lodestar/utils";
9
9
  import {ExecutionPayloadEnvelopeError, ExecutionPayloadEnvelopeErrorCode, GossipAction} from "../errors/index.js";
10
10
  import {IBeaconChain} from "../index.js";
11
11
  import {RegenCaller} from "../regen/index.js";
@@ -53,7 +53,7 @@ async function validateExecutionPayloadEnvelope(
53
53
  throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
54
54
  code: ExecutionPayloadEnvelopeErrorCode.ENVELOPE_ALREADY_KNOWN,
55
55
  blockRoot: blockRootHex,
56
- slot: envelope.slot,
56
+ slot: payload.slotNumber,
57
57
  });
58
58
  }
59
59
 
@@ -65,13 +65,13 @@ async function validateExecutionPayloadEnvelope(
65
65
  });
66
66
  }
67
67
 
68
- // [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `envelope.slot >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
68
+ // [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `payload.slotNumber >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
69
69
  const finalizedCheckpoint = chain.forkChoice.getFinalizedCheckpoint();
70
70
  const finalizedSlot = computeStartSlotAtEpoch(finalizedCheckpoint.epoch);
71
- if (envelope.slot < finalizedSlot) {
71
+ if (payload.slotNumber < finalizedSlot) {
72
72
  throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
73
73
  code: ExecutionPayloadEnvelopeErrorCode.BELONG_TO_FINALIZED_BLOCK,
74
- envelopeSlot: envelope.slot,
74
+ envelopeSlot: payload.slotNumber,
75
75
  finalizedSlot,
76
76
  });
77
77
  }
@@ -80,11 +80,11 @@ async function validateExecutionPayloadEnvelope(
80
80
  // TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
81
81
  // it is possible that the block didn't pass the validation
82
82
 
83
- // [REJECT] `block.slot` equals `envelope.slot`.
84
- if (block.slot !== envelope.slot) {
83
+ // [REJECT] `block.slot` equals `payload.slotNumber`.
84
+ if (block.slot !== payload.slotNumber) {
85
85
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
86
86
  code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH,
87
- envelopeSlot: envelope.slot,
87
+ envelopeSlot: payload.slotNumber,
88
88
  blockSlot: block.slot,
89
89
  });
90
90
  }
@@ -107,6 +107,16 @@ async function validateExecutionPayloadEnvelope(
107
107
  });
108
108
  }
109
109
 
110
+ // [REJECT] `hash_tree_root(envelope.execution_requests) == bid.execution_requests_root`
111
+ const requestsRoot = ssz.electra.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
112
+ if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
113
+ throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
114
+ code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
115
+ envelopeRequestsRoot: toRootHex(requestsRoot),
116
+ bidRequestsRoot: toRootHex(payloadInput.getBid().executionRequestsRoot),
117
+ });
118
+ }
119
+
110
120
  // Get the block state to verify the builder's signature.
111
121
  const blockState = await chain.regen
112
122
  .getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
@@ -114,7 +124,7 @@ async function validateExecutionPayloadEnvelope(
114
124
  throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
115
125
  code: ExecutionPayloadEnvelopeErrorCode.UNKNOWN_BLOCK_STATE,
116
126
  blockRoot: blockRootHex,
117
- slot: envelope.slot,
127
+ slot: payload.slotNumber,
118
128
  });
119
129
  });
120
130
  if (!isStatePostGloas(blockState)) {
@@ -0,0 +1,110 @@
1
+ import {SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {
3
+ computeEpochAtSlot,
4
+ createSingleSignatureSetFromComponents,
5
+ getProposerPreferencesSigningRoot,
6
+ } from "@lodestar/state-transition";
7
+ import {ValidatorIndex, gloas} from "@lodestar/types";
8
+ import {toRootHex} from "@lodestar/utils";
9
+ import {GossipAction, ProposerPreferencesError, ProposerPreferencesErrorCode} from "../errors/index.js";
10
+ import {IBeaconChain} from "../index.js";
11
+
12
+ /**
13
+ * Validates a gossiped `SignedProposerPreferences` per
14
+ * https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md#proposer_preferences
15
+ */
16
+ export async function validateGossipProposerPreferences(
17
+ chain: IBeaconChain,
18
+ signedProposerPreferences: gloas.SignedProposerPreferences
19
+ ): Promise<void> {
20
+ const preferences = signedProposerPreferences.message;
21
+ const {proposalSlot, validatorIndex, dependentRoot} = preferences;
22
+ const dependentRootHex = toRootHex(dependentRoot);
23
+ const proposalEpoch = computeEpochAtSlot(proposalSlot);
24
+
25
+ // [IGNORE] `preferences.proposal_slot` is in the current or next epoch.
26
+ const currentEpoch = chain.clock.currentEpoch;
27
+ if (proposalEpoch < currentEpoch || proposalEpoch > currentEpoch + 1) {
28
+ throw new ProposerPreferencesError(GossipAction.IGNORE, {
29
+ code: ProposerPreferencesErrorCode.INVALID_EPOCH,
30
+ proposalSlot,
31
+ currentEpoch,
32
+ });
33
+ }
34
+
35
+ // [IGNORE] `preferences.proposal_slot` has not already passed.
36
+ const currentSlot = chain.clock.currentSlot;
37
+ if (proposalSlot <= currentSlot) {
38
+ throw new ProposerPreferencesError(GossipAction.IGNORE, {
39
+ code: ProposerPreferencesErrorCode.PROPOSAL_SLOT_PASSED,
40
+ proposalSlot,
41
+ currentSlot,
42
+ });
43
+ }
44
+
45
+ // [IGNORE] The block with root `dependent_root` has been seen by the node.
46
+ // Resolve the proposer lookahead for the message's branch via head state (fast path) or
47
+ // the previous-root checkpoint state (populated by `processSlotsToNearestCheckpoint` for
48
+ // any imported branch crossing into `proposalEpoch - 1`). The head-state path also handles
49
+ // narrow timing windows where the checkpoint state isn't yet populated.
50
+ const headState = chain.getHeadState();
51
+ let proposers: ValidatorIndex[] | null = null;
52
+ if (headState.epoch === proposalEpoch && headState.currentDecisionRoot === dependentRootHex) {
53
+ proposers = headState.currentProposers;
54
+ } else if (headState.epoch === proposalEpoch - 1 && headState.nextDecisionRoot === dependentRootHex) {
55
+ proposers = headState.nextProposers;
56
+ } else {
57
+ // Sync lookup only to not trigger disk reload from gossip input.
58
+ const checkpointState = chain.regen.getCheckpointStateSync({epoch: proposalEpoch - 1, rootHex: dependentRootHex});
59
+ if (checkpointState !== null) {
60
+ // State is at `proposalEpoch - 1`, so proposers for `proposalSlot` (next epoch from
61
+ // the state's perspective) live in `nextProposers`.
62
+ proposers = checkpointState.nextProposers;
63
+ }
64
+ }
65
+ if (proposers === null) {
66
+ throw new ProposerPreferencesError(GossipAction.IGNORE, {
67
+ code: ProposerPreferencesErrorCode.UNKNOWN_DEPENDENT_ROOT,
68
+ proposalSlot,
69
+ dependentRoot: dependentRootHex,
70
+ });
71
+ }
72
+
73
+ // [REJECT] `is_valid_proposal_slot(state, preferences)` returns True.
74
+ if (proposers[proposalSlot % SLOTS_PER_EPOCH] !== validatorIndex) {
75
+ throw new ProposerPreferencesError(GossipAction.REJECT, {
76
+ code: ProposerPreferencesErrorCode.INVALID_PROPOSER,
77
+ proposalSlot,
78
+ validatorIndex,
79
+ dependentRoot: dependentRootHex,
80
+ });
81
+ }
82
+
83
+ // [IGNORE] First valid message for (dependent_root, proposal_slot, validator_index).
84
+ if (chain.seenProposerPreferences.isKnown(dependentRootHex, proposalSlot, validatorIndex)) {
85
+ throw new ProposerPreferencesError(GossipAction.IGNORE, {
86
+ code: ProposerPreferencesErrorCode.ALREADY_KNOWN,
87
+ proposalSlot,
88
+ validatorIndex,
89
+ dependentRoot: dependentRootHex,
90
+ });
91
+ }
92
+
93
+ // [REJECT] `signed_proposer_preferences.signature` is valid with respect to the validator's public key.
94
+ const signatureSet = createSingleSignatureSetFromComponents(
95
+ chain.pubkeyCache.getOrThrow(validatorIndex),
96
+ getProposerPreferencesSigningRoot(chain.config, preferences),
97
+ signedProposerPreferences.signature
98
+ );
99
+
100
+ if (!(await chain.bls.verifySignatureSets([signatureSet], {batchable: true}))) {
101
+ throw new ProposerPreferencesError(GossipAction.REJECT, {
102
+ code: ProposerPreferencesErrorCode.INVALID_SIGNATURE,
103
+ proposalSlot,
104
+ validatorIndex,
105
+ });
106
+ }
107
+
108
+ // Valid
109
+ chain.seenProposerPreferences.add(dependentRootHex, proposalSlot, validatorIndex);
110
+ }
@@ -19,7 +19,7 @@ export class ExecutionPayloadEnvelopeArchiveRepository extends Repository<Slot,
19
19
  * Id is the slot from the envelope
20
20
  */
21
21
  getId(value: gloas.SignedExecutionPayloadEnvelope): Slot {
22
- return value.message.slot;
22
+ return value.message.payload.slotNumber;
23
23
  }
24
24
 
25
25
  encodeKey(id: Slot): Uint8Array {