@lodestar/beacon-node 1.51.0-dev.ab56d90f0c → 1.51.0-dev.ab8f3f6381

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 (414) hide show
  1. package/lib/api/impl/api.js +1 -1
  2. package/lib/api/impl/api.js.map +1 -1
  3. package/lib/api/impl/beacon/blocks/index.d.ts +2 -1
  4. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/blocks/index.js +29 -34
  6. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  7. package/lib/api/impl/beacon/index.d.ts +2 -1
  8. package/lib/api/impl/beacon/index.d.ts.map +1 -1
  9. package/lib/api/impl/beacon/index.js +2 -2
  10. package/lib/api/impl/beacon/index.js.map +1 -1
  11. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  12. package/lib/api/impl/beacon/pool/index.js +22 -2
  13. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  14. package/lib/api/impl/beacon/state/utils.d.ts +1 -1
  15. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  16. package/lib/api/impl/validator/index.d.ts +6 -0
  17. package/lib/api/impl/validator/index.d.ts.map +1 -1
  18. package/lib/api/impl/validator/index.js +42 -26
  19. package/lib/api/impl/validator/index.js.map +1 -1
  20. package/lib/api/options.d.ts +2 -0
  21. package/lib/api/options.d.ts.map +1 -1
  22. package/lib/api/options.js +1 -0
  23. package/lib/api/options.js.map +1 -1
  24. package/lib/chain/GetBlobsTracker.d.ts +13 -2
  25. package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
  26. package/lib/chain/GetBlobsTracker.js +85 -27
  27. package/lib/chain/GetBlobsTracker.js.map +1 -1
  28. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  29. package/lib/chain/archiveStore/archiveStore.js +1 -0
  30. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  31. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +4 -0
  32. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  33. package/lib/chain/archiveStore/utils/archiveBlocks.js +61 -97
  34. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  35. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  36. package/lib/chain/blocks/importBlock.js +39 -50
  37. package/lib/chain/blocks/importBlock.js.map +1 -1
  38. package/lib/chain/blocks/importExecutionPayload.d.ts +10 -11
  39. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  40. package/lib/chain/blocks/importExecutionPayload.js +32 -18
  41. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  42. package/lib/chain/blocks/index.d.ts.map +1 -1
  43. package/lib/chain/blocks/index.js +4 -4
  44. package/lib/chain/blocks/index.js.map +1 -1
  45. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +12 -9
  46. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
  47. package/lib/chain/blocks/payloadEnvelopeProcessor.js +24 -19
  48. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  49. package/lib/chain/blocks/types.d.ts +4 -1
  50. package/lib/chain/blocks/types.d.ts.map +1 -1
  51. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  52. package/lib/chain/blocks/verifyBlock.js +18 -7
  53. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  54. package/lib/chain/blocks/verifyBlocksDataAvailability.d.ts.map +1 -1
  55. package/lib/chain/blocks/verifyBlocksDataAvailability.js +8 -1
  56. package/lib/chain/blocks/verifyBlocksDataAvailability.js.map +1 -1
  57. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +1 -1
  58. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  59. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  60. package/lib/chain/blocks/verifyBlocksSanityChecks.js +4 -0
  61. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  62. package/lib/chain/blocks/verifyBlocksSignatures.d.ts.map +1 -1
  63. package/lib/chain/blocks/verifyBlocksSignatures.js +7 -5
  64. package/lib/chain/blocks/verifyBlocksSignatures.js.map +1 -1
  65. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +3 -3
  66. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  67. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +11 -6
  68. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  69. package/lib/chain/bls/interface.d.ts +2 -0
  70. package/lib/chain/bls/interface.d.ts.map +1 -1
  71. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  72. package/lib/chain/bls/multithread/index.js +13 -11
  73. package/lib/chain/bls/multithread/index.js.map +1 -1
  74. package/lib/chain/bls/multithread/types.d.ts +1 -1
  75. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  76. package/lib/chain/bls/multithread/worker.js +1 -1
  77. package/lib/chain/bls/multithread/worker.js.map +1 -1
  78. package/lib/chain/bls/singleThread.js +1 -1
  79. package/lib/chain/bls/singleThread.js.map +1 -1
  80. package/lib/chain/bls/utils.js +1 -1
  81. package/lib/chain/bls/utils.js.map +1 -1
  82. package/lib/chain/chain.d.ts +25 -3
  83. package/lib/chain/chain.d.ts.map +1 -1
  84. package/lib/chain/chain.js +123 -10
  85. package/lib/chain/chain.js.map +1 -1
  86. package/lib/chain/emitter.d.ts +6 -0
  87. package/lib/chain/emitter.d.ts.map +1 -1
  88. package/lib/chain/emitter.js +5 -0
  89. package/lib/chain/emitter.js.map +1 -1
  90. package/lib/chain/errors/blockError.d.ts +1 -1
  91. package/lib/chain/errors/blockError.d.ts.map +1 -1
  92. package/lib/chain/errors/blockError.js +1 -1
  93. package/lib/chain/errors/blockError.js.map +1 -1
  94. package/lib/chain/errors/executionPayloadEnvelope.d.ts +7 -0
  95. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  96. package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
  97. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  98. package/lib/chain/forkChoice/index.d.ts +11 -1
  99. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  100. package/lib/chain/forkChoice/index.js +11 -0
  101. package/lib/chain/forkChoice/index.js.map +1 -1
  102. package/lib/chain/forkChoiceExecutionEngine.d.ts +53 -0
  103. package/lib/chain/forkChoiceExecutionEngine.d.ts.map +1 -0
  104. package/lib/chain/forkChoiceExecutionEngine.js +121 -0
  105. package/lib/chain/forkChoiceExecutionEngine.js.map +1 -0
  106. package/lib/chain/interface.d.ts +9 -6
  107. package/lib/chain/interface.d.ts.map +1 -1
  108. package/lib/chain/interface.js.map +1 -1
  109. package/lib/chain/opPools/aggregatedAttestationPool.d.ts +17 -4
  110. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  111. package/lib/chain/opPools/aggregatedAttestationPool.js +199 -46
  112. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  113. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  114. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  115. package/lib/chain/opPools/attestationPool.js +15 -4
  116. package/lib/chain/opPools/attestationPool.js.map +1 -1
  117. package/lib/chain/opPools/index.d.ts +1 -0
  118. package/lib/chain/opPools/index.d.ts.map +1 -1
  119. package/lib/chain/opPools/index.js +1 -0
  120. package/lib/chain/opPools/index.js.map +1 -1
  121. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  122. package/lib/chain/opPools/payloadAttestationPool.js +17 -7
  123. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  124. package/lib/chain/opPools/singleAttestationPool.d.ts +77 -0
  125. package/lib/chain/opPools/singleAttestationPool.d.ts.map +1 -0
  126. package/lib/chain/opPools/singleAttestationPool.js +209 -0
  127. package/lib/chain/opPools/singleAttestationPool.js.map +1 -0
  128. package/lib/chain/opPools/syncCommitteeMessagePool.d.ts.map +1 -1
  129. package/lib/chain/opPools/syncCommitteeMessagePool.js +9 -3
  130. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  131. package/lib/chain/opPools/syncContributionAndProofPool.d.ts.map +1 -1
  132. package/lib/chain/opPools/syncContributionAndProofPool.js +11 -3
  133. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  134. package/lib/chain/opPools/utils.d.ts +1 -1
  135. package/lib/chain/opPools/utils.d.ts.map +1 -1
  136. package/lib/chain/opPools/utils.js +1 -1
  137. package/lib/chain/opPools/utils.js.map +1 -1
  138. package/lib/chain/options.d.ts +5 -1
  139. package/lib/chain/options.d.ts.map +1 -1
  140. package/lib/chain/options.js +0 -1
  141. package/lib/chain/options.js.map +1 -1
  142. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -2
  143. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  144. package/lib/chain/produceBlock/produceBlockBody.js +3 -2
  145. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  146. package/lib/chain/proposerTracker.d.ts +31 -0
  147. package/lib/chain/proposerTracker.d.ts.map +1 -0
  148. package/lib/chain/proposerTracker.js +75 -0
  149. package/lib/chain/proposerTracker.js.map +1 -0
  150. package/lib/chain/regen/interface.d.ts +1 -2
  151. package/lib/chain/regen/interface.d.ts.map +1 -1
  152. package/lib/chain/regen/interface.js +0 -1
  153. package/lib/chain/regen/interface.js.map +1 -1
  154. package/lib/chain/regen/queued.d.ts +1 -1
  155. package/lib/chain/regen/queued.d.ts.map +1 -1
  156. package/lib/chain/regen/queued.js +5 -3
  157. package/lib/chain/regen/queued.js.map +1 -1
  158. package/lib/chain/validation/executionPayloadBid.d.ts +0 -15
  159. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  160. package/lib/chain/validation/executionPayloadBid.js +13 -166
  161. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  162. package/lib/chain/validation/executionPayloadEnvelope.d.ts +6 -0
  163. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  164. package/lib/chain/validation/executionPayloadEnvelope.js +53 -0
  165. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  166. package/lib/db/beacon.d.ts +7 -0
  167. package/lib/db/beacon.d.ts.map +1 -1
  168. package/lib/db/beacon.js +34 -0
  169. package/lib/db/beacon.js.map +1 -1
  170. package/lib/db/dataColumnStore.d.ts +1 -1
  171. package/lib/db/dataColumnStore.d.ts.map +1 -1
  172. package/lib/db/dataColumnStore.js +1 -4
  173. package/lib/db/dataColumnStore.js.map +1 -1
  174. package/lib/db/flatFileStore/flatFileStore.js +5 -5
  175. package/lib/db/flatFileStore/flatFileStore.js.map +1 -1
  176. package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -1
  177. package/lib/db/flatFileStore/slotIndex.js +2 -2
  178. package/lib/db/flatFileStore/slotIndex.js.map +1 -1
  179. package/lib/db/interface.d.ts +1 -0
  180. package/lib/db/interface.d.ts.map +1 -1
  181. package/lib/db/repositories/dataColumnSidecarArchive.d.ts +6 -0
  182. package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
  183. package/lib/db/repositories/dataColumnSidecarArchive.js +19 -0
  184. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  185. package/lib/execution/builder/apiClient.d.ts +14 -8
  186. package/lib/execution/builder/apiClient.d.ts.map +1 -1
  187. package/lib/execution/builder/apiClient.js +12 -10
  188. package/lib/execution/builder/apiClient.js.map +1 -1
  189. package/lib/execution/builder/http.d.ts +2 -0
  190. package/lib/execution/builder/http.d.ts.map +1 -1
  191. package/lib/execution/builder/http.js +2 -0
  192. package/lib/execution/builder/http.js.map +1 -1
  193. package/lib/execution/engine/http.d.ts +2 -2
  194. package/lib/execution/engine/http.d.ts.map +1 -1
  195. package/lib/execution/engine/http.js +5 -4
  196. package/lib/execution/engine/http.js.map +1 -1
  197. package/lib/execution/engine/index.d.ts +1 -1
  198. package/lib/execution/engine/index.d.ts.map +1 -1
  199. package/lib/execution/engine/index.js +1 -1
  200. package/lib/execution/engine/index.js.map +1 -1
  201. package/lib/execution/engine/interface.d.ts +17 -3
  202. package/lib/execution/engine/interface.d.ts.map +1 -1
  203. package/lib/execution/engine/interface.js.map +1 -1
  204. package/lib/execution/engine/jsonRpcTransport.d.ts +2 -2
  205. package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -1
  206. package/lib/execution/engine/jsonRpcTransport.js +1 -1
  207. package/lib/execution/engine/jsonRpcTransport.js.map +1 -1
  208. package/lib/execution/engine/restTransport.d.ts +2 -2
  209. package/lib/execution/engine/restTransport.d.ts.map +1 -1
  210. package/lib/execution/engine/restTransport.js.map +1 -1
  211. package/lib/execution/engine/transport.d.ts +1 -12
  212. package/lib/execution/engine/transport.d.ts.map +1 -1
  213. package/lib/execution/engine/types.d.ts.map +1 -1
  214. package/lib/execution/engine/types.js +2 -2
  215. package/lib/execution/engine/types.js.map +1 -1
  216. package/lib/execution/engine/utils.d.ts.map +1 -1
  217. package/lib/execution/engine/utils.js +3 -2
  218. package/lib/execution/engine/utils.js.map +1 -1
  219. package/lib/index.d.ts +1 -0
  220. package/lib/index.d.ts.map +1 -1
  221. package/lib/index.js +2 -0
  222. package/lib/index.js.map +1 -1
  223. package/lib/metrics/metrics/lodestar.d.ts +33 -0
  224. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  225. package/lib/metrics/metrics/lodestar.js +84 -0
  226. package/lib/metrics/metrics/lodestar.js.map +1 -1
  227. package/lib/metrics/server/http.js +5 -5
  228. package/lib/metrics/server/http.js.map +1 -1
  229. package/lib/monitoring/service.js +1 -1
  230. package/lib/monitoring/service.js.map +1 -1
  231. package/lib/network/core/networkCore.d.ts +4 -1
  232. package/lib/network/core/networkCore.d.ts.map +1 -1
  233. package/lib/network/core/networkCore.js +7 -0
  234. package/lib/network/core/networkCore.js.map +1 -1
  235. package/lib/network/core/networkCoreWorker.js +2 -0
  236. package/lib/network/core/networkCoreWorker.js.map +1 -1
  237. package/lib/network/core/networkCoreWorkerHandler.d.ts +4 -1
  238. package/lib/network/core/networkCoreWorkerHandler.d.ts.map +1 -1
  239. package/lib/network/core/networkCoreWorkerHandler.js +6 -0
  240. package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
  241. package/lib/network/core/types.d.ts +6 -1
  242. package/lib/network/core/types.d.ts.map +1 -1
  243. package/lib/network/gossip/gossipsub.d.ts +1 -0
  244. package/lib/network/gossip/gossipsub.d.ts.map +1 -1
  245. package/lib/network/gossip/gossipsub.js +10 -0
  246. package/lib/network/gossip/gossipsub.js.map +1 -1
  247. package/lib/network/interface.d.ts +2 -0
  248. package/lib/network/interface.d.ts.map +1 -1
  249. package/lib/network/libp2p/index.d.ts.map +1 -1
  250. package/lib/network/libp2p/index.js +5 -1
  251. package/lib/network/libp2p/index.js.map +1 -1
  252. package/lib/network/network.d.ts +6 -1
  253. package/lib/network/network.d.ts.map +1 -1
  254. package/lib/network/network.js +53 -15
  255. package/lib/network/network.js.map +1 -1
  256. package/lib/network/options.d.ts +2 -0
  257. package/lib/network/options.d.ts.map +1 -1
  258. package/lib/network/options.js +2 -0
  259. package/lib/network/options.js.map +1 -1
  260. package/lib/network/peers/peerManager.d.ts +2 -1
  261. package/lib/network/peers/peerManager.d.ts.map +1 -1
  262. package/lib/network/peers/peerManager.js +28 -1
  263. package/lib/network/peers/peerManager.js.map +1 -1
  264. package/lib/network/peers/utils/assertPeerRelevance.d.ts +9 -1
  265. package/lib/network/peers/utils/assertPeerRelevance.d.ts.map +1 -1
  266. package/lib/network/peers/utils/assertPeerRelevance.js +32 -21
  267. package/lib/network/peers/utils/assertPeerRelevance.js.map +1 -1
  268. package/lib/network/peers/utils/prioritizePeers.d.ts +4 -1
  269. package/lib/network/peers/utils/prioritizePeers.d.ts.map +1 -1
  270. package/lib/network/peers/utils/prioritizePeers.js +8 -16
  271. package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
  272. package/lib/network/peers/utils/subnetMap.d.ts +5 -2
  273. package/lib/network/peers/utils/subnetMap.d.ts.map +1 -1
  274. package/lib/network/peers/utils/subnetMap.js +6 -3
  275. package/lib/network/peers/utils/subnetMap.js.map +1 -1
  276. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  277. package/lib/network/processor/gossipHandlers.js +77 -37
  278. package/lib/network/processor/gossipHandlers.js.map +1 -1
  279. package/lib/network/processor/index.d.ts +6 -0
  280. package/lib/network/processor/index.d.ts.map +1 -1
  281. package/lib/network/processor/index.js +29 -3
  282. package/lib/network/processor/index.js.map +1 -1
  283. package/lib/network/subnets/attnetsService.d.ts +35 -5
  284. package/lib/network/subnets/attnetsService.d.ts.map +1 -1
  285. package/lib/network/subnets/attnetsService.js +84 -6
  286. package/lib/network/subnets/attnetsService.js.map +1 -1
  287. package/lib/network/subnets/interface.d.ts +7 -0
  288. package/lib/network/subnets/interface.d.ts.map +1 -1
  289. package/lib/network/subnets/interface.js.map +1 -1
  290. package/lib/node/nodejs.d.ts +3 -1
  291. package/lib/node/nodejs.d.ts.map +1 -1
  292. package/lib/node/nodejs.js +47 -21
  293. package/lib/node/nodejs.js.map +1 -1
  294. package/lib/node/options.d.ts +3 -1
  295. package/lib/node/options.d.ts.map +1 -1
  296. package/lib/node/options.js +4 -2
  297. package/lib/node/options.js.map +1 -1
  298. package/lib/sync/interface.d.ts.map +1 -1
  299. package/lib/sync/interface.js.map +1 -1
  300. package/lib/sync/sync.d.ts +7 -0
  301. package/lib/sync/sync.d.ts.map +1 -1
  302. package/lib/sync/sync.js +61 -3
  303. package/lib/sync/sync.js.map +1 -1
  304. package/lib/sync/unknownBlock.d.ts.map +1 -1
  305. package/lib/sync/unknownBlock.js +42 -18
  306. package/lib/sync/unknownBlock.js.map +1 -1
  307. package/lib/util/execution.d.ts +4 -4
  308. package/lib/util/execution.d.ts.map +1 -1
  309. package/lib/util/execution.js.map +1 -1
  310. package/lib/util/file.d.ts.map +1 -1
  311. package/lib/util/file.js +8 -9
  312. package/lib/util/file.js.map +1 -1
  313. package/lib/util/queue/itemQueue.js +1 -1
  314. package/lib/util/queue/itemQueue.js.map +1 -1
  315. package/lib/util/sszBytes.d.ts +5 -0
  316. package/lib/util/sszBytes.d.ts.map +1 -1
  317. package/lib/util/sszBytes.js +35 -2
  318. package/lib/util/sszBytes.js.map +1 -1
  319. package/package.json +16 -17
  320. package/src/api/impl/api.ts +1 -1
  321. package/src/api/impl/beacon/blocks/index.ts +57 -62
  322. package/src/api/impl/beacon/index.ts +3 -1
  323. package/src/api/impl/beacon/pool/index.ts +31 -1
  324. package/src/api/impl/beacon/state/utils.ts +1 -1
  325. package/src/api/impl/validator/index.ts +47 -30
  326. package/src/api/options.ts +3 -0
  327. package/src/chain/GetBlobsTracker.ts +99 -40
  328. package/src/chain/archiveStore/archiveStore.ts +1 -0
  329. package/src/chain/archiveStore/utils/archiveBlocks.ts +76 -130
  330. package/src/chain/blocks/importBlock.ts +60 -71
  331. package/src/chain/blocks/importExecutionPayload.ts +55 -34
  332. package/src/chain/blocks/index.ts +11 -4
  333. package/src/chain/blocks/payloadEnvelopeProcessor.ts +35 -24
  334. package/src/chain/blocks/types.ts +4 -1
  335. package/src/chain/blocks/verifyBlock.ts +31 -16
  336. package/src/chain/blocks/verifyBlocksDataAvailability.ts +10 -1
  337. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +1 -1
  338. package/src/chain/blocks/verifyBlocksSanityChecks.ts +4 -1
  339. package/src/chain/blocks/verifyBlocksSignatures.ts +7 -5
  340. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +14 -6
  341. package/src/chain/bls/interface.ts +2 -0
  342. package/src/chain/bls/multithread/index.ts +14 -10
  343. package/src/chain/bls/multithread/types.ts +1 -1
  344. package/src/chain/bls/multithread/worker.ts +1 -5
  345. package/src/chain/bls/singleThread.ts +1 -1
  346. package/src/chain/bls/utils.ts +1 -1
  347. package/src/chain/chain.ts +139 -8
  348. package/src/chain/emitter.ts +7 -0
  349. package/src/chain/errors/blockError.ts +1 -1
  350. package/src/chain/errors/executionPayloadEnvelope.ts +8 -0
  351. package/src/chain/forkChoice/index.ts +11 -0
  352. package/src/chain/forkChoiceExecutionEngine.ts +197 -0
  353. package/src/chain/interface.ts +9 -5
  354. package/src/chain/opPools/aggregatedAttestationPool.ts +246 -43
  355. package/src/chain/opPools/attestationPool.ts +16 -4
  356. package/src/chain/opPools/index.ts +1 -0
  357. package/src/chain/opPools/payloadAttestationPool.ts +18 -7
  358. package/src/chain/opPools/singleAttestationPool.ts +287 -0
  359. package/src/chain/opPools/syncCommitteeMessagePool.ts +10 -3
  360. package/src/chain/opPools/syncContributionAndProofPool.ts +13 -3
  361. package/src/chain/opPools/utils.ts +1 -1
  362. package/src/chain/options.ts +5 -2
  363. package/src/chain/produceBlock/produceBlockBody.ts +7 -3
  364. package/src/chain/proposerTracker.ts +94 -0
  365. package/src/chain/regen/interface.ts +1 -2
  366. package/src/chain/regen/queued.ts +6 -4
  367. package/src/chain/validation/executionPayloadBid.ts +14 -193
  368. package/src/chain/validation/executionPayloadEnvelope.ts +68 -1
  369. package/src/db/beacon.ts +44 -0
  370. package/src/db/dataColumnStore.ts +2 -4
  371. package/src/db/flatFileStore/flatFileStore.ts +7 -7
  372. package/src/db/flatFileStore/slotIndex.ts +4 -3
  373. package/src/db/interface.ts +2 -0
  374. package/src/db/repositories/dataColumnSidecarArchive.ts +20 -0
  375. package/src/execution/builder/apiClient.ts +16 -13
  376. package/src/execution/builder/http.ts +4 -0
  377. package/src/execution/engine/http.ts +12 -13
  378. package/src/execution/engine/index.ts +1 -1
  379. package/src/execution/engine/interface.ts +19 -3
  380. package/src/execution/engine/jsonRpcTransport.ts +10 -2
  381. package/src/execution/engine/restTransport.ts +3 -1
  382. package/src/execution/engine/transport.ts +8 -14
  383. package/src/execution/engine/types.ts +3 -1
  384. package/src/execution/engine/utils.ts +4 -2
  385. package/src/index.ts +2 -0
  386. package/src/metrics/metrics/lodestar.ts +85 -0
  387. package/src/metrics/server/http.ts +5 -5
  388. package/src/monitoring/service.ts +1 -1
  389. package/src/network/core/networkCore.ts +11 -1
  390. package/src/network/core/networkCoreWorker.ts +2 -0
  391. package/src/network/core/networkCoreWorkerHandler.ts +8 -1
  392. package/src/network/core/types.ts +6 -1
  393. package/src/network/gossip/gossipsub.ts +11 -0
  394. package/src/network/interface.ts +2 -0
  395. package/src/network/libp2p/index.ts +5 -1
  396. package/src/network/network.ts +55 -19
  397. package/src/network/options.ts +4 -0
  398. package/src/network/peers/peerManager.ts +28 -3
  399. package/src/network/peers/utils/assertPeerRelevance.ts +39 -23
  400. package/src/network/peers/utils/prioritizePeers.ts +14 -22
  401. package/src/network/peers/utils/subnetMap.ts +6 -3
  402. package/src/network/processor/gossipHandlers.ts +98 -40
  403. package/src/network/processor/index.ts +25 -3
  404. package/src/network/subnets/attnetsService.ts +94 -6
  405. package/src/network/subnets/interface.ts +7 -0
  406. package/src/node/nodejs.ts +58 -22
  407. package/src/node/options.ts +9 -6
  408. package/src/sync/interface.ts +1 -0
  409. package/src/sync/sync.ts +65 -4
  410. package/src/sync/unknownBlock.ts +47 -28
  411. package/src/util/execution.ts +6 -6
  412. package/src/util/file.ts +8 -9
  413. package/src/util/queue/itemQueue.ts +1 -1
  414. package/src/util/sszBytes.ts +42 -2
@@ -24,6 +24,7 @@ import {
24
24
  deneb,
25
25
  fulu,
26
26
  gloas,
27
+ isGloasBeaconBlock,
27
28
  isGloasDataColumnSidecar,
28
29
  ssz,
29
30
  sszTypesFor,
@@ -65,7 +66,10 @@ import {
65
66
  validateGossipGloasDataColumnSidecar,
66
67
  } from "../../chain/validation/dataColumnSidecar.js";
67
68
  import {validateGossipExecutionPayloadBid} from "../../chain/validation/executionPayloadBid.js";
68
- import {validateGossipExecutionPayloadEnvelope} from "../../chain/validation/executionPayloadEnvelope.js";
69
+ import {
70
+ validateGossipExecutionPayloadEnvelope,
71
+ validateGossipExecutionPayloadEnvelopeSerialized,
72
+ } from "../../chain/validation/executionPayloadEnvelope.js";
69
73
  import {InclusionListValidationResult, validateGossipInclusionList} from "../../chain/validation/inclusionList.js";
70
74
  import {
71
75
  AggregateAndProofValidationResult,
@@ -150,6 +154,31 @@ export function getGossipHandlers(modules: ValidatorFnsModules, options: GossipH
150
154
  */
151
155
  function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHandlerOpts): SequentialGossipHandlers {
152
156
  const {chain, config, metrics, logger, core} = modules;
157
+ const incompletePayloadWaits = new WeakSet<PayloadEnvelopeInput>();
158
+
159
+ function scheduleIncompletePayloadEnvelope(payloadInput: PayloadEnvelopeInput, peer: string): void {
160
+ if (payloadInput.isComplete() || incompletePayloadWaits.has(payloadInput)) {
161
+ return;
162
+ }
163
+ incompletePayloadWaits.add(payloadInput);
164
+
165
+ const fork = config.getForkName(payloadInput.slot);
166
+ const cutoffTimeMs = getCutoffTimeMs(chain, payloadInput.slot, config.getPayloadDueMs(fork));
167
+ payloadInput.waitForEnvelopeAndAllData(cutoffTimeMs).catch(() => {
168
+ if (
169
+ payloadInput.isComplete() ||
170
+ chain.seenPayloadEnvelopeInputCache.get(payloadInput.blockRootHex) !== payloadInput
171
+ ) {
172
+ return;
173
+ }
174
+ chain.logger.debug("Payload envelope incomplete at payload deadline", payloadInput.getLogMeta());
175
+ chain.emitter.emit(ChainEvent.incompletePayloadEnvelope, {
176
+ payloadInput,
177
+ peer,
178
+ source: BlockInputSource.gossip,
179
+ });
180
+ });
181
+ }
153
182
 
154
183
  /**
155
184
  * Add a gossip block to the seen caches. The proposer signature of the block MUST be verified.
@@ -679,10 +708,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
679
708
 
680
709
  // Immediately attempt fetch of data columns from execution engine as the bid contains kzg commitments
681
710
  // which is all the information we need so there is no reason to delay until execution payload arrives
682
- // TODO GLOAS: If we want EL retries after this initial attempt, add an explicit retry policy here
683
- // (for example later in the slot). Do not couple retries to incoming gossip columns.
684
- // Columns fetched here feed payloadInput.addColumn, which resolves waitForAllData for any
685
- // in-flight importExecutionPayload. No processExecutionPayload trigger needed from this path.
711
+ // Columns fetched here feed payloadInput.addColumn, which resolves the waitForAllData of a
712
+ // pending payload import. No processExecutionPayload trigger needed from this path.
686
713
  chain.getBlobsTracker.triggerGetBlobs(payloadInput);
687
714
  }
688
715
  })
@@ -692,11 +719,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
692
719
 
693
720
  if (e instanceof BlockError) {
694
721
  switch (e.type.code) {
695
- case BlockErrorCode.DATA_UNAVAILABLE: {
696
- // Error is quite frequent and not critical
722
+ case BlockErrorCode.DATA_UNAVAILABLE:
723
+ // Data did not arrive in time, BlockInputSync keeps pulling it and retries the import
697
724
  logLevel = LogLevel.debug;
698
725
  break;
699
- }
700
726
  // ALREADY_KNOWN should not happen with ignoreIfKnown=true above
701
727
  // PARENT_BLOCK_UNKNOWN should not happen, we handled this in validateBeaconBlock() function above
702
728
  case BlockErrorCode.ALREADY_KNOWN:
@@ -764,7 +790,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
764
790
  // roots per proposer and slot, which bounds full imports over gossip to one alternate
765
791
  chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root) &&
766
792
  // A sibling with an unknown parent cannot be imported, sync fetches it if its branch becomes relevant
767
- chain.forkChoice.getBlockHexDefaultStatus(toRootHex(signedBlock.message.parentRoot)) !== null
793
+ chain.forkChoice.getBlockHexDefaultStatus(toRootHex(signedBlock.message.parentRoot)) !== null &&
794
+ (!isGloasBeaconBlock(signedBlock.message) ||
795
+ chain.forkChoice.getBlockHexAndBlockHash(
796
+ toRootHex(signedBlock.message.parentRoot),
797
+ toRootHex(signedBlock.message.body.signedExecutionPayloadBid.message.parentBlockHash)
798
+ ) !== null)
768
799
  ) {
769
800
  const blockInput = cacheSignatureVerifiedBlock(
770
801
  signedBlock,
@@ -899,28 +930,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
899
930
  }
900
931
 
901
932
  // NOTE: we do NOT call chain.processExecutionPayload here. That is triggered only by
902
- // envelope arrival (gossip or API). An in-flight importExecutionPayload is awaiting
933
+ // envelope arrival (gossip or API). A pending payload import is awaiting
903
934
  // payloadInput.waitForAllData(); addColumn above will resolve it once hasAllData flips.
904
935
 
905
- if (!payloadInput.isComplete()) {
906
- const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
907
- // do not await here to not delay gossip validation
908
- payloadInput.waitForEnvelopeAndAllData(cutoffTimeMs).catch((_e) => {
909
- chain.logger.debug(
910
- "Waited for envelope and data after receiving gossip column. Cut-off reached so emitting incompletePayloadEnvelope",
911
- {
912
- dataColumnIndex: index,
913
- ...payloadInputMeta,
914
- }
915
- );
916
- // TODO GLOAS: UnknownBlockSync to handle this event
917
- chain.emitter.emit(ChainEvent.incompletePayloadEnvelope, {
918
- payloadInput,
919
- peer: peerIdStr,
920
- source: BlockInputSource.gossip,
921
- });
922
- });
923
- }
936
+ scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
924
937
  } catch (e) {
925
938
  logger.debug(
926
939
  "Error handling gossip data column",
@@ -1266,6 +1279,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1266
1279
  seenTimestampSec,
1267
1280
  }: GossipHandlerParamGeneric<GossipType.execution_payload>) => {
1268
1281
  const {serializedData} = gossipData;
1282
+ validateGossipExecutionPayloadEnvelopeSerialized(chain, serializedData);
1269
1283
  const signedEnvelope = sszDeserialize(topic, serializedData);
1270
1284
  const envelope = signedEnvelope.message;
1271
1285
 
@@ -1318,17 +1332,23 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1318
1332
  });
1319
1333
  }
1320
1334
 
1335
+ // Add the envelope in the same tick as validation, otherwise a by-root download that completes
1336
+ // before the next event loop sees no envelope, validates it again and adds it first
1337
+ if (!payloadInput.hasPayloadEnvelope()) {
1338
+ chain.serializedCache.set(signedEnvelope, serializedData);
1339
+
1340
+ payloadInput.addPayloadEnvelope({
1341
+ envelope: signedEnvelope,
1342
+ source: PayloadEnvelopeInputSource.gossip,
1343
+ seenTimestampSec,
1344
+ peerIdStr,
1345
+ });
1346
+ }
1347
+
1321
1348
  // Handler - deferred to next event loop so the validation result propagates first
1322
1349
  callInNextEventLoop(() => {
1323
1350
  try {
1324
- chain.serializedCache.set(signedEnvelope, serializedData);
1325
-
1326
- payloadInput.addPayloadEnvelope({
1327
- envelope: signedEnvelope,
1328
- source: PayloadEnvelopeInputSource.gossip,
1329
- seenTimestampSec,
1330
- peerIdStr,
1331
- });
1351
+ scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
1332
1352
 
1333
1353
  chain.emitter.emit(routes.events.EventType.executionPayloadGossip, {
1334
1354
  slot,
@@ -1337,12 +1357,18 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1337
1357
  blockRoot: blockRootHex,
1338
1358
  });
1339
1359
 
1340
- chain.processExecutionPayload(payloadInput, {validSignature: true}).catch((e) => {
1360
+ // Signature is verified above only for this envelope, one added by another source is verified at import
1361
+ const validSignature = payloadInput.getPayloadEnvelope() === signedEnvelope;
1362
+ chain.processExecutionPayload(payloadInput, {validSignature}).catch((e) => {
1341
1363
  // Adjust verbosity based on error type
1342
1364
  let logLevel: LogLevel;
1343
1365
 
1344
1366
  if (e instanceof PayloadError) {
1345
1367
  switch (e.type.code) {
1368
+ case PayloadErrorCode.DATA_UNAVAILABLE:
1369
+ // Columns did not arrive in time, BlockInputSync keeps pulling them and retries the import
1370
+ logLevel = LogLevel.debug;
1371
+ break;
1346
1372
  // BLOCK_NOT_IN_FORK_CHOICE should not happen, validateGossipExecutionPayloadEnvelope above
1347
1373
  // already verified the block is in fork choice
1348
1374
  case PayloadErrorCode.BLOCK_NOT_IN_FORK_CHOICE:
@@ -1351,11 +1377,17 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1351
1377
  // Errors might indicate an issue with our node or the connected EL client
1352
1378
  logLevel = LogLevel.error;
1353
1379
  break;
1354
- // INVALID_SIGNATURE should not happen, signature is verified during gossip validation
1380
+ case PayloadErrorCode.BLOCK_EXECUTION_INVALID:
1381
+ logLevel = LogLevel.warn;
1382
+ break;
1383
+ // INVALID_SIGNATURE only happens for an envelope added by another source
1355
1384
  case PayloadErrorCode.INVALID_SIGNATURE:
1356
1385
  case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
1357
1386
  case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
1358
- core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
1387
+ // Penalize only when the stored envelope is the one this peer sent, not one added by another source
1388
+ if (validSignature) {
1389
+ core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
1390
+ }
1359
1391
  // The builder may have signed another envelope that is valid, keeping this one would make
1360
1392
  // by-root and range sync reuse it and never import the payload
1361
1393
  chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
@@ -1630,6 +1662,32 @@ function getBatchHandlers(modules: ValidatorFnsModules, options: GossipHandlerOp
1630
1662
  logger.debug("Error adding gossip unaggregated attestation to pool", {subnet}, e as Error);
1631
1663
  }
1632
1664
 
1665
+ // Collected only for vote slots that an upcoming block of a connected validator can include
1666
+ if (isForkPostElectra(fork) && chain.proposerTracker.shouldCollect(indexedAttestation.data.slot)) {
1667
+ try {
1668
+ const insertOutcome = chain.singleAttestationPool.add(
1669
+ attestation as SingleAttestation<ForkPostElectra>,
1670
+ attDataRootHex,
1671
+ validatorCommitteeIndex,
1672
+ committeeSize
1673
+ );
1674
+ metrics?.opPool.singleAttestationPool.gossipInsertOutcome.inc({insertOutcome});
1675
+ } catch (e) {
1676
+ logger.debug(
1677
+ "Error adding gossip single attestation to pool",
1678
+ {
1679
+ slot: indexedAttestation.data.slot,
1680
+ committeeIndex,
1681
+ validatorCommitteeIndex,
1682
+ validatorIndex: (attestation as SingleAttestation<ForkPostElectra>).attesterIndex,
1683
+ subnet,
1684
+ attDataRoot: attDataRootHex,
1685
+ },
1686
+ e as Error
1687
+ );
1688
+ }
1689
+ }
1690
+
1633
1691
  // Separate boundary: a pool insertion error above must not skip the fork-choice vote
1634
1692
  if (!options.dontSendGossipAttestationsToForkchoice) {
1635
1693
  try {
@@ -244,6 +244,12 @@ type SearchUnknownRootTarget =
244
244
  * Such that enough work is processed to fill either one of the queue.
245
245
  */
246
246
  export class NetworkProcessor {
247
+ private stopped = false;
248
+ /**
249
+ * Replaced by dropAllJobs(). Loops that yield while re-queueing awaiting messages compare it after each yield
250
+ * and stop, so no message is queued after a drop.
251
+ */
252
+ private jobGeneration = Symbol();
247
253
  private readonly chain: IBeaconChain;
248
254
  private readonly core: INetworkCore;
249
255
  private readonly events: NetworkEventBus;
@@ -335,16 +341,27 @@ export class NetworkProcessor {
335
341
  }
336
342
 
337
343
  async stop(): Promise<void> {
344
+ if (this.stopped) return;
345
+ this.stopped = true;
338
346
  this.events.off(NetworkEvent.pendingGossipsubMessage, this.onPendingGossipsubMessage);
339
347
  this.chain.emitter.off(routes.events.EventType.block, this.onBlockProcessed);
340
348
  this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadEnvelopeProcessed);
341
- this.chain.emitter.off(ClockEvent.slot, this.onClockSlot);
349
+ this.chain.clock.off(ClockEvent.slot, this.onClockSlot);
350
+ this.dropAllJobs();
342
351
  }
343
352
 
344
353
  dropAllJobs(): void {
354
+ this.jobGeneration = Symbol();
345
355
  for (const topic of executeGossipWorkOrder) {
346
356
  this.gossipQueues[topic].clear();
347
357
  }
358
+ this.awaitingMessagesByBlockRoot.clear();
359
+ this.awaitingBlockMessageCount = 0;
360
+ this.awaitingMessagesByPayloadBlockRoot.clear();
361
+ this.awaitingPayloadMessageCount = 0;
362
+ this.searchedRootsBySlot.clear();
363
+ this.bufferedRootsBySlot.clear();
364
+ this.fatalPeers.clear();
348
365
  }
349
366
 
350
367
  dumpGossipQueue(topic: GossipType): PendingGossipsubMessage[] {
@@ -364,7 +381,7 @@ export class NetworkProcessor {
364
381
  * prefer them as by-root download candidates.
365
382
  */
366
383
  searchUnknownBlock({slot, root}: SlotRootHex, source: BlockInputSource, peer?: PeerIdStr): void {
367
- if (this.chain.seenBlock(root)) {
384
+ if (this.stopped || this.chain.seenBlock(root)) {
368
385
  return;
369
386
  }
370
387
  const entry = this.searchedRootsBySlot.getOrDefault(slot).getOrDefault(root);
@@ -398,7 +415,7 @@ export class NetworkProcessor {
398
415
  peer?: PeerIdStr,
399
416
  slotIsPayloadSlot = false
400
417
  ): void {
401
- if (this.chain.seenPayloadEnvelope(root)) {
418
+ if (this.stopped || this.chain.seenPayloadEnvelope(root)) {
402
419
  return;
403
420
  }
404
421
  const entry = this.searchedRootsBySlot.getOrDefault(slot).getOrDefault(root);
@@ -801,6 +818,7 @@ export class NetworkProcessor {
801
818
  this.awaitingBlockMessageCount -= total;
802
819
  }
803
820
 
821
+ const generation = this.jobGeneration;
804
822
  const nowSec = Date.now() / 1000;
805
823
  let count = 0;
806
824
  // TODO: we can group attestations to process in batches but since we have the SeenAttestationDatas
@@ -819,6 +837,7 @@ export class NetworkProcessor {
819
837
  if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
820
838
  count = 0;
821
839
  await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
840
+ if (generation !== this.jobGeneration) return;
822
841
  }
823
842
  }
824
843
  }
@@ -843,6 +862,7 @@ export class NetworkProcessor {
843
862
  this.awaitingPayloadMessageCount -= total;
844
863
  }
845
864
 
865
+ const generation = this.jobGeneration;
846
866
  const nowSec = Date.now() / 1000;
847
867
  let count = 0;
848
868
  for (const messages of messagesByTopic.values()) {
@@ -858,6 +878,7 @@ export class NetworkProcessor {
858
878
  if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
859
879
  count = 0;
860
880
  await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
881
+ if (generation !== this.jobGeneration) return;
861
882
  }
862
883
  }
863
884
  }
@@ -937,6 +958,7 @@ export class NetworkProcessor {
937
958
  };
938
959
 
939
960
  private executeWork(): void {
961
+ if (this.stopped) return;
940
962
  // TODO: Maybe de-bounce by timing the last time executeWork was run
941
963
 
942
964
  this.metrics?.networkProcessor.executeWorkCalls.inc();
@@ -20,6 +20,7 @@ const gossipType = GossipType.beacon_attestation;
20
20
  export enum SubnetSource {
21
21
  committee = "committee",
22
22
  longLived = "long_lived",
23
+ beforeProposal = "before_proposal",
23
24
  }
24
25
 
25
26
  // map of subnet to time to form stable mesh as seconds, null if not yet formed
@@ -40,7 +41,7 @@ export class AttnetsService implements IAttnetsService {
40
41
  /** Committee subnets - PeerManager must find peers for those */
41
42
  private committeeSubnets = new SubnetMap();
42
43
  /**
43
- * All currently subscribed short-lived subnets, for attestation aggregation
44
+ * All currently subscribed short-lived subnets, for attestation aggregation and before local block proposals
44
45
  * This class will tell gossip to subscribe and un-subscribe
45
46
  * If a value exists for `SubscriptionId` it means that gossip subscription is active in network.gossip
46
47
  */
@@ -52,6 +53,11 @@ export class AttnetsService implements IAttnetsService {
52
53
  * Used to determine if we should process an attestation.
53
54
  */
54
55
  private aggregatorSlotSubnet = new MapDef<Slot, AggregatorDutyInfo>(() => new Map());
56
+ /**
57
+ * Block proposal slots of validators connected to this node.
58
+ * Used to subscribe to all subnets `slotsToSubscribeBeforeProposalDuty` slots before, see onSlot
59
+ */
60
+ private localProposalSlots = new Set<Slot>();
55
61
 
56
62
  constructor(
57
63
  private readonly config: BeaconConfig,
@@ -119,6 +125,22 @@ export class AttnetsService implements IAttnetsService {
119
125
  }
120
126
  }
121
127
 
128
+ /**
129
+ * Called from the main thread with upcoming block proposal slots of connected validators.
130
+ * Tracked here, the subscription happens in onSlot like aggregator duties, or right away for a proposal learned
131
+ * after its subscription slot (node restart, newly connected validator)
132
+ */
133
+ addLocalProposalSlots(slots: Slot[]): void {
134
+ const clockSlot = this.clock.currentSlot;
135
+ for (const proposalSlot of slots) {
136
+ this.localProposalSlots.add(proposalSlot);
137
+ // not from the slot before the proposal, its votes are published before a mesh could form
138
+ if (proposalSlot - this.opts.slotsToSubscribeBeforeProposalDuty <= clockSlot && clockSlot < proposalSlot - 1) {
139
+ this.subscribeAllSubnetsUntilProposal(proposalSlot);
140
+ }
141
+ }
142
+ }
143
+
122
144
  /**
123
145
  * Check if a subscription is still active before handling a gossip object
124
146
  */
@@ -158,7 +180,8 @@ export class AttnetsService implements IAttnetsService {
158
180
 
159
181
  /**
160
182
  * Run per slot.
161
- * - Subscribe to gossip subnets 2 slots in advance
183
+ * - Subscribe to gossip subnets 2 slots in advance of aggregator duties
184
+ * - Subscribe to all subnets `slotsToSubscribeBeforeProposalDuty` slots in advance of local block proposals
162
185
  * - Unsubscribe from expired subnets
163
186
  * - Track time to stable mesh if not yet formed
164
187
  */
@@ -186,19 +209,76 @@ export class AttnetsService implements IAttnetsService {
186
209
  this.trackTimeToStableMesh(clockSlot, dutiedSlot, dutiedInfo);
187
210
  }
188
211
 
212
+ // Must run before unsubscribeExpiredCommitteeSubnets so that a subnet whose subscription expires in this slot
213
+ // gets its ttl extended instead of being unsubscribed and subscribed again
214
+ this.subscribeAllSubnetsBeforeProposal(clockSlot);
215
+
189
216
  this.unsubscribeExpiredCommitteeSubnets(clockSlot);
190
217
  this.pruneExpiredAggregator(clockSlot);
218
+ this.pruneExpiredLocalProposals(clockSlot);
191
219
  } catch (e) {
192
220
  this.logger.error("Error on AttnetsService.onSlot", {slot: clockSlot}, e as Error);
193
221
  }
194
222
  };
195
223
 
196
224
  private onHalfSlot = (clockSlot: Slot): void => {
197
- for (const [dutiedSlot, dutiedInfo] of this.aggregatorSlotSubnet.entries()) {
198
- this.trackTimeToStableMesh(clockSlot, dutiedSlot, dutiedInfo);
225
+ try {
226
+ for (const [dutiedSlot, dutiedInfo] of this.aggregatorSlotSubnet.entries()) {
227
+ this.trackTimeToStableMesh(clockSlot, dutiedSlot, dutiedInfo);
228
+ }
229
+
230
+ this.unsubscribeAllSubnetsBeforeProposal(clockSlot);
231
+ } catch (e) {
232
+ this.logger.error("Error on AttnetsService.onHalfSlot", {slot: clockSlot}, e as Error);
199
233
  }
200
234
  };
201
235
 
236
+ /**
237
+ * Subscribe to all subnets `slotsToSubscribeBeforeProposalDuty` slots before a block proposal of a connected
238
+ * validator, so that single attestations of those slots can be packed into the block. The subscription expires in
239
+ * the slot before the proposal, see unsubscribeAllSubnetsBeforeProposal
240
+ */
241
+ private subscribeAllSubnetsBeforeProposal(clockSlot: Slot): void {
242
+ for (const proposalSlot of this.localProposalSlots) {
243
+ if (proposalSlot === clockSlot + this.opts.slotsToSubscribeBeforeProposalDuty) {
244
+ this.subscribeAllSubnetsUntilProposal(proposalSlot);
245
+ }
246
+ }
247
+ }
248
+
249
+ private subscribeAllSubnetsUntilProposal(proposalSlot: Slot): void {
250
+ if (this.opts.slotsToSubscribeBeforeProposalDuty === 0 || this.opts.subscribeAllSubnets) {
251
+ return;
252
+ }
253
+
254
+ const subnets = Array.from({length: ATTESTATION_SUBNET_COUNT}, (_, subnet) => subnet);
255
+ // Same as aggregator duties: trigger gossip subscription first, then register the subscriptions.
256
+ // Do not add to committeeSubnets: it drives peer discovery, we only use the peers we already have
257
+ this.subscribeToSubnets(subnets, SubnetSource.beforeProposal);
258
+ for (const subnet of subnets) {
259
+ this.shortLivedSubscriptions.request({subnet, toSlot: proposalSlot - 1});
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Unsubscribe from subnets of subscribeAllSubnetsBeforeProposal at half of the slot before the proposal instead of
265
+ * the proposal slot start, so the proposal slot does not carry their traffic. Single attestations are published at
266
+ * 1/3 of the slot so they have arrived by then.
267
+ * Short-lived subscriptions are either aggregator duties or these, an aggregator duty ends in its dutied slot and is
268
+ * kept here (aggregates are due later), it expires at the next slot start in onSlot as before. So only subnets of
269
+ * subscribeAllSubnetsBeforeProposal are unsubscribed here.
270
+ */
271
+ private unsubscribeAllSubnetsBeforeProposal(clockSlot: Slot): void {
272
+ const aggregatorSubnets = this.aggregatorSlotSubnet.get(clockSlot);
273
+ const expiring = this.shortLivedSubscriptions.getExpired(
274
+ clockSlot + 1,
275
+ (subnet) => aggregatorSubnets?.has(subnet) === true
276
+ );
277
+ if (expiring.length > 0) {
278
+ this.unsubscribeSubnets(expiring, clockSlot + 1, SubnetSource.beforeProposal);
279
+ }
280
+ }
281
+
202
282
  /**
203
283
  * Track time to form stable mesh if not yet formed
204
284
  */
@@ -291,8 +371,8 @@ export class AttnetsService implements IAttnetsService {
291
371
  }
292
372
 
293
373
  /**
294
- * Unsubscribe to a committee subnet from subscribedCommitteeSubnets.
295
- * If a random subnet is present, we do not unsubscribe from it.
374
+ * Unsubscribe from short-lived subnets whose subscription expired.
375
+ * If a long-lived subnet is present, we do not unsubscribe from it.
296
376
  */
297
377
  private unsubscribeExpiredCommitteeSubnets(slot: Slot): void {
298
378
  const expired = this.shortLivedSubscriptions.getExpired(slot);
@@ -313,6 +393,14 @@ export class AttnetsService implements IAttnetsService {
313
393
  }
314
394
  }
315
395
 
396
+ private pruneExpiredLocalProposals(currentSlot: Slot): void {
397
+ for (const proposalSlot of this.localProposalSlots) {
398
+ if (currentSlot > proposalSlot) {
399
+ this.localProposalSlots.delete(proposalSlot);
400
+ }
401
+ }
402
+ }
403
+
316
404
  /** Update ENR */
317
405
  private updateMetadata(): void {
318
406
  const subnets = ssz.phase0.AttestationSubnets.defaultValue();
@@ -25,6 +25,8 @@ export type SubnetsService = {
25
25
 
26
26
  export interface IAttnetsService extends SubnetsService {
27
27
  shouldProcess(subnet: SubnetID, slot: Slot): boolean;
28
+ /** Block proposal slots of validators connected to this node, see `slotsToSubscribeBeforeProposalDuty` */
29
+ addLocalProposalSlots(slots: Slot[]): void;
28
30
  }
29
31
 
30
32
  export type RandBetweenFn = (min: number, max: number) => number;
@@ -33,6 +35,11 @@ export type ShuffleFn = <T>(arr: T[]) => T[];
33
35
  export type SubnetsServiceOpts = {
34
36
  subscribeAllSubnets?: boolean;
35
37
  slotsToSubscribeBeforeAggregatorDuty: number;
38
+ /**
39
+ * Subscribe to all attestation subnets this many slots before a block proposal of a connected validator, so that
40
+ * its single attestations can be packed into the block. In `[0, SLOTS_PER_EPOCH]`, 0 disables
41
+ */
42
+ slotsToSubscribeBeforeProposalDuty: number;
36
43
  };
37
44
 
38
45
  export type SubnetsServiceTestOpts = {
@@ -1,23 +1,29 @@
1
1
  import {setMaxListeners} from "node:events";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Registry} from "prom-client";
4
+ import {hasher} from "@chainsafe/persistent-merkle-tree";
5
+ import {BeaconApiMethods} from "@lodestar/api/beacon/server";
6
+ import {BeaconConfig} from "@lodestar/config";
7
+ import type {LoggerNode} from "@lodestar/logger/node";
4
8
  import {
5
9
  init as initNativeMetrics,
6
10
  registerLocalValidator as registerNativeLocalValidator,
7
11
  scrapeMetrics as scrapeNativeMetrics,
8
12
  unregisterLocalValidator as unregisterNativeLocalValidator,
9
- } from "@chainsafe/lodestar-z/metrics";
10
- import {type PubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
11
- import {hasher} from "@chainsafe/persistent-merkle-tree";
12
- import {BeaconApiMethods} from "@lodestar/api/beacon/server";
13
- import {BeaconConfig} from "@lodestar/config";
14
- import type {LoggerNode} from "@lodestar/logger/node";
13
+ } from "@lodestar/native/metrics";
14
+ import {type PubkeyCache} from "@lodestar/native/pubkeys";
15
15
  import {ZERO_HASH_HEX} from "@lodestar/params";
16
- import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
16
+ import {
17
+ IBeaconStateView,
18
+ computeStartSlotAtEpoch,
19
+ isStatePostBellatrix,
20
+ isStatePostGloas,
21
+ } from "@lodestar/state-transition";
17
22
  import {Slot} from "@lodestar/types";
18
- import {sleep, toRootHex} from "@lodestar/utils";
23
+ import {defer, sleep, toRootHex} from "@lodestar/utils";
19
24
  import {ProcessShutdownCallback} from "@lodestar/validator";
20
25
  import {BeaconRestApiServer, getApi} from "../api/index.js";
26
+ import {getDataColumnSidecarsMinEpoch} from "../chain/archiveStore/utils/archiveBlocks.js";
21
27
  import {BeaconChain, IBeaconChain, initBeaconMetrics} from "../chain/index.js";
22
28
  import {ValidatorMonitor, createValidatorMonitor} from "../chain/validatorMonitor.js";
23
29
  import {IBeaconDb} from "../db/index.js";
@@ -111,6 +117,7 @@ export class BeaconNode {
111
117
 
112
118
  status: BeaconNodeStatus;
113
119
  private controller?: AbortController;
120
+ private closePromise: Promise<void> | undefined;
114
121
 
115
122
  constructor({
116
123
  opts,
@@ -212,6 +219,17 @@ export class BeaconNode {
212
219
 
213
220
  const clock = new Clock({config, genesisTime: anchorState.genesisTime, signal});
214
221
 
222
+ // Before any lookup into the legacy column buckets, which can be slow until compacted
223
+ const columnsMinEpoch =
224
+ opts.chain.archiveDataEpochs === Infinity
225
+ ? null
226
+ : getDataColumnSidecarsMinEpoch(config, clock.currentEpoch, opts.chain.archiveDataEpochs);
227
+ await db.compactLegacyDataColumns(
228
+ columnsMinEpoch !== null && columnsMinEpoch >= config.FULU_FORK_EPOCH
229
+ ? computeStartSlotAtEpoch(columnsMinEpoch)
230
+ : 0
231
+ );
232
+
215
233
  await db.init();
216
234
 
217
235
  // Prune hot db repos
@@ -280,6 +298,7 @@ export class BeaconNode {
280
298
  : undefined,
281
299
  builderApiClientOpts: {
282
300
  timeout: opts.executionBuilder.timeout,
301
+ bidDeadline: opts.executionBuilder.bidDeadline,
283
302
  // Sent with all builder api requests, unless the node runs in private mode
284
303
  userAgent: opts.executionBuilder.userAgent,
285
304
  },
@@ -375,23 +394,40 @@ export class BeaconNode {
375
394
  /**
376
395
  * Stop beacon node and its sub-components.
377
396
  */
378
- async close(): Promise<void> {
379
- if (this.status === BeaconNodeStatus.started) {
380
- this.status = BeaconNodeStatus.closing;
381
- this.sync.close();
382
- if (this.restApi) await this.restApi.close();
383
- await this.network.close();
384
- if (this.metricsServer) await this.metricsServer.close();
385
- if (this.monitoring) await this.monitoring.close();
386
- await this.chain.persistToDisk();
387
- await this.chain.close();
397
+ close(): Promise<void> {
398
+ if (this.closePromise !== undefined) return this.closePromise;
399
+ const completion = defer<void>();
400
+ this.closePromise = completion.promise;
401
+ this.status = BeaconNodeStatus.closing;
402
+ void this.closeInternal().then(completion.resolve, completion.reject);
403
+ return this.closePromise;
404
+ }
405
+
406
+ private async closeInternal(): Promise<void> {
407
+ const errors: unknown[] = [];
408
+ for (const cleanup of [
409
+ () => this.sync.close(),
410
+ () => this.restApi?.close(),
411
+ () => this.network.close(),
412
+ () => this.metricsServer?.close(),
413
+ () => this.monitoring?.close(),
414
+ () => this.chain.persistToDisk(),
415
+ () => this.chain.close(),
388
416
  // Abort signal last: close() calls above clear intervals/timeouts so no new
389
417
  // operations get scheduled. If we aborted first, a still-pending interval could
390
418
  // fire and schedule a new operation after abort, leaving it stuck and delaying shutdown.
391
- if (this.controller) this.controller.abort();
392
- await sleep(DELAY_BEFORE_CLOSING_DB_MS);
393
- await this.db.close();
394
- this.status = BeaconNodeStatus.closed;
419
+ () => this.controller?.abort(),
420
+ () => sleep(DELAY_BEFORE_CLOSING_DB_MS),
421
+ () => this.db.close(),
422
+ ]) {
423
+ try {
424
+ await cleanup();
425
+ } catch (error) {
426
+ errors.push(error);
427
+ }
395
428
  }
429
+ this.status = BeaconNodeStatus.closed;
430
+ if (errors.length === 1) throw errors[0];
431
+ if (errors.length > 1) throw new AggregateError(errors, "Beacon node cleanup failed");
396
432
  }
397
433
  }