@lodestar/beacon-node 1.47.0-dev.2a04194b90 → 1.47.0-dev.2fba076c28

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 (317) 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 -6
  82. package/lib/chain/builderCircuitBreaker.d.ts.map +1 -1
  83. package/lib/chain/builderCircuitBreaker.js +19 -17
  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/metrics/metrics/beacon.d.ts +3 -3
  183. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  184. package/lib/metrics/metrics/beacon.js +9 -9
  185. package/lib/metrics/metrics/beacon.js.map +1 -1
  186. package/lib/metrics/metrics/lodestar.d.ts +72 -22
  187. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  188. package/lib/metrics/metrics/lodestar.js +130 -52
  189. package/lib/metrics/metrics/lodestar.js.map +1 -1
  190. package/lib/network/gossip/topic.d.ts +13 -0
  191. package/lib/network/gossip/topic.d.ts.map +1 -1
  192. package/lib/network/gossip/topic.js +37 -0
  193. package/lib/network/gossip/topic.js.map +1 -1
  194. package/lib/network/libp2p/index.d.ts.map +1 -1
  195. package/lib/network/libp2p/index.js +10 -2
  196. package/lib/network/libp2p/index.js.map +1 -1
  197. package/lib/network/network.d.ts.map +1 -1
  198. package/lib/network/network.js +22 -43
  199. package/lib/network/network.js.map +1 -1
  200. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  201. package/lib/network/processor/gossipHandlers.js +12 -3
  202. package/lib/network/processor/gossipHandlers.js.map +1 -1
  203. package/lib/network/reqresp/score.d.ts.map +1 -1
  204. package/lib/network/reqresp/score.js +5 -11
  205. package/lib/network/reqresp/score.js.map +1 -1
  206. package/lib/network/util.d.ts +0 -1
  207. package/lib/network/util.d.ts.map +1 -1
  208. package/lib/network/util.js +0 -4
  209. package/lib/network/util.js.map +1 -1
  210. package/lib/node/deferredVoluntaryExitPublisher.d.ts +15 -0
  211. package/lib/node/deferredVoluntaryExitPublisher.d.ts.map +1 -0
  212. package/lib/node/deferredVoluntaryExitPublisher.js +38 -0
  213. package/lib/node/deferredVoluntaryExitPublisher.js.map +1 -0
  214. package/lib/node/nodejs.d.ts +2 -1
  215. package/lib/node/nodejs.d.ts.map +1 -1
  216. package/lib/node/nodejs.js +2 -0
  217. package/lib/node/nodejs.js.map +1 -1
  218. package/lib/sync/range/batch.d.ts +23 -5
  219. package/lib/sync/range/batch.d.ts.map +1 -1
  220. package/lib/sync/range/batch.js +61 -22
  221. package/lib/sync/range/batch.js.map +1 -1
  222. package/lib/sync/range/chain.d.ts +2 -1
  223. package/lib/sync/range/chain.d.ts.map +1 -1
  224. package/lib/sync/range/chain.js +23 -10
  225. package/lib/sync/range/chain.js.map +1 -1
  226. package/lib/sync/types.d.ts +32 -0
  227. package/lib/sync/types.d.ts.map +1 -1
  228. package/lib/sync/types.js +33 -0
  229. package/lib/sync/types.js.map +1 -1
  230. package/lib/sync/unknownBlock.d.ts +9 -0
  231. package/lib/sync/unknownBlock.d.ts.map +1 -1
  232. package/lib/sync/unknownBlock.js +173 -45
  233. package/lib/sync/unknownBlock.js.map +1 -1
  234. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  235. package/lib/sync/utils/downloadByRange.js +16 -11
  236. package/lib/sync/utils/downloadByRange.js.map +1 -1
  237. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  238. package/lib/sync/utils/downloadByRoot.js +3 -1
  239. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  240. package/package.json +35 -35
  241. package/src/api/impl/beacon/blocks/index.ts +7 -8
  242. package/src/api/impl/beacon/pool/index.ts +27 -2
  243. package/src/api/impl/beacon/state/utils.ts +2 -1
  244. package/src/api/impl/events/index.ts +12 -4
  245. package/src/api/impl/lodestar/index.ts +1 -1
  246. package/src/api/impl/validator/index.ts +1 -1
  247. package/src/api/rest/base.ts +7 -0
  248. package/src/chain/archiveStore/archiveStore.ts +1 -1
  249. package/src/chain/archiveStore/historicalState/metrics.ts +22 -0
  250. package/src/chain/blocks/importBlock.ts +1 -6
  251. package/src/chain/blocks/importExecutionPayload.ts +12 -11
  252. package/src/chain/blocks/index.ts +18 -6
  253. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
  254. package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
  255. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
  256. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
  257. package/src/chain/bls/index.ts +1 -1
  258. package/src/chain/bls/interface.ts +7 -3
  259. package/src/chain/bls/multithread/index.ts +148 -120
  260. package/src/chain/bls/multithread/jobItem.ts +12 -82
  261. package/src/chain/bls/multithread/types.ts +33 -8
  262. package/src/chain/bls/multithread/utils.ts +11 -0
  263. package/src/chain/bls/multithread/worker.ts +85 -27
  264. package/src/chain/bls/singleThread.ts +11 -55
  265. package/src/chain/bls/utils.ts +23 -24
  266. package/src/chain/builderCircuitBreaker.ts +22 -19
  267. package/src/chain/chain.ts +17 -6
  268. package/src/chain/errors/blockError.ts +8 -2
  269. package/src/chain/errors/proposerPreferences.ts +7 -0
  270. package/src/chain/forkChoice/index.ts +4 -0
  271. package/src/chain/interface.ts +4 -1
  272. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  273. package/src/chain/opPools/attestationPool.ts +1 -1
  274. package/src/chain/opPools/deferredVoluntaryExitPool.ts +65 -0
  275. package/src/chain/opPools/index.ts +1 -0
  276. package/src/chain/opPools/payloadAttestationPool.ts +3 -3
  277. package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
  278. package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
  279. package/src/chain/opPools/utils.ts +1 -1
  280. package/src/chain/options.ts +1 -1
  281. package/src/chain/prepareNextSlot.ts +24 -6
  282. package/src/chain/regen/interface.ts +4 -2
  283. package/src/chain/regen/queued.ts +5 -5
  284. package/src/chain/seenCache/seenAttesters.ts +31 -5
  285. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
  286. package/src/chain/stateCache/fifoBlockStateCache.ts +8 -1
  287. package/src/chain/stateCache/persistentCheckpointsCache.ts +133 -8
  288. package/src/chain/stateCache/types.ts +3 -3
  289. package/src/chain/validation/attestation.ts +1 -7
  290. package/src/chain/validation/attesterSlashing.ts +1 -8
  291. package/src/chain/validation/executionPayloadBid.ts +1 -2
  292. package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
  293. package/src/chain/validation/payloadAttestationMessage.ts +6 -17
  294. package/src/chain/validation/proposerPreferences.ts +15 -5
  295. package/src/chain/validation/proposerSlashing.ts +1 -1
  296. package/src/chain/validation/voluntaryExit.ts +59 -2
  297. package/src/metrics/metrics/beacon.ts +9 -9
  298. package/src/metrics/metrics/lodestar.ts +142 -57
  299. package/src/network/gossip/topic.ts +38 -0
  300. package/src/network/libp2p/index.ts +11 -2
  301. package/src/network/network.ts +27 -55
  302. package/src/network/processor/gossipHandlers.ts +14 -1
  303. package/src/network/reqresp/score.ts +5 -11
  304. package/src/network/util.ts +0 -5
  305. package/src/node/deferredVoluntaryExitPublisher.ts +54 -0
  306. package/src/node/nodejs.ts +5 -1
  307. package/src/sync/range/batch.ts +83 -28
  308. package/src/sync/range/chain.ts +26 -11
  309. package/src/sync/types.ts +36 -1
  310. package/src/sync/unknownBlock.ts +197 -51
  311. package/src/sync/utils/downloadByRange.ts +21 -12
  312. package/src/sync/utils/downloadByRoot.ts +3 -1
  313. package/lib/chain/bls/maybeBatch.d.ts +0 -12
  314. package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
  315. package/lib/chain/bls/maybeBatch.js +0 -35
  316. package/lib/chain/bls/maybeBatch.js.map +0 -1
  317. package/src/chain/bls/maybeBatch.ts +0 -45
@@ -1,5 +1,5 @@
1
1
  import { routes } from "@lodestar/api";
2
- import { ForkSeq, isForkPostGloas } from "@lodestar/params";
2
+ import { ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas } from "@lodestar/params";
3
3
  import { RequestError, RequestErrorCode } from "@lodestar/reqresp";
4
4
  import { computeTimeAtSlot } from "@lodestar/state-transition";
5
5
  import { fromHex, prettyPrintIndices, pruneSetToMax, sleep, toRootHex } from "@lodestar/utils";
@@ -16,13 +16,17 @@ import { shuffle } from "../util/shuffle.js";
16
16
  import { sortBy } from "../util/sortBy.js";
17
17
  import { wrapError } from "../util/wrapError.js";
18
18
  import { MAX_CONCURRENT_REQUESTS } from "./constants.js";
19
- import { PendingBlockInputStatus, PendingBlockType, PendingPayloadInputStatus, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, getPayloadSyncCacheItemRootHex, getPayloadSyncCacheItemSlot, isPendingBlockInput, isPendingPayloadEnvelope, isPendingPayloadInput, } from "./types.js";
19
+ import { DroppedItemReason, PendingBlockInputStatus, PendingBlockType, PendingPayloadInputStatus, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, getPayloadSyncCacheItemRootHex, getPayloadSyncCacheItemSlot, isPendingBlockInput, isPendingPayloadEnvelope, isPendingPayloadInput, } from "./types.js";
20
20
  import { DownloadByRootError, downloadByRoot } from "./utils/downloadByRoot.js";
21
21
  import { getAllDescendantBlocks, getUnknownAndAncestorBlocks } from "./utils/pendingBlocksTree.js";
22
22
  import { getRateLimitedUntilMs } from "./utils/rateLimit.js";
23
23
  const MAX_ATTEMPTS_PER_BLOCK = 5;
24
24
  const MAX_KNOWN_BAD_BLOCKS = 500;
25
25
  const MAX_PENDING_BLOCKS = 100;
26
+ /**
27
+ * A pending payload without slot can only stay at most 2 epochs.
28
+ */
29
+ const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
26
30
  var FetchResult;
27
31
  (function (FetchResult) {
28
32
  FetchResult["SuccessResolved"] = "success_resolved";
@@ -115,6 +119,7 @@ export class BlockInputSync {
115
119
  this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
116
120
  this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
117
121
  this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
122
+ this.chain.emitter.on(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
118
123
  this.chain.emitter.on(routes.events.EventType.block, this.onBlockImported);
119
124
  this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
120
125
  this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
@@ -130,6 +135,7 @@ export class BlockInputSync {
130
135
  this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
131
136
  this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
132
137
  this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
138
+ this.chain.emitter.off(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
133
139
  this.chain.emitter.off(routes.events.EventType.block, this.onBlockImported);
134
140
  this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadImported);
135
141
  this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
@@ -172,7 +178,7 @@ export class BlockInputSync {
172
178
  };
173
179
  onUnknownEnvelopeBlockRoot = (data) => {
174
180
  try {
175
- this.addByPayloadRootHex(data.rootHex, data.peer, data.slot);
181
+ this.addByPayloadRootHex(data.rootHex, data.peer, this.resolvePayloadSlot(data.rootHex));
176
182
  this.triggerUnknownBlockSearch();
177
183
  this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT });
178
184
  this.metrics?.blockInputSync.payloadSource.inc({ source: data.source });
@@ -208,12 +214,12 @@ export class BlockInputSync {
208
214
  parentRoot: missingDependency.parentRootHex,
209
215
  parentBlockHash: missingDependency.parentBlockHashHex,
210
216
  });
211
- this.removeAndDownScoreAllDescendants(pendingBlock);
217
+ this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
212
218
  }
213
219
  return;
214
220
  }
215
221
  if (missingDependency.kind === "parentPayload") {
216
- this.addByPayloadRootHex(missingDependency.rootHex, data.peer);
222
+ this.addByPayloadRootHex(missingDependency.rootHex, data.peer, missingDependency.slot);
217
223
  }
218
224
  else if (missingDependency.kind === "parentBlock") {
219
225
  this.addByRootHex(missingDependency.rootHex, data.peer);
@@ -236,6 +242,71 @@ export class BlockInputSync {
236
242
  this.pendingPayloads.delete(blockRoot);
237
243
  this.triggerUnknownBlockSearch();
238
244
  };
245
+ /**
246
+ * Drop pending payloads and blocks that fell below the finalized slot.
247
+ * See https://github.com/ChainSafe/lodestar/issues/9800
248
+ */
249
+ pruneFinalized = (checkpoint) => {
250
+ // Use the actual finalized block slot, not computeStartSlotAtEpoch(checkpoint.epoch)
251
+ // because the payload at the finalized block is not finalized
252
+ const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
253
+ const nowSec = Date.now() / 1000;
254
+ const maxSecInCache = (PRUNE_UNRESOLVED_SLOT_EPOCHS * SLOTS_PER_EPOCH * this.config.SLOT_DURATION_MS) / 1000;
255
+ let belowFinalizedPayloads = 0;
256
+ let agedOutPayloads = 0;
257
+ let unresolvedSlotPayloads = 0;
258
+ for (const [rootHex, payload] of this.pendingPayloads) {
259
+ // Last-effort resolve for a slot-less PendingPayloadRootHex, and store it back for later passes.
260
+ if (!isPendingPayloadInput(payload) && !isPendingPayloadEnvelope(payload) && payload.slot === undefined) {
261
+ payload.slot = this.resolvePayloadSlot(rootHex);
262
+ if (payload.slot === undefined) {
263
+ // Still unresolvable: age it out so the MAX_PENDING_BLOCKS cap cannot stay saturated.
264
+ if (nowSec - payload.timeAddedSec > maxSecInCache) {
265
+ this.pendingPayloads.delete(rootHex);
266
+ agedOutPayloads++;
267
+ }
268
+ else {
269
+ unresolvedSlotPayloads++;
270
+ this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
271
+ root: rootHex,
272
+ finalizedSlot,
273
+ });
274
+ }
275
+ continue;
276
+ }
277
+ }
278
+ const slot = getPayloadSyncCacheItemSlot(payload);
279
+ if (typeof slot === "number" && slot < finalizedSlot) {
280
+ this.pendingPayloads.delete(rootHex);
281
+ belowFinalizedPayloads++;
282
+ }
283
+ }
284
+ let deletedBlocks = 0;
285
+ for (const [rootHex, block] of this.pendingBlocks) {
286
+ const slot = getBlockInputSyncCacheItemSlot(block);
287
+ if (typeof slot === "number" && slot < finalizedSlot) {
288
+ this.pendingBlocks.delete(rootHex);
289
+ deletedBlocks++;
290
+ }
291
+ }
292
+ if (belowFinalizedPayloads > 0) {
293
+ this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.belowFinalized }, belowFinalizedPayloads);
294
+ }
295
+ if (agedOutPayloads > 0) {
296
+ this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.agedOut }, agedOutPayloads);
297
+ }
298
+ if (deletedBlocks > 0) {
299
+ this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.belowFinalized }, deletedBlocks);
300
+ }
301
+ this.logger.debug("BlockInputSync.pruneFinalized dropped pre-finalized pending items", {
302
+ finalizedSlot,
303
+ finalizedRoot: checkpoint.rootHex,
304
+ belowFinalizedPayloads,
305
+ agedOutPayloads,
306
+ deletedBlocks,
307
+ unresolvedSlotPayloads,
308
+ });
309
+ };
239
310
  addByRootHex = (rootHex, peerIdStr) => {
240
311
  let pendingBlock = this.pendingBlocks.get(rootHex);
241
312
  let added = false;
@@ -260,6 +331,7 @@ export class BlockInputSync {
260
331
  // Limit pending blocks to prevent DOS attacks that cause OOM
261
332
  const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
262
333
  if (prunedItemCount > 0) {
334
+ this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
263
335
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
264
336
  }
265
337
  return added;
@@ -290,6 +362,7 @@ export class BlockInputSync {
290
362
  // Limit pending blocks to prevent DOS attacks that cause OOM
291
363
  const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
292
364
  if (prunedItemCount > 0) {
365
+ this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
293
366
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
294
367
  }
295
368
  };
@@ -313,15 +386,44 @@ export class BlockInputSync {
313
386
  peerIdStr: peerIdStr ?? "unknown peer",
314
387
  });
315
388
  }
389
+ else if (
390
+ // update a previously slot-less entry once a slot becomes available
391
+ slot !== undefined &&
392
+ !isPendingPayloadInput(pendingPayload) &&
393
+ !isPendingPayloadEnvelope(pendingPayload) &&
394
+ pendingPayload.slot === undefined) {
395
+ pendingPayload.slot = slot;
396
+ }
316
397
  if (peerIdStr) {
317
398
  pendingPayload.peerIdStrings.add(peerIdStr);
318
399
  }
319
400
  const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
320
401
  if (prunedItemCount > 0) {
402
+ this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
321
403
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
322
404
  }
323
405
  return added;
324
406
  };
407
+ /**
408
+ * Resolve the block slot for a payload root.
409
+ */
410
+ resolvePayloadSlot(rootHex) {
411
+ // a pending block of the same root
412
+ const pendingBlock = this.pendingBlocks.get(rootHex);
413
+ if (pendingBlock) {
414
+ const pendingBlockSlot = getBlockInputSyncCacheItemSlot(pendingBlock);
415
+ if (typeof pendingBlockSlot === "number") {
416
+ return pendingBlockSlot;
417
+ }
418
+ }
419
+ // the block is in fork choice
420
+ const forkChoiceSlot = this.chain.forkChoice.getBlockHexDefaultStatus(rootHex)?.slot;
421
+ if (forkChoiceSlot !== undefined) {
422
+ return forkChoiceSlot;
423
+ }
424
+ // a seen payload envelope input
425
+ return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
426
+ }
325
427
  addByPayloadInput = (payloadInput, peerIdStr, envelope) => {
326
428
  const pendingPayload = this.toPendingPayloadInput(payloadInput, this.pendingPayloads.get(payloadInput.blockRootHex), envelope);
327
429
  if (peerIdStr) {
@@ -335,6 +437,7 @@ export class BlockInputSync {
335
437
  });
336
438
  const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
337
439
  if (prunedItemCount > 0) {
440
+ this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
338
441
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
339
442
  }
340
443
  };
@@ -360,7 +463,8 @@ export class BlockInputSync {
360
463
  */
361
464
  getMissingBlockDependency(blockInput) {
362
465
  const parentRootHex = blockInput.parentRootHex;
363
- if (!this.chain.forkChoice.hasBlockHex(parentRootHex)) {
466
+ const parentBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRootHex);
467
+ if (!parentBlock) {
364
468
  return { kind: "parentBlock", rootHex: parentRootHex };
365
469
  }
366
470
  if (!blockInput.hasBlock()) {
@@ -377,14 +481,8 @@ export class BlockInputSync {
377
481
  if (this.chain.forkChoice.hasPayloadHexUnsafe(parentRootHex)) {
378
482
  return { kind: "invalidParentPayload", parentRootHex, parentBlockHashHex };
379
483
  }
380
- const parentPayloadInput = this.chain.seenPayloadEnvelopeInputCache.get(parentRootHex);
381
- if (parentPayloadInput) {
382
- if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
383
- return { kind: "parentPayload", rootHex: parentRootHex };
384
- }
385
- return { kind: "invalidParentPayload", parentRootHex, parentBlockHashHex };
386
- }
387
- return { kind: "parentPayload", rootHex: parentRootHex };
484
+ // Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
485
+ return { kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot };
388
486
  }
389
487
  advancePendingBlock(pendingBlock) {
390
488
  const missingDependency = this.getMissingBlockDependency(pendingBlock.blockInput);
@@ -402,9 +500,9 @@ export class BlockInputSync {
402
500
  return added ? "queued_parent_block" : "blocked";
403
501
  }
404
502
  case "parentPayload": {
405
- let added = this.addByPayloadRootHex(missingDependency.rootHex);
503
+ let added = this.addByPayloadRootHex(missingDependency.rootHex, undefined, missingDependency.slot);
406
504
  for (const peerIdStr of pendingBlock.peerIdStrings) {
407
- added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr) || added;
505
+ added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr, missingDependency.slot) || added;
408
506
  }
409
507
  return added ? "queued_parent_payload" : "blocked";
410
508
  }
@@ -415,7 +513,7 @@ export class BlockInputSync {
415
513
  parentRoot: missingDependency.parentRootHex,
416
514
  parentBlockHash: missingDependency.parentBlockHashHex,
417
515
  });
418
- this.removeAndDownScoreAllDescendants(pendingBlock);
516
+ this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
419
517
  return "removed";
420
518
  }
421
519
  }
@@ -604,7 +702,7 @@ export class BlockInputSync {
604
702
  ...logCtx2,
605
703
  finalizedSlot,
606
704
  });
607
- this.removeAndDownScoreAllDescendants(block);
705
+ this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
608
706
  }
609
707
  else {
610
708
  this.onUnknownBlockRoot({ rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot });
@@ -622,7 +720,7 @@ export class BlockInputSync {
622
720
  }
623
721
  this.metrics?.blockInputSync.downloadedBlocksError.inc();
624
722
  this.logger.debug("Ignoring unknown block root after many failed downloads", logCtx, res.err);
625
- this.removeAndDownScoreAllDescendants(block);
723
+ this.removeAndDownScoreAllDescendants(block, DroppedItemReason.unavailable);
626
724
  }
627
725
  }
628
726
  /**
@@ -679,9 +777,9 @@ export class BlockInputSync {
679
777
  // no need to update status to "processed", delete anyway
680
778
  this.pendingBlocks.delete(blockRootHex);
681
779
  if (isForkPostGloas(fork)) {
682
- const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
780
+ const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
683
781
  if (!payloadInput) {
684
- this.logger.warn("PayloadEnvelopeInput not seeded during unknown sync processReadyBlock()", logCtx);
782
+ this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
685
783
  }
686
784
  else {
687
785
  // Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
@@ -717,17 +815,17 @@ export class BlockInputSync {
717
815
  case BlockErrorCode.EXECUTION_ENGINE_ERROR:
718
816
  // Removing the block(s) without penalizing the peers, hoping for EL to
719
817
  // recover on a latter download + verify attempt.
720
- this.removeAllDescendants(pendingBlock);
818
+ this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
721
819
  break;
722
820
  case BlockErrorCode.EXECUTION_ENGINE_INVALID:
723
821
  // the peer served a bad block
724
822
  this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
725
- this.removeAndDownScoreAllDescendants(pendingBlock);
823
+ this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
726
824
  break;
727
825
  default:
728
826
  // Block is not correct with respect to our chain. Log error loudly
729
827
  this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
730
- this.removeAndDownScoreAllDescendants(pendingBlock);
828
+ this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
731
829
  }
732
830
  }
733
831
  // Probably a queue error or something unwanted happened, mark as pending to try again latter
@@ -748,7 +846,6 @@ export class BlockInputSync {
748
846
  this.pendingPayloads.delete(rootHex);
749
847
  return;
750
848
  }
751
- const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
752
849
  if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
753
850
  // Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
754
851
  // non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
@@ -762,8 +859,9 @@ export class BlockInputSync {
762
859
  }
763
860
  return;
764
861
  }
862
+ const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
765
863
  if (!payloadInput) {
766
- this.logger.debug("Missing PayloadEnvelopeInput for known block while reconciling payload envelope", {
864
+ this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
767
865
  root: rootHex,
768
866
  });
769
867
  return;
@@ -775,6 +873,7 @@ export class BlockInputSync {
775
873
  const pendingPayloadByRoot = {
776
874
  status: PendingPayloadInputStatus.pending,
777
875
  rootHex,
876
+ slot: pendingPayload.envelope.message.payload.slotNumber,
778
877
  timeAddedSec: pendingPayload.timeAddedSec,
779
878
  peerIdStrings: new Set(pendingPayload.peerIdStrings),
780
879
  };
@@ -806,6 +905,9 @@ export class BlockInputSync {
806
905
  if (payload.status !== PendingPayloadInputStatus.pending) {
807
906
  return;
808
907
  }
908
+ if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
909
+ payload.slot = this.resolvePayloadSlot(rootHex);
910
+ }
809
911
  const payloadSlot = getPayloadSyncCacheItemSlot(payload);
810
912
  const logCtx = {
811
913
  slot: payloadSlot,
@@ -818,7 +920,13 @@ export class BlockInputSync {
818
920
  const res = await wrapError(this.fetchPayloadInput(payload));
819
921
  if (!res.err) {
820
922
  const pendingPayload = res.result;
821
- this.pendingPayloads.set(getPayloadSyncCacheItemRootHex(pendingPayload), pendingPayload);
923
+ const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
924
+ // finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
925
+ if (!this.pendingPayloads.has(resultRootHex)) {
926
+ this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
927
+ return;
928
+ }
929
+ this.pendingPayloads.set(resultRootHex, pendingPayload);
822
930
  if (isPendingPayloadEnvelope(pendingPayload)) {
823
931
  await this.reconcilePayloadEnvelope(pendingPayload);
824
932
  }
@@ -885,16 +993,24 @@ export class BlockInputSync {
885
993
  pendingPayload.status = PendingPayloadInputStatus.downloaded;
886
994
  break;
887
995
  case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
996
+ this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
997
+ this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
998
+ break;
888
999
  case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
1000
+ this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1001
+ this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
1002
+ break;
889
1003
  case PayloadErrorCode.INVALID_SIGNATURE:
890
1004
  // TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
891
1005
  // reintroducing envelope replacement / recreation flows.
892
1006
  this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
893
- this.removePendingPayloadAndDescendants(rootHex);
1007
+ this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
894
1008
  break;
895
1009
  default:
896
1010
  this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
897
- this.pendingPayloads.delete(rootHex);
1011
+ if (this.pendingPayloads.delete(rootHex)) {
1012
+ this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.unknown }, 1);
1013
+ }
898
1014
  }
899
1015
  return;
900
1016
  }
@@ -913,7 +1029,7 @@ export class BlockInputSync {
913
1029
  let slot = getPayloadSyncCacheItemSlot(cacheItem);
914
1030
  let payloadInput = isPendingPayloadInput(cacheItem)
915
1031
  ? cacheItem.payloadInput
916
- : this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
1032
+ : await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
917
1033
  let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
918
1034
  let i = 0;
919
1035
  let deferredByRateLimit = false;
@@ -926,7 +1042,10 @@ export class BlockInputSync {
926
1042
  if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
927
1043
  throw new UnknownBlockRateLimitedError(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`);
928
1044
  }
929
- throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`);
1045
+ const reason = pendingColumns.size > 0
1046
+ ? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
1047
+ : "no peer available to download pending payload";
1048
+ throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
930
1049
  }
931
1050
  const { peerId, client: peerClient } = peerMeta;
932
1051
  cacheItem.peerIdStrings.add(peerId);
@@ -935,7 +1054,7 @@ export class BlockInputSync {
935
1054
  envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
936
1055
  slot = envelope.message.payload.slotNumber;
937
1056
  }
938
- payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
1057
+ payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
939
1058
  if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
940
1059
  // Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
941
1060
  // envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
@@ -1206,9 +1325,9 @@ export class BlockInputSync {
1206
1325
  * Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
1207
1326
  * referenced more than one bad block.
1208
1327
  */
1209
- removeAndDownScoreAllDescendants(block) {
1328
+ removeAndDownScoreAllDescendants(block, headReason) {
1210
1329
  // Get all blocks that are a descendant of this one
1211
- const badPendingBlocks = this.removeAllDescendants(block);
1330
+ const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
1212
1331
  // just console log and do not penalize on pending/bad blocks for debugging
1213
1332
  // console.log("removeAndDownscoreAllDescendants", {block});
1214
1333
  for (const block of badPendingBlocks) {
@@ -1232,16 +1351,21 @@ export class BlockInputSync {
1232
1351
  pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
1233
1352
  }
1234
1353
  // Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
1235
- removePendingPayloadAndDescendants(rootHex) {
1354
+ removePendingPayloadAndDescendants(rootHex, headReason) {
1236
1355
  // Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
1237
1356
  // later validation/finalization logic decides when it can leave memory.
1238
- this.pendingPayloads.delete(rootHex);
1357
+ if (this.pendingPayloads.delete(rootHex)) {
1358
+ this.metrics?.blockInputSync.removedPayloads.inc({ reason: headReason }, 1);
1359
+ }
1239
1360
  const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
1240
- this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
1241
1361
  for (const block of badPendingBlocks) {
1242
1362
  const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
1243
- this.pendingBlocks.delete(descendantRootHex);
1244
- this.pendingPayloads.delete(descendantRootHex);
1363
+ if (this.pendingBlocks.delete(descendantRootHex)) {
1364
+ this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.invalidParent }, 1);
1365
+ }
1366
+ if (this.pendingPayloads.delete(descendantRootHex)) {
1367
+ this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.invalidParent }, 1);
1368
+ }
1245
1369
  this.chain.seenBlockInputCache.prune(descendantRootHex);
1246
1370
  this.logger.debug("Removing pending descendant after invalid parent payload", {
1247
1371
  slot: getBlockInputSyncCacheItemSlot(block),
@@ -1250,16 +1374,20 @@ export class BlockInputSync {
1250
1374
  });
1251
1375
  }
1252
1376
  }
1253
- removeAllDescendants(block) {
1377
+ removeAllDescendants(block, headReason, descendantReason = DroppedItemReason.invalidParent) {
1254
1378
  const rootHex = getBlockInputSyncCacheItemRootHex(block);
1255
1379
  const slot = getBlockInputSyncCacheItemSlot(block);
1256
- // Get all blocks that are a descendant of this one
1380
+ // Get all blocks that are a descendant of this one (index 0 is the head item itself)
1257
1381
  const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
1258
- this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
1259
- for (const block of badPendingBlocks) {
1382
+ for (const [i, block] of badPendingBlocks.entries()) {
1260
1383
  const rootHex = getBlockInputSyncCacheItemRootHex(block);
1261
- this.pendingBlocks.delete(rootHex);
1262
- this.pendingPayloads.delete(rootHex);
1384
+ const reason = i === 0 ? headReason : descendantReason;
1385
+ if (this.pendingBlocks.delete(rootHex)) {
1386
+ this.metrics?.blockInputSync.removedBlocks.inc({ reason }, 1);
1387
+ }
1388
+ if (this.pendingPayloads.delete(rootHex)) {
1389
+ this.metrics?.blockInputSync.removedPayloads.inc({ reason }, 1);
1390
+ }
1263
1391
  this.chain.seenBlockInputCache.prune(rootHex);
1264
1392
  // Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
1265
1393
  // importBlock()-owned lifecycle.