@lodestar/beacon-node 1.47.0-dev.f591cb177e → 1.48.0-dev.67344272c0

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 (342) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +44 -12
  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 +21 -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.map +1 -1
  11. package/lib/api/impl/events/index.js +2 -0
  12. package/lib/api/impl/events/index.js.map +1 -1
  13. package/lib/api/impl/lodestar/index.js +1 -1
  14. package/lib/api/impl/lodestar/index.js.map +1 -1
  15. package/lib/api/impl/validator/index.d.ts.map +1 -1
  16. package/lib/api/impl/validator/index.js +237 -49
  17. package/lib/api/impl/validator/index.js.map +1 -1
  18. package/lib/api/impl/validator/utils.js +1 -1
  19. package/lib/api/impl/validator/utils.js.map +1 -1
  20. package/lib/api/rest/base.d.ts.map +1 -1
  21. package/lib/api/rest/base.js +7 -0
  22. package/lib/api/rest/base.js.map +1 -1
  23. package/lib/chain/archiveStore/archiveStore.js +1 -1
  24. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  25. package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
  26. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  27. package/lib/chain/blocks/importExecutionPayload.js +11 -9
  28. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  29. package/lib/chain/blocks/index.d.ts.map +1 -1
  30. package/lib/chain/blocks/index.js +21 -13
  31. package/lib/chain/blocks/index.js.map +1 -1
  32. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
  33. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  34. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
  35. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  36. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
  37. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  38. package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
  39. package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
  40. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  41. package/lib/chain/blocks/verifyBlock.js +5 -2
  42. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  43. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  44. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
  45. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  46. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  47. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  48. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  49. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  50. package/lib/chain/bls/index.d.ts +1 -1
  51. package/lib/chain/bls/index.d.ts.map +1 -1
  52. package/lib/chain/bls/interface.d.ts +5 -5
  53. package/lib/chain/bls/interface.d.ts.map +1 -1
  54. package/lib/chain/bls/multithread/index.d.ts +5 -11
  55. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  56. package/lib/chain/bls/multithread/index.js +116 -101
  57. package/lib/chain/bls/multithread/index.js.map +1 -1
  58. package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
  59. package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
  60. package/lib/chain/bls/multithread/jobItem.js +10 -65
  61. package/lib/chain/bls/multithread/jobItem.js.map +1 -1
  62. package/lib/chain/bls/multithread/types.d.ts +29 -8
  63. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  64. package/lib/chain/bls/multithread/types.js +12 -0
  65. package/lib/chain/bls/multithread/types.js.map +1 -1
  66. package/lib/chain/bls/multithread/utils.d.ts +4 -0
  67. package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
  68. package/lib/chain/bls/multithread/utils.js +10 -0
  69. package/lib/chain/bls/multithread/utils.js.map +1 -1
  70. package/lib/chain/bls/multithread/worker.js +49 -23
  71. package/lib/chain/bls/multithread/worker.js.map +1 -1
  72. package/lib/chain/bls/singleThread.d.ts +4 -10
  73. package/lib/chain/bls/singleThread.d.ts.map +1 -1
  74. package/lib/chain/bls/singleThread.js +8 -45
  75. package/lib/chain/bls/singleThread.js.map +1 -1
  76. package/lib/chain/bls/utils.d.ts +4 -4
  77. package/lib/chain/bls/utils.d.ts.map +1 -1
  78. package/lib/chain/bls/utils.js +18 -19
  79. package/lib/chain/bls/utils.js.map +1 -1
  80. package/lib/chain/chain.d.ts +9 -4
  81. package/lib/chain/chain.d.ts.map +1 -1
  82. package/lib/chain/chain.js +22 -8
  83. package/lib/chain/chain.js.map +1 -1
  84. package/lib/chain/errors/blockError.d.ts +1 -0
  85. package/lib/chain/errors/blockError.d.ts.map +1 -1
  86. package/lib/chain/errors/blockError.js +6 -1
  87. package/lib/chain/errors/blockError.js.map +1 -1
  88. package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
  89. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  90. package/lib/chain/errors/executionPayloadBid.js +1 -0
  91. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  92. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  93. package/lib/chain/forkChoice/index.js +4 -0
  94. package/lib/chain/forkChoice/index.js.map +1 -1
  95. package/lib/chain/interface.d.ts +8 -4
  96. package/lib/chain/interface.d.ts.map +1 -1
  97. package/lib/chain/interface.js.map +1 -1
  98. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  99. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  100. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  101. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  102. package/lib/chain/opPools/attestationPool.js +1 -1
  103. package/lib/chain/opPools/attestationPool.js.map +1 -1
  104. package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts +16 -0
  105. package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts.map +1 -0
  106. package/lib/chain/opPools/deferredVoluntaryExitPool.js +57 -0
  107. package/lib/chain/opPools/deferredVoluntaryExitPool.js.map +1 -0
  108. package/lib/chain/opPools/executionPayloadBidPool.d.ts +7 -2
  109. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
  110. package/lib/chain/opPools/executionPayloadBidPool.js +4 -4
  111. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
  112. package/lib/chain/opPools/index.d.ts +2 -1
  113. package/lib/chain/opPools/index.d.ts.map +1 -1
  114. package/lib/chain/opPools/index.js +1 -0
  115. package/lib/chain/opPools/index.js.map +1 -1
  116. package/lib/chain/opPools/payloadAttestationPool.js +3 -3
  117. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  118. package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
  119. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  120. package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
  121. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  122. package/lib/chain/opPools/utils.d.ts +1 -1
  123. package/lib/chain/opPools/utils.d.ts.map +1 -1
  124. package/lib/chain/opPools/utils.js +1 -1
  125. package/lib/chain/opPools/utils.js.map +1 -1
  126. package/lib/chain/prepareNextSlot.d.ts +5 -0
  127. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  128. package/lib/chain/prepareNextSlot.js +47 -6
  129. package/lib/chain/prepareNextSlot.js.map +1 -1
  130. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  131. package/lib/chain/produceBlock/computeNewStateRoot.js +3 -25
  132. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  133. package/lib/chain/produceBlock/produceBlockBody.js +1 -1
  134. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  135. package/lib/chain/regen/interface.d.ts +5 -2
  136. package/lib/chain/regen/interface.d.ts.map +1 -1
  137. package/lib/chain/regen/interface.js +3 -0
  138. package/lib/chain/regen/interface.js.map +1 -1
  139. package/lib/chain/regen/queued.d.ts +2 -2
  140. package/lib/chain/regen/queued.d.ts.map +1 -1
  141. package/lib/chain/regen/queued.js +5 -5
  142. package/lib/chain/regen/queued.js.map +1 -1
  143. package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
  144. package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
  145. package/lib/chain/seenCache/seenAttesters.js +25 -4
  146. package/lib/chain/seenCache/seenAttesters.js.map +1 -1
  147. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
  148. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  149. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
  150. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  151. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +5 -1
  152. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  153. package/lib/chain/stateCache/fifoBlockStateCache.js +6 -0
  154. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  155. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +47 -5
  156. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  157. package/lib/chain/stateCache/persistentCheckpointsCache.js +117 -7
  158. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  159. package/lib/chain/stateCache/types.d.ts +3 -3
  160. package/lib/chain/stateCache/types.d.ts.map +1 -1
  161. package/lib/chain/validation/attestation.d.ts.map +1 -1
  162. package/lib/chain/validation/attestation.js +1 -4
  163. package/lib/chain/validation/attestation.js.map +1 -1
  164. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  165. package/lib/chain/validation/attesterSlashing.js +1 -1
  166. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  167. package/lib/chain/validation/block.d.ts.map +1 -1
  168. package/lib/chain/validation/block.js +40 -10
  169. package/lib/chain/validation/block.js.map +1 -1
  170. package/lib/chain/validation/executionPayloadBid.d.ts +15 -3
  171. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  172. package/lib/chain/validation/executionPayloadBid.js +166 -12
  173. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  174. package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
  175. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  176. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  177. package/lib/chain/validation/payloadAttestationMessage.js +5 -14
  178. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  179. package/lib/chain/validation/proposerPreferences.js +2 -2
  180. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  181. package/lib/chain/validation/proposerSlashing.js +1 -1
  182. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  183. package/lib/chain/validation/voluntaryExit.d.ts +9 -1
  184. package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
  185. package/lib/chain/validation/voluntaryExit.js +46 -1
  186. package/lib/chain/validation/voluntaryExit.js.map +1 -1
  187. package/lib/execution/builder/apiClient.d.ts +70 -0
  188. package/lib/execution/builder/apiClient.d.ts.map +1 -0
  189. package/lib/execution/builder/apiClient.js +202 -0
  190. package/lib/execution/builder/apiClient.js.map +1 -0
  191. package/lib/execution/builder/validateBid.d.ts +23 -0
  192. package/lib/execution/builder/validateBid.d.ts.map +1 -0
  193. package/lib/execution/builder/validateBid.js +119 -0
  194. package/lib/execution/builder/validateBid.js.map +1 -0
  195. package/lib/metrics/metrics/lodestar.d.ts +84 -23
  196. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  197. package/lib/metrics/metrics/lodestar.js +161 -55
  198. package/lib/metrics/metrics/lodestar.js.map +1 -1
  199. package/lib/network/gossip/topic.d.ts +13 -0
  200. package/lib/network/gossip/topic.d.ts.map +1 -1
  201. package/lib/network/gossip/topic.js +37 -0
  202. package/lib/network/gossip/topic.js.map +1 -1
  203. package/lib/network/network.d.ts.map +1 -1
  204. package/lib/network/network.js +23 -43
  205. package/lib/network/network.js.map +1 -1
  206. package/lib/network/processor/constants.d.ts +7 -0
  207. package/lib/network/processor/constants.d.ts.map +1 -0
  208. package/lib/network/processor/constants.js +7 -0
  209. package/lib/network/processor/constants.js.map +1 -0
  210. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  211. package/lib/network/processor/gossipHandlers.js +6 -2
  212. package/lib/network/processor/gossipHandlers.js.map +1 -1
  213. package/lib/network/processor/gossipQueues/indexed.d.ts.map +1 -1
  214. package/lib/network/processor/gossipQueues/indexed.js +0 -1
  215. package/lib/network/processor/gossipQueues/indexed.js.map +1 -1
  216. package/lib/network/processor/index.d.ts +3 -0
  217. package/lib/network/processor/index.d.ts.map +1 -1
  218. package/lib/network/processor/index.js +52 -23
  219. package/lib/network/processor/index.js.map +1 -1
  220. package/lib/network/reqresp/score.d.ts.map +1 -1
  221. package/lib/network/reqresp/score.js +5 -11
  222. package/lib/network/reqresp/score.js.map +1 -1
  223. package/lib/network/util.d.ts +0 -1
  224. package/lib/network/util.d.ts.map +1 -1
  225. package/lib/network/util.js +0 -4
  226. package/lib/network/util.js.map +1 -1
  227. package/lib/node/deferredVoluntaryExitPublisher.d.ts +15 -0
  228. package/lib/node/deferredVoluntaryExitPublisher.d.ts.map +1 -0
  229. package/lib/node/deferredVoluntaryExitPublisher.js +38 -0
  230. package/lib/node/deferredVoluntaryExitPublisher.js.map +1 -0
  231. package/lib/node/nodejs.d.ts +2 -1
  232. package/lib/node/nodejs.d.ts.map +1 -1
  233. package/lib/node/nodejs.js +7 -0
  234. package/lib/node/nodejs.js.map +1 -1
  235. package/lib/sync/range/batch.d.ts +23 -5
  236. package/lib/sync/range/batch.d.ts.map +1 -1
  237. package/lib/sync/range/batch.js +61 -22
  238. package/lib/sync/range/batch.js.map +1 -1
  239. package/lib/sync/range/chain.d.ts +2 -1
  240. package/lib/sync/range/chain.d.ts.map +1 -1
  241. package/lib/sync/range/chain.js +32 -12
  242. package/lib/sync/range/chain.js.map +1 -1
  243. package/lib/sync/range/range.js +1 -1
  244. package/lib/sync/range/range.js.map +1 -1
  245. package/lib/sync/types.d.ts +32 -0
  246. package/lib/sync/types.d.ts.map +1 -1
  247. package/lib/sync/types.js +33 -0
  248. package/lib/sync/types.js.map +1 -1
  249. package/lib/sync/unknownBlock.d.ts +6 -2
  250. package/lib/sync/unknownBlock.d.ts.map +1 -1
  251. package/lib/sync/unknownBlock.js +143 -68
  252. package/lib/sync/unknownBlock.js.map +1 -1
  253. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  254. package/lib/sync/utils/downloadByRange.js +16 -11
  255. package/lib/sync/utils/downloadByRange.js.map +1 -1
  256. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  257. package/lib/sync/utils/downloadByRoot.js +3 -1
  258. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  259. package/package.json +35 -35
  260. package/src/api/impl/beacon/blocks/index.ts +51 -15
  261. package/src/api/impl/beacon/pool/index.ts +22 -2
  262. package/src/api/impl/beacon/state/utils.ts +2 -1
  263. package/src/api/impl/events/index.ts +3 -1
  264. package/src/api/impl/lodestar/index.ts +1 -1
  265. package/src/api/impl/validator/index.ts +290 -49
  266. package/src/api/impl/validator/utils.ts +1 -1
  267. package/src/api/rest/base.ts +7 -0
  268. package/src/chain/archiveStore/archiveStore.ts +1 -1
  269. package/src/chain/blocks/importExecutionPayload.ts +12 -11
  270. package/src/chain/blocks/index.ts +25 -13
  271. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
  272. package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
  273. package/src/chain/blocks/verifyBlock.ts +5 -2
  274. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
  275. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
  276. package/src/chain/bls/index.ts +1 -1
  277. package/src/chain/bls/interface.ts +7 -3
  278. package/src/chain/bls/multithread/index.ts +148 -120
  279. package/src/chain/bls/multithread/jobItem.ts +12 -82
  280. package/src/chain/bls/multithread/types.ts +33 -8
  281. package/src/chain/bls/multithread/utils.ts +11 -0
  282. package/src/chain/bls/multithread/worker.ts +85 -27
  283. package/src/chain/bls/singleThread.ts +11 -55
  284. package/src/chain/bls/utils.ts +23 -24
  285. package/src/chain/chain.ts +25 -7
  286. package/src/chain/errors/blockError.ts +8 -2
  287. package/src/chain/errors/executionPayloadBid.ts +6 -0
  288. package/src/chain/forkChoice/index.ts +4 -0
  289. package/src/chain/interface.ts +8 -3
  290. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  291. package/src/chain/opPools/attestationPool.ts +1 -1
  292. package/src/chain/opPools/deferredVoluntaryExitPool.ts +65 -0
  293. package/src/chain/opPools/executionPayloadBidPool.ts +13 -7
  294. package/src/chain/opPools/index.ts +2 -1
  295. package/src/chain/opPools/payloadAttestationPool.ts +3 -3
  296. package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
  297. package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
  298. package/src/chain/opPools/utils.ts +1 -1
  299. package/src/chain/prepareNextSlot.ts +49 -6
  300. package/src/chain/produceBlock/computeNewStateRoot.ts +3 -38
  301. package/src/chain/produceBlock/produceBlockBody.ts +1 -1
  302. package/src/chain/regen/interface.ts +5 -2
  303. package/src/chain/regen/queued.ts +5 -5
  304. package/src/chain/seenCache/seenAttesters.ts +31 -5
  305. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
  306. package/src/chain/stateCache/fifoBlockStateCache.ts +8 -1
  307. package/src/chain/stateCache/persistentCheckpointsCache.ts +133 -8
  308. package/src/chain/stateCache/types.ts +3 -3
  309. package/src/chain/validation/attestation.ts +1 -7
  310. package/src/chain/validation/attesterSlashing.ts +1 -8
  311. package/src/chain/validation/block.ts +43 -10
  312. package/src/chain/validation/executionPayloadBid.ts +193 -14
  313. package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
  314. package/src/chain/validation/payloadAttestationMessage.ts +6 -17
  315. package/src/chain/validation/proposerPreferences.ts +3 -3
  316. package/src/chain/validation/proposerSlashing.ts +1 -1
  317. package/src/chain/validation/voluntaryExit.ts +59 -2
  318. package/src/execution/builder/apiClient.ts +264 -0
  319. package/src/execution/builder/validateBid.ts +181 -0
  320. package/src/metrics/metrics/lodestar.ts +176 -61
  321. package/src/network/gossip/topic.ts +38 -0
  322. package/src/network/network.ts +29 -54
  323. package/src/network/processor/constants.ts +6 -0
  324. package/src/network/processor/gossipHandlers.ts +6 -2
  325. package/src/network/processor/gossipQueues/indexed.ts +0 -1
  326. package/src/network/processor/index.ts +67 -30
  327. package/src/network/reqresp/score.ts +5 -11
  328. package/src/network/util.ts +0 -5
  329. package/src/node/deferredVoluntaryExitPublisher.ts +54 -0
  330. package/src/node/nodejs.ts +10 -1
  331. package/src/sync/range/batch.ts +83 -28
  332. package/src/sync/range/chain.ts +35 -13
  333. package/src/sync/range/range.ts +1 -1
  334. package/src/sync/types.ts +33 -0
  335. package/src/sync/unknownBlock.ts +167 -73
  336. package/src/sync/utils/downloadByRange.ts +21 -12
  337. package/src/sync/utils/downloadByRoot.ts +3 -1
  338. package/lib/chain/bls/maybeBatch.d.ts +0 -12
  339. package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
  340. package/lib/chain/bls/maybeBatch.js +0 -35
  341. package/lib/chain/bls/maybeBatch.js.map +0 -1
  342. package/src/chain/bls/maybeBatch.ts +0 -45
@@ -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) {
@@ -1302,7 +1306,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1302
1306
 
1303
1307
  // Handle valid payload bid by storing in a bid pool
1304
1308
  try {
1305
- const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid);
1309
+ const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid, Math.floor(elapsedSec * 1000));
1306
1310
  metrics?.opPool.executionPayloadBidPool.gossipInsertOutcome.inc({insertOutcome});
1307
1311
  } catch (e) {
1308
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,
@@ -39,6 +39,7 @@ export enum BatchStatus {
39
39
  AwaitingValidation = "AwaitingValidation",
40
40
  }
41
41
 
42
+ /** Identity of one download attempt*/
42
43
  export type Attempt = {
43
44
  /** The peer that made the attempt */
44
45
  peers: PeerIdStr[];
@@ -46,6 +47,16 @@ export type Attempt = {
46
47
  hash: RootHex;
47
48
  };
48
49
 
50
+ /**
51
+ * A failed attempt that has been classified for blame.
52
+ */
53
+ export type FailedAttempt = Attempt & {
54
+ /**
55
+ * True when the failure is evidence the peers served bad data, so they may be downscored..
56
+ */
57
+ peerAttributable: boolean;
58
+ };
59
+
49
60
  type TrackedRequest = {
50
61
  /** only happen for the 1st batch in checkpoint sync */
51
62
  parentPayload: boolean;
@@ -108,10 +119,12 @@ export type BatchMetadata = {
108
119
  // Retry counters
109
120
  downloadAttempts: number;
110
121
  processingAttempts: number;
122
+ executionErrorAttempts: number;
111
123
 
112
124
  // Cumulative peer attribution for failed attempts (only present when non-empty)
113
125
  failedDownloadPeers?: string;
114
126
  failedProcessingPeers?: string;
127
+ executionErrorPeers?: string;
115
128
  };
116
129
 
117
130
  function formatRangeReq(req: {startSlot: Slot; count: number}): string {
@@ -152,10 +165,10 @@ export class Batch {
152
165
  state: BatchState = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
153
166
  /** Peers that provided good data, with column coverage for by_range requests */
154
167
  private readonly successfulDownloads = new Map<PeerIdStr, TrackedRequest>();
155
- /** The `Attempts` that have been made and failed to send us this batch. */
156
- readonly failedProcessingAttempts: Attempt[] = [];
157
- /** The `Attempts` that have been made and failed because of execution malfunction. */
158
- readonly executionErrorAttempts: Attempt[] = [];
168
+ /** The attempts that have been made and failed to send us this batch. */
169
+ readonly failedProcessingAttempts: FailedAttempt[] = [];
170
+ /** The attempts that have been made and failed because of execution malfunction. */
171
+ readonly executionErrorAttempts: FailedAttempt[] = [];
159
172
  /** The number of download retries this batch has undergone due to a failed request. */
160
173
  private readonly failedDownloadAttempts: PeerIdStr[] = [];
161
174
  private readonly config: ChainForkConfig;
@@ -419,10 +432,18 @@ export class Batch {
419
432
  }
420
433
 
421
434
  /**
422
- * Gives a list of peers from which this batch has had a failed download or processing attempt.
435
+ * Gives a list of peers from which this batch has had a failed download attempt, or a
436
+ * peer-attributable failed processing/execution attempt.
423
437
  */
424
438
  getFailedPeers(): PeerIdStr[] {
425
- return [...this.failedDownloadAttempts, ...this.failedProcessingAttempts.flatMap((a) => a.peers)];
439
+ return [...this.failedDownloadAttempts, ...this.getPeerAttributableAttempts().flatMap((a) => a.peers)];
440
+ }
441
+
442
+ /**
443
+ * Attempts whose failure is peer-attributable, across both the processing and execution error
444
+ */
445
+ getPeerAttributableAttempts(): FailedAttempt[] {
446
+ return [...this.failedProcessingAttempts, ...this.executionErrorAttempts].filter((a) => a.peerAttributable);
426
447
  }
427
448
 
428
449
  /**
@@ -457,6 +478,7 @@ export class Batch {
457
478
  getMetadata(): BatchMetadata {
458
479
  const {blocksRequest, blobsRequest, columnsRequest, envelopesRequest} = this.requests;
459
480
  const failedProcessingPeerList = this.failedProcessingAttempts.flatMap((a) => a.peers);
481
+ const executionErrorPeerList = this.executionErrorAttempts.flatMap((a) => a.peers);
460
482
  return {
461
483
  startEpoch: this.startEpoch,
462
484
  startSlot: this.startSlot,
@@ -468,12 +490,16 @@ export class Batch {
468
490
  ...(envelopesRequest && {envelopesReq: formatRangeReq(envelopesRequest)}),
469
491
  downloadAttempts: this.failedDownloadAttempts.length,
470
492
  processingAttempts: this.failedProcessingAttempts.length,
493
+ executionErrorAttempts: this.executionErrorAttempts.length,
471
494
  ...(this.failedDownloadAttempts.length > 0 && {
472
495
  failedDownloadPeers: this.failedDownloadAttempts.join(","),
473
496
  }),
474
497
  ...(failedProcessingPeerList.length > 0 && {
475
498
  failedProcessingPeers: failedProcessingPeerList.join(","),
476
499
  }),
500
+ ...(executionErrorPeerList.length > 0 && {
501
+ executionErrorPeers: executionErrorPeerList.join(","),
502
+ }),
477
503
  };
478
504
  }
479
505
 
@@ -703,11 +729,7 @@ export class Batch {
703
729
  throw new BatchError(this.wrongStatusErrorType(BatchStatus.Processing));
704
730
  }
705
731
 
706
- if (isExecutionEngineError(err)) {
707
- this.onExecutionEngineError(this.state.attempt);
708
- } else {
709
- this.onProcessingError(this.state.attempt);
710
- }
732
+ this.routeProcessingFailure(err, this.state.attempt);
711
733
  }
712
734
 
713
735
  /**
@@ -718,10 +740,17 @@ export class Batch {
718
740
  throw new BatchError(this.wrongStatusErrorType(BatchStatus.AwaitingValidation));
719
741
  }
720
742
 
721
- if (isExecutionEngineError(err)) {
722
- this.onExecutionEngineError(this.state.attempt);
743
+ this.routeProcessingFailure(err, this.state.attempt);
744
+ }
745
+
746
+ private routeProcessingFailure(err: Error, attempt: Attempt): void {
747
+ const code = err instanceof BlockError || err instanceof PayloadError ? err.type.code : null;
748
+ const failedAttempt: FailedAttempt = {...attempt, peerAttributable: isPeerAttributableFailure(code)};
749
+
750
+ if (isExecutionEngineFailure(code)) {
751
+ this.onExecutionEngineError(failedAttempt);
723
752
  } else {
724
- this.onProcessingError(this.state.attempt);
753
+ this.onProcessingError(failedAttempt);
725
754
  }
726
755
  }
727
756
 
@@ -735,7 +764,7 @@ export class Batch {
735
764
  return this.state.attempt;
736
765
  }
737
766
 
738
- private onExecutionEngineError(attempt: Attempt): void {
767
+ private onExecutionEngineError(attempt: FailedAttempt): void {
739
768
  this.executionErrorAttempts.push(attempt);
740
769
  if (this.executionErrorAttempts.length > MAX_BATCH_PROCESSING_ATTEMPTS) {
741
770
  throw new BatchError(this.errorType({code: BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS}));
@@ -746,7 +775,7 @@ export class Batch {
746
775
  this.state = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
747
776
  }
748
777
 
749
- private onProcessingError(attempt: Attempt): void {
778
+ private onProcessingError(attempt: FailedAttempt): void {
750
779
  this.failedProcessingAttempts.push(attempt);
751
780
  if (this.failedProcessingAttempts.length > MAX_BATCH_PROCESSING_ATTEMPTS) {
752
781
  throw new BatchError(this.errorType({code: BatchErrorCode.MAX_PROCESSING_ATTEMPTS}));
@@ -789,18 +818,44 @@ type BatchErrorMetadata = {
789
818
 
790
819
  export class BatchError extends LodestarError<BatchErrorType & BatchErrorMetadata> {}
791
820
 
792
- function isExecutionEngineError(err: Error): boolean {
793
- if (!(err instanceof BlockError)) {
794
- return false;
795
- }
796
-
797
- if (err.type.code === BlockErrorCode.EXECUTION_ENGINE_ERROR) {
798
- return true;
799
- }
800
-
821
+ /**
822
+ * Whether a processing failure is an execution-engine failure. Pre-gloas, all EL errors stay in
823
+ * BlockErrorCode, post-gloas, they stay in PayloadErrorCode.
824
+ */
825
+ function isExecutionEngineFailure(code: BlockErrorCode | PayloadErrorCode | null): boolean {
801
826
  return (
802
- err.type.code === BlockErrorCode.BEACON_CHAIN_ERROR &&
803
- err.type.error instanceof PayloadError &&
804
- err.type.error.type.code === PayloadErrorCode.EXECUTION_ENGINE_ERROR
827
+ code === BlockErrorCode.EXECUTION_ENGINE_INVALID ||
828
+ code === BlockErrorCode.EXECUTION_ENGINE_ERROR ||
829
+ code === PayloadErrorCode.EXECUTION_ENGINE_INVALID ||
830
+ code === PayloadErrorCode.EXECUTION_ENGINE_ERROR
805
831
  );
806
832
  }
833
+
834
+ /**
835
+ * Based on an error code, return if peer is attributable for FailedAttempt.
836
+ */
837
+ function isPeerAttributableFailure(code: BlockErrorCode | PayloadErrorCode | null): boolean {
838
+ switch (code) {
839
+ // EL returned a definitive INVALID verdict on the payload
840
+ case BlockErrorCode.EXECUTION_ENGINE_INVALID:
841
+ case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
842
+ // a block or envelope signature is invalid
843
+ case BlockErrorCode.INVALID_SIGNATURE:
844
+ case PayloadErrorCode.INVALID_SIGNATURE:
845
+ // the block's state transition produced an invalid state root, or failed per_slot/per_block processing
846
+ case BlockErrorCode.INVALID_STATE_ROOT:
847
+ case BlockErrorCode.PER_BLOCK_PROCESSING_ERROR:
848
+ // the segment this peer served is internally inconsistent (in-segment link breaks, see PR #9684)
849
+ case BlockErrorCode.NON_LINEAR_SLOTS:
850
+ case BlockErrorCode.NON_LINEAR_PARENT_ROOTS:
851
+ case BlockErrorCode.NON_LINEAR_PAYLOAD_ROOTS:
852
+ // the envelope references a mismatched block root, or fails field verification
853
+ case BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH:
854
+ case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
855
+ // the peer served a block we have explicitly blacklisted
856
+ case BlockErrorCode.BLACKLISTED_BLOCK:
857
+ return true;
858
+ default:
859
+ return false;
860
+ }
861
+ }