@lodestar/beacon-node 1.47.0-dev.ec596194e2 → 1.47.0-dev.f0f26cd2b6

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 (357) 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.js +1 -1
  15. package/lib/api/impl/lodestar/index.js.map +1 -1
  16. package/lib/api/impl/validator/index.d.ts.map +1 -1
  17. package/lib/api/impl/validator/index.js +232 -49
  18. package/lib/api/impl/validator/index.js.map +1 -1
  19. package/lib/api/impl/validator/utils.js +1 -1
  20. package/lib/api/impl/validator/utils.js.map +1 -1
  21. package/lib/api/rest/base.d.ts.map +1 -1
  22. package/lib/api/rest/base.js +7 -0
  23. package/lib/api/rest/base.js.map +1 -1
  24. package/lib/chain/archiveStore/archiveStore.js +1 -1
  25. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  26. package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  27. package/lib/chain/archiveStore/historicalState/metrics.js +21 -0
  28. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  29. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  30. package/lib/chain/blocks/importBlock.js +2 -7
  31. package/lib/chain/blocks/importBlock.js.map +1 -1
  32. package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
  33. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  34. package/lib/chain/blocks/importExecutionPayload.js +11 -9
  35. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  36. package/lib/chain/blocks/index.d.ts.map +1 -1
  37. package/lib/chain/blocks/index.js +21 -13
  38. package/lib/chain/blocks/index.js.map +1 -1
  39. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
  40. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  41. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
  42. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  43. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
  44. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  45. package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
  46. package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
  47. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  48. package/lib/chain/blocks/verifyBlock.js +5 -2
  49. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  50. package/lib/chain/blocks/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/chain.d.ts +9 -4
  88. package/lib/chain/chain.d.ts.map +1 -1
  89. package/lib/chain/chain.js +22 -8
  90. package/lib/chain/chain.js.map +1 -1
  91. package/lib/chain/errors/blockError.d.ts +1 -0
  92. package/lib/chain/errors/blockError.d.ts.map +1 -1
  93. package/lib/chain/errors/blockError.js +6 -1
  94. package/lib/chain/errors/blockError.js.map +1 -1
  95. package/lib/chain/errors/proposerPreferences.d.ts +6 -0
  96. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
  97. package/lib/chain/errors/proposerPreferences.js +1 -0
  98. package/lib/chain/errors/proposerPreferences.js.map +1 -1
  99. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  100. package/lib/chain/forkChoice/index.js +4 -0
  101. package/lib/chain/forkChoice/index.js.map +1 -1
  102. package/lib/chain/interface.d.ts +8 -4
  103. package/lib/chain/interface.d.ts.map +1 -1
  104. package/lib/chain/interface.js.map +1 -1
  105. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  106. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  107. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  108. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  109. package/lib/chain/opPools/attestationPool.js +1 -1
  110. package/lib/chain/opPools/attestationPool.js.map +1 -1
  111. package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts +16 -0
  112. package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts.map +1 -0
  113. package/lib/chain/opPools/deferredVoluntaryExitPool.js +57 -0
  114. package/lib/chain/opPools/deferredVoluntaryExitPool.js.map +1 -0
  115. package/lib/chain/opPools/executionPayloadBidPool.d.ts +7 -2
  116. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
  117. package/lib/chain/opPools/executionPayloadBidPool.js +4 -4
  118. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
  119. package/lib/chain/opPools/index.d.ts +2 -1
  120. package/lib/chain/opPools/index.d.ts.map +1 -1
  121. package/lib/chain/opPools/index.js +1 -0
  122. package/lib/chain/opPools/index.js.map +1 -1
  123. package/lib/chain/opPools/payloadAttestationPool.js +3 -3
  124. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  125. package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
  126. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  127. package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
  128. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  129. package/lib/chain/opPools/utils.d.ts +1 -1
  130. package/lib/chain/opPools/utils.d.ts.map +1 -1
  131. package/lib/chain/opPools/utils.js +1 -1
  132. package/lib/chain/opPools/utils.js.map +1 -1
  133. package/lib/chain/prepareNextSlot.d.ts +5 -0
  134. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  135. package/lib/chain/prepareNextSlot.js +47 -6
  136. package/lib/chain/prepareNextSlot.js.map +1 -1
  137. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  138. package/lib/chain/produceBlock/computeNewStateRoot.js +3 -25
  139. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  140. package/lib/chain/produceBlock/produceBlockBody.js +1 -1
  141. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  142. package/lib/chain/regen/interface.d.ts +5 -2
  143. package/lib/chain/regen/interface.d.ts.map +1 -1
  144. package/lib/chain/regen/interface.js +3 -0
  145. package/lib/chain/regen/interface.js.map +1 -1
  146. package/lib/chain/regen/queued.d.ts +2 -2
  147. package/lib/chain/regen/queued.d.ts.map +1 -1
  148. package/lib/chain/regen/queued.js +5 -5
  149. package/lib/chain/regen/queued.js.map +1 -1
  150. package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
  151. package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
  152. package/lib/chain/seenCache/seenAttesters.js +25 -4
  153. package/lib/chain/seenCache/seenAttesters.js.map +1 -1
  154. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
  155. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  156. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
  157. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  158. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +5 -1
  159. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  160. package/lib/chain/stateCache/fifoBlockStateCache.js +6 -0
  161. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  162. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +47 -5
  163. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  164. package/lib/chain/stateCache/persistentCheckpointsCache.js +117 -7
  165. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  166. package/lib/chain/stateCache/types.d.ts +3 -3
  167. package/lib/chain/stateCache/types.d.ts.map +1 -1
  168. package/lib/chain/validation/attestation.d.ts.map +1 -1
  169. package/lib/chain/validation/attestation.js +1 -4
  170. package/lib/chain/validation/attestation.js.map +1 -1
  171. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  172. package/lib/chain/validation/attesterSlashing.js +1 -1
  173. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  174. package/lib/chain/validation/block.d.ts.map +1 -1
  175. package/lib/chain/validation/block.js +40 -10
  176. package/lib/chain/validation/block.js.map +1 -1
  177. package/lib/chain/validation/executionPayloadBid.d.ts +13 -3
  178. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  179. package/lib/chain/validation/executionPayloadBid.js +147 -12
  180. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  181. package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
  182. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  183. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  184. package/lib/chain/validation/payloadAttestationMessage.js +5 -14
  185. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  186. package/lib/chain/validation/proposerPreferences.d.ts +1 -1
  187. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  188. package/lib/chain/validation/proposerPreferences.js +13 -4
  189. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  190. package/lib/chain/validation/proposerSlashing.js +1 -1
  191. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  192. package/lib/chain/validation/voluntaryExit.d.ts +9 -1
  193. package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
  194. package/lib/chain/validation/voluntaryExit.js +46 -1
  195. package/lib/chain/validation/voluntaryExit.js.map +1 -1
  196. package/lib/execution/builder/apiClient.d.ts +70 -0
  197. package/lib/execution/builder/apiClient.d.ts.map +1 -0
  198. package/lib/execution/builder/apiClient.js +202 -0
  199. package/lib/execution/builder/apiClient.js.map +1 -0
  200. package/lib/execution/builder/validateBid.d.ts +22 -0
  201. package/lib/execution/builder/validateBid.d.ts.map +1 -0
  202. package/lib/execution/builder/validateBid.js +111 -0
  203. package/lib/execution/builder/validateBid.js.map +1 -0
  204. package/lib/metrics/metrics/lodestar.d.ts +94 -23
  205. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  206. package/lib/metrics/metrics/lodestar.js +177 -55
  207. package/lib/metrics/metrics/lodestar.js.map +1 -1
  208. package/lib/network/gossip/topic.d.ts +13 -0
  209. package/lib/network/gossip/topic.d.ts.map +1 -1
  210. package/lib/network/gossip/topic.js +37 -0
  211. package/lib/network/gossip/topic.js.map +1 -1
  212. package/lib/network/libp2p/index.d.ts.map +1 -1
  213. package/lib/network/libp2p/index.js +10 -2
  214. package/lib/network/libp2p/index.js.map +1 -1
  215. package/lib/network/network.d.ts.map +1 -1
  216. package/lib/network/network.js +23 -43
  217. package/lib/network/network.js.map +1 -1
  218. package/lib/network/processor/constants.d.ts +7 -0
  219. package/lib/network/processor/constants.d.ts.map +1 -0
  220. package/lib/network/processor/constants.js +7 -0
  221. package/lib/network/processor/constants.js.map +1 -0
  222. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  223. package/lib/network/processor/gossipHandlers.js +13 -4
  224. package/lib/network/processor/gossipHandlers.js.map +1 -1
  225. package/lib/network/processor/gossipQueues/indexed.d.ts.map +1 -1
  226. package/lib/network/processor/gossipQueues/indexed.js +0 -1
  227. package/lib/network/processor/gossipQueues/indexed.js.map +1 -1
  228. package/lib/network/processor/index.d.ts +3 -0
  229. package/lib/network/processor/index.d.ts.map +1 -1
  230. package/lib/network/processor/index.js +52 -23
  231. package/lib/network/processor/index.js.map +1 -1
  232. package/lib/network/reqresp/score.d.ts.map +1 -1
  233. package/lib/network/reqresp/score.js +5 -11
  234. package/lib/network/reqresp/score.js.map +1 -1
  235. package/lib/network/util.d.ts +0 -1
  236. package/lib/network/util.d.ts.map +1 -1
  237. package/lib/network/util.js +0 -4
  238. package/lib/network/util.js.map +1 -1
  239. package/lib/node/deferredVoluntaryExitPublisher.d.ts +15 -0
  240. package/lib/node/deferredVoluntaryExitPublisher.d.ts.map +1 -0
  241. package/lib/node/deferredVoluntaryExitPublisher.js +38 -0
  242. package/lib/node/deferredVoluntaryExitPublisher.js.map +1 -0
  243. package/lib/node/nodejs.d.ts +2 -1
  244. package/lib/node/nodejs.d.ts.map +1 -1
  245. package/lib/node/nodejs.js +7 -0
  246. package/lib/node/nodejs.js.map +1 -1
  247. package/lib/sync/range/batch.d.ts +23 -5
  248. package/lib/sync/range/batch.d.ts.map +1 -1
  249. package/lib/sync/range/batch.js +61 -22
  250. package/lib/sync/range/batch.js.map +1 -1
  251. package/lib/sync/range/chain.d.ts +2 -1
  252. package/lib/sync/range/chain.d.ts.map +1 -1
  253. package/lib/sync/range/chain.js +32 -12
  254. package/lib/sync/range/chain.js.map +1 -1
  255. package/lib/sync/range/range.js +1 -1
  256. package/lib/sync/range/range.js.map +1 -1
  257. package/lib/sync/types.d.ts +32 -0
  258. package/lib/sync/types.d.ts.map +1 -1
  259. package/lib/sync/types.js +33 -0
  260. package/lib/sync/types.js.map +1 -1
  261. package/lib/sync/unknownBlock.d.ts +15 -2
  262. package/lib/sync/unknownBlock.d.ts.map +1 -1
  263. package/lib/sync/unknownBlock.js +220 -61
  264. package/lib/sync/unknownBlock.js.map +1 -1
  265. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  266. package/lib/sync/utils/downloadByRange.js +16 -11
  267. package/lib/sync/utils/downloadByRange.js.map +1 -1
  268. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  269. package/lib/sync/utils/downloadByRoot.js +3 -1
  270. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  271. package/package.json +35 -35
  272. package/src/api/impl/beacon/blocks/index.ts +55 -21
  273. package/src/api/impl/beacon/pool/index.ts +27 -2
  274. package/src/api/impl/beacon/state/utils.ts +2 -1
  275. package/src/api/impl/events/index.ts +12 -4
  276. package/src/api/impl/lodestar/index.ts +1 -1
  277. package/src/api/impl/validator/index.ts +285 -49
  278. package/src/api/impl/validator/utils.ts +1 -1
  279. package/src/api/rest/base.ts +7 -0
  280. package/src/chain/archiveStore/archiveStore.ts +1 -1
  281. package/src/chain/archiveStore/historicalState/metrics.ts +22 -0
  282. package/src/chain/blocks/importBlock.ts +1 -6
  283. package/src/chain/blocks/importExecutionPayload.ts +12 -11
  284. package/src/chain/blocks/index.ts +25 -13
  285. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
  286. package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
  287. package/src/chain/blocks/verifyBlock.ts +5 -2
  288. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
  289. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
  290. package/src/chain/bls/index.ts +1 -1
  291. package/src/chain/bls/interface.ts +7 -3
  292. package/src/chain/bls/multithread/index.ts +148 -120
  293. package/src/chain/bls/multithread/jobItem.ts +12 -82
  294. package/src/chain/bls/multithread/types.ts +33 -8
  295. package/src/chain/bls/multithread/utils.ts +11 -0
  296. package/src/chain/bls/multithread/worker.ts +85 -27
  297. package/src/chain/bls/singleThread.ts +11 -55
  298. package/src/chain/bls/utils.ts +23 -24
  299. package/src/chain/chain.ts +25 -7
  300. package/src/chain/errors/blockError.ts +8 -2
  301. package/src/chain/errors/proposerPreferences.ts +7 -0
  302. package/src/chain/forkChoice/index.ts +4 -0
  303. package/src/chain/interface.ts +8 -3
  304. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  305. package/src/chain/opPools/attestationPool.ts +1 -1
  306. package/src/chain/opPools/deferredVoluntaryExitPool.ts +65 -0
  307. package/src/chain/opPools/executionPayloadBidPool.ts +13 -7
  308. package/src/chain/opPools/index.ts +2 -1
  309. package/src/chain/opPools/payloadAttestationPool.ts +3 -3
  310. package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
  311. package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
  312. package/src/chain/opPools/utils.ts +1 -1
  313. package/src/chain/prepareNextSlot.ts +49 -6
  314. package/src/chain/produceBlock/computeNewStateRoot.ts +3 -38
  315. package/src/chain/produceBlock/produceBlockBody.ts +1 -1
  316. package/src/chain/regen/interface.ts +5 -2
  317. package/src/chain/regen/queued.ts +5 -5
  318. package/src/chain/seenCache/seenAttesters.ts +31 -5
  319. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
  320. package/src/chain/stateCache/fifoBlockStateCache.ts +8 -1
  321. package/src/chain/stateCache/persistentCheckpointsCache.ts +133 -8
  322. package/src/chain/stateCache/types.ts +3 -3
  323. package/src/chain/validation/attestation.ts +1 -7
  324. package/src/chain/validation/attesterSlashing.ts +1 -8
  325. package/src/chain/validation/block.ts +43 -10
  326. package/src/chain/validation/executionPayloadBid.ts +168 -14
  327. package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
  328. package/src/chain/validation/payloadAttestationMessage.ts +6 -17
  329. package/src/chain/validation/proposerPreferences.ts +15 -5
  330. package/src/chain/validation/proposerSlashing.ts +1 -1
  331. package/src/chain/validation/voluntaryExit.ts +59 -2
  332. package/src/execution/builder/apiClient.ts +264 -0
  333. package/src/execution/builder/validateBid.ts +166 -0
  334. package/src/metrics/metrics/lodestar.ts +192 -61
  335. package/src/network/gossip/topic.ts +38 -0
  336. package/src/network/libp2p/index.ts +11 -2
  337. package/src/network/network.ts +29 -54
  338. package/src/network/processor/constants.ts +6 -0
  339. package/src/network/processor/gossipHandlers.ts +15 -2
  340. package/src/network/processor/gossipQueues/indexed.ts +0 -1
  341. package/src/network/processor/index.ts +67 -30
  342. package/src/network/reqresp/score.ts +5 -11
  343. package/src/network/util.ts +0 -5
  344. package/src/node/deferredVoluntaryExitPublisher.ts +54 -0
  345. package/src/node/nodejs.ts +10 -1
  346. package/src/sync/range/batch.ts +83 -28
  347. package/src/sync/range/chain.ts +35 -13
  348. package/src/sync/range/range.ts +1 -1
  349. package/src/sync/types.ts +36 -1
  350. package/src/sync/unknownBlock.ts +258 -68
  351. package/src/sync/utils/downloadByRange.ts +21 -12
  352. package/src/sync/utils/downloadByRoot.ts +3 -1
  353. package/lib/chain/bls/maybeBatch.d.ts +0 -12
  354. package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
  355. package/lib/chain/bls/maybeBatch.js +0 -35
  356. package/lib/chain/bls/maybeBatch.js.map +0 -1
  357. package/src/chain/bls/maybeBatch.ts +0 -45
@@ -49,7 +49,12 @@ import {INetworkCore, NetworkCore, WorkerNetworkCore} from "./core/index.js";
49
49
  import {INetworkEventBus, NetworkEvent, NetworkEventBus, NetworkEventData} from "./events.js";
50
50
  import {getActiveForkBoundaries} from "./forks.js";
51
51
  import {GossipHandlers, GossipTopicMap, GossipType, GossipTypeMap} from "./gossip/index.js";
52
- import {getGossipSSZType, gossipTopicIgnoreDuplicatePublishError, stringifyGossipTopic} from "./gossip/topic.js";
52
+ import {
53
+ getGossipSSZType,
54
+ gossipTopicAllowPublishToZeroPeers,
55
+ gossipTopicIgnoreDuplicatePublishError,
56
+ stringifyGossipTopic,
57
+ } from "./gossip/topic.js";
53
58
  import {INetwork} from "./interface.js";
54
59
  import {NetworkOptions} from "./options.js";
55
60
  import {PeerAction, PeerScoreStats} from "./peers/index.js";
@@ -65,7 +70,7 @@ import {
65
70
  } from "./reqresp/utils/collect.js";
66
71
  import {collectSequentialBlocksInRange} from "./reqresp/utils/collectSequentialBlocksInRange.js";
67
72
  import {CommitteeSubscription} from "./subnets/index.js";
68
- import {isPublishDuplicateError, isPublishToZeroPeersError, prettyPrintPeerIdStr} from "./util.js";
73
+ import {isPublishDuplicateError, prettyPrintPeerIdStr} from "./util.js";
69
74
 
70
75
  type NetworkModules = {
71
76
  opts: NetworkOptions;
@@ -352,9 +357,7 @@ export class Network implements INetwork {
352
357
  const epoch = computeEpochAtSlot(signedBlock.message.slot);
353
358
  const boundary = this.config.getForkBoundaryAtEpoch(epoch);
354
359
 
355
- return this.publishGossip<GossipType.beacon_block>({type: GossipType.beacon_block, boundary}, signedBlock, {
356
- ignoreDuplicatePublishError: true,
357
- });
360
+ return this.publishGossip<GossipType.beacon_block>({type: GossipType.beacon_block, boundary}, signedBlock);
358
361
  }
359
362
 
360
363
  async publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise<number> {
@@ -363,9 +366,7 @@ export class Network implements INetwork {
363
366
 
364
367
  const subnet = blobSidecar.index;
365
368
 
366
- return this.publishGossip<GossipType.blob_sidecar>({type: GossipType.blob_sidecar, boundary, subnet}, blobSidecar, {
367
- ignoreDuplicatePublishError: true,
368
- });
369
+ return this.publishGossip<GossipType.blob_sidecar>({type: GossipType.blob_sidecar, boundary, subnet}, blobSidecar);
369
370
  }
370
371
 
371
372
  async publishDataColumnSidecar(
@@ -381,14 +382,7 @@ export class Network implements INetwork {
381
382
  try {
382
383
  const sentPeers = await this.publishGossip<GossipType.data_column_sidecar>(
383
384
  {type: GossipType.data_column_sidecar, boundary, subnet},
384
- dataColumnSidecar,
385
- {
386
- // we ensure having all topic peers via prioritizePeers() function
387
- // in the worse case, if there is 0 peer on the topic, the overall publish operation could be still a success
388
- // because supernode will rebuild and publish missing data column sidecars for us
389
- // hence we want to track sent peers as 0 instead of an error
390
- allowPublishToZeroTopicPeers: true,
391
- }
385
+ dataColumnSidecar
392
386
  );
393
387
  return {sentPeers, alreadyPublished: false};
394
388
  } catch (e) {
@@ -405,8 +399,7 @@ export class Network implements INetwork {
405
399
 
406
400
  return this.publishGossip<GossipType.beacon_aggregate_and_proof>(
407
401
  {type: GossipType.beacon_aggregate_and_proof, boundary},
408
- aggregateAndProof,
409
- {ignoreDuplicatePublishError: true}
402
+ aggregateAndProof
410
403
  );
411
404
  }
412
405
 
@@ -416,8 +409,7 @@ export class Network implements INetwork {
416
409
 
417
410
  return this.publishGossip<GossipType.beacon_attestation>(
418
411
  {type: GossipType.beacon_attestation, boundary, subnet},
419
- attestation,
420
- {ignoreDuplicatePublishError: true}
412
+ attestation
421
413
  );
422
414
  }
423
415
 
@@ -425,9 +417,7 @@ export class Network implements INetwork {
425
417
  const epoch = voluntaryExit.message.epoch;
426
418
  const boundary = this.config.getForkBoundaryAtEpoch(epoch);
427
419
 
428
- return this.publishGossip<GossipType.voluntary_exit>({type: GossipType.voluntary_exit, boundary}, voluntaryExit, {
429
- ignoreDuplicatePublishError: true,
430
- });
420
+ return this.publishGossip<GossipType.voluntary_exit>({type: GossipType.voluntary_exit, boundary}, voluntaryExit);
431
421
  }
432
422
 
433
423
  async publishBlsToExecutionChange(blsToExecutionChange: capella.SignedBLSToExecutionChange): Promise<number> {
@@ -438,8 +428,7 @@ export class Network implements INetwork {
438
428
  if (fork >= ForkSeq.capella) {
439
429
  const publishPromise = this.publishGossip<GossipType.bls_to_execution_change>(
440
430
  {type: GossipType.bls_to_execution_change, boundary},
441
- blsToExecutionChange,
442
- {ignoreDuplicatePublishError: true}
431
+ blsToExecutionChange
443
432
  );
444
433
  publishChanges.push(publishPromise);
445
434
  }
@@ -457,8 +446,7 @@ export class Network implements INetwork {
457
446
 
458
447
  return this.publishGossip<GossipType.proposer_slashing>(
459
448
  {type: GossipType.proposer_slashing, boundary},
460
- proposerSlashing,
461
- {ignoreDuplicatePublishError: true}
449
+ proposerSlashing
462
450
  );
463
451
  }
464
452
 
@@ -478,10 +466,7 @@ export class Network implements INetwork {
478
466
 
479
467
  return this.publishGossip<GossipType.sync_committee>(
480
468
  {type: GossipType.sync_committee, boundary, subnet},
481
- signature,
482
- {
483
- ignoreDuplicatePublishError: true,
484
- }
469
+ signature
485
470
  );
486
471
  }
487
472
 
@@ -491,8 +476,7 @@ export class Network implements INetwork {
491
476
 
492
477
  return this.publishGossip<GossipType.sync_committee_contribution_and_proof>(
493
478
  {type: GossipType.sync_committee_contribution_and_proof, boundary},
494
- contributionAndProof,
495
- {ignoreDuplicatePublishError: true}
479
+ contributionAndProof
496
480
  );
497
481
  }
498
482
 
@@ -522,8 +506,7 @@ export class Network implements INetwork {
522
506
 
523
507
  return this.publishGossip<GossipType.execution_payload>(
524
508
  {type: GossipType.execution_payload, boundary},
525
- signedEnvelope,
526
- {ignoreDuplicatePublishError: true}
509
+ signedEnvelope
527
510
  );
528
511
  }
529
512
 
@@ -534,7 +517,7 @@ export class Network implements INetwork {
534
517
  return this.publishGossip<GossipType.execution_payload_bid>(
535
518
  {type: GossipType.execution_payload_bid, boundary},
536
519
  signedBid,
537
- {ignoreDuplicatePublishError: true}
520
+ {floodPublish: true}
538
521
  );
539
522
  }
540
523
 
@@ -544,8 +527,7 @@ export class Network implements INetwork {
544
527
 
545
528
  return this.publishGossip<GossipType.payload_attestation_message>(
546
529
  {type: GossipType.payload_attestation_message, boundary},
547
- payloadAttestationMessage,
548
- {ignoreDuplicatePublishError: true}
530
+ payloadAttestationMessage
549
531
  );
550
532
  }
551
533
 
@@ -555,24 +537,25 @@ export class Network implements INetwork {
555
537
 
556
538
  return this.publishGossip<GossipType.proposer_preferences>(
557
539
  {type: GossipType.proposer_preferences, boundary},
558
- signedProposerPreferences,
559
- {ignoreDuplicatePublishError: true}
540
+ signedProposerPreferences
560
541
  );
561
542
  }
562
543
 
563
544
  private async publishGossip<K extends GossipType>(
564
545
  topic: GossipTopicMap[K],
565
546
  object: GossipTypeMap[K],
566
- opts?: PublishOpts | undefined
547
+ opts?: PublishOpts
567
548
  ): Promise<number> {
568
549
  const topicStr = stringifyGossipTopic(this.config, topic);
569
550
  const sszType = getGossipSSZType(topic);
570
551
  const messageData = (sszType.serialize as (object: GossipTypeMap[GossipType]) => Uint8Array)(object);
571
- opts = {
572
- ...opts,
552
+ const publishOpts: PublishOpts = {
573
553
  ignoreDuplicatePublishError: gossipTopicIgnoreDuplicatePublishError[topic.type],
554
+ // Leave undefined unless the topic opts out, so `--network.allowPublishToZeroPeers` still applies
555
+ allowPublishToZeroTopicPeers: gossipTopicAllowPublishToZeroPeers[topic.type] ? true : undefined,
556
+ ...opts,
574
557
  };
575
- const sentPeers = await this.core.publishGossip(topicStr, messageData, opts);
558
+ const sentPeers = await this.core.publishGossip(topicStr, messageData, publishOpts);
576
559
 
577
560
  this.logger.verbose("Publish to topic", {topic: topicStr, sentPeers, currentSlot: this.clock.currentSlot});
578
561
  return sentPeers;
@@ -813,11 +796,7 @@ export class Network implements INetwork {
813
796
  await this.waitForSyncMessageCutoff(finalityUpdate.signatureSlot);
814
797
  await this.publishLightClientFinalityUpdate(finalityUpdate);
815
798
  } catch (e) {
816
- // Non-mandatory route on most of network as of Oct 2022. May not have found any peers on topic yet
817
- // Remove once https://github.com/ChainSafe/js-libp2p-gossipsub/issues/367
818
- if (!isPublishToZeroPeersError(e as Error)) {
819
- this.logger.debug("Error on BeaconGossipHandler.onLightclientFinalityUpdate", {}, e as Error);
820
- }
799
+ this.logger.debug("Error on BeaconGossipHandler.onLightclientFinalityUpdate", {}, e as Error);
821
800
  }
822
801
  };
823
802
 
@@ -830,11 +809,7 @@ export class Network implements INetwork {
830
809
  await this.waitForSyncMessageCutoff(optimisticUpdate.signatureSlot);
831
810
  await this.publishLightClientOptimisticUpdate(optimisticUpdate);
832
811
  } catch (e) {
833
- // Non-mandatory route on most of network as of Oct 2022. May not have found any peers on topic yet
834
- // Remove once https://github.com/ChainSafe/js-libp2p-gossipsub/issues/367
835
- if (!isPublishToZeroPeersError(e as Error)) {
836
- this.logger.debug("Error on BeaconGossipHandler.onLightclientOptimisticUpdate", {}, e as Error);
837
- }
812
+ this.logger.debug("Error on BeaconGossipHandler.onLightclientOptimisticUpdate", {}, e as Error);
838
813
  }
839
814
  };
840
815
 
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Max distinct peers tracked per unknown block/payload root.
3
+ * Normally we have 8 mesh peers per topic, and multi topics may point to the same root.
4
+ * Having 32 peers per root seems enough to search for missing blocks/payloads.
5
+ **/
6
+ export const MAX_PEERS_PER_ROOT = 32;
@@ -196,7 +196,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
196
196
  forkName: fork,
197
197
  sampledColumns: chain.custodyConfig.sampledColumns,
198
198
  custodyColumns: chain.custodyConfig.custodyColumns,
199
- timeCreatedSec: seenTimestampSec,
199
+ seenTimestampSec,
200
+ source: PayloadEnvelopeInputSource.gossip,
200
201
  });
201
202
  }
202
203
 
@@ -640,6 +641,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
640
641
  metrics?.gossipBlock.elapsedTimeTillProcessed.observe(delaySec);
641
642
 
642
643
  if (isForkPostGloas(blockInput.forkName)) {
644
+ // we should have the payloadInput in the seen cache so no need getOrReload() here
643
645
  const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockInput.blockRootHex);
644
646
  // This payloadInput should have been created just after gossip validation
645
647
  if (!payloadInput) {
@@ -1189,6 +1191,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1189
1191
  chain.validatorMonitor?.registerExecutionPayloadEnvelope(OpSource.gossip, delaySec, signedEnvelope);
1190
1192
 
1191
1193
  const blockRootHex = toRootHex(envelope.beaconBlockRoot);
1194
+ // a gossip payload cannot be `MAX_PAYLOAD_ENVELOPE_INPUT_CACHE_SIZE` slots after the block
1195
+ // otherwise it'll get to UnknownBlockInput flow
1192
1196
  const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
1193
1197
 
1194
1198
  if (!payloadInput) {
@@ -1255,11 +1259,15 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1255
1259
  [GossipType.payload_attestation_message]: async ({
1256
1260
  gossipData,
1257
1261
  topic,
1262
+ seenTimestampSec,
1258
1263
  }: GossipHandlerParamGeneric<GossipType.payload_attestation_message>) => {
1259
1264
  const {serializedData} = gossipData;
1260
1265
  const payloadAttestationMessage = sszDeserialize(topic, serializedData);
1261
1266
  const validationResult = await validateGossipPayloadAttestationMessage(chain, payloadAttestationMessage);
1262
1267
 
1268
+ const delaySec = chain.clock.secFromSlot(payloadAttestationMessage.data.slot, seenTimestampSec);
1269
+ metrics?.gossipPayloadAttestationMessage.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
1270
+
1263
1271
  try {
1264
1272
  const insertOutcome = chain.payloadAttestationPool.add(
1265
1273
  payloadAttestationMessage,
@@ -1286,14 +1294,19 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1286
1294
  [GossipType.execution_payload_bid]: async ({
1287
1295
  gossipData,
1288
1296
  topic,
1297
+ seenTimestampSec,
1289
1298
  }: GossipHandlerParamGeneric<GossipType.execution_payload_bid>) => {
1290
1299
  const {serializedData} = gossipData;
1291
1300
  const executionPayloadBid = sszDeserialize(topic, serializedData);
1292
1301
  const {proposerIndex} = await validateGossipExecutionPayloadBid(chain, executionPayloadBid);
1293
1302
 
1303
+ // this could be negative, because it's most likely the bid of next slot comes at this clock slot
1304
+ const elapsedSec = chain.clock.secFromSlot(executionPayloadBid.message.slot, seenTimestampSec);
1305
+ metrics?.gossipExecutionPayloadBid.elapsedTimeTillReceived.observe({source: OpSource.gossip}, elapsedSec);
1306
+
1294
1307
  // Handle valid payload bid by storing in a bid pool
1295
1308
  try {
1296
- const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid);
1309
+ const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid, Math.floor(elapsedSec * 1000));
1297
1310
  metrics?.opPool.executionPayloadBidPool.gossipInsertOutcome.inc({insertOutcome});
1298
1311
  } catch (e) {
1299
1312
  logger.error("Error adding to executionPayloadBid pool", {}, e as Error);
@@ -86,7 +86,6 @@ export class IndexedGossipQueueMinSize<T extends {indexed?: string; queueAddedMs
86
86
  const now = Date.now();
87
87
  // here we mutate item, which is used for gossip validation later
88
88
  item.indexed = key;
89
- item.queueAddedMs = now;
90
89
  let queueItem = this.indexedItems.get(key);
91
90
  if (queueItem == null) {
92
91
  queueItem = {firstSeenMs: now, listItems: new LinkedList<T>()};
@@ -1,3 +1,4 @@
1
+ import {TopicValidatorResult} from "@libp2p/gossipsub";
1
2
  import {routes} from "@lodestar/api";
2
3
  import {ForkSeq} from "@lodestar/params";
3
4
  import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
@@ -30,6 +31,7 @@ import {
30
31
  GossipValidatorBatchFn,
31
32
  GossipValidatorFn,
32
33
  } from "../gossip/interface.js";
34
+ import {MAX_PEERS_PER_ROOT} from "./constants.js";
33
35
  import {createExtractBlockSlotRootFns} from "./extractSlotRootFns.js";
34
36
  import {GossipHandlerOpts, ValidatorFnsModules, getGossipHandlers} from "./gossipHandlers.js";
35
37
  import {createGossipQueues} from "./gossipQueues/index.js";
@@ -191,8 +193,12 @@ export class NetworkProcessor {
191
193
  // in that case PendingGossipsubMessage needs to be stored in this Map and reprocessed once the payload comes
192
194
  private readonly awaitingMessagesByPayloadBlockRoot: MapDef<RootHex, Set<PendingGossipsubMessage>>;
193
195
  private awaitingPayloadMessageCount = 0;
194
- private unknownBlocksBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
195
- private unknownEnvelopesBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
196
+ private unknownBlocksBySlot = new MapDef<Slot, MapDef<RootHex, Set<PeerIdStr>>>(
197
+ () => new MapDef<RootHex, Set<PeerIdStr>>(() => new Set())
198
+ );
199
+ private unknownEnvelopesBySlot = new MapDef<Slot, MapDef<RootHex, Set<PeerIdStr>>>(
200
+ () => new MapDef<RootHex, Set<PeerIdStr>>(() => new Set())
201
+ );
196
202
 
197
203
  constructor(
198
204
  modules: NetworkProcessorModules,
@@ -271,35 +277,53 @@ export class NetworkProcessor {
271
277
  * Search block via `ChainEvent.unknownBlockRoot` event
272
278
  * Slot is the message slot, which is not necessarily the same as the block's slot, but it can be used for a good prune strategy.
273
279
  * In the rare case, if 2 messages on 2 slots search for the same root (for example beacon_attestation) we may emit the same root twice but BlockInputSync should handle it well.
280
+ * We forward every distinct peer that gossiped this root (not just the first) so BlockInputSync can
281
+ * prefer them as by-root download candidates.
274
282
  */
275
283
  searchUnknownBlock({slot, root}: SlotRootHex, source: BlockInputSource, peer?: PeerIdStr): void {
276
- if (
277
- this.chain.seenBlock(root) ||
278
- this.awaitingMessagesByBlockRoot.has(root) ||
279
- this.unknownBlocksBySlot.getOrDefault(slot).has(root)
280
- ) {
284
+ if (this.chain.seenBlock(root)) {
281
285
  return;
282
286
  }
283
- // Search for the unknown block
284
- this.unknownBlocksBySlot.getOrDefault(slot).add(root);
285
- this.chain.emitter.emit(ChainEvent.unknownBlockRoot, {rootHex: root, peer, source});
287
+ const peersForRoot = this.unknownBlocksBySlot.getOrDefault(slot);
288
+ const alreadySearching = peersForRoot.has(root) || this.awaitingMessagesByBlockRoot.has(root);
289
+ const forwardedPeers = peersForRoot.getOrDefault(root);
290
+
291
+ // brand-new search always emits (peer may be undefined)
292
+ let shouldEmit = !alreadySearching;
293
+ if (peer !== undefined && !forwardedPeers.has(peer) && forwardedPeers.size < MAX_PEERS_PER_ROOT) {
294
+ // First time we forward THIS peer for THIS root: emit so BlockInputSync accumulates it.
295
+ forwardedPeers.add(peer);
296
+ shouldEmit = true;
297
+ }
298
+ if (shouldEmit) {
299
+ this.chain.emitter.emit(ChainEvent.unknownBlockRoot, {rootHex: root, peer, source});
300
+ }
286
301
  }
287
302
 
288
303
  /**
289
304
  * Search envelope via `ChainEvent.unknownEnvelopeBlockRoot` event
290
305
  * Slot is the message slot, which is not necessarily the same as the envelope's slot, but it can be used for a good prune strategy.
291
306
  * In the rare case, if 2 messages on 2 slots search for the same root (for example beacon_attestation) we may emit the same root twice but BlockInputSync should handle it well.
307
+ * Same peer-forwarding + dedup behavior as searchUnknownBlock.
292
308
  */
293
309
  searchUnknownEnvelope({slot, root}: SlotRootHex, source: BlockInputSource, peer?: PeerIdStr): void {
294
- if (
295
- this.chain.seenPayloadEnvelope(root) ||
296
- this.awaitingMessagesByPayloadBlockRoot.has(root) ||
297
- this.unknownEnvelopesBySlot.getOrDefault(slot).has(root)
298
- ) {
310
+ if (this.chain.seenPayloadEnvelope(root)) {
299
311
  return;
300
312
  }
301
- this.unknownEnvelopesBySlot.getOrDefault(slot).add(root);
302
- this.chain.emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, {rootHex: root, slot, peer, source});
313
+ const peersForRoot = this.unknownEnvelopesBySlot.getOrDefault(slot);
314
+ // capture "already searching" BEFORE getOrDefault(root) creates the entry
315
+ const alreadySearching = peersForRoot.has(root) || this.awaitingMessagesByPayloadBlockRoot.has(root);
316
+ const forwardedPeers = peersForRoot.getOrDefault(root);
317
+
318
+ let shouldEmit = !alreadySearching; // brand-new search always emits (peer may be undefined)
319
+ if (peer !== undefined && !forwardedPeers.has(peer) && forwardedPeers.size < MAX_PEERS_PER_ROOT) {
320
+ // First time we forward THIS peer for THIS root: emit so BlockInputSync accumulates it.
321
+ forwardedPeers.add(peer);
322
+ shouldEmit = true;
323
+ }
324
+ if (shouldEmit) {
325
+ this.chain.emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, {rootHex: root, slot, peer, source});
326
+ }
303
327
  }
304
328
 
305
329
  private onPendingGossipsubMessage = (message: PendingGossipsubMessage): void => {
@@ -537,6 +561,7 @@ export class NetworkProcessor {
537
561
 
538
562
  private pushPendingGossipsubMessageToQueue(message: PendingGossipsubMessage): void {
539
563
  const topicType = message.topic.type;
564
+ message.queueAddedMs = Date.now();
540
565
  const droppedCount = this.gossipQueues[topicType].add(message);
541
566
  if (droppedCount) {
542
567
  // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
@@ -616,7 +641,7 @@ export class NetworkProcessor {
616
641
 
617
642
  for (const [slot, roots] of this.unknownBlocksBySlot) {
618
643
  if (slot > minSlot) continue;
619
- for (const rootHex of roots) {
644
+ for (const rootHex of roots.keys()) {
620
645
  const gossipMessages = this.awaitingMessagesByBlockRoot.get(rootHex);
621
646
  if (gossipMessages !== undefined) {
622
647
  for (const message of gossipMessages) {
@@ -641,7 +666,7 @@ export class NetworkProcessor {
641
666
 
642
667
  for (const [slot, roots] of this.unknownEnvelopesBySlot) {
643
668
  if (slot > minSlot) continue;
644
- for (const rootHex of roots) {
669
+ for (const rootHex of roots.keys()) {
645
670
  const gossipMessages = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
646
671
  if (gossipMessages !== undefined) {
647
672
  for (const message of gossipMessages) {
@@ -723,12 +748,20 @@ export class NetworkProcessor {
723
748
  for (const msg of messageOrArray) {
724
749
  msg.startProcessUnixSec = nowSec;
725
750
  if (msg.queueAddedMs !== undefined) {
726
- this.metrics?.gossipValidationQueue.queueTime.observe(nowSec - msg.queueAddedMs / 1000);
751
+ this.metrics?.gossipValidationQueue.queueTime.observe(
752
+ {topic: msg.topic.type},
753
+ nowSec - msg.queueAddedMs / 1000
754
+ );
727
755
  }
728
756
  }
729
757
  } else {
730
- // indexed queue is not used here
731
758
  messageOrArray.startProcessUnixSec = nowSec;
759
+ if (messageOrArray.queueAddedMs !== undefined) {
760
+ this.metrics?.gossipValidationQueue.queueTime.observe(
761
+ {topic: messageOrArray.topic.type},
762
+ nowSec - messageOrArray.queueAddedMs / 1000
763
+ );
764
+ }
732
765
  }
733
766
 
734
767
  const acceptanceArr = Array.isArray(messageOrArray)
@@ -741,11 +774,11 @@ export class NetworkProcessor {
741
774
  ];
742
775
 
743
776
  if (Array.isArray(messageOrArray)) {
744
- for (const msg of messageOrArray) {
745
- this.trackJobTime(msg, messageOrArray.length);
777
+ for (const [i, msg] of messageOrArray.entries()) {
778
+ this.trackJobTime(msg, messageOrArray.length, acceptanceArr[i]);
746
779
  }
747
780
  } else {
748
- this.trackJobTime(messageOrArray, 1);
781
+ this.trackJobTime(messageOrArray, 1, acceptanceArr[0]);
749
782
  }
750
783
 
751
784
  // Use setTimeout to yield to the macro queue
@@ -773,17 +806,21 @@ export class NetworkProcessor {
773
806
  }
774
807
  }
775
808
 
776
- private trackJobTime(message: PendingGossipsubMessage, numJob: number): void {
809
+ private trackJobTime(message: PendingGossipsubMessage, numJob: number, acceptance: TopicValidatorResult): void {
777
810
  if (message.startProcessUnixSec !== null) {
811
+ // record job wait time for all messages
778
812
  this.metrics?.gossipValidationQueue.jobWaitTime.observe(
779
813
  {topic: message.topic.type},
780
814
  message.startProcessUnixSec - message.seenTimestampSec
781
815
  );
782
- // if it takes 64ms to process 64 jobs, the average job time is 1ms
783
- this.metrics?.gossipValidationQueue.jobTime.observe(
784
- {topic: message.topic.type},
785
- (Date.now() / 1000 - message.startProcessUnixSec) / numJob
786
- );
816
+ // but only record job time for ACCEPT messages
817
+ if (acceptance === TopicValidatorResult.Accept) {
818
+ // if it takes 64ms to process 64 jobs, the average job time is 1ms
819
+ this.metrics?.gossipValidationQueue.jobTime.observe(
820
+ {topic: message.topic.type},
821
+ (Date.now() / 1000 - message.startProcessUnixSec) / numJob
822
+ );
823
+ }
787
824
  }
788
825
  }
789
826
 
@@ -44,17 +44,11 @@ export function onOutgoingReqRespError(e: RequestError, method: ReqRespMethod):
44
44
  // failure, so keep the stronger penalty (Fatal for Ping, as before).
45
45
  return method === ReqRespMethod.Ping ? PeerAction.Fatal : PeerAction.LowToleranceError;
46
46
  }
47
- switch (method) {
48
- // Ping and Status are liveness probes; their dial timeouts are dominated by transient
49
- // network congestion rather than peer misbehavior, so penalize leniently to avoid
50
- // self-inflicted peer starvation (https://github.com/ChainSafe/lodestar/issues/9562),
51
- // while still applying some penalty so genuinely dead peers eventually free the slot.
52
- case ReqRespMethod.Ping:
53
- case ReqRespMethod.Status:
54
- return PeerAction.HighToleranceError;
55
- default:
56
- return PeerAction.LowToleranceError;
57
- }
47
+ // A dial failure means we could not open a stream. That is evidence about the transport, not
48
+ // about the peer's behavior, and it is dominated by transient congestion on our side, so
49
+ // penalize leniently for every method to avoid self-inflicted peer starvation (#9562). A
50
+ // genuinely dead peer still accumulates penalty and is eventually evicted, freeing the slot.
51
+ return PeerAction.HighToleranceError;
58
52
  // TODO: Detect SSZDecodeError and return PeerAction.Fatal
59
53
 
60
54
  case RequestErrorCode.RESP_TIMEOUT:
@@ -23,11 +23,6 @@ export function getConnection(libp2p: Libp2p, peerIdStr: string): Connection | u
23
23
  return getConnectionsMap(libp2p).get(peerIdStr)?.value[0] ?? undefined;
24
24
  }
25
25
 
26
- // https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
27
- export function isPublishToZeroPeersError(e: Error): boolean {
28
- return e.message.includes("PublishError.NoPeersSubscribedToTopic");
29
- }
30
-
31
26
  // https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
32
27
  export function isPublishDuplicateError(e: Error): boolean {
33
28
  return e.message.includes("PublishError.Duplicate");
@@ -0,0 +1,54 @@
1
+ import {routes} from "@lodestar/api";
2
+ import {Logger} from "@lodestar/utils";
3
+ import {IBeaconChain} from "../chain/index.js";
4
+ import {INetwork} from "../network/index.js";
5
+ import {ClockEvent} from "../util/clock.js";
6
+
7
+ /**
8
+ * On every epoch, drain the deferred voluntary exit pool and publish the exits that have become
9
+ * processable. Lives at the node layer rather than inside `BeaconChain` because publishing needs
10
+ * `network`, which the chain does not (and should not) depend on.
11
+ */
12
+ export function startDeferredVoluntaryExitPublisher({
13
+ chain,
14
+ network,
15
+ logger,
16
+ signal,
17
+ }: {
18
+ chain: IBeaconChain;
19
+ network: INetwork;
20
+ logger: Logger;
21
+ signal: AbortSignal;
22
+ }): void {
23
+ const onEpoch = async (): Promise<void> => {
24
+ try {
25
+ // Use the cached head state directly rather than getHeadStateAtCurrentEpoch, which would regen
26
+ // the head forward to the current epoch. At init from a stale db-finalized state that regen can
27
+ // be many epoch transitions. The head state is fine here: this is a best-effort drain that
28
+ // re-runs every epoch, so a just-processable exit is republished next tick.
29
+ const state = chain.getHeadState();
30
+ const exits = chain.deferredVoluntaryExitPool.drainProcessableExits(state);
31
+ for (const exit of exits) {
32
+ try {
33
+ chain.opPool.insertVoluntaryExit(exit);
34
+ chain.emitter.emit(routes.events.EventType.voluntaryExit, exit);
35
+ await network.publishVoluntaryExit(exit);
36
+ logger.info("Voluntary exit successfully published for validator", {
37
+ validatorIndex: exit.message.validatorIndex,
38
+ });
39
+ } catch (e) {
40
+ logger.warn(
41
+ "Failed to publish deferred voluntary exit",
42
+ {validatorIndex: exit.message.validatorIndex},
43
+ e as Error
44
+ );
45
+ }
46
+ }
47
+ } catch (e) {
48
+ logger.warn("Failed to drain deferred voluntary exit pool", {}, e as Error);
49
+ }
50
+ };
51
+
52
+ chain.clock.addListener(ClockEvent.epoch, onEpoch);
53
+ signal.addEventListener("abort", () => chain.clock.removeListener(ClockEvent.epoch, onEpoch), {once: true});
54
+ }
@@ -1,12 +1,13 @@
1
1
  import {setMaxListeners} from "node:events";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Registry} from "prom-client";
4
+ import {type PubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
4
5
  import {hasher} from "@chainsafe/persistent-merkle-tree";
5
6
  import {BeaconApiMethods} from "@lodestar/api/beacon/server";
6
7
  import {BeaconConfig} from "@lodestar/config";
7
8
  import type {LoggerNode} from "@lodestar/logger/node";
8
9
  import {ZERO_HASH_HEX} from "@lodestar/params";
9
- import {IBeaconStateView, PubkeyCache, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
10
+ import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
10
11
  import {phase0} from "@lodestar/types";
11
12
  import {sleep, toRootHex} from "@lodestar/utils";
12
13
  import {ProcessShutdownCallback} from "@lodestar/validator";
@@ -21,6 +22,7 @@ import {Network, getReqRespHandlers} from "../network/index.js";
21
22
  import {BackfillSync} from "../sync/backfill/index.js";
22
23
  import {BeaconSync, IBeaconSync} from "../sync/index.js";
23
24
  import {Clock} from "../util/clock.js";
25
+ import {startDeferredVoluntaryExitPublisher} from "./deferredVoluntaryExitPublisher.js";
24
26
  import {runNodeNotifier} from "./notifier.js";
25
27
  import {IBeaconNodeOptions} from "./options.js";
26
28
 
@@ -258,6 +260,11 @@ export class BeaconNode {
258
260
  executionBuilder: opts.executionBuilder.enabled
259
261
  ? initializeExecutionBuilder(opts.executionBuilder, config, metrics, logger)
260
262
  : undefined,
263
+ builderApiClientOpts: {
264
+ timeout: opts.executionBuilder.timeout,
265
+ // Sent with all builder api requests, unless the node runs in private mode
266
+ userAgent: opts.executionBuilder.userAgent,
267
+ },
261
268
  });
262
269
 
263
270
  // Load persisted data from disk to in-memory caches
@@ -334,6 +341,8 @@ export class BeaconNode {
334
341
 
335
342
  void runNodeNotifier({network, chain, sync, config, logger, signal});
336
343
 
344
+ startDeferredVoluntaryExitPublisher({chain, network, logger, signal});
345
+
337
346
  return new BeaconNode({
338
347
  opts,
339
348
  config,