@lodestar/beacon-node 1.47.0 → 1.48.0-dev.042c96183c

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 (328) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +8 -11
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/state/utils.js +14 -0
  6. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  7. package/lib/api/impl/config/index.d.ts.map +1 -1
  8. package/lib/api/impl/config/index.js +4 -1
  9. package/lib/api/impl/config/index.js.map +1 -1
  10. package/lib/api/impl/validator/index.d.ts.map +1 -1
  11. package/lib/api/impl/validator/index.js +44 -39
  12. package/lib/api/impl/validator/index.js.map +1 -1
  13. package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
  14. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/archiveStore.js +5 -1
  16. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  17. package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
  18. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -0
  19. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  20. package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  21. package/lib/chain/archiveStore/historicalState/metrics.js +12 -1
  22. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  23. package/lib/chain/archiveStore/historicalState/types.d.ts +2 -0
  24. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  25. package/lib/chain/archiveStore/historicalState/types.js.map +1 -1
  26. package/lib/chain/archiveStore/historicalState/worker.js +4 -1
  27. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  28. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +9 -1
  29. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  30. package/lib/chain/archiveStore/utils/archiveBlocks.js +68 -38
  31. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  32. package/lib/chain/blocks/importBlock.d.ts +5 -1
  33. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  34. package/lib/chain/blocks/importBlock.js +12 -18
  35. package/lib/chain/blocks/importBlock.js.map +1 -1
  36. package/lib/chain/blocks/index.d.ts +8 -4
  37. package/lib/chain/blocks/index.d.ts.map +1 -1
  38. package/lib/chain/blocks/index.js +89 -23
  39. package/lib/chain/blocks/index.js.map +1 -1
  40. package/lib/chain/blocks/types.d.ts +8 -0
  41. package/lib/chain/blocks/types.d.ts.map +1 -1
  42. package/lib/chain/blocks/types.js.map +1 -1
  43. package/lib/chain/blocks/utils/chainSegment.d.ts +1 -1
  44. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  45. package/lib/chain/blocks/utils/chainSegment.js +26 -11
  46. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  47. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  48. package/lib/chain/blocks/verifyBlock.js +5 -2
  49. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  50. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  51. package/lib/chain/blocks/writeBlockInputToDb.js +9 -16
  52. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  53. package/lib/chain/chain.d.ts +12 -6
  54. package/lib/chain/chain.d.ts.map +1 -1
  55. package/lib/chain/chain.js +131 -76
  56. package/lib/chain/chain.js.map +1 -1
  57. package/lib/chain/emitter.d.ts +1 -1
  58. package/lib/chain/emitter.js +1 -1
  59. package/lib/chain/errors/blockError.d.ts +19 -7
  60. package/lib/chain/errors/blockError.d.ts.map +1 -1
  61. package/lib/chain/errors/blockError.js +2 -2
  62. package/lib/chain/errors/blockError.js.map +1 -1
  63. package/lib/chain/errors/dataColumnSidecarError.d.ts +15 -2
  64. package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
  65. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
  66. package/lib/chain/errors/executionPayloadBid.d.ts +9 -0
  67. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  68. package/lib/chain/errors/executionPayloadBid.js +2 -0
  69. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  70. package/lib/chain/errors/executionPayloadEnvelope.d.ts +10 -11
  71. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  72. package/lib/chain/errors/executionPayloadEnvelope.js +0 -2
  73. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  74. package/lib/chain/errors/gossipValidation.d.ts +2 -1
  75. package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
  76. package/lib/chain/errors/gossipValidation.js +1 -0
  77. package/lib/chain/errors/gossipValidation.js.map +1 -1
  78. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  79. package/lib/chain/forkChoice/index.js +2 -0
  80. package/lib/chain/forkChoice/index.js.map +1 -1
  81. package/lib/chain/interface.d.ts +6 -6
  82. package/lib/chain/interface.d.ts.map +1 -1
  83. package/lib/chain/lightClient/index.d.ts +3 -3
  84. package/lib/chain/lightClient/index.d.ts.map +1 -1
  85. package/lib/chain/lightClient/index.js +21 -23
  86. package/lib/chain/lightClient/index.js.map +1 -1
  87. package/lib/chain/lightClient/proofs.d.ts +2 -1
  88. package/lib/chain/lightClient/proofs.d.ts.map +1 -1
  89. package/lib/chain/lightClient/proofs.js +5 -1
  90. package/lib/chain/lightClient/proofs.js.map +1 -1
  91. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  92. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  93. package/lib/chain/prepareNextSlot.d.ts +5 -0
  94. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  95. package/lib/chain/prepareNextSlot.js +40 -15
  96. package/lib/chain/prepareNextSlot.js.map +1 -1
  97. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  98. package/lib/chain/produceBlock/computeNewStateRoot.js +3 -25
  99. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  100. package/lib/chain/produceBlock/produceBlockBody.d.ts +4 -2
  101. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  102. package/lib/chain/produceBlock/produceBlockBody.js +57 -72
  103. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  104. package/lib/chain/regen/interface.d.ts +1 -0
  105. package/lib/chain/regen/interface.d.ts.map +1 -1
  106. package/lib/chain/regen/interface.js +1 -0
  107. package/lib/chain/regen/interface.js.map +1 -1
  108. package/lib/chain/validation/block.d.ts.map +1 -1
  109. package/lib/chain/validation/block.js +74 -63
  110. package/lib/chain/validation/block.js.map +1 -1
  111. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  112. package/lib/chain/validation/dataColumnSidecar.js +38 -13
  113. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  114. package/lib/chain/validation/executionPayloadBid.d.ts +15 -3
  115. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  116. package/lib/chain/validation/executionPayloadBid.js +199 -28
  117. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  118. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  119. package/lib/chain/validation/executionPayloadEnvelope.js +10 -29
  120. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  121. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  122. package/lib/chain/validation/payloadAttestationMessage.js +5 -6
  123. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  124. package/lib/chain/validatorMonitor.d.ts +7 -1
  125. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  126. package/lib/chain/validatorMonitor.js +22 -5
  127. package/lib/chain/validatorMonitor.js.map +1 -1
  128. package/lib/db/beacon.d.ts +14 -5
  129. package/lib/db/beacon.d.ts.map +1 -1
  130. package/lib/db/beacon.js +17 -3
  131. package/lib/db/beacon.js.map +1 -1
  132. package/lib/db/dataColumnStore.d.ts +39 -0
  133. package/lib/db/dataColumnStore.d.ts.map +1 -0
  134. package/lib/db/dataColumnStore.js +81 -0
  135. package/lib/db/dataColumnStore.js.map +1 -0
  136. package/lib/db/flatFileStore/atomicWrite.d.ts +8 -0
  137. package/lib/db/flatFileStore/atomicWrite.d.ts.map +1 -0
  138. package/lib/db/flatFileStore/atomicWrite.js +61 -0
  139. package/lib/db/flatFileStore/atomicWrite.js.map +1 -0
  140. package/lib/db/flatFileStore/dcolFormat.d.ts +90 -0
  141. package/lib/db/flatFileStore/dcolFormat.d.ts.map +1 -0
  142. package/lib/db/flatFileStore/dcolFormat.js +286 -0
  143. package/lib/db/flatFileStore/dcolFormat.js.map +1 -0
  144. package/lib/db/flatFileStore/errors.d.ts +82 -0
  145. package/lib/db/flatFileStore/errors.d.ts.map +1 -0
  146. package/lib/db/flatFileStore/errors.js +32 -0
  147. package/lib/db/flatFileStore/errors.js.map +1 -0
  148. package/lib/db/flatFileStore/flatFileStore.d.ts +41 -0
  149. package/lib/db/flatFileStore/flatFileStore.d.ts.map +1 -0
  150. package/lib/db/flatFileStore/flatFileStore.js +251 -0
  151. package/lib/db/flatFileStore/flatFileStore.js.map +1 -0
  152. package/lib/db/flatFileStore/index.d.ts +4 -0
  153. package/lib/db/flatFileStore/index.d.ts.map +1 -0
  154. package/lib/db/flatFileStore/index.js +2 -0
  155. package/lib/db/flatFileStore/index.js.map +1 -0
  156. package/lib/db/flatFileStore/interface.d.ts +25 -0
  157. package/lib/db/flatFileStore/interface.d.ts.map +1 -0
  158. package/lib/db/flatFileStore/interface.js +2 -0
  159. package/lib/db/flatFileStore/interface.js.map +1 -0
  160. package/lib/db/flatFileStore/metrics.d.ts +24 -0
  161. package/lib/db/flatFileStore/metrics.d.ts.map +1 -0
  162. package/lib/db/flatFileStore/metrics.js +25 -0
  163. package/lib/db/flatFileStore/metrics.js.map +1 -0
  164. package/lib/db/flatFileStore/path.d.ts +9 -0
  165. package/lib/db/flatFileStore/path.d.ts.map +1 -0
  166. package/lib/db/flatFileStore/path.js +21 -0
  167. package/lib/db/flatFileStore/path.js.map +1 -0
  168. package/lib/db/flatFileStore/slotIndex.d.ts +14 -0
  169. package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -0
  170. package/lib/db/flatFileStore/slotIndex.js +46 -0
  171. package/lib/db/flatFileStore/slotIndex.js.map +1 -0
  172. package/lib/db/flatFileStore/snappy.d.ts +3 -0
  173. package/lib/db/flatFileStore/snappy.d.ts.map +1 -0
  174. package/lib/db/flatFileStore/snappy.js +12 -0
  175. package/lib/db/flatFileStore/snappy.js.map +1 -0
  176. package/lib/db/index.d.ts +1 -0
  177. package/lib/db/index.d.ts.map +1 -1
  178. package/lib/db/interface.d.ts +8 -1
  179. package/lib/db/interface.d.ts.map +1 -1
  180. package/lib/db/repositories/blockArchive.d.ts +8 -1
  181. package/lib/db/repositories/blockArchive.d.ts.map +1 -1
  182. package/lib/db/repositories/blockArchive.js +65 -28
  183. package/lib/db/repositories/blockArchive.js.map +1 -1
  184. package/lib/db/repositories/blockArchiveIndex.d.ts +1 -2
  185. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  186. package/lib/db/repositories/blockArchiveIndex.js +3 -7
  187. package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
  188. package/lib/execution/builder/apiClient.d.ts +6 -0
  189. package/lib/execution/builder/apiClient.d.ts.map +1 -1
  190. package/lib/execution/builder/apiClient.js +19 -1
  191. package/lib/execution/builder/apiClient.js.map +1 -1
  192. package/lib/execution/builder/validateBid.d.ts +1 -0
  193. package/lib/execution/builder/validateBid.d.ts.map +1 -1
  194. package/lib/execution/builder/validateBid.js +17 -6
  195. package/lib/execution/builder/validateBid.js.map +1 -1
  196. package/lib/execution/engine/mock.d.ts.map +1 -1
  197. package/lib/execution/engine/mock.js +4 -1
  198. package/lib/execution/engine/mock.js.map +1 -1
  199. package/lib/metrics/metrics/lodestar.d.ts +32 -4
  200. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  201. package/lib/metrics/metrics/lodestar.js +78 -8
  202. package/lib/metrics/metrics/lodestar.js.map +1 -1
  203. package/lib/monitoring/clientStats.js +1 -1
  204. package/lib/monitoring/clientStats.js.map +1 -1
  205. package/lib/network/gossip/encoding.d.ts.map +1 -1
  206. package/lib/network/gossip/encoding.js +0 -7
  207. package/lib/network/gossip/encoding.js.map +1 -1
  208. package/lib/network/gossip/topic.d.ts +2 -1
  209. package/lib/network/gossip/topic.d.ts.map +1 -1
  210. package/lib/network/gossip/topic.js +4 -20
  211. package/lib/network/gossip/topic.js.map +1 -1
  212. package/lib/network/network.d.ts.map +1 -1
  213. package/lib/network/network.js +7 -6
  214. package/lib/network/network.js.map +1 -1
  215. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  216. package/lib/network/processor/gossipHandlers.js +388 -285
  217. package/lib/network/processor/gossipHandlers.js.map +1 -1
  218. package/lib/network/processor/gossipQueues/indexed.d.ts.map +1 -1
  219. package/lib/network/processor/gossipQueues/indexed.js +0 -1
  220. package/lib/network/processor/gossipQueues/indexed.js.map +1 -1
  221. package/lib/network/processor/index.d.ts +49 -3
  222. package/lib/network/processor/index.d.ts.map +1 -1
  223. package/lib/network/processor/index.js +270 -122
  224. package/lib/network/processor/index.js.map +1 -1
  225. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  226. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +112 -91
  227. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  228. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
  229. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +2 -0
  230. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  231. package/lib/network/reqresp/protocols.d.ts.map +1 -1
  232. package/lib/network/reqresp/protocols.js +6 -22
  233. package/lib/network/reqresp/protocols.js.map +1 -1
  234. package/lib/network/reqresp/rateLimit.js +2 -1
  235. package/lib/network/reqresp/rateLimit.js.map +1 -1
  236. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts +2 -1
  237. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
  238. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +14 -9
  239. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  240. package/lib/node/nodejs.d.ts +2 -1
  241. package/lib/node/nodejs.d.ts.map +1 -1
  242. package/lib/node/nodejs.js +4 -2
  243. package/lib/node/nodejs.js.map +1 -1
  244. package/lib/sync/range/chain.d.ts.map +1 -1
  245. package/lib/sync/range/chain.js +9 -2
  246. package/lib/sync/range/chain.js.map +1 -1
  247. package/lib/sync/range/range.d.ts.map +1 -1
  248. package/lib/sync/range/range.js +25 -2
  249. package/lib/sync/range/range.js.map +1 -1
  250. package/lib/util/sszBytes.d.ts +6 -0
  251. package/lib/util/sszBytes.d.ts.map +1 -1
  252. package/lib/util/sszBytes.js +1 -1
  253. package/lib/util/sszBytes.js.map +1 -1
  254. package/package.json +19 -20
  255. package/src/api/impl/beacon/blocks/index.ts +12 -14
  256. package/src/api/impl/beacon/state/utils.ts +15 -0
  257. package/src/api/impl/config/index.ts +4 -1
  258. package/src/api/impl/validator/index.ts +49 -43
  259. package/src/chain/archiveStore/archiveStore.ts +12 -1
  260. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -0
  261. package/src/chain/archiveStore/historicalState/metrics.ts +14 -1
  262. package/src/chain/archiveStore/historicalState/types.ts +2 -0
  263. package/src/chain/archiveStore/historicalState/worker.ts +4 -1
  264. package/src/chain/archiveStore/utils/archiveBlocks.ts +73 -42
  265. package/src/chain/blocks/importBlock.ts +25 -20
  266. package/src/chain/blocks/index.ts +126 -29
  267. package/src/chain/blocks/types.ts +9 -0
  268. package/src/chain/blocks/utils/chainSegment.ts +26 -12
  269. package/src/chain/blocks/verifyBlock.ts +5 -2
  270. package/src/chain/blocks/writeBlockInputToDb.ts +10 -17
  271. package/src/chain/chain.ts +155 -85
  272. package/src/chain/emitter.ts +1 -1
  273. package/src/chain/errors/blockError.ts +22 -10
  274. package/src/chain/errors/dataColumnSidecarError.ts +24 -8
  275. package/src/chain/errors/executionPayloadBid.ts +8 -0
  276. package/src/chain/errors/executionPayloadEnvelope.ts +14 -11
  277. package/src/chain/errors/gossipValidation.ts +1 -0
  278. package/src/chain/forkChoice/index.ts +2 -0
  279. package/src/chain/interface.ts +6 -6
  280. package/src/chain/lightClient/index.ts +32 -36
  281. package/src/chain/lightClient/proofs.ts +10 -0
  282. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  283. package/src/chain/prepareNextSlot.ts +47 -18
  284. package/src/chain/produceBlock/computeNewStateRoot.ts +3 -38
  285. package/src/chain/produceBlock/produceBlockBody.ts +62 -79
  286. package/src/chain/regen/interface.ts +1 -0
  287. package/src/chain/validation/block.ts +77 -79
  288. package/src/chain/validation/dataColumnSidecar.ts +47 -14
  289. package/src/chain/validation/executionPayloadBid.ts +230 -32
  290. package/src/chain/validation/executionPayloadEnvelope.ts +10 -37
  291. package/src/chain/validation/payloadAttestationMessage.ts +8 -9
  292. package/src/chain/validatorMonitor.ts +24 -7
  293. package/src/db/beacon.ts +33 -6
  294. package/src/db/dataColumnStore.ts +113 -0
  295. package/src/db/flatFileStore/atomicWrite.ts +65 -0
  296. package/src/db/flatFileStore/dcolFormat.ts +389 -0
  297. package/src/db/flatFileStore/errors.ts +53 -0
  298. package/src/db/flatFileStore/flatFileStore.ts +310 -0
  299. package/src/db/flatFileStore/index.ts +3 -0
  300. package/src/db/flatFileStore/interface.ts +21 -0
  301. package/src/db/flatFileStore/metrics.ts +48 -0
  302. package/src/db/flatFileStore/path.ts +32 -0
  303. package/src/db/flatFileStore/slotIndex.ts +56 -0
  304. package/src/db/flatFileStore/snappy.ts +14 -0
  305. package/src/db/index.ts +1 -0
  306. package/src/db/interface.ts +9 -1
  307. package/src/db/repositories/blockArchive.ts +89 -30
  308. package/src/db/repositories/blockArchiveIndex.ts +4 -9
  309. package/src/execution/builder/apiClient.ts +21 -1
  310. package/src/execution/builder/validateBid.ts +26 -7
  311. package/src/execution/engine/mock.ts +4 -1
  312. package/src/metrics/metrics/lodestar.ts +83 -9
  313. package/src/monitoring/clientStats.ts +1 -1
  314. package/src/network/gossip/encoding.ts +0 -7
  315. package/src/network/gossip/topic.ts +3 -25
  316. package/src/network/network.ts +9 -6
  317. package/src/network/processor/gossipHandlers.ts +500 -355
  318. package/src/network/processor/gossipQueues/indexed.ts +0 -1
  319. package/src/network/processor/index.ts +312 -176
  320. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +139 -105
  321. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +2 -0
  322. package/src/network/reqresp/protocols.ts +7 -24
  323. package/src/network/reqresp/rateLimit.ts +2 -1
  324. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +15 -7
  325. package/src/node/nodejs.ts +6 -1
  326. package/src/sync/range/chain.ts +9 -2
  327. package/src/sync/range/range.ts +24 -2
  328. package/src/util/sszBytes.ts +1 -1
@@ -11,7 +11,7 @@ import {
11
11
  computeStartSlotAtEpoch,
12
12
  getBlockHeaderProposerSignatureSetByHeaderSlot,
13
13
  } from "@lodestar/state-transition";
14
- import {DataColumnSidecar, Root, Slot, SubnetID, ValidatorIndex, fulu, gloas, ssz} from "@lodestar/types";
14
+ import {DataColumnSidecar, Root, RootHex, Slot, SubnetID, ValidatorIndex, fulu, gloas, ssz} from "@lodestar/types";
15
15
  import {byteArrayEquals, toRootHex, verifyMerkleBranch} from "@lodestar/utils";
16
16
  import {BeaconMetrics} from "../../metrics/metrics/beacon.js";
17
17
  import {Metrics} from "../../metrics/metrics.js";
@@ -39,12 +39,14 @@ export async function validateGossipFuluDataColumnSidecar(
39
39
  const blockRootHex = toRootHex(ssz.phase0.BeaconBlockHeader.hashTreeRoot(blockHeader));
40
40
 
41
41
  // 1) [REJECT] The sidecar is valid as verified by verify_data_column_sidecar
42
- verifyFuluDataColumnSidecar(chain.config, dataColumnSidecar);
42
+ verifyFuluDataColumnSidecar(chain.config, dataColumnSidecar, blockRootHex);
43
43
 
44
44
  // 2) [REJECT] The sidecar is for the correct subnet -- i.e. compute_subnet_for_data_column_sidecar(sidecar.index) == subnet_id
45
45
  if (computeSubnetForDataColumnSidecar(chain.config, dataColumnSidecar) !== gossipSubnet) {
46
46
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
47
47
  code: DataColumnSidecarErrorCode.INVALID_SUBNET,
48
+ slot: blockHeader.slot,
49
+ root: blockRootHex,
48
50
  columnIndex: dataColumnSidecar.index,
49
51
  gossipSubnet: gossipSubnet,
50
52
  });
@@ -100,8 +102,9 @@ export async function validateGossipFuluDataColumnSidecar(
100
102
  if (parentBlock.slot >= blockHeader.slot) {
101
103
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
102
104
  code: DataColumnSidecarErrorCode.NOT_LATER_THAN_PARENT,
103
- parentSlot: parentBlock.slot,
104
105
  slot: blockHeader.slot,
106
+ root: blockRootHex,
107
+ parentSlot: parentBlock.slot,
105
108
  });
106
109
  }
107
110
 
@@ -140,6 +143,8 @@ export async function validateGossipFuluDataColumnSidecar(
140
143
  if (proposerIndex !== expectedProposerIndex) {
141
144
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
142
145
  code: DataColumnSidecarErrorCode.INCORRECT_PROPOSER,
146
+ slot: blockHeader.slot,
147
+ root: blockRootHex,
143
148
  actualProposerIndex: proposerIndex,
144
149
  expectedProposerIndex,
145
150
  });
@@ -161,9 +166,9 @@ export async function validateGossipFuluDataColumnSidecar(
161
166
  ) {
162
167
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
163
168
  code: DataColumnSidecarErrorCode.PROPOSAL_SIGNATURE_INVALID,
164
- blockRoot: blockRootHex,
165
- index: dataColumnSidecar.index,
166
169
  slot: blockHeader.slot,
170
+ root: blockRootHex,
171
+ index: dataColumnSidecar.index,
167
172
  });
168
173
  }
169
174
 
@@ -185,7 +190,8 @@ export async function validateGossipFuluDataColumnSidecar(
185
190
  if (!valid) {
186
191
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
187
192
  code: DataColumnSidecarErrorCode.INCLUSION_PROOF_INVALID,
188
- slot: dataColumnSidecar.signedBlockHeader.message.slot,
193
+ slot: blockHeader.slot,
194
+ root: blockRootHex,
189
195
  columnIndex: dataColumnSidecar.index,
190
196
  });
191
197
  }
@@ -204,6 +210,7 @@ export async function validateGossipFuluDataColumnSidecar(
204
210
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
205
211
  code: DataColumnSidecarErrorCode.INVALID_KZG_PROOF,
206
212
  slot: blockHeader.slot,
213
+ root: blockRootHex,
207
214
  columnIndex: dataColumnSidecar.index,
208
215
  });
209
216
  } finally {
@@ -240,6 +247,7 @@ export async function validateGossipGloasDataColumnSidecar(
240
247
  if (block.slot !== dataColumnSidecar.slot) {
241
248
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
242
249
  code: DataColumnSidecarErrorCode.INCORRECT_SIDECAR_SLOT,
250
+ root: blockRootHex,
243
251
  columnIndex: dataColumnSidecar.index,
244
252
  expected: block.slot,
245
253
  actual: dataColumnSidecar.slot,
@@ -248,12 +256,14 @@ export async function validateGossipGloasDataColumnSidecar(
248
256
 
249
257
  // [REJECT] The sidecar must pass verify_data_column_sidecar against the block commitments
250
258
  const kzgCommitments = payloadInput.getBlobKzgCommitments();
251
- verifyGloasDataColumnSidecar(dataColumnSidecar, kzgCommitments);
259
+ verifyGloasDataColumnSidecar(dataColumnSidecar, kzgCommitments, blockRootHex);
252
260
 
253
261
  // [REJECT] The sidecar must be on the correct subnet
254
262
  if (computeSubnetForDataColumnSidecar(chain.config, dataColumnSidecar) !== gossipSubnet) {
255
263
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
256
264
  code: DataColumnSidecarErrorCode.INVALID_SUBNET,
265
+ slot: dataColumnSidecar.slot,
266
+ root: blockRootHex,
257
267
  columnIndex: dataColumnSidecar.index,
258
268
  gossipSubnet,
259
269
  });
@@ -272,6 +282,7 @@ export async function validateGossipGloasDataColumnSidecar(
272
282
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
273
283
  code: DataColumnSidecarErrorCode.INVALID_KZG_PROOF,
274
284
  slot: dataColumnSidecar.slot,
285
+ root: blockRootHex,
275
286
  columnIndex: dataColumnSidecar.index,
276
287
  });
277
288
  } finally {
@@ -283,11 +294,17 @@ export async function validateGossipGloasDataColumnSidecar(
283
294
  * SPEC FUNCTION
284
295
  * https://github.com/ethereum/consensus-specs/blob/v1.6.0-alpha.4/specs/fulu/p2p-interface.md#verify_data_column_sidecar
285
296
  */
286
- function verifyFuluDataColumnSidecar(config: ChainForkConfig, dataColumnSidecar: fulu.DataColumnSidecar): void {
297
+ function verifyFuluDataColumnSidecar(
298
+ config: ChainForkConfig,
299
+ dataColumnSidecar: fulu.DataColumnSidecar,
300
+ blockRootHex: RootHex
301
+ ): void {
302
+ const slot = dataColumnSidecar.signedBlockHeader.message.slot;
287
303
  if (dataColumnSidecar.index >= NUMBER_OF_COLUMNS) {
288
304
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
289
305
  code: DataColumnSidecarErrorCode.INVALID_INDEX,
290
- slot: dataColumnSidecar.signedBlockHeader.message.slot,
306
+ slot,
307
+ root: blockRootHex,
291
308
  columnIndex: dataColumnSidecar.index,
292
309
  });
293
310
  }
@@ -295,18 +312,20 @@ function verifyFuluDataColumnSidecar(config: ChainForkConfig, dataColumnSidecar:
295
312
  if (dataColumnSidecar.kzgCommitments.length === 0) {
296
313
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
297
314
  code: DataColumnSidecarErrorCode.NO_COMMITMENTS,
298
- slot: dataColumnSidecar.signedBlockHeader.message.slot,
315
+ slot,
316
+ root: blockRootHex,
299
317
  columnIndex: dataColumnSidecar.index,
300
318
  });
301
319
  }
302
320
 
303
- const epoch = computeEpochAtSlot(dataColumnSidecar.signedBlockHeader.message.slot);
321
+ const epoch = computeEpochAtSlot(slot);
304
322
  const maxBlobsPerBlock = config.getMaxBlobsPerBlock(epoch);
305
323
 
306
324
  if (dataColumnSidecar.kzgCommitments.length > maxBlobsPerBlock) {
307
325
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
308
326
  code: DataColumnSidecarErrorCode.TOO_MANY_KZG_COMMITMENTS,
309
- slot: dataColumnSidecar.signedBlockHeader.message.slot,
327
+ slot,
328
+ root: blockRootHex,
310
329
  columnIndex: dataColumnSidecar.index,
311
330
  count: dataColumnSidecar.kzgCommitments.length,
312
331
  limit: maxBlobsPerBlock,
@@ -319,6 +338,8 @@ function verifyFuluDataColumnSidecar(config: ChainForkConfig, dataColumnSidecar:
319
338
  ) {
320
339
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
321
340
  code: DataColumnSidecarErrorCode.MISMATCHED_LENGTHS,
341
+ slot,
342
+ root: blockRootHex,
322
343
  columnLength: dataColumnSidecar.column.length,
323
344
  commitmentsLength: dataColumnSidecar.kzgCommitments.length,
324
345
  proofsLength: dataColumnSidecar.kzgProofs.length,
@@ -330,12 +351,17 @@ function verifyFuluDataColumnSidecar(config: ChainForkConfig, dataColumnSidecar:
330
351
  * SPEC FUNCTION
331
352
  * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.3/specs/gloas/p2p-interface.md#modified-verify_data_column_sidecar
332
353
  */
333
- function verifyGloasDataColumnSidecar(dataColumnSidecar: gloas.DataColumnSidecar, kzgCommitments: Uint8Array[]): void {
354
+ function verifyGloasDataColumnSidecar(
355
+ dataColumnSidecar: gloas.DataColumnSidecar,
356
+ kzgCommitments: Uint8Array[],
357
+ blockRootHex: RootHex
358
+ ): void {
334
359
  const slot = getDataColumnSidecarSlot(dataColumnSidecar);
335
360
  if (dataColumnSidecar.index >= NUMBER_OF_COLUMNS) {
336
361
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
337
362
  code: DataColumnSidecarErrorCode.INVALID_INDEX,
338
363
  slot,
364
+ root: blockRootHex,
339
365
  columnIndex: dataColumnSidecar.index,
340
366
  });
341
367
  }
@@ -344,6 +370,7 @@ function verifyGloasDataColumnSidecar(dataColumnSidecar: gloas.DataColumnSidecar
344
370
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
345
371
  code: DataColumnSidecarErrorCode.NO_COMMITMENTS,
346
372
  slot,
373
+ root: blockRootHex,
347
374
  columnIndex: dataColumnSidecar.index,
348
375
  });
349
376
  }
@@ -354,6 +381,8 @@ function verifyGloasDataColumnSidecar(dataColumnSidecar: gloas.DataColumnSidecar
354
381
  ) {
355
382
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
356
383
  code: DataColumnSidecarErrorCode.MISMATCHED_LENGTHS,
384
+ slot,
385
+ root: blockRootHex,
357
386
  columnLength: dataColumnSidecar.column.length,
358
387
  commitmentsLength: kzgCommitments.length,
359
388
  proofsLength: dataColumnSidecar.kzgProofs.length,
@@ -465,8 +494,8 @@ export async function validateFuluBlockDataColumnSidecars(
465
494
  ) {
466
495
  throw new DataColumnSidecarValidationError({
467
496
  code: DataColumnSidecarErrorCode.PROPOSAL_SIGNATURE_INVALID,
468
- blockRoot: rootHex,
469
497
  slot: blockSlot,
498
+ root: rootHex,
470
499
  index: dataColumnSidecars[0].index,
471
500
  });
472
501
  }
@@ -499,6 +528,7 @@ export async function validateFuluBlockDataColumnSidecars(
499
528
  {
500
529
  code: DataColumnSidecarErrorCode.INVALID_INDEX,
501
530
  slot: blockSlot,
531
+ root: toRootHex(blockRoot),
502
532
  columnIndex: columnSidecar.index,
503
533
  },
504
534
  "DataColumnSidecar has invalid index"
@@ -543,6 +573,7 @@ export async function validateFuluBlockDataColumnSidecars(
543
573
  {
544
574
  code: DataColumnSidecarErrorCode.INCLUSION_PROOF_INVALID,
545
575
  slot: blockSlot,
576
+ root: toRootHex(blockRoot),
546
577
  columnIndex: columnSidecar.index,
547
578
  },
548
579
  "DataColumnSidecar has invalid inclusion proof"
@@ -623,6 +654,7 @@ export async function validateGloasBlockDataColumnSidecars(
623
654
  if (columnSidecar.slot !== blockSlot) {
624
655
  throw new DataColumnSidecarValidationError({
625
656
  code: DataColumnSidecarErrorCode.INCORRECT_SIDECAR_SLOT,
657
+ root: toRootHex(blockRoot),
626
658
  columnIndex: columnSidecar.index,
627
659
  expected: blockSlot,
628
660
  actual: columnSidecar.slot,
@@ -644,6 +676,7 @@ export async function validateGloasBlockDataColumnSidecars(
644
676
  {
645
677
  code: DataColumnSidecarErrorCode.INVALID_INDEX,
646
678
  slot: blockSlot,
679
+ root: toRootHex(blockRoot),
647
680
  columnIndex: columnSidecar.index,
648
681
  },
649
682
  "DataColumnSidecar has invalid index"
@@ -1,4 +1,4 @@
1
- import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
1
+ import {ProtoBlock} from "@lodestar/fork-choice";
2
2
  import {PAYLOAD_BUILDER_VERSION} from "@lodestar/params";
3
3
  import {
4
4
  computeEpochAtSlot,
@@ -46,11 +46,12 @@ function getMinBidValue(currentHighestBid: number): number {
46
46
  /**
47
47
  * Check whether a bid builds on one of the paths compatible with the local head branch.
48
48
  *
49
- * Building directly on the parent is allowed for proposer-boost reorgs outside epoch boundaries.
49
+ * Building directly on the parent is allowed for proposer-boost reorgs, including at epoch
50
+ * boundaries when the head is weak enough that a reorg onto the strong parent is predicted.
50
51
  * Otherwise the bid must build on the local head's full or empty payload variant, as selected for its slot.
51
52
  */
52
53
  function isBidCompatibleWithHead(
53
- forkChoice: IForkChoice,
54
+ chain: IBeaconChain,
54
55
  head: ProtoBlock,
55
56
  bidSlot: Slot,
56
57
  bidParentBlockRoot: RootHex,
@@ -60,9 +61,13 @@ function isBidCompatibleWithHead(
60
61
  const buildsOnParentPayload = bidParentBlockHash === head.parentBlockHash;
61
62
 
62
63
  if (buildsOnParentBlock && buildsOnParentPayload) {
63
- // The spec allows this at epoch boundaries, but Lodestar does not propagate these bids because validating
64
- // them requires an epoch transition for a parent state that cannot be used for proposer-boost reorgs.
65
- return !isStartSlotOfEpoch(bidSlot);
64
+ if (!isStartSlotOfEpoch(bidSlot)) {
65
+ return true;
66
+ }
67
+ // Epoch-boundary reorgs are allowed, but only propagate the bid if its parent matches the block
68
+ // we predict a proposer would reorg onto — predictProposerHead returns the strong parent only
69
+ // when the head is weak.
70
+ return chain.predictProposerHead().blockRoot === bidParentBlockRoot;
66
71
  }
67
72
 
68
73
  if (bidParentBlockRoot !== head.blockRoot) {
@@ -71,18 +76,179 @@ function isBidCompatibleWithHead(
71
76
 
72
77
  const buildsOnHeadPayload = bidParentBlockHash === head.executionPayloadBlockHash;
73
78
 
74
- if (forkChoice.shouldBuildOnFull(head, bidSlot)) {
79
+ if (chain.forkChoice.shouldBuildOnFull(head, bidSlot)) {
75
80
  return buildsOnHeadPayload;
76
81
  }
77
82
 
78
83
  return buildsOnParentPayload;
79
84
  }
80
85
 
86
+ /**
87
+ * Validation for bids submitted via the API by the operator's own builder.
88
+ *
89
+ * Only non-transient checks are applied: slot later than parent, zero execution payment, blob
90
+ * commitment count, builder eligibility, version and balance coverage, prev_randao, and signature.
91
+ * Transient gossip rules (head compatibility, first bid per tuple, value increment, proposer
92
+ * preferences) are not applied, since those only limit forwarding of peers' messages and the
93
+ * builder may legitimately bid on a branch this node does not consider head.
94
+ * The parent-payload builder-exit rule is omitted because this endpoint only accepts bids from
95
+ * the operator's own builder.
96
+ *
97
+ * Throws on any failed check. Also throws if the bid's parent block is unknown or its state is
98
+ * unavailable, since the checks cannot be evaluated against the parent branch.
99
+ */
81
100
  export async function validateApiExecutionPayloadBid(
82
101
  chain: IBeaconChain,
83
102
  signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
84
- ): Promise<{proposerIndex: ValidatorIndex}> {
85
- return validateExecutionPayloadBid(chain, signedExecutionPayloadBid);
103
+ ): Promise<void> {
104
+ const bid = signedExecutionPayloadBid.message;
105
+ const bidParentBlockRoot = toRootHex(bid.parentBlockRoot);
106
+
107
+ // [REJECT] `bid.execution_payment` is zero.
108
+ if (bid.executionPayment !== 0n) {
109
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
110
+ code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
111
+ builderIndex: bid.builderIndex,
112
+ executionPayment: bid.executionPayment,
113
+ });
114
+ }
115
+
116
+ // [REJECT] The bid's block hash is not equal to its parent block hash -- i.e.
117
+ // `bid.block_hash != bid.parent_block_hash`.
118
+ if (byteArrayEquals(bid.blockHash, bid.parentBlockHash)) {
119
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
120
+ code: ExecutionPayloadBidErrorCode.BLOCK_HASH_EQUALS_PARENT_BLOCK_HASH,
121
+ blockHash: toRootHex(bid.blockHash),
122
+ });
123
+ }
124
+
125
+ // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
126
+ // consensus layer -- i.e. validate that
127
+ // `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
128
+ const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
129
+ const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
130
+ if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
131
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
132
+ code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
133
+ blobKzgCommitmentsLen,
134
+ commitmentLimit: maxBlobsPerBlock,
135
+ });
136
+ }
137
+
138
+ // [IGNORE] `bid.slot` is the current slot, or the next slot (`bid.slot - 1` is current), allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
139
+ if (
140
+ !chain.clock.isCurrentSlotGivenGossipDisparity(bid.slot) &&
141
+ !chain.clock.isCurrentSlotGivenGossipDisparity(bid.slot - 1)
142
+ ) {
143
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
144
+ code: ExecutionPayloadBidErrorCode.INVALID_SLOT,
145
+ builderIndex: bid.builderIndex,
146
+ slot: bid.slot,
147
+ });
148
+ }
149
+
150
+ // [IGNORE] `bid.parent_block_root` is the hash tree root of a known beacon block in fork choice.
151
+ // Moved earlier than the spec ordering so we can derive the proposer dependent root for the
152
+ // proposer-preferences lookup below from a known fork-choice block.
153
+ const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(bidParentBlockRoot);
154
+ if (parentBlock === null) {
155
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
156
+ code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
157
+ parentBlockRoot: bidParentBlockRoot,
158
+ });
159
+ }
160
+
161
+ // [REJECT] The bid is for a higher slot than its parent block -- i.e.
162
+ // validate that `bid.slot` is greater than the slot of the block with root
163
+ // `bid.parent_block_root`.
164
+ if (bid.slot <= parentBlock.slot) {
165
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
166
+ code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT,
167
+ parentSlot: parentBlock.slot,
168
+ slot: bid.slot,
169
+ });
170
+ }
171
+
172
+ const state = await chain.regen
173
+ .getBlockSlotState(parentBlock, bid.slot, {dontTransferCache: true}, RegenCaller.validateApiExecutionPayloadBid)
174
+ .catch(() => {
175
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
176
+ code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
177
+ parentBlockRoot: bidParentBlockRoot,
178
+ });
179
+ });
180
+
181
+ if (!isStatePostGloas(state)) {
182
+ throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
183
+ }
184
+
185
+ // [REJECT] `bid.builder_index` is within bounds -- i.e. `bid.builder_index < len(state.builders)`.
186
+ // `state.getBuilder` returns a lazy SSZ `getReadonly` view that is not bounds-checked eagerly; an
187
+ // out-of-range index only throws (`LeafNode has no right node`) on deferred field access (e.g. inside
188
+ // `isActiveBuilder`), escaping a try/catch around `getBuilder`. Check the length explicitly instead.
189
+ if (bid.builderIndex >= state.getBuildersLength()) {
190
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
191
+ code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
192
+ builderIndex: bid.builderIndex,
193
+ });
194
+ }
195
+
196
+ // [REJECT] `bid.builder_index` is a valid/active builder index -- i.e.
197
+ // `is_active_builder(state, bid.builder_index)` returns `True`.
198
+ const builder = state.getBuilder(bid.builderIndex);
199
+ if (!isActiveBuilder(builder, state.finalizedCheckpoint.epoch)) {
200
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
201
+ code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
202
+ builderIndex: bid.builderIndex,
203
+ });
204
+ }
205
+
206
+ // [REJECT] The builder version is `PAYLOAD_BUILDER_VERSION` -- i.e.
207
+ // `state.builders[bid.builder_index].version == PAYLOAD_BUILDER_VERSION`.
208
+ if (builder.version !== PAYLOAD_BUILDER_VERSION) {
209
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
210
+ code: ExecutionPayloadBidErrorCode.INVALID_BUILDER_VERSION,
211
+ builderIndex: bid.builderIndex,
212
+ version: builder.version,
213
+ expectedVersion: PAYLOAD_BUILDER_VERSION,
214
+ });
215
+ }
216
+
217
+ // [IGNORE] `bid.value` is less or equal than the builder's excess balance --
218
+ // i.e. `can_builder_cover_bid(state, builder_index, amount)` returns `True`.
219
+ if (!state.canBuilderCoverBid(bid.builderIndex, bid.value)) {
220
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
221
+ code: ExecutionPayloadBidErrorCode.BID_TOO_HIGH,
222
+ bidValue: bid.value,
223
+ builderBalance: builder.balance,
224
+ });
225
+ }
226
+
227
+ // [REJECT] `bid.prev_randao` is the correct RANDAO mix -- i.e. validate that
228
+ // `bid.prev_randao == get_randao_mix(parent_state, get_current_epoch(parent_state))`.
229
+ const randaoMix = state.getRandaoMix(computeEpochAtSlot(state.slot));
230
+ if (!byteArrayEquals(bid.prevRandao, randaoMix)) {
231
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
232
+ code: ExecutionPayloadBidErrorCode.INVALID_PREV_RANDAO,
233
+ builderIndex: bid.builderIndex,
234
+ bidPrevRandao: toHex(bid.prevRandao),
235
+ expectedPrevRandao: toHex(randaoMix),
236
+ });
237
+ }
238
+
239
+ // [REJECT] `signed_execution_payload_bid.signature` is valid with respect to the `bid.builder_index`.
240
+ const signatureSet = createSingleSignatureSetFromComponents(
241
+ builder.pubkey,
242
+ getExecutionPayloadBidSigningRoot(chain.config, bid),
243
+ signedExecutionPayloadBid.signature
244
+ );
245
+ if (!(await chain.bls.verifySignatureSets([signatureSet]))) {
246
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
247
+ code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE,
248
+ builderIndex: bid.builderIndex,
249
+ slot: bid.slot,
250
+ });
251
+ }
86
252
  }
87
253
 
88
254
  export async function validateGossipExecutionPayloadBid(
@@ -100,6 +266,37 @@ async function validateExecutionPayloadBid(
100
266
  const bidParentBlockRoot = toRootHex(bid.parentBlockRoot);
101
267
  const bidParentBlockHash = toRootHex(bid.parentBlockHash);
102
268
 
269
+ // [REJECT] `bid.execution_payment` is zero.
270
+ if (bid.executionPayment !== 0n) {
271
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
272
+ code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
273
+ builderIndex: bid.builderIndex,
274
+ executionPayment: bid.executionPayment,
275
+ });
276
+ }
277
+
278
+ // [REJECT] The bid's block hash is not equal to its parent block hash -- i.e.
279
+ // `bid.block_hash != bid.parent_block_hash`.
280
+ if (byteArrayEquals(bid.blockHash, bid.parentBlockHash)) {
281
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
282
+ code: ExecutionPayloadBidErrorCode.BLOCK_HASH_EQUALS_PARENT_BLOCK_HASH,
283
+ blockHash: toRootHex(bid.blockHash),
284
+ });
285
+ }
286
+
287
+ // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
288
+ // consensus layer -- i.e. validate that
289
+ // `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
290
+ const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
291
+ const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
292
+ if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
293
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
294
+ code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
295
+ blobKzgCommitmentsLen,
296
+ commitmentLimit: maxBlobsPerBlock,
297
+ });
298
+ }
299
+
103
300
  // [IGNORE] `bid.slot` is the current slot, or the next slot (`bid.slot - 1` is current), allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
104
301
  if (
105
302
  !chain.clock.isCurrentSlotGivenGossipDisparity(bid.slot) &&
@@ -114,7 +311,7 @@ async function validateExecutionPayloadBid(
114
311
 
115
312
  // [IGNORE] The bid is compatible with the current head branch.
116
313
  const head = chain.forkChoice.getHead();
117
- if (!isBidCompatibleWithHead(chain.forkChoice, head, bid.slot, bidParentBlockRoot, bidParentBlockHash)) {
314
+ if (!isBidCompatibleWithHead(chain, head, bid.slot, bidParentBlockRoot, bidParentBlockHash)) {
118
315
  throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
119
316
  code: ExecutionPayloadBidErrorCode.INCOMPATIBLE_WITH_HEAD,
120
317
  slot: bid.slot,
@@ -245,15 +442,6 @@ async function validateExecutionPayloadBid(
245
442
  });
246
443
  }
247
444
 
248
- // [REJECT] `bid.execution_payment` is zero.
249
- if (bid.executionPayment !== 0n) {
250
- throw new ExecutionPayloadBidError(GossipAction.REJECT, {
251
- code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
252
- builderIndex: bid.builderIndex,
253
- executionPayment: bid.executionPayment,
254
- });
255
- }
256
-
257
445
  // [IGNORE] `bid.fee_recipient == proposer_preferences.fee_recipient`.
258
446
  if (!byteArrayEquals(bid.feeRecipient, proposerPreferences.message.feeRecipient)) {
259
447
  throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
@@ -294,19 +482,6 @@ async function validateExecutionPayloadBid(
294
482
  });
295
483
  }
296
484
 
297
- // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
298
- // consensus layer -- i.e. validate that
299
- // `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
300
- const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
301
- const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
302
- if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
303
- throw new ExecutionPayloadBidError(GossipAction.REJECT, {
304
- code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
305
- blobKzgCommitmentsLen,
306
- commitmentLimit: maxBlobsPerBlock,
307
- });
308
- }
309
-
310
485
  // [IGNORE] this bid is the highest value bid seen for the tuple
311
486
  // `(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
312
487
  // As a DoS prevention measure, the bid must also exceed the current highest bid by a minimum
@@ -342,6 +517,29 @@ async function validateExecutionPayloadBid(
342
517
  });
343
518
  }
344
519
 
520
+ // [IGNORE] The parent's payload does not try to exit the builder
521
+ if (byteArrayEquals(bid.parentBlockHash, state.latestExecutionPayloadBid.blockHash)) {
522
+ const requests = await chain.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot).catch(() => {
523
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
524
+ code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH,
525
+ parentBlockHash: bidParentBlockHash,
526
+ });
527
+ });
528
+ if (
529
+ requests.builderExits.some(
530
+ (request) =>
531
+ byteArrayEquals(request.pubkey, builder.pubkey) &&
532
+ byteArrayEquals(request.sourceAddress, builder.executionAddress)
533
+ )
534
+ ) {
535
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
536
+ code: ExecutionPayloadBidErrorCode.BUILDER_MAY_EXIT,
537
+ builderIndex: bid.builderIndex,
538
+ parentBlockRoot: bidParentBlockRoot,
539
+ });
540
+ }
541
+ }
542
+
345
543
  // [REJECT] `signed_execution_payload_bid.signature` is valid with respect to the `bid.builder_index`.
346
544
  const signatureSet = createSingleSignatureSetFromComponents(
347
545
  builder.pubkey,
@@ -1,11 +1,4 @@
1
1
  import {PayloadStatus} from "@lodestar/fork-choice";
2
- import {
3
- MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
4
- MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD,
5
- MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
6
- MAX_WITHDRAWALS_PER_PAYLOAD,
7
- MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
8
- } from "@lodestar/params";
9
2
  import {
10
3
  computeStartSlotAtEpoch,
11
4
  getExecutionPayloadEnvelopeSignatureSet,
@@ -89,6 +82,8 @@ async function validateExecutionPayloadEnvelope(
89
82
  if (block.slot !== payload.slotNumber) {
90
83
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
91
84
  code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH,
85
+ slot: payload.slotNumber,
86
+ root: blockRootHex,
92
87
  envelopeSlot: payload.slotNumber,
93
88
  blockSlot: block.slot,
94
89
  });
@@ -98,6 +93,8 @@ async function validateExecutionPayloadEnvelope(
98
93
  if (envelope.builderIndex !== payloadInput.getBuilderIndex()) {
99
94
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
100
95
  code: ExecutionPayloadEnvelopeErrorCode.BUILDER_INDEX_MISMATCH,
96
+ slot: payload.slotNumber,
97
+ root: blockRootHex,
101
98
  envelopeBuilderIndex: envelope.builderIndex,
102
99
  bidBuilderIndex: payloadInput.getBuilderIndex(),
103
100
  });
@@ -107,6 +104,8 @@ async function validateExecutionPayloadEnvelope(
107
104
  if (toRootHex(payload.blockHash) !== payloadInput.getBlockHashHex()) {
108
105
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
109
106
  code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
107
+ slot: payload.slotNumber,
108
+ root: blockRootHex,
110
109
  envelopeBlockHash: toRootHex(payload.blockHash),
111
110
  bidBlockHash: payloadInput.getBlockHashHex(),
112
111
  });
@@ -117,41 +116,13 @@ async function validateExecutionPayloadEnvelope(
117
116
  if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
118
117
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
119
118
  code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
119
+ slot: payload.slotNumber,
120
+ root: blockRootHex,
120
121
  envelopeRequestsRoot: toRootHex(requestsRoot),
121
122
  bidRequestsRoot: toRootHex(payloadInput.getBid().executionRequestsRoot),
122
123
  });
123
124
  }
124
125
 
125
- // [REJECT] The counts of `execution_requests` are within their respective limits.
126
- // New in Gloas:EIP7688 — progressive lists are unbounded at the type level, so bounds
127
- // are enforced here in gossip validation.
128
- const {executionRequests} = envelope;
129
- const requestCountLimits: [string, number, number][] = [
130
- ["withdrawals", executionRequests.withdrawals.length, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
131
- ["consolidations", executionRequests.consolidations.length, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD],
132
- ["builderDeposits", executionRequests.builderDeposits.length, MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD],
133
- ["builderExits", executionRequests.builderExits.length, MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD],
134
- ];
135
- for (const [name, count, limit] of requestCountLimits) {
136
- if (count > limit) {
137
- throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
138
- code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_COUNT_EXCEEDED,
139
- name,
140
- count,
141
- limit,
142
- });
143
- }
144
- }
145
-
146
- // [REJECT] The number of withdrawals is within the limit.
147
- if (payload.withdrawals.length > MAX_WITHDRAWALS_PER_PAYLOAD) {
148
- throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
149
- code: ExecutionPayloadEnvelopeErrorCode.WITHDRAWALS_COUNT_EXCEEDED,
150
- count: payload.withdrawals.length,
151
- limit: MAX_WITHDRAWALS_PER_PAYLOAD,
152
- });
153
- }
154
-
155
126
  // Get the block state to verify the builder's signature.
156
127
  const blockState = await chain.regen
157
128
  .getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
@@ -178,6 +149,8 @@ async function validateExecutionPayloadEnvelope(
178
149
  if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: true}))) {
179
150
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
180
151
  code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE,
152
+ slot: payload.slotNumber,
153
+ root: blockRootHex,
181
154
  });
182
155
  }
183
156
  }
@@ -7,7 +7,6 @@ import {RootHex, gloas, ssz} from "@lodestar/types";
7
7
  import {toRootHex} from "@lodestar/utils";
8
8
  import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js";
9
9
  import {IBeaconChain} from "../index.js";
10
- import {RegenCaller} from "../regen/index.js";
11
10
 
12
11
  export type PayloadAttestationValidationResult = {
13
12
  attDataRootHex: RootHex;
@@ -82,15 +81,15 @@ async function validatePayloadAttestationMessage(
82
81
  // TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
83
82
  // it is possible that the block didn't pass the validation
84
83
 
85
- // Use the referenced block's branch state for the PTC committee check
86
- const state = await chain.regen
87
- .getBlockSlotState(block, data.slot, {dontTransferCache: true}, RegenCaller.validateGossipPayloadAttestationMessage)
88
- .catch(() => {
89
- throw new PayloadAttestationError(GossipAction.IGNORE, {
90
- code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
91
- blockRoot: toRootHex(data.beaconBlockRoot),
92
- });
84
+ // block.slot === data.slot is enforced above, so use the block's post-state directly to avoid
85
+ // getting through regen queue
86
+ const state = chain.regen.getStateSync(block.stateRoot);
87
+ if (state == null) {
88
+ throw new PayloadAttestationError(GossipAction.IGNORE, {
89
+ code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
90
+ blockRoot: toRootHex(data.beaconBlockRoot),
93
91
  });
92
+ }
94
93
 
95
94
  if (!isStatePostGloas(state)) {
96
95
  throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);