@lodestar/beacon-node 1.47.0-dev.d2f4b96574 → 1.47.0-dev.d79d81f994

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 (377) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +47 -18
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +25 -2
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/beacon/state/utils.d.ts +2 -1
  8. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  9. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  10. package/lib/api/impl/events/index.d.ts +1 -1
  11. package/lib/api/impl/events/index.d.ts.map +1 -1
  12. package/lib/api/impl/events/index.js +12 -3
  13. package/lib/api/impl/events/index.js.map +1 -1
  14. package/lib/api/impl/lodestar/index.d.ts.map +1 -1
  15. package/lib/api/impl/lodestar/index.js +12 -13
  16. package/lib/api/impl/lodestar/index.js.map +1 -1
  17. package/lib/api/impl/validator/index.d.ts.map +1 -1
  18. package/lib/api/impl/validator/index.js +238 -65
  19. package/lib/api/impl/validator/index.js.map +1 -1
  20. package/lib/api/impl/validator/utils.d.ts +5 -0
  21. package/lib/api/impl/validator/utils.d.ts.map +1 -1
  22. package/lib/api/impl/validator/utils.js +18 -12
  23. package/lib/api/impl/validator/utils.js.map +1 -1
  24. package/lib/api/rest/base.d.ts.map +1 -1
  25. package/lib/api/rest/base.js +7 -0
  26. package/lib/api/rest/base.js.map +1 -1
  27. package/lib/chain/archiveStore/archiveStore.js +1 -1
  28. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  29. package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  30. package/lib/chain/archiveStore/historicalState/metrics.js +21 -0
  31. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  32. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  33. package/lib/chain/blocks/importBlock.js +2 -7
  34. package/lib/chain/blocks/importBlock.js.map +1 -1
  35. package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
  36. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  37. package/lib/chain/blocks/importExecutionPayload.js +11 -9
  38. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  39. package/lib/chain/blocks/index.d.ts.map +1 -1
  40. package/lib/chain/blocks/index.js +15 -7
  41. package/lib/chain/blocks/index.js.map +1 -1
  42. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
  43. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  44. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
  45. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  46. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
  47. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  48. package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
  49. package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
  50. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  51. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
  52. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  53. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  54. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  55. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  56. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  57. package/lib/chain/bls/index.d.ts +1 -1
  58. package/lib/chain/bls/index.d.ts.map +1 -1
  59. package/lib/chain/bls/interface.d.ts +5 -5
  60. package/lib/chain/bls/interface.d.ts.map +1 -1
  61. package/lib/chain/bls/multithread/index.d.ts +5 -11
  62. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  63. package/lib/chain/bls/multithread/index.js +116 -101
  64. package/lib/chain/bls/multithread/index.js.map +1 -1
  65. package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
  66. package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
  67. package/lib/chain/bls/multithread/jobItem.js +10 -65
  68. package/lib/chain/bls/multithread/jobItem.js.map +1 -1
  69. package/lib/chain/bls/multithread/types.d.ts +29 -8
  70. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  71. package/lib/chain/bls/multithread/types.js +12 -0
  72. package/lib/chain/bls/multithread/types.js.map +1 -1
  73. package/lib/chain/bls/multithread/utils.d.ts +4 -0
  74. package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
  75. package/lib/chain/bls/multithread/utils.js +10 -0
  76. package/lib/chain/bls/multithread/utils.js.map +1 -1
  77. package/lib/chain/bls/multithread/worker.js +49 -23
  78. package/lib/chain/bls/multithread/worker.js.map +1 -1
  79. package/lib/chain/bls/singleThread.d.ts +4 -10
  80. package/lib/chain/bls/singleThread.d.ts.map +1 -1
  81. package/lib/chain/bls/singleThread.js +8 -45
  82. package/lib/chain/bls/singleThread.js.map +1 -1
  83. package/lib/chain/bls/utils.d.ts +4 -4
  84. package/lib/chain/bls/utils.d.ts.map +1 -1
  85. package/lib/chain/bls/utils.js +18 -19
  86. package/lib/chain/bls/utils.js.map +1 -1
  87. package/lib/chain/builderCircuitBreaker.d.ts +6 -5
  88. package/lib/chain/builderCircuitBreaker.d.ts.map +1 -1
  89. package/lib/chain/builderCircuitBreaker.js +28 -15
  90. package/lib/chain/builderCircuitBreaker.js.map +1 -1
  91. package/lib/chain/chain.d.ts +10 -5
  92. package/lib/chain/chain.d.ts.map +1 -1
  93. package/lib/chain/chain.js +24 -9
  94. package/lib/chain/chain.js.map +1 -1
  95. package/lib/chain/errors/blockError.d.ts +2 -0
  96. package/lib/chain/errors/blockError.d.ts.map +1 -1
  97. package/lib/chain/errors/blockError.js +6 -1
  98. package/lib/chain/errors/blockError.js.map +1 -1
  99. package/lib/chain/errors/proposerPreferences.d.ts +6 -0
  100. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
  101. package/lib/chain/errors/proposerPreferences.js +1 -0
  102. package/lib/chain/errors/proposerPreferences.js.map +1 -1
  103. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  104. package/lib/chain/forkChoice/index.js +4 -0
  105. package/lib/chain/forkChoice/index.js.map +1 -1
  106. package/lib/chain/interface.d.ts +8 -4
  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.js +1 -1
  110. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  111. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  112. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  113. package/lib/chain/opPools/attestationPool.js +1 -1
  114. package/lib/chain/opPools/attestationPool.js.map +1 -1
  115. package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts +16 -0
  116. package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts.map +1 -0
  117. package/lib/chain/opPools/deferredVoluntaryExitPool.js +57 -0
  118. package/lib/chain/opPools/deferredVoluntaryExitPool.js.map +1 -0
  119. package/lib/chain/opPools/executionPayloadBidPool.d.ts +7 -2
  120. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
  121. package/lib/chain/opPools/executionPayloadBidPool.js +4 -4
  122. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
  123. package/lib/chain/opPools/index.d.ts +2 -1
  124. package/lib/chain/opPools/index.d.ts.map +1 -1
  125. package/lib/chain/opPools/index.js +1 -0
  126. package/lib/chain/opPools/index.js.map +1 -1
  127. package/lib/chain/opPools/payloadAttestationPool.js +3 -3
  128. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  129. package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
  130. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  131. package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
  132. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  133. package/lib/chain/opPools/utils.d.ts +1 -1
  134. package/lib/chain/opPools/utils.d.ts.map +1 -1
  135. package/lib/chain/opPools/utils.js +1 -1
  136. package/lib/chain/opPools/utils.js.map +1 -1
  137. package/lib/chain/options.d.ts +1 -1
  138. package/lib/chain/options.d.ts.map +1 -1
  139. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  140. package/lib/chain/prepareNextSlot.js +25 -7
  141. package/lib/chain/prepareNextSlot.js.map +1 -1
  142. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  143. package/lib/chain/produceBlock/computeNewStateRoot.js +3 -25
  144. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  145. package/lib/chain/produceBlock/produceBlockBody.d.ts +2 -0
  146. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  147. package/lib/chain/produceBlock/produceBlockBody.js +7 -2
  148. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  149. package/lib/chain/regen/interface.d.ts +5 -2
  150. package/lib/chain/regen/interface.d.ts.map +1 -1
  151. package/lib/chain/regen/interface.js +3 -0
  152. package/lib/chain/regen/interface.js.map +1 -1
  153. package/lib/chain/regen/queued.d.ts +2 -2
  154. package/lib/chain/regen/queued.d.ts.map +1 -1
  155. package/lib/chain/regen/queued.js +5 -5
  156. package/lib/chain/regen/queued.js.map +1 -1
  157. package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
  158. package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
  159. package/lib/chain/seenCache/seenAttesters.js +25 -4
  160. package/lib/chain/seenCache/seenAttesters.js.map +1 -1
  161. package/lib/chain/seenCache/seenBlockProposers.d.ts +5 -1
  162. package/lib/chain/seenCache/seenBlockProposers.d.ts.map +1 -1
  163. package/lib/chain/seenCache/seenBlockProposers.js +10 -3
  164. package/lib/chain/seenCache/seenBlockProposers.js.map +1 -1
  165. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
  166. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  167. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
  168. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  169. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +5 -1
  170. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  171. package/lib/chain/stateCache/fifoBlockStateCache.js +6 -0
  172. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  173. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +47 -5
  174. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  175. package/lib/chain/stateCache/persistentCheckpointsCache.js +117 -7
  176. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  177. package/lib/chain/stateCache/types.d.ts +3 -3
  178. package/lib/chain/stateCache/types.d.ts.map +1 -1
  179. package/lib/chain/validation/attestation.d.ts.map +1 -1
  180. package/lib/chain/validation/attestation.js +1 -4
  181. package/lib/chain/validation/attestation.js.map +1 -1
  182. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  183. package/lib/chain/validation/attesterSlashing.js +1 -1
  184. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  185. package/lib/chain/validation/block.d.ts.map +1 -1
  186. package/lib/chain/validation/block.js +21 -7
  187. package/lib/chain/validation/block.js.map +1 -1
  188. package/lib/chain/validation/executionPayloadBid.d.ts +13 -3
  189. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  190. package/lib/chain/validation/executionPayloadBid.js +147 -12
  191. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  192. package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
  193. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  194. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  195. package/lib/chain/validation/payloadAttestationMessage.js +5 -14
  196. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  197. package/lib/chain/validation/proposerPreferences.d.ts +1 -1
  198. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  199. package/lib/chain/validation/proposerPreferences.js +13 -4
  200. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  201. package/lib/chain/validation/proposerSlashing.js +1 -1
  202. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  203. package/lib/chain/validation/voluntaryExit.d.ts +9 -1
  204. package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
  205. package/lib/chain/validation/voluntaryExit.js +46 -1
  206. package/lib/chain/validation/voluntaryExit.js.map +1 -1
  207. package/lib/execution/builder/apiClient.d.ts +64 -0
  208. package/lib/execution/builder/apiClient.d.ts.map +1 -0
  209. package/lib/execution/builder/apiClient.js +184 -0
  210. package/lib/execution/builder/apiClient.js.map +1 -0
  211. package/lib/execution/builder/http.d.ts +10 -7
  212. package/lib/execution/builder/http.d.ts.map +1 -1
  213. package/lib/execution/builder/http.js +11 -8
  214. package/lib/execution/builder/http.js.map +1 -1
  215. package/lib/execution/builder/validateBid.d.ts +22 -0
  216. package/lib/execution/builder/validateBid.d.ts.map +1 -0
  217. package/lib/execution/builder/validateBid.js +111 -0
  218. package/lib/execution/builder/validateBid.js.map +1 -0
  219. package/lib/metrics/metrics/beacon.d.ts +3 -3
  220. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  221. package/lib/metrics/metrics/beacon.js +9 -9
  222. package/lib/metrics/metrics/beacon.js.map +1 -1
  223. package/lib/metrics/metrics/lodestar.d.ts +88 -23
  224. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  225. package/lib/metrics/metrics/lodestar.js +164 -52
  226. package/lib/metrics/metrics/lodestar.js.map +1 -1
  227. package/lib/network/gossip/topic.d.ts +13 -0
  228. package/lib/network/gossip/topic.d.ts.map +1 -1
  229. package/lib/network/gossip/topic.js +37 -0
  230. package/lib/network/gossip/topic.js.map +1 -1
  231. package/lib/network/libp2p/index.d.ts.map +1 -1
  232. package/lib/network/libp2p/index.js +10 -2
  233. package/lib/network/libp2p/index.js.map +1 -1
  234. package/lib/network/network.d.ts.map +1 -1
  235. package/lib/network/network.js +23 -43
  236. package/lib/network/network.js.map +1 -1
  237. package/lib/network/processor/constants.d.ts +7 -0
  238. package/lib/network/processor/constants.d.ts.map +1 -0
  239. package/lib/network/processor/constants.js +7 -0
  240. package/lib/network/processor/constants.js.map +1 -0
  241. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  242. package/lib/network/processor/gossipHandlers.js +38 -7
  243. package/lib/network/processor/gossipHandlers.js.map +1 -1
  244. package/lib/network/processor/gossipQueues/indexed.d.ts.map +1 -1
  245. package/lib/network/processor/gossipQueues/indexed.js +0 -1
  246. package/lib/network/processor/gossipQueues/indexed.js.map +1 -1
  247. package/lib/network/processor/index.d.ts +3 -0
  248. package/lib/network/processor/index.d.ts.map +1 -1
  249. package/lib/network/processor/index.js +41 -17
  250. package/lib/network/processor/index.js.map +1 -1
  251. package/lib/network/reqresp/score.d.ts.map +1 -1
  252. package/lib/network/reqresp/score.js +5 -11
  253. package/lib/network/reqresp/score.js.map +1 -1
  254. package/lib/network/util.d.ts +0 -1
  255. package/lib/network/util.d.ts.map +1 -1
  256. package/lib/network/util.js +0 -4
  257. package/lib/network/util.js.map +1 -1
  258. package/lib/node/deferredVoluntaryExitPublisher.d.ts +15 -0
  259. package/lib/node/deferredVoluntaryExitPublisher.d.ts.map +1 -0
  260. package/lib/node/deferredVoluntaryExitPublisher.js +38 -0
  261. package/lib/node/deferredVoluntaryExitPublisher.js.map +1 -0
  262. package/lib/node/nodejs.d.ts +2 -1
  263. package/lib/node/nodejs.d.ts.map +1 -1
  264. package/lib/node/nodejs.js +7 -0
  265. package/lib/node/nodejs.js.map +1 -1
  266. package/lib/sync/range/batch.d.ts +23 -5
  267. package/lib/sync/range/batch.d.ts.map +1 -1
  268. package/lib/sync/range/batch.js +61 -22
  269. package/lib/sync/range/batch.js.map +1 -1
  270. package/lib/sync/range/chain.d.ts +2 -1
  271. package/lib/sync/range/chain.d.ts.map +1 -1
  272. package/lib/sync/range/chain.js +23 -10
  273. package/lib/sync/range/chain.js.map +1 -1
  274. package/lib/sync/types.d.ts +32 -0
  275. package/lib/sync/types.d.ts.map +1 -1
  276. package/lib/sync/types.js +33 -0
  277. package/lib/sync/types.js.map +1 -1
  278. package/lib/sync/unknownBlock.d.ts +15 -2
  279. package/lib/sync/unknownBlock.d.ts.map +1 -1
  280. package/lib/sync/unknownBlock.js +234 -63
  281. package/lib/sync/unknownBlock.js.map +1 -1
  282. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  283. package/lib/sync/utils/downloadByRange.js +16 -11
  284. package/lib/sync/utils/downloadByRange.js.map +1 -1
  285. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  286. package/lib/sync/utils/downloadByRoot.js +3 -1
  287. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  288. package/package.json +35 -35
  289. package/src/api/impl/beacon/blocks/index.ts +55 -21
  290. package/src/api/impl/beacon/pool/index.ts +27 -2
  291. package/src/api/impl/beacon/state/utils.ts +2 -1
  292. package/src/api/impl/events/index.ts +12 -4
  293. package/src/api/impl/lodestar/index.ts +12 -13
  294. package/src/api/impl/validator/index.ts +300 -69
  295. package/src/api/impl/validator/utils.ts +30 -15
  296. package/src/api/rest/base.ts +7 -0
  297. package/src/chain/archiveStore/archiveStore.ts +1 -1
  298. package/src/chain/archiveStore/historicalState/metrics.ts +22 -0
  299. package/src/chain/blocks/importBlock.ts +1 -6
  300. package/src/chain/blocks/importExecutionPayload.ts +12 -11
  301. package/src/chain/blocks/index.ts +19 -7
  302. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
  303. package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
  304. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
  305. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
  306. package/src/chain/bls/index.ts +1 -1
  307. package/src/chain/bls/interface.ts +7 -3
  308. package/src/chain/bls/multithread/index.ts +148 -120
  309. package/src/chain/bls/multithread/jobItem.ts +12 -82
  310. package/src/chain/bls/multithread/types.ts +33 -8
  311. package/src/chain/bls/multithread/utils.ts +11 -0
  312. package/src/chain/bls/multithread/worker.ts +85 -27
  313. package/src/chain/bls/singleThread.ts +11 -55
  314. package/src/chain/bls/utils.ts +23 -24
  315. package/src/chain/builderCircuitBreaker.ts +31 -17
  316. package/src/chain/chain.ts +27 -7
  317. package/src/chain/errors/blockError.ts +9 -3
  318. package/src/chain/errors/proposerPreferences.ts +7 -0
  319. package/src/chain/forkChoice/index.ts +4 -0
  320. package/src/chain/interface.ts +8 -3
  321. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  322. package/src/chain/opPools/attestationPool.ts +1 -1
  323. package/src/chain/opPools/deferredVoluntaryExitPool.ts +65 -0
  324. package/src/chain/opPools/executionPayloadBidPool.ts +13 -7
  325. package/src/chain/opPools/index.ts +2 -1
  326. package/src/chain/opPools/payloadAttestationPool.ts +3 -3
  327. package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
  328. package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
  329. package/src/chain/opPools/utils.ts +1 -1
  330. package/src/chain/options.ts +1 -1
  331. package/src/chain/prepareNextSlot.ts +25 -7
  332. package/src/chain/produceBlock/computeNewStateRoot.ts +3 -38
  333. package/src/chain/produceBlock/produceBlockBody.ts +14 -1
  334. package/src/chain/regen/interface.ts +5 -2
  335. package/src/chain/regen/queued.ts +5 -5
  336. package/src/chain/seenCache/seenAttesters.ts +31 -5
  337. package/src/chain/seenCache/seenBlockProposers.ts +13 -3
  338. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
  339. package/src/chain/stateCache/fifoBlockStateCache.ts +8 -1
  340. package/src/chain/stateCache/persistentCheckpointsCache.ts +133 -8
  341. package/src/chain/stateCache/types.ts +3 -3
  342. package/src/chain/validation/attestation.ts +1 -7
  343. package/src/chain/validation/attesterSlashing.ts +1 -8
  344. package/src/chain/validation/block.ts +21 -7
  345. package/src/chain/validation/executionPayloadBid.ts +168 -14
  346. package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
  347. package/src/chain/validation/payloadAttestationMessage.ts +6 -17
  348. package/src/chain/validation/proposerPreferences.ts +15 -5
  349. package/src/chain/validation/proposerSlashing.ts +1 -1
  350. package/src/chain/validation/voluntaryExit.ts +59 -2
  351. package/src/execution/builder/apiClient.ts +244 -0
  352. package/src/execution/builder/http.ts +11 -11
  353. package/src/execution/builder/validateBid.ts +166 -0
  354. package/src/metrics/metrics/beacon.ts +9 -9
  355. package/src/metrics/metrics/lodestar.ts +178 -58
  356. package/src/network/gossip/topic.ts +38 -0
  357. package/src/network/libp2p/index.ts +11 -2
  358. package/src/network/network.ts +29 -54
  359. package/src/network/processor/constants.ts +6 -0
  360. package/src/network/processor/gossipHandlers.ts +41 -5
  361. package/src/network/processor/gossipQueues/indexed.ts +0 -1
  362. package/src/network/processor/index.ts +53 -21
  363. package/src/network/reqresp/score.ts +5 -11
  364. package/src/network/util.ts +0 -5
  365. package/src/node/deferredVoluntaryExitPublisher.ts +54 -0
  366. package/src/node/nodejs.ts +10 -1
  367. package/src/sync/range/batch.ts +83 -28
  368. package/src/sync/range/chain.ts +26 -11
  369. package/src/sync/types.ts +36 -1
  370. package/src/sync/unknownBlock.ts +273 -70
  371. package/src/sync/utils/downloadByRange.ts +21 -12
  372. package/src/sync/utils/downloadByRoot.ts +3 -1
  373. package/lib/chain/bls/maybeBatch.d.ts +0 -12
  374. package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
  375. package/lib/chain/bls/maybeBatch.js +0 -35
  376. package/lib/chain/bls/maybeBatch.js.map +0 -1
  377. package/src/chain/bls/maybeBatch.ts +0 -45
@@ -1,6 +1,7 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
- import {ForkSeq} from "@lodestar/params";
3
+ import {CheckpointWithHex} from "@lodestar/fork-choice";
4
+ import {ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas} from "@lodestar/params";
4
5
  import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
5
6
  import {computeTimeAtSlot} from "@lodestar/state-transition";
6
7
  import {RootHex, Slot, gloas} from "@lodestar/types";
@@ -16,6 +17,7 @@ import {validateGossipExecutionPayloadEnvelope} from "../chain/validation/execut
16
17
  import {Metrics} from "../metrics/index.js";
17
18
  import {INetwork, NetworkEvent, NetworkEventData, prettyPrintPeerIdStr} from "../network/index.js";
18
19
  import {PeerSyncMeta} from "../network/peers/peersData.js";
20
+ import {MAX_PEERS_PER_ROOT} from "../network/processor/constants.js";
19
21
  import {PeerIdStr} from "../util/peerId.js";
20
22
  import {shuffle} from "../util/shuffle.js";
21
23
  import {sortBy} from "../util/sortBy.js";
@@ -24,6 +26,7 @@ import {MAX_CONCURRENT_REQUESTS} from "./constants.js";
24
26
  import {SyncOptions} from "./options.js";
25
27
  import {
26
28
  BlockInputSyncCacheItem,
29
+ DroppedItemReason,
27
30
  PayloadSyncCacheItem,
28
31
  PendingBlockInput,
29
32
  PendingBlockInputStatus,
@@ -47,6 +50,10 @@ import {getRateLimitedUntilMs} from "./utils/rateLimit.js";
47
50
  const MAX_ATTEMPTS_PER_BLOCK = 5;
48
51
  const MAX_KNOWN_BAD_BLOCKS = 500;
49
52
  const MAX_PENDING_BLOCKS = 100;
53
+ /**
54
+ * A pending payload without slot can only stay at most 2 epochs.
55
+ */
56
+ const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
50
57
 
51
58
  type AdvancePendingBlockResult =
52
59
  | "ready"
@@ -154,10 +161,21 @@ export class BlockInputSync {
154
161
  this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
155
162
  this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
156
163
  this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
164
+ this.chain.emitter.on(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
157
165
  this.chain.emitter.on(routes.events.EventType.block, this.onBlockImported);
158
166
  this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
159
167
  this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
160
168
  this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
169
+
170
+ // Seed the balancer with peers that connected before we subscribed
171
+ for (const peerId of this.network.getConnectedPeers()) {
172
+ try {
173
+ this.peerBalancer.onPeerConnected(peerId, this.network.getConnectedPeerSyncMeta(peerId));
174
+ } catch (e) {
175
+ this.logger.debug("Failed to seed connected peer into BlockInputSync balancer", {peerId}, e as Error);
176
+ }
177
+ }
178
+
161
179
  this.subscribedToNetworkEvents = true;
162
180
  }
163
181
  }
@@ -170,10 +188,12 @@ export class BlockInputSync {
170
188
  this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
171
189
  this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
172
190
  this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
191
+ this.chain.emitter.off(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
173
192
  this.chain.emitter.off(routes.events.EventType.block, this.onBlockImported);
174
193
  this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadImported);
175
194
  this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
176
195
  this.network.events.off(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
196
+ this.peerBalancer.clear();
177
197
  this.subscribedToNetworkEvents = false;
178
198
  }
179
199
 
@@ -190,10 +210,12 @@ export class BlockInputSync {
190
210
  */
191
211
  private onUnknownBlockRoot = (data: ChainEventData[ChainEvent.unknownBlockRoot]): void => {
192
212
  try {
193
- this.addByRootHex(data.rootHex, data.peer);
194
- this.triggerUnknownBlockSearch();
195
- this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_BLOCK_ROOT});
196
- this.metrics?.blockInputSync.source.inc({source: data.source});
213
+ const isNewRoot = this.addByRootHex(data.rootHex, data.peer);
214
+ if (isNewRoot) {
215
+ this.triggerUnknownBlockSearch();
216
+ this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_BLOCK_ROOT});
217
+ this.metrics?.blockInputSync.source.inc({source: data.source});
218
+ }
197
219
  } catch (e) {
198
220
  this.logger.debug("Error handling unknownBlockRoot event", {}, e as Error);
199
221
  }
@@ -215,10 +237,12 @@ export class BlockInputSync {
215
237
 
216
238
  private onUnknownEnvelopeBlockRoot = (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRoot]): void => {
217
239
  try {
218
- this.addByPayloadRootHex(data.rootHex, data.peer, data.slot);
219
- this.triggerUnknownBlockSearch();
220
- this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT});
221
- this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
240
+ const isNewRoot = this.addByPayloadRootHex(data.rootHex, data.peer, this.resolvePayloadSlot(data.rootHex));
241
+ if (isNewRoot) {
242
+ this.triggerUnknownBlockSearch();
243
+ this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT});
244
+ this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
245
+ }
222
246
  } catch (e) {
223
247
  this.logger.debug("Error handling unknownEnvelopeBlockRoot event", {}, e as Error);
224
248
  }
@@ -252,13 +276,13 @@ export class BlockInputSync {
252
276
  parentRoot: missingDependency.parentRootHex,
253
277
  parentBlockHash: missingDependency.parentBlockHashHex,
254
278
  });
255
- this.removeAndDownScoreAllDescendants(pendingBlock);
279
+ this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
256
280
  }
257
281
  return;
258
282
  }
259
283
 
260
284
  if (missingDependency.kind === "parentPayload") {
261
- this.addByPayloadRootHex(missingDependency.rootHex, data.peer);
285
+ this.addByPayloadRootHex(missingDependency.rootHex, data.peer, missingDependency.slot);
262
286
  } else if (missingDependency.kind === "parentBlock") {
263
287
  this.addByRootHex(missingDependency.rootHex, data.peer);
264
288
  }
@@ -284,6 +308,80 @@ export class BlockInputSync {
284
308
  this.triggerUnknownBlockSearch();
285
309
  };
286
310
 
311
+ /**
312
+ * Drop pending payloads and blocks that fell below the finalized slot.
313
+ * See https://github.com/ChainSafe/lodestar/issues/9800
314
+ */
315
+ private pruneFinalized = (checkpoint: CheckpointWithHex): void => {
316
+ // Use the actual finalized block slot, not computeStartSlotAtEpoch(checkpoint.epoch)
317
+ // because the payload at the finalized block is not finalized
318
+ const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
319
+ const nowSec = Date.now() / 1000;
320
+ const maxSecInCache = (PRUNE_UNRESOLVED_SLOT_EPOCHS * SLOTS_PER_EPOCH * this.config.SLOT_DURATION_MS) / 1000;
321
+
322
+ let belowFinalizedPayloads = 0;
323
+ let agedOutPayloads = 0;
324
+ let unresolvedSlotPayloads = 0;
325
+
326
+ for (const [rootHex, payload] of this.pendingPayloads) {
327
+ // Last-effort resolve for a slot-less PendingPayloadRootHex, and store it back for later passes.
328
+ if (!isPendingPayloadInput(payload) && !isPendingPayloadEnvelope(payload) && payload.slot === undefined) {
329
+ payload.slot = this.resolvePayloadSlot(rootHex);
330
+ if (payload.slot === undefined) {
331
+ // Still unresolvable: age it out so the MAX_PENDING_BLOCKS cap cannot stay saturated.
332
+ if (nowSec - payload.timeAddedSec > maxSecInCache) {
333
+ this.pendingPayloads.delete(rootHex);
334
+ agedOutPayloads++;
335
+ } else {
336
+ unresolvedSlotPayloads++;
337
+ this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
338
+ root: rootHex,
339
+ finalizedSlot,
340
+ });
341
+ }
342
+ continue;
343
+ }
344
+ }
345
+
346
+ const slot = getPayloadSyncCacheItemSlot(payload);
347
+ if (typeof slot === "number" && slot < finalizedSlot) {
348
+ this.pendingPayloads.delete(rootHex);
349
+ belowFinalizedPayloads++;
350
+ }
351
+ }
352
+
353
+ let deletedBlocks = 0;
354
+ for (const [rootHex, block] of this.pendingBlocks) {
355
+ const slot = getBlockInputSyncCacheItemSlot(block);
356
+ if (typeof slot === "number" && slot < finalizedSlot) {
357
+ this.pendingBlocks.delete(rootHex);
358
+ deletedBlocks++;
359
+ }
360
+ }
361
+
362
+ if (belowFinalizedPayloads > 0) {
363
+ this.metrics?.blockInputSync.removedPayloads.inc(
364
+ {reason: DroppedItemReason.belowFinalized},
365
+ belowFinalizedPayloads
366
+ );
367
+ }
368
+ if (agedOutPayloads > 0) {
369
+ this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.agedOut}, agedOutPayloads);
370
+ }
371
+ if (deletedBlocks > 0) {
372
+ this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.belowFinalized}, deletedBlocks);
373
+ }
374
+
375
+ this.logger.debug("BlockInputSync.pruneFinalized dropped pre-finalized pending items", {
376
+ finalizedSlot,
377
+ finalizedRoot: checkpoint.rootHex,
378
+ belowFinalizedPayloads,
379
+ agedOutPayloads,
380
+ deletedBlocks,
381
+ unresolvedSlotPayloads,
382
+ });
383
+ };
384
+
287
385
  private addByRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr): boolean => {
288
386
  let pendingBlock = this.pendingBlocks.get(rootHex);
289
387
  let added = false;
@@ -303,7 +401,9 @@ export class BlockInputSync {
303
401
  });
304
402
  }
305
403
 
306
- if (peerIdStr) {
404
+ // bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
405
+ // Once at the cap, ignore further peers rather than evicting already-tracked ones.
406
+ if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
307
407
  pendingBlock.peerIdStrings.add(peerIdStr);
308
408
  }
309
409
 
@@ -311,6 +411,7 @@ export class BlockInputSync {
311
411
  // Limit pending blocks to prevent DOS attacks that cause OOM
312
412
  const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
313
413
  if (prunedItemCount > 0) {
414
+ this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
314
415
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
315
416
  }
316
417
  return added;
@@ -337,7 +438,9 @@ export class BlockInputSync {
337
438
  });
338
439
  }
339
440
 
340
- if (peerIdStr) {
441
+ // bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
442
+ // Once at the cap, ignore further peers rather than evicting already-tracked ones.
443
+ if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
341
444
  pendingBlock.peerIdStrings.add(peerIdStr);
342
445
  }
343
446
 
@@ -345,6 +448,7 @@ export class BlockInputSync {
345
448
  // Limit pending blocks to prevent DOS attacks that cause OOM
346
449
  const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
347
450
  if (prunedItemCount > 0) {
451
+ this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
348
452
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
349
453
  }
350
454
  };
@@ -369,19 +473,50 @@ export class BlockInputSync {
369
473
  root: rootHex,
370
474
  peerIdStr: peerIdStr ?? "unknown peer",
371
475
  });
476
+ } else if (
477
+ // update a previously slot-less entry once a slot becomes available
478
+ slot !== undefined &&
479
+ !isPendingPayloadInput(pendingPayload) &&
480
+ !isPendingPayloadEnvelope(pendingPayload) &&
481
+ pendingPayload.slot === undefined
482
+ ) {
483
+ pendingPayload.slot = slot;
372
484
  }
373
-
374
- if (peerIdStr) {
485
+ if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
375
486
  pendingPayload.peerIdStrings.add(peerIdStr);
376
487
  }
377
488
 
378
489
  const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
379
490
  if (prunedItemCount > 0) {
491
+ this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
380
492
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
381
493
  }
382
494
  return added;
383
495
  };
384
496
 
497
+ /**
498
+ * Resolve the block slot for a payload root.
499
+ */
500
+ private resolvePayloadSlot(rootHex: RootHex): Slot | undefined {
501
+ // a pending block of the same root
502
+ const pendingBlock = this.pendingBlocks.get(rootHex);
503
+ if (pendingBlock) {
504
+ const pendingBlockSlot = getBlockInputSyncCacheItemSlot(pendingBlock);
505
+ if (typeof pendingBlockSlot === "number") {
506
+ return pendingBlockSlot;
507
+ }
508
+ }
509
+
510
+ // the block is in fork choice
511
+ const forkChoiceSlot = this.chain.forkChoice.getBlockHexDefaultStatus(rootHex)?.slot;
512
+ if (forkChoiceSlot !== undefined) {
513
+ return forkChoiceSlot;
514
+ }
515
+
516
+ // a seen payload envelope input
517
+ return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
518
+ }
519
+
385
520
  private addByPayloadInput = (
386
521
  payloadInput: PayloadEnvelopeInput,
387
522
  peerIdStr?: PeerIdStr,
@@ -393,7 +528,7 @@ export class BlockInputSync {
393
528
  envelope
394
529
  );
395
530
 
396
- if (peerIdStr) {
531
+ if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
397
532
  pendingPayload.peerIdStrings.add(peerIdStr);
398
533
  }
399
534
 
@@ -407,6 +542,7 @@ export class BlockInputSync {
407
542
 
408
543
  const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
409
544
  if (prunedItemCount > 0) {
545
+ this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
410
546
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
411
547
  }
412
548
  };
@@ -436,10 +572,12 @@ export class BlockInputSync {
436
572
  blockInput: IBlockInput
437
573
  ):
438
574
  | {kind: "ready"}
439
- | {kind: "block" | "parentBlock" | "parentPayload"; rootHex: RootHex}
575
+ | {kind: "block" | "parentBlock"; rootHex: RootHex}
576
+ | {kind: "parentPayload"; rootHex: RootHex; slot: Slot}
440
577
  | {kind: "invalidParentPayload"; parentRootHex: RootHex; parentBlockHashHex: RootHex} {
441
578
  const parentRootHex = blockInput.parentRootHex;
442
- if (!this.chain.forkChoice.hasBlockHex(parentRootHex)) {
579
+ const parentBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRootHex);
580
+ if (!parentBlock) {
443
581
  return {kind: "parentBlock", rootHex: parentRootHex};
444
582
  }
445
583
 
@@ -461,16 +599,8 @@ export class BlockInputSync {
461
599
  return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
462
600
  }
463
601
 
464
- const parentPayloadInput = this.chain.seenPayloadEnvelopeInputCache.get(parentRootHex);
465
- if (parentPayloadInput) {
466
- if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
467
- return {kind: "parentPayload", rootHex: parentRootHex};
468
- }
469
-
470
- return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
471
- }
472
-
473
- return {kind: "parentPayload", rootHex: parentRootHex};
602
+ // Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
603
+ return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
474
604
  }
475
605
 
476
606
  private advancePendingBlock(pendingBlock: PendingBlockInput): AdvancePendingBlockResult {
@@ -493,9 +623,9 @@ export class BlockInputSync {
493
623
  }
494
624
 
495
625
  case "parentPayload": {
496
- let added = this.addByPayloadRootHex(missingDependency.rootHex);
626
+ let added = this.addByPayloadRootHex(missingDependency.rootHex, undefined, missingDependency.slot);
497
627
  for (const peerIdStr of pendingBlock.peerIdStrings) {
498
- added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr) || added;
628
+ added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr, missingDependency.slot) || added;
499
629
  }
500
630
  return added ? "queued_parent_payload" : "blocked";
501
631
  }
@@ -507,7 +637,7 @@ export class BlockInputSync {
507
637
  parentRoot: missingDependency.parentRootHex,
508
638
  parentBlockHash: missingDependency.parentBlockHashHex,
509
639
  });
510
- this.removeAndDownScoreAllDescendants(pendingBlock);
640
+ this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
511
641
  return "removed";
512
642
  }
513
643
  }
@@ -730,7 +860,7 @@ export class BlockInputSync {
730
860
  ...logCtx2,
731
861
  finalizedSlot,
732
862
  });
733
- this.removeAndDownScoreAllDescendants(block);
863
+ this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
734
864
  } else {
735
865
  this.onUnknownBlockRoot({rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot});
736
866
  }
@@ -747,7 +877,7 @@ export class BlockInputSync {
747
877
 
748
878
  this.metrics?.blockInputSync.downloadedBlocksError.inc();
749
879
  this.logger.debug("Ignoring unknown block root after many failed downloads", logCtx, res.err);
750
- this.removeAndDownScoreAllDescendants(block);
880
+ this.removeAndDownScoreAllDescendants(block, DroppedItemReason.unavailable);
751
881
  }
752
882
  }
753
883
 
@@ -764,9 +894,10 @@ export class BlockInputSync {
764
894
  // this prevents unbundling attack
765
895
  // see https://lighthouse-blog.sigmaprime.io/mev-unbundling-rpc.html
766
896
  const {slot: blockSlot, proposerIndex} = pendingBlock.blockInput.getBlock().message;
897
+ const blockRootHex = pendingBlock.blockInput.blockRootHex;
767
898
  const logCtx = {
768
899
  slot: blockSlot,
769
- root: pendingBlock.blockInput.blockRootHex,
900
+ root: blockRootHex,
770
901
  delaySec: this.chain.clock.secFromSlot(blockSlot),
771
902
  };
772
903
  this.logger.verbose("Processing downloaded block", logCtx);
@@ -780,7 +911,7 @@ export class BlockInputSync {
780
911
  // eligible for proposer boost to prevent unbundling attack
781
912
  this.logger.verbose("Avoid proposer boost for this block of known proposer", {
782
913
  slot: blockSlot,
783
- root: pendingBlock.blockInput.blockRootHex,
914
+ root: blockRootHex,
784
915
  proposerIndex,
785
916
  });
786
917
  await sleep(proposerBoostWindowMs);
@@ -807,12 +938,24 @@ export class BlockInputSync {
807
938
  if (!res.err) {
808
939
  this.logger.verbose("Processed block from unknown sync", logCtx);
809
940
  // no need to update status to "processed", delete anyway
810
- this.pendingBlocks.delete(pendingBlock.blockInput.blockRootHex);
941
+ this.pendingBlocks.delete(blockRootHex);
942
+
943
+ if (isForkPostGloas(fork)) {
944
+ const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
945
+ if (!payloadInput) {
946
+ this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
947
+ } else {
948
+ // Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
949
+ // The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
950
+ this.chain.getBlobsTracker.triggerGetBlobs(payloadInput);
951
+ }
952
+ }
953
+
811
954
  // Re-enter the scheduler so descendants blocked on either parent blocks or parent payloads
812
955
  // are advanced through the same dependency checks as every other pending item.
813
956
  this.triggerUnknownBlockSearch();
814
957
  } else {
815
- const errorData = {slot: pendingBlock.blockInput.slot, root: pendingBlock.blockInput.blockRootHex};
958
+ const errorData = {slot: pendingBlock.blockInput.slot, root: blockRootHex};
816
959
  if (res.err instanceof BlockError) {
817
960
  switch (res.err.type.code) {
818
961
  // This cases are already handled with `{ignoreIfKnown: true}`
@@ -842,19 +985,19 @@ export class BlockInputSync {
842
985
  case BlockErrorCode.EXECUTION_ENGINE_ERROR:
843
986
  // Removing the block(s) without penalizing the peers, hoping for EL to
844
987
  // recover on a latter download + verify attempt.
845
- this.removeAllDescendants(pendingBlock);
988
+ this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
846
989
  break;
847
990
 
848
991
  case BlockErrorCode.EXECUTION_ENGINE_INVALID:
849
992
  // the peer served a bad block
850
993
  this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
851
- this.removeAndDownScoreAllDescendants(pendingBlock);
994
+ this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
852
995
  break;
853
996
 
854
997
  default:
855
998
  // Block is not correct with respect to our chain. Log error loudly
856
999
  this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
857
- this.removeAndDownScoreAllDescendants(pendingBlock);
1000
+ this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
858
1001
  }
859
1002
  }
860
1003
 
@@ -873,12 +1016,12 @@ export class BlockInputSync {
873
1016
  */
874
1017
  private async reconcilePayloadEnvelope(pendingPayload: PendingPayloadEnvelope): Promise<void> {
875
1018
  const rootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
1019
+
876
1020
  if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
877
1021
  this.pendingPayloads.delete(rootHex);
878
1022
  return;
879
1023
  }
880
1024
 
881
- const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
882
1025
  if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
883
1026
  // Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
884
1027
  // non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
@@ -894,8 +1037,9 @@ export class BlockInputSync {
894
1037
  return;
895
1038
  }
896
1039
 
1040
+ const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
897
1041
  if (!payloadInput) {
898
- this.logger.debug("Missing PayloadEnvelopeInput for known block while reconciling payload envelope", {
1042
+ this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
899
1043
  root: rootHex,
900
1044
  });
901
1045
  return;
@@ -915,6 +1059,7 @@ export class BlockInputSync {
915
1059
  const pendingPayloadByRoot: PendingPayloadRootHex = {
916
1060
  status: PendingPayloadInputStatus.pending,
917
1061
  rootHex,
1062
+ slot: pendingPayload.envelope.message.payload.slotNumber,
918
1063
  timeAddedSec: pendingPayload.timeAddedSec,
919
1064
  peerIdStrings: new Set(pendingPayload.peerIdStrings),
920
1065
  };
@@ -954,6 +1099,10 @@ export class BlockInputSync {
954
1099
  return;
955
1100
  }
956
1101
 
1102
+ if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
1103
+ payload.slot = this.resolvePayloadSlot(rootHex);
1104
+ }
1105
+
957
1106
  const payloadSlot = getPayloadSyncCacheItemSlot(payload);
958
1107
  const logCtx = {
959
1108
  slot: payloadSlot,
@@ -969,7 +1118,13 @@ export class BlockInputSync {
969
1118
  const res = await wrapError(this.fetchPayloadInput(payload));
970
1119
  if (!res.err) {
971
1120
  const pendingPayload = res.result;
972
- this.pendingPayloads.set(getPayloadSyncCacheItemRootHex(pendingPayload), pendingPayload);
1121
+ const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
1122
+ // finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
1123
+ if (!this.pendingPayloads.has(resultRootHex)) {
1124
+ this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
1125
+ return;
1126
+ }
1127
+ this.pendingPayloads.set(resultRootHex, pendingPayload);
973
1128
 
974
1129
  if (isPendingPayloadEnvelope(pendingPayload)) {
975
1130
  await this.reconcilePayloadEnvelope(pendingPayload);
@@ -1046,17 +1201,27 @@ export class BlockInputSync {
1046
1201
  break;
1047
1202
 
1048
1203
  case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
1204
+ this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1205
+ this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
1206
+ break;
1207
+
1049
1208
  case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
1209
+ this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1210
+ this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
1211
+ break;
1212
+
1050
1213
  case PayloadErrorCode.INVALID_SIGNATURE:
1051
1214
  // TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
1052
1215
  // reintroducing envelope replacement / recreation flows.
1053
1216
  this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1054
- this.removePendingPayloadAndDescendants(rootHex);
1217
+ this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
1055
1218
  break;
1056
1219
 
1057
1220
  default:
1058
1221
  this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1059
- this.pendingPayloads.delete(rootHex);
1222
+ if (this.pendingPayloads.delete(rootHex)) {
1223
+ this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.unknown}, 1);
1224
+ }
1060
1225
  }
1061
1226
  return;
1062
1227
  }
@@ -1080,7 +1245,7 @@ export class BlockInputSync {
1080
1245
  let slot = getPayloadSyncCacheItemSlot(cacheItem);
1081
1246
  let payloadInput = isPendingPayloadInput(cacheItem)
1082
1247
  ? cacheItem.payloadInput
1083
- : this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
1248
+ : await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
1084
1249
  let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
1085
1250
 
1086
1251
  let i = 0;
@@ -1089,7 +1254,12 @@ export class BlockInputSync {
1089
1254
  const pendingColumns = payloadInput?.hasAllData()
1090
1255
  ? new Set<number>()
1091
1256
  : new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
1092
- const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers);
1257
+ // prefer peers that gossiped this payload root to us (#9923)
1258
+ const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
1259
+ pendingColumns,
1260
+ excludedPeers,
1261
+ cacheItem.peerIdStrings
1262
+ );
1093
1263
  if (peerMeta === null) {
1094
1264
  if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
1095
1265
  throw new UnknownBlockRateLimitedError(
@@ -1097,9 +1267,11 @@ export class BlockInputSync {
1097
1267
  );
1098
1268
  }
1099
1269
 
1100
- throw Error(
1101
- `Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
1102
- );
1270
+ const reason =
1271
+ pendingColumns.size > 0
1272
+ ? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
1273
+ : "no peer available to download pending payload";
1274
+ throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
1103
1275
  }
1104
1276
 
1105
1277
  const {peerId, client: peerClient} = peerMeta;
@@ -1111,7 +1283,7 @@ export class BlockInputSync {
1111
1283
  slot = envelope.message.payload.slotNumber;
1112
1284
  }
1113
1285
 
1114
- payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
1286
+ payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
1115
1287
  if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
1116
1288
  // Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
1117
1289
  // envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
@@ -1296,7 +1468,12 @@ export class BlockInputSync {
1296
1468
  isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
1297
1469
  ? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
1298
1470
  : defaultPendingColumns;
1299
- const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers);
1471
+ // prefer peers that gossiped this block root to us (#9923)
1472
+ const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
1473
+ pendingColumns,
1474
+ excludedPeers,
1475
+ cacheItem.peerIdStrings
1476
+ );
1300
1477
  if (peerMeta === null) {
1301
1478
  if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
1302
1479
  throw new UnknownBlockRateLimitedError(
@@ -1444,9 +1621,9 @@ export class BlockInputSync {
1444
1621
  * Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
1445
1622
  * referenced more than one bad block.
1446
1623
  */
1447
- private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem): void {
1624
+ private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
1448
1625
  // Get all blocks that are a descendant of this one
1449
- const badPendingBlocks = this.removeAllDescendants(block);
1626
+ const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
1450
1627
  // just console log and do not penalize on pending/bad blocks for debugging
1451
1628
  // console.log("removeAndDownscoreAllDescendants", {block});
1452
1629
 
@@ -1473,18 +1650,22 @@ export class BlockInputSync {
1473
1650
  }
1474
1651
 
1475
1652
  // Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
1476
- private removePendingPayloadAndDescendants(rootHex: RootHex): void {
1653
+ private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
1477
1654
  // Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
1478
1655
  // later validation/finalization logic decides when it can leave memory.
1479
- this.pendingPayloads.delete(rootHex);
1656
+ if (this.pendingPayloads.delete(rootHex)) {
1657
+ this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
1658
+ }
1480
1659
 
1481
1660
  const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
1482
- this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
1483
-
1484
1661
  for (const block of badPendingBlocks) {
1485
1662
  const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
1486
- this.pendingBlocks.delete(descendantRootHex);
1487
- this.pendingPayloads.delete(descendantRootHex);
1663
+ if (this.pendingBlocks.delete(descendantRootHex)) {
1664
+ this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.invalidParent}, 1);
1665
+ }
1666
+ if (this.pendingPayloads.delete(descendantRootHex)) {
1667
+ this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.invalidParent}, 1);
1668
+ }
1488
1669
  this.chain.seenBlockInputCache.prune(descendantRootHex);
1489
1670
  this.logger.debug("Removing pending descendant after invalid parent payload", {
1490
1671
  slot: getBlockInputSyncCacheItemSlot(block),
@@ -1494,18 +1675,25 @@ export class BlockInputSync {
1494
1675
  }
1495
1676
  }
1496
1677
 
1497
- private removeAllDescendants(block: BlockInputSyncCacheItem): BlockInputSyncCacheItem[] {
1678
+ private removeAllDescendants(
1679
+ block: BlockInputSyncCacheItem,
1680
+ headReason: DroppedItemReason,
1681
+ descendantReason: DroppedItemReason = DroppedItemReason.invalidParent
1682
+ ): BlockInputSyncCacheItem[] {
1498
1683
  const rootHex = getBlockInputSyncCacheItemRootHex(block);
1499
1684
  const slot = getBlockInputSyncCacheItemSlot(block);
1500
- // Get all blocks that are a descendant of this one
1685
+ // Get all blocks that are a descendant of this one (index 0 is the head item itself)
1501
1686
  const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
1502
1687
 
1503
- this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
1504
-
1505
- for (const block of badPendingBlocks) {
1688
+ for (const [i, block] of badPendingBlocks.entries()) {
1506
1689
  const rootHex = getBlockInputSyncCacheItemRootHex(block);
1507
- this.pendingBlocks.delete(rootHex);
1508
- this.pendingPayloads.delete(rootHex);
1690
+ const reason = i === 0 ? headReason : descendantReason;
1691
+ if (this.pendingBlocks.delete(rootHex)) {
1692
+ this.metrics?.blockInputSync.removedBlocks.inc({reason}, 1);
1693
+ }
1694
+ if (this.pendingPayloads.delete(rootHex)) {
1695
+ this.metrics?.blockInputSync.removedPayloads.inc({reason}, 1);
1696
+ }
1509
1697
  this.chain.seenBlockInputCache.prune(rootHex);
1510
1698
  // Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
1511
1699
  // importBlock()-owned lifecycle.
@@ -1561,6 +1749,13 @@ export class UnknownBlockPeerBalancer {
1561
1749
  this.rateLimitedUntilByPeer.delete(peerId);
1562
1750
  }
1563
1751
 
1752
+ /** Reset all tracked peer state, e.g. when BlockInputSync unsubscribes from the network. */
1753
+ clear(): void {
1754
+ this.peersMeta.clear();
1755
+ this.activeRequests.clear();
1756
+ this.rateLimitedUntilByPeer.clear();
1757
+ }
1758
+
1564
1759
  onRateLimited(peerId: PeerIdStr, rateLimitedUntilMs: number): void {
1565
1760
  this.rateLimitedUntilByPeer.set(peerId, rateLimitedUntilMs);
1566
1761
  }
@@ -1596,11 +1791,17 @@ export class UnknownBlockPeerBalancer {
1596
1791
  }
1597
1792
 
1598
1793
  /**
1599
- * called from fetchBlockInput() where we only have block root and nothing else
1794
+ * called from fetchBlockInput()/fetchPayloadInput() with a block/payload root.
1600
1795
  * excludedPeers are the peers that we requested already so we don't want to try again
1601
1796
  * pendingColumns is empty for prefulu, or the 1st time we we download a block by root
1797
+ * preferredPeers are the peers NetworkProcessor told us gossiped this root (from
1798
+ * pendingBlock/cacheItem.peerIdStrings). They are preferred but not required.
1602
1799
  */
1603
- bestPeerForPendingColumns(pendingColumns: Set<number>, excludedPeers: Set<PeerIdStr>): PeerSyncMeta | null {
1800
+ bestPeerForPendingColumns(
1801
+ pendingColumns: Set<number>,
1802
+ excludedPeers: Set<PeerIdStr>,
1803
+ preferredPeers?: Set<PeerIdStr>
1804
+ ): PeerSyncMeta | null {
1604
1805
  const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
1605
1806
  if (eligiblePeers.length === 0) {
1606
1807
  return null;
@@ -1608,7 +1809,9 @@ export class UnknownBlockPeerBalancer {
1608
1809
 
1609
1810
  const sortedEligiblePeers = sortBy(
1610
1811
  shuffle(eligiblePeers),
1611
- // prefer peers with least active req
1812
+ // 1st key: prefer peers we heard about this root from
1813
+ (peerId) => (preferredPeers?.has(peerId) ? 0 : 1),
1814
+ // 2nd key (tiebreak): prefer peers with least active req
1612
1815
  (peerId) => this.activeRequests.get(peerId) ?? 0
1613
1816
  );
1614
1817