@lodestar/beacon-node 1.51.0-dev.2b933d3b9a → 1.51.0-dev.2bcbe7ce2d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. package/lib/api/impl/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 +5 -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 +49 -31
  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 +11 -0
  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 +6 -5
  37. package/lib/chain/blocks/importBlock.js.map +1 -1
  38. package/lib/chain/blocks/importExecutionPayload.d.ts +6 -11
  39. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  40. package/lib/chain/blocks/importExecutionPayload.js +10 -16
  41. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  42. package/lib/chain/blocks/index.js +2 -2
  43. package/lib/chain/blocks/index.js.map +1 -1
  44. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +12 -9
  45. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
  46. package/lib/chain/blocks/payloadEnvelopeProcessor.js +24 -19
  47. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  48. package/lib/chain/blocks/types.d.ts +4 -1
  49. package/lib/chain/blocks/types.d.ts.map +1 -1
  50. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  51. package/lib/chain/blocks/verifyBlock.js +18 -7
  52. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  53. package/lib/chain/blocks/verifyBlocksDataAvailability.d.ts.map +1 -1
  54. package/lib/chain/blocks/verifyBlocksDataAvailability.js +8 -1
  55. package/lib/chain/blocks/verifyBlocksDataAvailability.js.map +1 -1
  56. package/lib/chain/blocks/verifyBlocksSignatures.d.ts.map +1 -1
  57. package/lib/chain/blocks/verifyBlocksSignatures.js +7 -5
  58. package/lib/chain/blocks/verifyBlocksSignatures.js.map +1 -1
  59. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +3 -3
  60. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  61. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +11 -6
  62. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  63. package/lib/chain/bls/interface.d.ts +2 -0
  64. package/lib/chain/bls/interface.d.ts.map +1 -1
  65. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  66. package/lib/chain/bls/multithread/index.js +13 -11
  67. package/lib/chain/bls/multithread/index.js.map +1 -1
  68. package/lib/chain/bls/multithread/types.d.ts +1 -1
  69. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  70. package/lib/chain/bls/multithread/worker.js +1 -1
  71. package/lib/chain/bls/multithread/worker.js.map +1 -1
  72. package/lib/chain/bls/singleThread.js +1 -1
  73. package/lib/chain/bls/singleThread.js.map +1 -1
  74. package/lib/chain/bls/utils.js +1 -1
  75. package/lib/chain/bls/utils.js.map +1 -1
  76. package/lib/chain/chain.d.ts +1 -1
  77. package/lib/chain/chain.d.ts.map +1 -1
  78. package/lib/chain/chain.js +4 -4
  79. package/lib/chain/chain.js.map +1 -1
  80. package/lib/chain/errors/blockError.d.ts +1 -1
  81. package/lib/chain/errors/blockError.d.ts.map +1 -1
  82. package/lib/chain/errors/blockError.js +1 -1
  83. package/lib/chain/errors/blockError.js.map +1 -1
  84. package/lib/chain/interface.d.ts +2 -3
  85. package/lib/chain/interface.d.ts.map +1 -1
  86. package/lib/chain/interface.js.map +1 -1
  87. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  88. package/lib/chain/opPools/aggregatedAttestationPool.js +23 -10
  89. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  90. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  91. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  92. package/lib/chain/opPools/attestationPool.js +15 -4
  93. package/lib/chain/opPools/attestationPool.js.map +1 -1
  94. package/lib/chain/opPools/inclusionListStore.d.ts.map +1 -1
  95. package/lib/chain/opPools/inclusionListStore.js +2 -2
  96. package/lib/chain/opPools/inclusionListStore.js.map +1 -1
  97. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  98. package/lib/chain/opPools/payloadAttestationPool.js +17 -7
  99. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  100. package/lib/chain/opPools/syncCommitteeMessagePool.d.ts.map +1 -1
  101. package/lib/chain/opPools/syncCommitteeMessagePool.js +9 -3
  102. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  103. package/lib/chain/opPools/syncContributionAndProofPool.d.ts.map +1 -1
  104. package/lib/chain/opPools/syncContributionAndProofPool.js +11 -3
  105. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  106. package/lib/chain/opPools/utils.d.ts +1 -1
  107. package/lib/chain/opPools/utils.d.ts.map +1 -1
  108. package/lib/chain/opPools/utils.js +1 -1
  109. package/lib/chain/opPools/utils.js.map +1 -1
  110. package/lib/chain/options.d.ts +0 -1
  111. package/lib/chain/options.d.ts.map +1 -1
  112. package/lib/chain/options.js +0 -1
  113. package/lib/chain/options.js.map +1 -1
  114. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  115. package/lib/chain/produceBlock/produceBlockBody.js +0 -1
  116. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  117. package/lib/chain/regen/interface.d.ts +0 -1
  118. package/lib/chain/regen/interface.d.ts.map +1 -1
  119. package/lib/chain/regen/interface.js +0 -1
  120. package/lib/chain/regen/interface.js.map +1 -1
  121. package/lib/chain/validation/executionPayloadBid.d.ts +0 -15
  122. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  123. package/lib/chain/validation/executionPayloadBid.js +13 -166
  124. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  125. package/lib/chain/validation/inclusionList.d.ts.map +1 -1
  126. package/lib/chain/validation/inclusionList.js +11 -2
  127. package/lib/chain/validation/inclusionList.js.map +1 -1
  128. package/lib/db/beacon.d.ts +7 -0
  129. package/lib/db/beacon.d.ts.map +1 -1
  130. package/lib/db/beacon.js +34 -0
  131. package/lib/db/beacon.js.map +1 -1
  132. package/lib/db/dataColumnStore.d.ts +1 -1
  133. package/lib/db/dataColumnStore.d.ts.map +1 -1
  134. package/lib/db/dataColumnStore.js +1 -4
  135. package/lib/db/dataColumnStore.js.map +1 -1
  136. package/lib/db/flatFileStore/flatFileStore.js +5 -5
  137. package/lib/db/flatFileStore/flatFileStore.js.map +1 -1
  138. package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -1
  139. package/lib/db/flatFileStore/slotIndex.js +2 -2
  140. package/lib/db/flatFileStore/slotIndex.js.map +1 -1
  141. package/lib/db/interface.d.ts +1 -0
  142. package/lib/db/interface.d.ts.map +1 -1
  143. package/lib/db/repositories/dataColumnSidecarArchive.d.ts +6 -0
  144. package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
  145. package/lib/db/repositories/dataColumnSidecarArchive.js +19 -0
  146. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  147. package/lib/execution/builder/apiClient.d.ts +14 -8
  148. package/lib/execution/builder/apiClient.d.ts.map +1 -1
  149. package/lib/execution/builder/apiClient.js +12 -10
  150. package/lib/execution/builder/apiClient.js.map +1 -1
  151. package/lib/execution/builder/http.d.ts +2 -0
  152. package/lib/execution/builder/http.d.ts.map +1 -1
  153. package/lib/execution/builder/http.js +2 -0
  154. package/lib/execution/builder/http.js.map +1 -1
  155. package/lib/execution/engine/blobVersionedHashes.d.ts +1 -1
  156. package/lib/execution/engine/blobVersionedHashes.d.ts.map +1 -1
  157. package/lib/execution/engine/blobVersionedHashes.js.map +1 -1
  158. package/lib/execution/engine/http.d.ts.map +1 -1
  159. package/lib/execution/engine/http.js +0 -1
  160. package/lib/execution/engine/http.js.map +1 -1
  161. package/lib/execution/engine/index.d.ts +1 -1
  162. package/lib/execution/engine/index.d.ts.map +1 -1
  163. package/lib/execution/engine/index.js +1 -1
  164. package/lib/execution/engine/index.js.map +1 -1
  165. package/lib/execution/engine/interface.d.ts +1 -1
  166. package/lib/execution/engine/interface.d.ts.map +1 -1
  167. package/lib/execution/engine/interface.js.map +1 -1
  168. package/lib/execution/engine/mock.d.ts.map +1 -1
  169. package/lib/execution/engine/mock.js +6 -4
  170. package/lib/execution/engine/mock.js.map +1 -1
  171. package/lib/execution/engine/payloadIdCache.d.ts +1 -2
  172. package/lib/execution/engine/payloadIdCache.d.ts.map +1 -1
  173. package/lib/execution/engine/payloadIdCache.js +3 -3
  174. package/lib/execution/engine/payloadIdCache.js.map +1 -1
  175. package/lib/execution/engine/sszTypes.d.ts +38 -38
  176. package/lib/execution/engine/types.d.ts +1 -1
  177. package/lib/execution/engine/types.d.ts.map +1 -1
  178. package/lib/execution/engine/types.js +9 -8
  179. package/lib/execution/engine/types.js.map +1 -1
  180. package/lib/index.d.ts +1 -0
  181. package/lib/index.d.ts.map +1 -1
  182. package/lib/index.js +2 -0
  183. package/lib/index.js.map +1 -1
  184. package/lib/metrics/metrics/lodestar.d.ts +7 -0
  185. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  186. package/lib/metrics/metrics/lodestar.js +15 -0
  187. package/lib/metrics/metrics/lodestar.js.map +1 -1
  188. package/lib/monitoring/service.js +1 -1
  189. package/lib/monitoring/service.js.map +1 -1
  190. package/lib/network/core/networkCore.d.ts +2 -0
  191. package/lib/network/core/networkCore.d.ts.map +1 -1
  192. package/lib/network/core/networkCore.js +3 -0
  193. package/lib/network/core/networkCore.js.map +1 -1
  194. package/lib/network/core/networkCoreWorker.js +1 -0
  195. package/lib/network/core/networkCoreWorker.js.map +1 -1
  196. package/lib/network/core/networkCoreWorkerHandler.d.ts +2 -0
  197. package/lib/network/core/networkCoreWorkerHandler.d.ts.map +1 -1
  198. package/lib/network/core/networkCoreWorkerHandler.js +3 -0
  199. package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
  200. package/lib/network/core/types.d.ts +2 -0
  201. package/lib/network/core/types.d.ts.map +1 -1
  202. package/lib/network/gossip/topic.d.ts +6 -6
  203. package/lib/network/interface.d.ts +2 -0
  204. package/lib/network/interface.d.ts.map +1 -1
  205. package/lib/network/libp2p/index.d.ts.map +1 -1
  206. package/lib/network/libp2p/index.js +5 -1
  207. package/lib/network/libp2p/index.js.map +1 -1
  208. package/lib/network/network.d.ts +2 -0
  209. package/lib/network/network.d.ts.map +1 -1
  210. package/lib/network/network.js +6 -1
  211. package/lib/network/network.js.map +1 -1
  212. package/lib/network/options.d.ts +2 -0
  213. package/lib/network/options.d.ts.map +1 -1
  214. package/lib/network/options.js.map +1 -1
  215. package/lib/network/peers/peerManager.d.ts +2 -1
  216. package/lib/network/peers/peerManager.d.ts.map +1 -1
  217. package/lib/network/peers/peerManager.js.map +1 -1
  218. package/lib/network/peers/utils/assertPeerRelevance.d.ts +9 -1
  219. package/lib/network/peers/utils/assertPeerRelevance.d.ts.map +1 -1
  220. package/lib/network/peers/utils/assertPeerRelevance.js +32 -21
  221. package/lib/network/peers/utils/assertPeerRelevance.js.map +1 -1
  222. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  223. package/lib/network/processor/gossipHandlers.js +36 -26
  224. package/lib/network/processor/gossipHandlers.js.map +1 -1
  225. package/lib/network/processor/index.d.ts.map +1 -1
  226. package/lib/network/processor/index.js +26 -25
  227. package/lib/network/processor/index.js.map +1 -1
  228. package/lib/node/nodejs.d.ts +1 -1
  229. package/lib/node/nodejs.d.ts.map +1 -1
  230. package/lib/node/nodejs.js +11 -2
  231. package/lib/node/nodejs.js.map +1 -1
  232. package/lib/node/options.d.ts +3 -1
  233. package/lib/node/options.d.ts.map +1 -1
  234. package/lib/node/options.js +4 -2
  235. package/lib/node/options.js.map +1 -1
  236. package/lib/sync/interface.d.ts.map +1 -1
  237. package/lib/sync/interface.js.map +1 -1
  238. package/lib/sync/sync.d.ts +7 -0
  239. package/lib/sync/sync.d.ts.map +1 -1
  240. package/lib/sync/sync.js +61 -3
  241. package/lib/sync/sync.js.map +1 -1
  242. package/lib/sync/unknownBlock.d.ts.map +1 -1
  243. package/lib/sync/unknownBlock.js +42 -18
  244. package/lib/sync/unknownBlock.js.map +1 -1
  245. package/lib/util/file.d.ts.map +1 -1
  246. package/lib/util/file.js +8 -9
  247. package/lib/util/file.js.map +1 -1
  248. package/lib/util/queue/itemQueue.js +1 -1
  249. package/lib/util/queue/itemQueue.js.map +1 -1
  250. package/lib/util/sszBytes.js +2 -2
  251. package/lib/util/sszBytes.js.map +1 -1
  252. package/package.json +31 -32
  253. package/src/api/impl/api.ts +1 -1
  254. package/src/api/impl/beacon/blocks/index.ts +57 -62
  255. package/src/api/impl/beacon/index.ts +3 -1
  256. package/src/api/impl/beacon/pool/index.ts +5 -1
  257. package/src/api/impl/beacon/state/utils.ts +1 -1
  258. package/src/api/impl/validator/index.ts +54 -37
  259. package/src/api/options.ts +3 -0
  260. package/src/chain/GetBlobsTracker.ts +97 -38
  261. package/src/chain/archiveStore/archiveStore.ts +1 -0
  262. package/src/chain/archiveStore/utils/archiveBlocks.ts +76 -130
  263. package/src/chain/blocks/importBlock.ts +8 -7
  264. package/src/chain/blocks/importExecutionPayload.ts +15 -22
  265. package/src/chain/blocks/index.ts +6 -2
  266. package/src/chain/blocks/payloadEnvelopeProcessor.ts +35 -24
  267. package/src/chain/blocks/types.ts +4 -1
  268. package/src/chain/blocks/verifyBlock.ts +31 -16
  269. package/src/chain/blocks/verifyBlocksDataAvailability.ts +10 -1
  270. package/src/chain/blocks/verifyBlocksSignatures.ts +7 -5
  271. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +14 -6
  272. package/src/chain/bls/interface.ts +2 -0
  273. package/src/chain/bls/multithread/index.ts +14 -10
  274. package/src/chain/bls/multithread/types.ts +1 -1
  275. package/src/chain/bls/multithread/worker.ts +1 -5
  276. package/src/chain/bls/singleThread.ts +1 -1
  277. package/src/chain/bls/utils.ts +1 -1
  278. package/src/chain/chain.ts +5 -5
  279. package/src/chain/errors/blockError.ts +1 -1
  280. package/src/chain/interface.ts +2 -3
  281. package/src/chain/opPools/aggregatedAttestationPool.ts +25 -9
  282. package/src/chain/opPools/attestationPool.ts +16 -4
  283. package/src/chain/opPools/inclusionListStore.ts +3 -3
  284. package/src/chain/opPools/payloadAttestationPool.ts +18 -7
  285. package/src/chain/opPools/syncCommitteeMessagePool.ts +10 -3
  286. package/src/chain/opPools/syncContributionAndProofPool.ts +13 -3
  287. package/src/chain/opPools/utils.ts +1 -1
  288. package/src/chain/options.ts +0 -2
  289. package/src/chain/produceBlock/produceBlockBody.ts +0 -1
  290. package/src/chain/regen/interface.ts +0 -1
  291. package/src/chain/validation/executionPayloadBid.ts +14 -193
  292. package/src/chain/validation/inclusionList.ts +11 -2
  293. package/src/db/beacon.ts +44 -0
  294. package/src/db/dataColumnStore.ts +2 -4
  295. package/src/db/flatFileStore/flatFileStore.ts +7 -7
  296. package/src/db/flatFileStore/slotIndex.ts +4 -3
  297. package/src/db/interface.ts +2 -0
  298. package/src/db/repositories/dataColumnSidecarArchive.ts +20 -0
  299. package/src/execution/builder/apiClient.ts +16 -13
  300. package/src/execution/builder/http.ts +4 -0
  301. package/src/execution/engine/blobVersionedHashes.ts +1 -1
  302. package/src/execution/engine/http.ts +0 -1
  303. package/src/execution/engine/index.ts +1 -1
  304. package/src/execution/engine/interface.ts +1 -1
  305. package/src/execution/engine/mock.ts +6 -4
  306. package/src/execution/engine/payloadIdCache.ts +6 -7
  307. package/src/execution/engine/types.ts +11 -9
  308. package/src/index.ts +2 -0
  309. package/src/metrics/metrics/lodestar.ts +15 -0
  310. package/src/monitoring/service.ts +1 -1
  311. package/src/network/core/networkCore.ts +5 -0
  312. package/src/network/core/networkCoreWorker.ts +1 -0
  313. package/src/network/core/networkCoreWorkerHandler.ts +4 -0
  314. package/src/network/core/types.ts +2 -0
  315. package/src/network/interface.ts +2 -0
  316. package/src/network/libp2p/index.ts +5 -1
  317. package/src/network/network.ts +8 -1
  318. package/src/network/options.ts +2 -0
  319. package/src/network/peers/peerManager.ts +1 -1
  320. package/src/network/peers/utils/assertPeerRelevance.ts +39 -23
  321. package/src/network/processor/gossipHandlers.ts +44 -28
  322. package/src/network/processor/index.ts +22 -23
  323. package/src/network/processor/types.ts +1 -1
  324. package/src/node/nodejs.ts +25 -7
  325. package/src/node/options.ts +9 -6
  326. package/src/sync/interface.ts +1 -0
  327. package/src/sync/sync.ts +65 -4
  328. package/src/sync/unknownBlock.ts +47 -28
  329. package/src/util/file.ts +8 -9
  330. package/src/util/queue/itemQueue.ts +1 -1
  331. package/src/util/sszBytes.ts +2 -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,
@@ -150,6 +151,31 @@ export function getGossipHandlers(modules: ValidatorFnsModules, options: GossipH
150
151
  */
151
152
  function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHandlerOpts): SequentialGossipHandlers {
152
153
  const {chain, config, metrics, logger, core} = modules;
154
+ const incompletePayloadWaits = new WeakSet<PayloadEnvelopeInput>();
155
+
156
+ function scheduleIncompletePayloadEnvelope(payloadInput: PayloadEnvelopeInput, peer: string): void {
157
+ if (payloadInput.isComplete() || incompletePayloadWaits.has(payloadInput)) {
158
+ return;
159
+ }
160
+ incompletePayloadWaits.add(payloadInput);
161
+
162
+ const fork = config.getForkName(payloadInput.slot);
163
+ const cutoffTimeMs = getCutoffTimeMs(chain, payloadInput.slot, config.getPayloadDueMs(fork));
164
+ payloadInput.waitForEnvelopeAndAllData(cutoffTimeMs).catch(() => {
165
+ if (
166
+ payloadInput.isComplete() ||
167
+ chain.seenPayloadEnvelopeInputCache.get(payloadInput.blockRootHex) !== payloadInput
168
+ ) {
169
+ return;
170
+ }
171
+ chain.logger.debug("Payload envelope incomplete at payload deadline", payloadInput.getLogMeta());
172
+ chain.emitter.emit(ChainEvent.incompletePayloadEnvelope, {
173
+ payloadInput,
174
+ peer,
175
+ source: BlockInputSource.gossip,
176
+ });
177
+ });
178
+ }
153
179
 
154
180
  /**
155
181
  * Add a gossip block to the seen caches. The proposer signature of the block MUST be verified.
@@ -679,10 +705,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
679
705
 
680
706
  // Immediately attempt fetch of data columns from execution engine as the bid contains kzg commitments
681
707
  // 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.
708
+ // Columns fetched here feed payloadInput.addColumn, which resolves the waitForAllData of a
709
+ // pending payload import. No processExecutionPayload trigger needed from this path.
686
710
  chain.getBlobsTracker.triggerGetBlobs(payloadInput);
687
711
  }
688
712
  })
@@ -692,11 +716,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
692
716
 
693
717
  if (e instanceof BlockError) {
694
718
  switch (e.type.code) {
695
- case BlockErrorCode.DATA_UNAVAILABLE: {
696
- // Error is quite frequent and not critical
719
+ case BlockErrorCode.DATA_UNAVAILABLE:
720
+ // Data did not arrive in time, BlockInputSync keeps pulling it and retries the import
697
721
  logLevel = LogLevel.debug;
698
722
  break;
699
- }
700
723
  // ALREADY_KNOWN should not happen with ignoreIfKnown=true above
701
724
  // PARENT_BLOCK_UNKNOWN should not happen, we handled this in validateBeaconBlock() function above
702
725
  case BlockErrorCode.ALREADY_KNOWN:
@@ -764,7 +787,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
764
787
  // roots per proposer and slot, which bounds full imports over gossip to one alternate
765
788
  chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root) &&
766
789
  // 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
790
+ chain.forkChoice.getBlockHexDefaultStatus(toRootHex(signedBlock.message.parentRoot)) !== null &&
791
+ (!isGloasBeaconBlock(signedBlock.message) ||
792
+ chain.forkChoice.getBlockHexAndBlockHash(
793
+ toRootHex(signedBlock.message.parentRoot),
794
+ toRootHex(signedBlock.message.body.signedExecutionPayloadBid.message.parentBlockHash)
795
+ ) !== null)
768
796
  ) {
769
797
  const blockInput = cacheSignatureVerifiedBlock(
770
798
  signedBlock,
@@ -899,28 +927,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
899
927
  }
900
928
 
901
929
  // 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
930
+ // envelope arrival (gossip or API). A pending payload import is awaiting
903
931
  // payloadInput.waitForAllData(); addColumn above will resolve it once hasAllData flips.
904
932
 
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
- }
933
+ scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
924
934
  } catch (e) {
925
935
  logger.debug(
926
936
  "Error handling gossip data column",
@@ -1330,6 +1340,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1330
1340
  peerIdStr,
1331
1341
  });
1332
1342
 
1343
+ scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
1344
+
1333
1345
  chain.emitter.emit(routes.events.EventType.executionPayloadGossip, {
1334
1346
  slot,
1335
1347
  builderIndex: envelope.builderIndex,
@@ -1343,6 +1355,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1343
1355
 
1344
1356
  if (e instanceof PayloadError) {
1345
1357
  switch (e.type.code) {
1358
+ case PayloadErrorCode.DATA_UNAVAILABLE:
1359
+ // Columns did not arrive in time, BlockInputSync keeps pulling them and retries the import
1360
+ logLevel = LogLevel.debug;
1361
+ break;
1346
1362
  // BLOCK_NOT_IN_FORK_CHOICE should not happen, validateGossipExecutionPayloadEnvelope above
1347
1363
  // already verified the block is in fork choice
1348
1364
  case PayloadErrorCode.BLOCK_NOT_IN_FORK_CHOICE:
@@ -867,19 +867,20 @@ export class NetworkProcessor {
867
867
  const nowSec = Date.now() / 1000;
868
868
  const minSlot = clockSlot - MAX_UNKNOWN_ROOTS_SLOT_CACHE_SIZE;
869
869
 
870
- for (const [slot, searchedRoots] of this.searchedRootsBySlot) {
870
+ for (const [slot, bufferedRoots] of this.bufferedRootsBySlot) {
871
871
  if (slot > minSlot) continue;
872
872
 
873
- // expire messages awaiting an unknown block for these roots. Only roots this slot did a BLOCK search
874
- // for (blockPeerIds set) have block waits keyed here; skipping the rest avoids deleting a payload wait
875
- // that a newer slot created for the same root (block/envelope searches share searchedRootsBySlot).
876
- for (const [rootHex, entry] of searchedRoots) {
877
- if (entry.blockPeerIds === undefined) continue;
873
+ for (const rootHex of bufferedRoots) {
878
874
  const messagesByTopic = this.awaitingMessagesByBlockRoot.get(rootHex);
879
875
  if (messagesByTopic === undefined) continue;
880
- let removed = 0;
876
+ let remaining = 0;
881
877
  for (const messages of messagesByTopic.values()) {
882
878
  for (const message of messages) {
879
+ // the same root may be buffered again from a newer slot, keep those messages.
880
+ // msgSlot is always set before buffering, a missing one expires rather than leaks
881
+ if ((message.msgSlot ?? minSlot) > minSlot) continue;
882
+ messages.delete(message);
883
+ this.awaitingBlockMessageCount--;
883
884
  const topicType = message.topic.type;
884
885
  this.metrics?.awaitingBlockGossipMessages.reject.inc({
885
886
  topic: topicType,
@@ -891,22 +892,22 @@ export class NetworkProcessor {
891
892
  );
892
893
  // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
893
894
  }
894
- removed += messages.size;
895
- }
896
- if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
897
- this.awaitingBlockMessageCount -= removed;
895
+ remaining += messages.size;
898
896
  }
897
+ if (remaining === 0) this.awaitingMessagesByBlockRoot.delete(rootHex);
899
898
  }
900
899
 
901
- // expire messages awaiting an unknown payload envelope for these roots. Symmetric to the block pass:
902
- // only roots this slot did an ENVELOPE search for (envelopePeerIds set) have payload waits keyed here.
903
- for (const [rootHex, entry] of searchedRoots) {
904
- if (entry.envelopePeerIds === undefined) continue;
900
+ for (const rootHex of bufferedRoots) {
905
901
  const messagesByTopic = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
906
902
  if (messagesByTopic === undefined) continue;
907
- let removed = 0;
903
+ let remaining = 0;
908
904
  for (const messages of messagesByTopic.values()) {
909
905
  for (const message of messages) {
906
+ // the same root may be buffered again from a newer slot, keep those messages.
907
+ // msgSlot is always set before buffering, a missing one expires rather than leaks
908
+ if ((message.msgSlot ?? minSlot) > minSlot) continue;
909
+ messages.delete(message);
910
+ this.awaitingPayloadMessageCount--;
910
911
  const topicType = message.topic.type;
911
912
  this.metrics?.awaitingPayloadGossipMessages.reject.inc({
912
913
  topic: topicType,
@@ -918,18 +919,16 @@ export class NetworkProcessor {
918
919
  );
919
920
  // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
920
921
  }
921
- removed += messages.size;
922
- }
923
- if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
924
- this.awaitingPayloadMessageCount -= removed;
922
+ remaining += messages.size;
925
923
  }
924
+ if (remaining === 0) this.awaitingMessagesByPayloadBlockRoot.delete(rootHex);
926
925
  }
927
926
 
928
- this.searchedRootsBySlot.delete(slot);
927
+ this.bufferedRootsBySlot.delete(slot);
929
928
  }
930
929
 
931
- for (const slot of this.bufferedRootsBySlot.keys()) {
932
- if (slot <= minSlot) this.bufferedRootsBySlot.delete(slot);
930
+ for (const slot of this.searchedRootsBySlot.keys()) {
931
+ if (slot <= minSlot) this.searchedRootsBySlot.delete(slot);
933
932
  }
934
933
 
935
934
  for (const [peer, slot] of this.fatalPeers) {
@@ -11,7 +11,7 @@ export type GossipAttestationsWork = {
11
11
  export type PendingGossipsubMessage = {
12
12
  topic: GossipTopic;
13
13
  msg: Message;
14
- // only available for beacon_attestation and aggregate_and_proof
14
+ // set for every topic with a slot, held messages expire by it
15
15
  msgSlot?: Slot;
16
16
  msgId: string;
17
17
  propagationSource: PeerIdStr;
@@ -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
23
  import {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";
@@ -212,6 +218,17 @@ export class BeaconNode {
212
218
 
213
219
  const clock = new Clock({config, genesisTime: anchorState.genesisTime, signal});
214
220
 
221
+ // Before any lookup into the legacy column buckets, which can be slow until compacted
222
+ const columnsMinEpoch =
223
+ opts.chain.archiveDataEpochs === Infinity
224
+ ? null
225
+ : getDataColumnSidecarsMinEpoch(config, clock.currentEpoch, opts.chain.archiveDataEpochs);
226
+ await db.compactLegacyDataColumns(
227
+ columnsMinEpoch !== null && columnsMinEpoch >= config.FULU_FORK_EPOCH
228
+ ? computeStartSlotAtEpoch(columnsMinEpoch)
229
+ : 0
230
+ );
231
+
215
232
  await db.init();
216
233
 
217
234
  // Prune hot db repos
@@ -280,6 +297,7 @@ export class BeaconNode {
280
297
  : undefined,
281
298
  builderApiClientOpts: {
282
299
  timeout: opts.executionBuilder.timeout,
300
+ bidDeadline: opts.executionBuilder.bidDeadline,
283
301
  // Sent with all builder api requests, unless the node runs in private mode
284
302
  userAgent: opts.executionBuilder.userAgent,
285
303
  },
@@ -1,7 +1,9 @@
1
+ import {BLOCK_PRODUCTION_RACE_CUTOFF_GLOAS_BPS} from "../api/impl/validator/index.js";
1
2
  import {ApiOptions, defaultApiOptions} from "../api/options.js";
2
3
  import {ArchiveMode, DEFAULT_ARCHIVE_MODE, IChainOptions, defaultChainOptions} from "../chain/options.js";
3
4
  import {ValidatorMonitorOpts, defaultValidatorMonitorOpts} from "../chain/validatorMonitor.js";
4
5
  import {DatabaseOptions, defaultDbOptions} from "../db/options.js";
6
+ import {BUILDER_BID_LAG_BUFFER_MS} from "../execution/builder/apiClient.js";
5
7
  import {
6
8
  EngineApiMode,
7
9
  ExecutionBuilderOpts,
@@ -19,16 +21,17 @@ import {SyncOptions, defaultSyncOptions} from "../sync/options.js";
19
21
 
20
22
  // Re-export so the CLI doesn't need to depend on lodestar-api
21
23
  export {allNamespaces} from "../api/rest/index.js";
22
-
23
- // Re-export to use as default values in CLI args
24
+ export type {EngineApiMode};
25
+ // Re-export to use as default values and for validation in CLI args
24
26
  export {
25
- defaultExecutionEngineHttpOpts,
26
- defaultExecutionBuilderHttpOpts,
27
- engineApiModes,
28
27
  ArchiveMode,
28
+ BLOCK_PRODUCTION_RACE_CUTOFF_GLOAS_BPS,
29
+ BUILDER_BID_LAG_BUFFER_MS,
29
30
  DEFAULT_ARCHIVE_MODE,
31
+ defaultExecutionBuilderHttpOpts,
32
+ defaultExecutionEngineHttpOpts,
33
+ engineApiModes,
30
34
  };
31
- export type {EngineApiMode};
32
35
 
33
36
  export interface IBeaconNodeOptions {
34
37
  api: ApiOptions;
@@ -7,6 +7,7 @@ import {IBeaconDb} from "../db/index.js";
7
7
  import {Metrics} from "../metrics/index.js";
8
8
  import {INetwork} from "../network/index.js";
9
9
  import {SyncChainDebugState} from "./range/chain.js";
10
+
10
11
  export type {SyncChainDebugState};
11
12
 
12
13
  export type SyncingStatus = routes.node.SyncingStatus;
package/src/sync/sync.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  import {SLOTS_PER_EPOCH} from "@lodestar/params";
2
- import {Slot} from "@lodestar/types";
2
+ import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
3
+ import {Slot, Status} from "@lodestar/types";
3
4
  import {Logger, toRootHex} from "@lodestar/utils";
4
5
  import {IBeaconChain} from "../chain/index.js";
5
- import {GENESIS_SLOT} from "../constants/constants.js";
6
+ import {GENESIS_EPOCH, GENESIS_SLOT} from "../constants/constants.js";
7
+ import {GoodByeReasonCode} from "../constants/index.js";
8
+ import {IBeaconDb} from "../db/index.js";
6
9
  import {ExecutionEngineState} from "../execution/index.js";
7
10
  import {Metrics} from "../metrics/index.js";
8
- import {INetwork, NetworkEvent, NetworkEventData} from "../network/index.js";
11
+ import {INetwork, NetworkEvent, NetworkEventData, PeerAction} from "../network/index.js";
9
12
  import {ClockEvent} from "../util/clock.js";
10
13
  import {isOptimisticBlock} from "../util/forkChoice.js";
14
+ import {PeerIdStr} from "../util/peerId.js";
11
15
  import {MIN_EPOCH_TO_START_GOSSIP} from "./constants.js";
12
16
  import {IBeaconSync, SyncChainDebugState, SyncModules, SyncState, SyncingStatus, syncStateMetric} from "./interface.js";
13
17
  import {SyncOptions} from "./options.js";
@@ -19,6 +23,7 @@ export class BeaconSync implements IBeaconSync {
19
23
  private readonly logger: Logger;
20
24
  private readonly network: INetwork;
21
25
  private readonly chain: IBeaconChain;
26
+ private readonly db: IBeaconDb;
22
27
  private readonly metrics: Metrics | null;
23
28
  private readonly opts: SyncOptions;
24
29
 
@@ -30,10 +35,11 @@ export class BeaconSync implements IBeaconSync {
30
35
  private readonly slotImportTolerance: Slot;
31
36
 
32
37
  constructor(opts: SyncOptions, modules: SyncModules) {
33
- const {config, chain, metrics, network, logger} = modules;
38
+ const {config, chain, db, metrics, network, logger} = modules;
34
39
  this.opts = opts;
35
40
  this.network = network;
36
41
  this.chain = chain;
42
+ this.db = db;
37
43
  this.metrics = metrics;
38
44
  this.logger = logger;
39
45
  this.rangeSync = new RangeSync(modules, opts);
@@ -192,6 +198,11 @@ export class BeaconSync implements IBeaconSync {
192
198
  */
193
199
  private addPeer = (data: NetworkEventData[NetworkEvent.peerConnected]): void => {
194
200
  const localStatus = this.chain.getStatus();
201
+ // Genesis and equal finalized epochs are already checked by assertPeerRelevance in the network worker
202
+ if (data.status.finalizedEpoch > GENESIS_EPOCH && data.status.finalizedEpoch < localStatus.finalizedEpoch) {
203
+ void this.verifyPastFinalizedCheckpoint(data.peer, data.status);
204
+ }
205
+
195
206
  const syncType = getPeerSyncType(localStatus, data.status, this.chain.forkChoice, this.slotImportTolerance);
196
207
  this.logger.verbose("Peer sync type classified", {
197
208
  peer: data.peer,
@@ -216,6 +227,56 @@ export class BeaconSync implements IBeaconSync {
216
227
  this.updateSyncState();
217
228
  };
218
229
 
230
+ /**
231
+ * A behind peer's finalized checkpoint must be the canonical block in effect at the start slot of its epoch
232
+ */
233
+ async verifyPastFinalizedCheckpoint(peer: PeerIdStr, remote: Status): Promise<void> {
234
+ const startSlot = computeStartSlotAtEpoch(remote.finalizedEpoch);
235
+ const root = toRootHex(remote.finalizedRoot);
236
+ try {
237
+ let reason: string | null = null;
238
+ // After a finality jump, finalized blocks stay in fork choice until the archiver migrates them.
239
+ // Fork choice is pruned only after archiving, so a miss here means the db already has this range.
240
+ const canonicalBlock = this.chain.forkChoice.getCanonicalBlockClosestLteSlot(startSlot);
241
+ if (canonicalBlock !== null) {
242
+ if (canonicalBlock.blockRoot === root) return;
243
+ reason = "finalized_root_not_canonical";
244
+ } else {
245
+ const slot = await this.db.blockArchive.getSlotByRoot(remote.finalizedRoot);
246
+ if (slot !== null && slot > startSlot) {
247
+ reason = "finalized_root_after_epoch_start";
248
+ } else if (slot !== startSlot) {
249
+ const childSlot = await this.db.blockArchive.getSlotByParentRoot(remote.finalizedRoot);
250
+ if (childSlot !== null) {
251
+ if (childSlot <= startSlot) reason = "finalized_root_not_in_effect_at_epoch_start";
252
+ } else if (slot === null && startSlot > this.chain.earliestAvailableSlot) {
253
+ // childSlot == null too, neither the root nor a child is canonical
254
+ // or they are canonical and both pruned but peer's finalized epoch starts after earliestAvailableSlot
255
+ reason = "unknown_finalized_root";
256
+ }
257
+ }
258
+ }
259
+
260
+ if (reason !== null) {
261
+ this.logger.debug("Peer finalized checkpoint not in our chain", {
262
+ peer,
263
+ finalizedEpoch: remote.finalizedEpoch,
264
+ root,
265
+ reason,
266
+ });
267
+ this.metrics?.syncPeerFinalizedCheckpointMismatch.inc({reason});
268
+ this.network.reportPeer(peer, PeerAction.Fatal, "PeerFinalizedCheckpointMismatch");
269
+ await this.network.goodbyeAndDisconnect(peer, GoodByeReasonCode.IRRELEVANT_NETWORK);
270
+ }
271
+ } catch (e) {
272
+ this.logger.debug(
273
+ "Error verifying peer finalized checkpoint",
274
+ {peer, finalizedEpoch: remote.finalizedEpoch, root},
275
+ e as Error
276
+ );
277
+ }
278
+ }
279
+
219
280
  /**
220
281
  * Must be called by libp2p when a peer is removed from the peer manager
221
282
  */
@@ -300,10 +300,19 @@ export class BlockInputSync {
300
300
 
301
301
  private onIncompletePayloadEnvelope = (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]): void => {
302
302
  try {
303
- this.addByPayloadInput(data.payloadInput, data.peer);
304
- this.triggerUnknownBlockSearch();
305
- this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.INCOMPLETE_PAYLOAD_ENVELOPE});
306
- this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
303
+ const isNewRoot = this.addByPayloadInput(data.payloadInput, data.peer);
304
+ const pendingPayload = this.pendingPayloads.get(data.payloadInput.blockRootHex);
305
+ if (
306
+ pendingPayload &&
307
+ pendingPayload.status !== PendingPayloadInputStatus.fetching &&
308
+ pendingPayload.status !== PendingPayloadInputStatus.processing
309
+ ) {
310
+ this.triggerUnknownBlockSearch();
311
+ }
312
+ if (isNewRoot) {
313
+ this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.INCOMPLETE_PAYLOAD_ENVELOPE});
314
+ this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
315
+ }
307
316
  } catch (e) {
308
317
  this.logger.debug("Error handling incompletePayloadEnvelope event", {}, e as Error);
309
318
  }
@@ -599,43 +608,44 @@ export class BlockInputSync {
599
608
  return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
600
609
  }
601
610
 
602
- private addByPayloadInput = (
603
- payloadInput: PayloadEnvelopeInput,
604
- peerIdStr?: PeerIdStr,
605
- envelope?: gloas.SignedExecutionPayloadEnvelope
606
- ): void => {
611
+ private addByPayloadInput = (payloadInput: PayloadEnvelopeInput, peerIdStr?: PeerIdStr): boolean => {
607
612
  if (this.knownBadBlocks.has(payloadInput.blockRootHex)) {
608
613
  this.logger.debug("Ignoring payload input for known bad block root", {
609
614
  slot: payloadInput.slot,
610
615
  root: payloadInput.blockRootHex,
611
616
  peerIdStr,
612
617
  });
613
- return;
618
+ return false;
614
619
  }
615
620
 
616
- const pendingPayload = this.toPendingPayloadInput(
617
- payloadInput,
618
- this.pendingPayloads.get(payloadInput.blockRootHex),
619
- envelope
620
- );
621
+ let pendingPayload = this.pendingPayloads.get(payloadInput.blockRootHex);
622
+ const isNewRoot = pendingPayload === undefined;
623
+ // Envelope-only entries must go through reconciliation to validate their speculative envelope.
624
+ if (
625
+ !pendingPayload ||
626
+ pendingPayload.status === PendingPayloadInputStatus.pending ||
627
+ pendingPayload.status === PendingPayloadInputStatus.downloaded
628
+ ) {
629
+ pendingPayload = this.toPendingPayloadInput(payloadInput, pendingPayload);
630
+ this.pendingPayloads.set(payloadInput.blockRootHex, pendingPayload);
631
+
632
+ this.logger.verbose("Added payloadInput to BlockInputSync.pendingPayloads", {
633
+ slot: payloadInput.slot,
634
+ root: payloadInput.blockRootHex,
635
+ delaySec: this.chain.clock.secFromSlot(payloadInput.slot),
636
+ });
637
+ }
621
638
 
622
639
  if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
623
640
  pendingPayload.peerIdStrings.add(peerIdStr);
624
641
  }
625
642
 
626
- this.pendingPayloads.set(payloadInput.blockRootHex, pendingPayload);
627
-
628
- this.logger.verbose("Added payloadInput to BlockInputSync.pendingPayloads", {
629
- slot: payloadInput.slot,
630
- root: payloadInput.blockRootHex,
631
- delaySec: this.chain.clock.secFromSlot(payloadInput.slot),
632
- });
633
-
634
643
  const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
635
644
  if (prunedItemCount > 0) {
636
645
  this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
637
646
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
638
647
  }
648
+ return isNewRoot;
639
649
  };
640
650
 
641
651
  private onPeerConnected = (data: NetworkEventData[NetworkEvent.peerConnected]): void => {
@@ -1267,6 +1277,9 @@ export class BlockInputSync {
1267
1277
  }
1268
1278
 
1269
1279
  const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
1280
+ if (this.pendingPayloads.get(rootHex) !== pendingPayload) {
1281
+ return;
1282
+ }
1270
1283
  if (!payloadInput) {
1271
1284
  this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
1272
1285
  root: rootHex,
@@ -1278,6 +1291,9 @@ export class BlockInputSync {
1278
1291
  const validationResult = await wrapError(
1279
1292
  validateGossipExecutionPayloadEnvelope(this.chain, pendingPayload.envelope)
1280
1293
  );
1294
+ if (this.pendingPayloads.get(rootHex) !== pendingPayload) {
1295
+ return;
1296
+ }
1281
1297
  if (validationResult.err) {
1282
1298
  this.logger.debug(
1283
1299
  "Pending payload envelope failed validation after block import, refetching by root",
@@ -1345,14 +1361,13 @@ export class BlockInputSync {
1345
1361
  payload.status = PendingPayloadInputStatus.fetching;
1346
1362
 
1347
1363
  const res = await wrapError(this.fetchPayloadInput(payload));
1364
+ if (this.pendingPayloads.get(rootHex) !== payload) {
1365
+ this.logger.verbose("Dropping downloaded payload, entry changed during fetch", logCtx);
1366
+ return;
1367
+ }
1348
1368
  if (!res.err) {
1349
1369
  const pendingPayload = res.result;
1350
1370
  const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
1351
- // finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
1352
- if (!this.pendingPayloads.has(resultRootHex)) {
1353
- this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
1354
- return;
1355
- }
1356
1371
  this.pendingPayloads.set(resultRootHex, pendingPayload);
1357
1372
  const result = isPendingPayloadEnvelope(pendingPayload)
1358
1373
  ? DownloadResult.WaitingForBlock
@@ -1423,12 +1438,16 @@ export class BlockInputSync {
1423
1438
  this.logger.debug("Processing downloaded payload", logCtx);
1424
1439
 
1425
1440
  const res = await wrapError(this.chain.processExecutionPayload(pendingPayload.payloadInput));
1441
+ // The executionPayload event emitted during import usually removes the entry before this point
1426
1442
  if (!res.err) {
1427
1443
  this.logger.debug("Processed payload from unknown sync", logCtx);
1428
1444
  this.pendingPayloads.delete(rootHex);
1429
1445
  this.triggerUnknownBlockSearch();
1430
1446
  return;
1431
1447
  }
1448
+ if (this.pendingPayloads.get(rootHex) !== pendingPayload) {
1449
+ return;
1450
+ }
1432
1451
 
1433
1452
  if (res.err instanceof PayloadError) {
1434
1453
  switch (res.err.type.code) {
package/src/util/file.ts CHANGED
@@ -1,25 +1,24 @@
1
- import fs from "node:fs";
2
- import {promisify} from "node:util";
1
+ import {readFile as fsReadFile, mkdir, readdir, stat, unlink, writeFile} from "node:fs/promises";
3
2
 
4
3
  /** Ensure a directory exists */
5
4
  export async function ensureDir(path: string): Promise<void> {
6
5
  try {
7
- await promisify(fs.stat)(path);
6
+ await stat(path);
8
7
  } catch (_) {
9
8
  // not exists
10
- await promisify(fs.mkdir)(path, {recursive: true});
9
+ await mkdir(path, {recursive: true});
11
10
  }
12
11
  }
13
12
 
14
13
  /** Write data to a file if it does not exist */
15
14
  export async function writeIfNotExist(filepath: string, bytes: Uint8Array): Promise<boolean> {
16
15
  try {
17
- await promisify(fs.stat)(filepath);
16
+ await stat(filepath);
18
17
  return false;
19
18
  // file exists, do nothing
20
19
  } catch (_) {
21
20
  // not exists
22
- await promisify(fs.writeFile)(filepath, bytes);
21
+ await writeFile(filepath, bytes);
23
22
  return true;
24
23
  }
25
24
  }
@@ -27,7 +26,7 @@ export async function writeIfNotExist(filepath: string, bytes: Uint8Array): Prom
27
26
  /** Remove a file if it exists */
28
27
  export async function removeFile(path: string): Promise<boolean> {
29
28
  try {
30
- await promisify(fs.unlink)(path);
29
+ await unlink(path);
31
30
  return true;
32
31
  } catch (_) {
33
32
  // may not exists
@@ -37,7 +36,7 @@ export async function removeFile(path: string): Promise<boolean> {
37
36
 
38
37
  export async function readFile(path: string): Promise<Uint8Array | null> {
39
38
  try {
40
- return await fs.promises.readFile(path);
39
+ return await fsReadFile(path);
41
40
  } catch (_) {
42
41
  return null;
43
42
  }
@@ -45,7 +44,7 @@ export async function readFile(path: string): Promise<Uint8Array | null> {
45
44
 
46
45
  export async function readFileNames(folderPath: string): Promise<string[]> {
47
46
  try {
48
- return await fs.promises.readdir(folderPath);
47
+ return await readdir(folderPath);
49
48
  } catch (_) {
50
49
  return [];
51
50
  }
@@ -69,7 +69,7 @@ export class JobItemQueue<Args extends any[], R> {
69
69
  if (this.jobs.length === 1 && this.opts.noYieldIfOneItem) {
70
70
  void this.runJob();
71
71
  } else if (this.runningJobs < this.opts.maxConcurrency) {
72
- callInNextEventLoop(this.runJob);
72
+ callInNextEventLoop(() => void this.runJob());
73
73
  }
74
74
  });
75
75
  }