@lodestar/beacon-node 1.47.0-dev.0bcaaaebb5 → 1.47.0-dev.101e2c290d

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 (332) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +6 -8
  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.js +1 -1
  17. package/lib/api/impl/validator/index.js.map +1 -1
  18. package/lib/api/rest/base.d.ts.map +1 -1
  19. package/lib/api/rest/base.js +7 -0
  20. package/lib/api/rest/base.js.map +1 -1
  21. package/lib/chain/archiveStore/archiveStore.js +1 -1
  22. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  23. package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  24. package/lib/chain/archiveStore/historicalState/metrics.js +21 -0
  25. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  26. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  27. package/lib/chain/blocks/importBlock.js +2 -7
  28. package/lib/chain/blocks/importBlock.js.map +1 -1
  29. package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
  30. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  31. package/lib/chain/blocks/importExecutionPayload.js +11 -9
  32. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  33. package/lib/chain/blocks/index.d.ts.map +1 -1
  34. package/lib/chain/blocks/index.js +14 -6
  35. package/lib/chain/blocks/index.js.map +1 -1
  36. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
  37. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  38. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
  39. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  40. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
  41. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  42. package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
  43. package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
  44. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  45. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
  46. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  47. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  48. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  49. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  50. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  51. package/lib/chain/bls/index.d.ts +1 -1
  52. package/lib/chain/bls/index.d.ts.map +1 -1
  53. package/lib/chain/bls/interface.d.ts +5 -5
  54. package/lib/chain/bls/interface.d.ts.map +1 -1
  55. package/lib/chain/bls/multithread/index.d.ts +5 -11
  56. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  57. package/lib/chain/bls/multithread/index.js +116 -101
  58. package/lib/chain/bls/multithread/index.js.map +1 -1
  59. package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
  60. package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
  61. package/lib/chain/bls/multithread/jobItem.js +10 -65
  62. package/lib/chain/bls/multithread/jobItem.js.map +1 -1
  63. package/lib/chain/bls/multithread/types.d.ts +29 -8
  64. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  65. package/lib/chain/bls/multithread/types.js +12 -0
  66. package/lib/chain/bls/multithread/types.js.map +1 -1
  67. package/lib/chain/bls/multithread/utils.d.ts +4 -0
  68. package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
  69. package/lib/chain/bls/multithread/utils.js +10 -0
  70. package/lib/chain/bls/multithread/utils.js.map +1 -1
  71. package/lib/chain/bls/multithread/worker.js +49 -23
  72. package/lib/chain/bls/multithread/worker.js.map +1 -1
  73. package/lib/chain/bls/singleThread.d.ts +4 -10
  74. package/lib/chain/bls/singleThread.d.ts.map +1 -1
  75. package/lib/chain/bls/singleThread.js +8 -45
  76. package/lib/chain/bls/singleThread.js.map +1 -1
  77. package/lib/chain/bls/utils.d.ts +4 -4
  78. package/lib/chain/bls/utils.d.ts.map +1 -1
  79. package/lib/chain/bls/utils.js +18 -19
  80. package/lib/chain/bls/utils.js.map +1 -1
  81. package/lib/chain/builderCircuitBreaker.d.ts +6 -5
  82. package/lib/chain/builderCircuitBreaker.d.ts.map +1 -1
  83. package/lib/chain/builderCircuitBreaker.js +28 -15
  84. package/lib/chain/builderCircuitBreaker.js.map +1 -1
  85. package/lib/chain/chain.d.ts +4 -2
  86. package/lib/chain/chain.d.ts.map +1 -1
  87. package/lib/chain/chain.js +16 -6
  88. package/lib/chain/chain.js.map +1 -1
  89. package/lib/chain/errors/blockError.d.ts +1 -0
  90. package/lib/chain/errors/blockError.d.ts.map +1 -1
  91. package/lib/chain/errors/blockError.js +6 -1
  92. package/lib/chain/errors/blockError.js.map +1 -1
  93. package/lib/chain/errors/proposerPreferences.d.ts +6 -0
  94. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
  95. package/lib/chain/errors/proposerPreferences.js +1 -0
  96. package/lib/chain/errors/proposerPreferences.js.map +1 -1
  97. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  98. package/lib/chain/forkChoice/index.js +4 -0
  99. package/lib/chain/forkChoice/index.js.map +1 -1
  100. package/lib/chain/interface.d.ts +4 -2
  101. package/lib/chain/interface.d.ts.map +1 -1
  102. package/lib/chain/interface.js.map +1 -1
  103. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  104. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  105. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  106. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  107. package/lib/chain/opPools/attestationPool.js +1 -1
  108. package/lib/chain/opPools/attestationPool.js.map +1 -1
  109. package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts +16 -0
  110. package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts.map +1 -0
  111. package/lib/chain/opPools/deferredVoluntaryExitPool.js +57 -0
  112. package/lib/chain/opPools/deferredVoluntaryExitPool.js.map +1 -0
  113. package/lib/chain/opPools/index.d.ts +1 -0
  114. package/lib/chain/opPools/index.d.ts.map +1 -1
  115. package/lib/chain/opPools/index.js +1 -0
  116. package/lib/chain/opPools/index.js.map +1 -1
  117. package/lib/chain/opPools/payloadAttestationPool.js +3 -3
  118. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  119. package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
  120. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  121. package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
  122. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  123. package/lib/chain/opPools/utils.d.ts +1 -1
  124. package/lib/chain/opPools/utils.d.ts.map +1 -1
  125. package/lib/chain/opPools/utils.js +1 -1
  126. package/lib/chain/opPools/utils.js.map +1 -1
  127. package/lib/chain/options.d.ts +1 -1
  128. package/lib/chain/options.d.ts.map +1 -1
  129. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  130. package/lib/chain/prepareNextSlot.js +24 -6
  131. package/lib/chain/prepareNextSlot.js.map +1 -1
  132. package/lib/chain/regen/interface.d.ts +4 -2
  133. package/lib/chain/regen/interface.d.ts.map +1 -1
  134. package/lib/chain/regen/interface.js +2 -0
  135. package/lib/chain/regen/interface.js.map +1 -1
  136. package/lib/chain/regen/queued.d.ts +2 -2
  137. package/lib/chain/regen/queued.d.ts.map +1 -1
  138. package/lib/chain/regen/queued.js +5 -5
  139. package/lib/chain/regen/queued.js.map +1 -1
  140. package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
  141. package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
  142. package/lib/chain/seenCache/seenAttesters.js +25 -4
  143. package/lib/chain/seenCache/seenAttesters.js.map +1 -1
  144. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
  145. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  146. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
  147. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  148. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +5 -1
  149. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  150. package/lib/chain/stateCache/fifoBlockStateCache.js +6 -0
  151. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  152. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +47 -5
  153. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  154. package/lib/chain/stateCache/persistentCheckpointsCache.js +117 -7
  155. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  156. package/lib/chain/stateCache/types.d.ts +3 -3
  157. package/lib/chain/stateCache/types.d.ts.map +1 -1
  158. package/lib/chain/validation/attestation.d.ts.map +1 -1
  159. package/lib/chain/validation/attestation.js +1 -4
  160. package/lib/chain/validation/attestation.js.map +1 -1
  161. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  162. package/lib/chain/validation/attesterSlashing.js +1 -1
  163. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  164. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  165. package/lib/chain/validation/executionPayloadBid.js +1 -2
  166. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  167. package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
  168. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  169. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  170. package/lib/chain/validation/payloadAttestationMessage.js +5 -14
  171. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  172. package/lib/chain/validation/proposerPreferences.d.ts +1 -1
  173. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  174. package/lib/chain/validation/proposerPreferences.js +13 -4
  175. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  176. package/lib/chain/validation/proposerSlashing.js +1 -1
  177. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  178. package/lib/chain/validation/voluntaryExit.d.ts +9 -1
  179. package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
  180. package/lib/chain/validation/voluntaryExit.js +46 -1
  181. package/lib/chain/validation/voluntaryExit.js.map +1 -1
  182. package/lib/execution/builder/http.d.ts +10 -7
  183. package/lib/execution/builder/http.d.ts.map +1 -1
  184. package/lib/execution/builder/http.js +11 -8
  185. package/lib/execution/builder/http.js.map +1 -1
  186. package/lib/metrics/metrics/beacon.d.ts +3 -3
  187. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  188. package/lib/metrics/metrics/beacon.js +9 -9
  189. package/lib/metrics/metrics/beacon.js.map +1 -1
  190. package/lib/metrics/metrics/lodestar.d.ts +72 -22
  191. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  192. package/lib/metrics/metrics/lodestar.js +130 -52
  193. package/lib/metrics/metrics/lodestar.js.map +1 -1
  194. package/lib/network/gossip/topic.d.ts +13 -0
  195. package/lib/network/gossip/topic.d.ts.map +1 -1
  196. package/lib/network/gossip/topic.js +37 -0
  197. package/lib/network/gossip/topic.js.map +1 -1
  198. package/lib/network/libp2p/index.d.ts.map +1 -1
  199. package/lib/network/libp2p/index.js +10 -2
  200. package/lib/network/libp2p/index.js.map +1 -1
  201. package/lib/network/network.d.ts.map +1 -1
  202. package/lib/network/network.js +22 -43
  203. package/lib/network/network.js.map +1 -1
  204. package/lib/network/processor/constants.d.ts +7 -0
  205. package/lib/network/processor/constants.d.ts.map +1 -0
  206. package/lib/network/processor/constants.js +7 -0
  207. package/lib/network/processor/constants.js.map +1 -0
  208. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  209. package/lib/network/processor/gossipHandlers.js +12 -3
  210. package/lib/network/processor/gossipHandlers.js.map +1 -1
  211. package/lib/network/processor/index.d.ts +3 -0
  212. package/lib/network/processor/index.d.ts.map +1 -1
  213. package/lib/network/processor/index.js +36 -15
  214. package/lib/network/processor/index.js.map +1 -1
  215. package/lib/network/reqresp/score.d.ts.map +1 -1
  216. package/lib/network/reqresp/score.js +5 -11
  217. package/lib/network/reqresp/score.js.map +1 -1
  218. package/lib/network/util.d.ts +0 -1
  219. package/lib/network/util.d.ts.map +1 -1
  220. package/lib/network/util.js +0 -4
  221. package/lib/network/util.js.map +1 -1
  222. package/lib/node/deferredVoluntaryExitPublisher.d.ts +15 -0
  223. package/lib/node/deferredVoluntaryExitPublisher.d.ts.map +1 -0
  224. package/lib/node/deferredVoluntaryExitPublisher.js +38 -0
  225. package/lib/node/deferredVoluntaryExitPublisher.js.map +1 -0
  226. package/lib/node/nodejs.d.ts +2 -1
  227. package/lib/node/nodejs.d.ts.map +1 -1
  228. package/lib/node/nodejs.js +2 -0
  229. package/lib/node/nodejs.js.map +1 -1
  230. package/lib/sync/range/batch.d.ts +23 -5
  231. package/lib/sync/range/batch.d.ts.map +1 -1
  232. package/lib/sync/range/batch.js +61 -22
  233. package/lib/sync/range/batch.js.map +1 -1
  234. package/lib/sync/range/chain.d.ts +2 -1
  235. package/lib/sync/range/chain.d.ts.map +1 -1
  236. package/lib/sync/range/chain.js +23 -10
  237. package/lib/sync/range/chain.js.map +1 -1
  238. package/lib/sync/types.d.ts +32 -0
  239. package/lib/sync/types.d.ts.map +1 -1
  240. package/lib/sync/types.js +33 -0
  241. package/lib/sync/types.js.map +1 -1
  242. package/lib/sync/unknownBlock.d.ts +13 -2
  243. package/lib/sync/unknownBlock.d.ts.map +1 -1
  244. package/lib/sync/unknownBlock.js +218 -63
  245. package/lib/sync/unknownBlock.js.map +1 -1
  246. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  247. package/lib/sync/utils/downloadByRange.js +16 -11
  248. package/lib/sync/utils/downloadByRange.js.map +1 -1
  249. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  250. package/lib/sync/utils/downloadByRoot.js +3 -1
  251. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  252. package/package.json +35 -35
  253. package/src/api/impl/beacon/blocks/index.ts +7 -8
  254. package/src/api/impl/beacon/pool/index.ts +27 -2
  255. package/src/api/impl/beacon/state/utils.ts +2 -1
  256. package/src/api/impl/events/index.ts +12 -4
  257. package/src/api/impl/lodestar/index.ts +1 -1
  258. package/src/api/impl/validator/index.ts +1 -1
  259. package/src/api/rest/base.ts +7 -0
  260. package/src/chain/archiveStore/archiveStore.ts +1 -1
  261. package/src/chain/archiveStore/historicalState/metrics.ts +22 -0
  262. package/src/chain/blocks/importBlock.ts +1 -6
  263. package/src/chain/blocks/importExecutionPayload.ts +12 -11
  264. package/src/chain/blocks/index.ts +18 -6
  265. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
  266. package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
  267. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
  268. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
  269. package/src/chain/bls/index.ts +1 -1
  270. package/src/chain/bls/interface.ts +7 -3
  271. package/src/chain/bls/multithread/index.ts +148 -120
  272. package/src/chain/bls/multithread/jobItem.ts +12 -82
  273. package/src/chain/bls/multithread/types.ts +33 -8
  274. package/src/chain/bls/multithread/utils.ts +11 -0
  275. package/src/chain/bls/multithread/worker.ts +85 -27
  276. package/src/chain/bls/singleThread.ts +11 -55
  277. package/src/chain/bls/utils.ts +23 -24
  278. package/src/chain/builderCircuitBreaker.ts +31 -17
  279. package/src/chain/chain.ts +17 -6
  280. package/src/chain/errors/blockError.ts +8 -2
  281. package/src/chain/errors/proposerPreferences.ts +7 -0
  282. package/src/chain/forkChoice/index.ts +4 -0
  283. package/src/chain/interface.ts +4 -1
  284. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  285. package/src/chain/opPools/attestationPool.ts +1 -1
  286. package/src/chain/opPools/deferredVoluntaryExitPool.ts +65 -0
  287. package/src/chain/opPools/index.ts +1 -0
  288. package/src/chain/opPools/payloadAttestationPool.ts +3 -3
  289. package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
  290. package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
  291. package/src/chain/opPools/utils.ts +1 -1
  292. package/src/chain/options.ts +1 -1
  293. package/src/chain/prepareNextSlot.ts +24 -6
  294. package/src/chain/regen/interface.ts +4 -2
  295. package/src/chain/regen/queued.ts +5 -5
  296. package/src/chain/seenCache/seenAttesters.ts +31 -5
  297. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
  298. package/src/chain/stateCache/fifoBlockStateCache.ts +8 -1
  299. package/src/chain/stateCache/persistentCheckpointsCache.ts +133 -8
  300. package/src/chain/stateCache/types.ts +3 -3
  301. package/src/chain/validation/attestation.ts +1 -7
  302. package/src/chain/validation/attesterSlashing.ts +1 -8
  303. package/src/chain/validation/executionPayloadBid.ts +1 -2
  304. package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
  305. package/src/chain/validation/payloadAttestationMessage.ts +6 -17
  306. package/src/chain/validation/proposerPreferences.ts +15 -5
  307. package/src/chain/validation/proposerSlashing.ts +1 -1
  308. package/src/chain/validation/voluntaryExit.ts +59 -2
  309. package/src/execution/builder/http.ts +11 -11
  310. package/src/metrics/metrics/beacon.ts +9 -9
  311. package/src/metrics/metrics/lodestar.ts +142 -57
  312. package/src/network/gossip/topic.ts +38 -0
  313. package/src/network/libp2p/index.ts +11 -2
  314. package/src/network/network.ts +27 -55
  315. package/src/network/processor/constants.ts +6 -0
  316. package/src/network/processor/gossipHandlers.ts +14 -1
  317. package/src/network/processor/index.ts +42 -19
  318. package/src/network/reqresp/score.ts +5 -11
  319. package/src/network/util.ts +0 -5
  320. package/src/node/deferredVoluntaryExitPublisher.ts +54 -0
  321. package/src/node/nodejs.ts +5 -1
  322. package/src/sync/range/batch.ts +83 -28
  323. package/src/sync/range/chain.ts +26 -11
  324. package/src/sync/types.ts +36 -1
  325. package/src/sync/unknownBlock.ts +255 -70
  326. package/src/sync/utils/downloadByRange.ts +21 -12
  327. package/src/sync/utils/downloadByRoot.ts +3 -1
  328. package/lib/chain/bls/maybeBatch.d.ts +0 -12
  329. package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
  330. package/lib/chain/bls/maybeBatch.js +0 -35
  331. package/lib/chain/bls/maybeBatch.js.map +0 -1
  332. 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
 
@@ -533,8 +516,7 @@ export class Network implements INetwork {
533
516
 
534
517
  return this.publishGossip<GossipType.execution_payload_bid>(
535
518
  {type: GossipType.execution_payload_bid, boundary},
536
- signedBid,
537
- {ignoreDuplicatePublishError: true}
519
+ signedBid
538
520
  );
539
521
  }
540
522
 
@@ -544,8 +526,7 @@ export class Network implements INetwork {
544
526
 
545
527
  return this.publishGossip<GossipType.payload_attestation_message>(
546
528
  {type: GossipType.payload_attestation_message, boundary},
547
- payloadAttestationMessage,
548
- {ignoreDuplicatePublishError: true}
529
+ payloadAttestationMessage
549
530
  );
550
531
  }
551
532
 
@@ -555,22 +536,21 @@ export class Network implements INetwork {
555
536
 
556
537
  return this.publishGossip<GossipType.proposer_preferences>(
557
538
  {type: GossipType.proposer_preferences, boundary},
558
- signedProposerPreferences,
559
- {ignoreDuplicatePublishError: true}
539
+ signedProposerPreferences
560
540
  );
561
541
  }
562
542
 
563
543
  private async publishGossip<K extends GossipType>(
564
544
  topic: GossipTopicMap[K],
565
- object: GossipTypeMap[K],
566
- opts?: PublishOpts | undefined
545
+ object: GossipTypeMap[K]
567
546
  ): Promise<number> {
568
547
  const topicStr = stringifyGossipTopic(this.config, topic);
569
548
  const sszType = getGossipSSZType(topic);
570
549
  const messageData = (sszType.serialize as (object: GossipTypeMap[GossipType]) => Uint8Array)(object);
571
- opts = {
572
- ...opts,
550
+ const opts: PublishOpts = {
573
551
  ignoreDuplicatePublishError: gossipTopicIgnoreDuplicatePublishError[topic.type],
552
+ // Leave undefined unless the topic opts out, so `--network.allowPublishToZeroPeers` still applies
553
+ allowPublishToZeroTopicPeers: gossipTopicAllowPublishToZeroPeers[topic.type] ? true : undefined,
574
554
  };
575
555
  const sentPeers = await this.core.publishGossip(topicStr, messageData, opts);
576
556
 
@@ -813,11 +793,7 @@ export class Network implements INetwork {
813
793
  await this.waitForSyncMessageCutoff(finalityUpdate.signatureSlot);
814
794
  await this.publishLightClientFinalityUpdate(finalityUpdate);
815
795
  } 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
- }
796
+ this.logger.debug("Error on BeaconGossipHandler.onLightclientFinalityUpdate", {}, e as Error);
821
797
  }
822
798
  };
823
799
 
@@ -830,11 +806,7 @@ export class Network implements INetwork {
830
806
  await this.waitForSyncMessageCutoff(optimisticUpdate.signatureSlot);
831
807
  await this.publishLightClientOptimisticUpdate(optimisticUpdate);
832
808
  } 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
- }
809
+ this.logger.debug("Error on BeaconGossipHandler.onLightclientOptimisticUpdate", {}, e as Error);
838
810
  }
839
811
  };
840
812
 
@@ -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,11 +1294,16 @@ 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
1309
  const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid);
@@ -30,6 +30,7 @@ import {
30
30
  GossipValidatorBatchFn,
31
31
  GossipValidatorFn,
32
32
  } from "../gossip/interface.js";
33
+ import {MAX_PEERS_PER_ROOT} from "./constants.js";
33
34
  import {createExtractBlockSlotRootFns} from "./extractSlotRootFns.js";
34
35
  import {GossipHandlerOpts, ValidatorFnsModules, getGossipHandlers} from "./gossipHandlers.js";
35
36
  import {createGossipQueues} from "./gossipQueues/index.js";
@@ -191,8 +192,12 @@ export class NetworkProcessor {
191
192
  // in that case PendingGossipsubMessage needs to be stored in this Map and reprocessed once the payload comes
192
193
  private readonly awaitingMessagesByPayloadBlockRoot: MapDef<RootHex, Set<PendingGossipsubMessage>>;
193
194
  private awaitingPayloadMessageCount = 0;
194
- private unknownBlocksBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
195
- private unknownEnvelopesBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
195
+ private unknownBlocksBySlot = new MapDef<Slot, MapDef<RootHex, Set<PeerIdStr>>>(
196
+ () => new MapDef<RootHex, Set<PeerIdStr>>(() => new Set())
197
+ );
198
+ private unknownEnvelopesBySlot = new MapDef<Slot, MapDef<RootHex, Set<PeerIdStr>>>(
199
+ () => new MapDef<RootHex, Set<PeerIdStr>>(() => new Set())
200
+ );
196
201
 
197
202
  constructor(
198
203
  modules: NetworkProcessorModules,
@@ -271,35 +276,53 @@ export class NetworkProcessor {
271
276
  * Search block via `ChainEvent.unknownBlockRoot` event
272
277
  * 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
278
  * 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.
279
+ * We forward every distinct peer that gossiped this root (not just the first) so BlockInputSync can
280
+ * prefer them as by-root download candidates.
274
281
  */
275
282
  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
- ) {
283
+ if (this.chain.seenBlock(root)) {
281
284
  return;
282
285
  }
283
- // Search for the unknown block
284
- this.unknownBlocksBySlot.getOrDefault(slot).add(root);
285
- this.chain.emitter.emit(ChainEvent.unknownBlockRoot, {rootHex: root, peer, source});
286
+ const peersForRoot = this.unknownBlocksBySlot.getOrDefault(slot);
287
+ const alreadySearching = peersForRoot.has(root) || this.awaitingMessagesByBlockRoot.has(root);
288
+ const forwardedPeers = peersForRoot.getOrDefault(root);
289
+
290
+ // brand-new search always emits (peer may be undefined)
291
+ let shouldEmit = !alreadySearching;
292
+ if (peer !== undefined && !forwardedPeers.has(peer) && forwardedPeers.size < MAX_PEERS_PER_ROOT) {
293
+ // First time we forward THIS peer for THIS root: emit so BlockInputSync accumulates it.
294
+ forwardedPeers.add(peer);
295
+ shouldEmit = true;
296
+ }
297
+ if (shouldEmit) {
298
+ this.chain.emitter.emit(ChainEvent.unknownBlockRoot, {rootHex: root, peer, source});
299
+ }
286
300
  }
287
301
 
288
302
  /**
289
303
  * Search envelope via `ChainEvent.unknownEnvelopeBlockRoot` event
290
304
  * 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
305
  * 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.
306
+ * Same peer-forwarding + dedup behavior as searchUnknownBlock.
292
307
  */
293
308
  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
- ) {
309
+ if (this.chain.seenPayloadEnvelope(root)) {
299
310
  return;
300
311
  }
301
- this.unknownEnvelopesBySlot.getOrDefault(slot).add(root);
302
- this.chain.emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, {rootHex: root, slot, peer, source});
312
+ const peersForRoot = this.unknownEnvelopesBySlot.getOrDefault(slot);
313
+ // capture "already searching" BEFORE getOrDefault(root) creates the entry
314
+ const alreadySearching = peersForRoot.has(root) || this.awaitingMessagesByPayloadBlockRoot.has(root);
315
+ const forwardedPeers = peersForRoot.getOrDefault(root);
316
+
317
+ let shouldEmit = !alreadySearching; // brand-new search always emits (peer may be undefined)
318
+ if (peer !== undefined && !forwardedPeers.has(peer) && forwardedPeers.size < MAX_PEERS_PER_ROOT) {
319
+ // First time we forward THIS peer for THIS root: emit so BlockInputSync accumulates it.
320
+ forwardedPeers.add(peer);
321
+ shouldEmit = true;
322
+ }
323
+ if (shouldEmit) {
324
+ this.chain.emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, {rootHex: root, slot, peer, source});
325
+ }
303
326
  }
304
327
 
305
328
  private onPendingGossipsubMessage = (message: PendingGossipsubMessage): void => {
@@ -616,7 +639,7 @@ export class NetworkProcessor {
616
639
 
617
640
  for (const [slot, roots] of this.unknownBlocksBySlot) {
618
641
  if (slot > minSlot) continue;
619
- for (const rootHex of roots) {
642
+ for (const rootHex of roots.keys()) {
620
643
  const gossipMessages = this.awaitingMessagesByBlockRoot.get(rootHex);
621
644
  if (gossipMessages !== undefined) {
622
645
  for (const message of gossipMessages) {
@@ -641,7 +664,7 @@ export class NetworkProcessor {
641
664
 
642
665
  for (const [slot, roots] of this.unknownEnvelopesBySlot) {
643
666
  if (slot > minSlot) continue;
644
- for (const rootHex of roots) {
667
+ for (const rootHex of roots.keys()) {
645
668
  const gossipMessages = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
646
669
  if (gossipMessages !== undefined) {
647
670
  for (const message of gossipMessages) {
@@ -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
 
@@ -334,6 +336,8 @@ export class BeaconNode {
334
336
 
335
337
  void runNodeNotifier({network, chain, sync, config, logger, signal});
336
338
 
339
+ startDeferredVoluntaryExitPublisher({chain, network, logger, signal});
340
+
337
341
  return new BeaconNode({
338
342
  opts,
339
343
  config,