@lodestar/beacon-node 1.35.0-dev.47c570ab76 → 1.35.0-dev.56313c7299

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 (500) hide show
  1. package/lib/api/impl/beacon/blocks/index.js +46 -58
  2. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  3. package/lib/api/impl/config/constants.d.ts +0 -5
  4. package/lib/api/impl/config/constants.js +1 -6
  5. package/lib/api/impl/config/constants.js.map +1 -1
  6. package/lib/api/impl/debug/index.js +1 -1
  7. package/lib/api/impl/debug/index.js.map +1 -1
  8. package/lib/api/impl/errors.js +0 -2
  9. package/lib/api/impl/errors.js.map +1 -1
  10. package/lib/api/impl/index.d.ts +3 -3
  11. package/lib/api/impl/index.js +3 -3
  12. package/lib/api/impl/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.js +1 -2
  16. package/lib/api/impl/validator/index.js.map +1 -1
  17. package/lib/api/rest/activeSockets.js +2 -3
  18. package/lib/api/rest/activeSockets.js.map +1 -1
  19. package/lib/api/rest/base.d.ts +1 -1
  20. package/lib/api/rest/base.js +2 -6
  21. package/lib/api/rest/base.js.map +1 -1
  22. package/lib/api/rest/index.js +0 -2
  23. package/lib/api/rest/index.js.map +1 -1
  24. package/lib/api/rest/swaggerUI.js +2 -4
  25. package/lib/api/rest/swaggerUI.js.map +1 -1
  26. package/lib/chain/archiveStore/archiveStore.js +38 -49
  27. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  28. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +0 -3
  29. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  30. package/lib/chain/archiveStore/index.d.ts +1 -1
  31. package/lib/chain/archiveStore/index.js +1 -1
  32. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js +0 -5
  33. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
  34. package/lib/chain/balancesCache.js +3 -1
  35. package/lib/chain/balancesCache.js.map +1 -1
  36. package/lib/chain/beaconProposerCache.js +0 -1
  37. package/lib/chain/beaconProposerCache.js.map +1 -1
  38. package/lib/chain/blocks/blockInput/blockInput.d.ts +7 -19
  39. package/lib/chain/blocks/blockInput/blockInput.js +88 -132
  40. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  41. package/lib/chain/blocks/blockInput/index.d.ts +1 -1
  42. package/lib/chain/blocks/blockInput/index.js +1 -1
  43. package/lib/chain/blocks/blockInput/types.d.ts +10 -19
  44. package/lib/chain/blocks/blockInput/types.js +0 -1
  45. package/lib/chain/blocks/blockInput/types.js.map +1 -1
  46. package/lib/chain/blocks/blockInput/utils.d.ts +4 -0
  47. package/lib/chain/blocks/blockInput/utils.js +29 -6
  48. package/lib/chain/blocks/blockInput/utils.js.map +1 -1
  49. package/lib/chain/blocks/importBlock.js +12 -16
  50. package/lib/chain/blocks/importBlock.js.map +1 -1
  51. package/lib/chain/blocks/index.d.ts +5 -6
  52. package/lib/chain/blocks/index.js +4 -5
  53. package/lib/chain/blocks/index.js.map +1 -1
  54. package/lib/chain/blocks/types.d.ts +106 -3
  55. package/lib/chain/blocks/types.js +119 -0
  56. package/lib/chain/blocks/types.js.map +1 -1
  57. package/lib/chain/blocks/utils/chainSegment.d.ts +2 -2
  58. package/lib/chain/blocks/utils/chainSegment.js +2 -2
  59. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  60. package/lib/chain/blocks/verifyBlock.d.ts +3 -3
  61. package/lib/chain/blocks/verifyBlock.js +14 -15
  62. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  63. package/lib/chain/blocks/verifyBlocksDataAvailability.d.ts +22 -7
  64. package/lib/chain/blocks/verifyBlocksDataAvailability.js +110 -18
  65. package/lib/chain/blocks/verifyBlocksDataAvailability.js.map +1 -1
  66. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +3 -4
  67. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +22 -24
  68. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  69. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +3 -4
  70. package/lib/chain/blocks/verifyBlocksSanityChecks.js +2 -2
  71. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  72. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -3
  73. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +2 -2
  74. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  75. package/lib/chain/blocks/writeBlockInputToDb.d.ts +3 -3
  76. package/lib/chain/blocks/writeBlockInputToDb.js +66 -61
  77. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  78. package/lib/chain/bls/multithread/index.js +163 -168
  79. package/lib/chain/bls/multithread/index.js.map +1 -1
  80. package/lib/chain/bls/singleThread.js +0 -1
  81. package/lib/chain/bls/singleThread.js.map +1 -1
  82. package/lib/chain/chain.d.ts +11 -13
  83. package/lib/chain/chain.js +20 -79
  84. package/lib/chain/chain.js.map +1 -1
  85. package/lib/chain/emitter.d.ts +2 -43
  86. package/lib/chain/emitter.js +0 -18
  87. package/lib/chain/emitter.js.map +1 -1
  88. package/lib/chain/errors/blobSidecarError.d.ts +0 -24
  89. package/lib/chain/errors/blobSidecarError.js +0 -10
  90. package/lib/chain/errors/blobSidecarError.js.map +1 -1
  91. package/lib/chain/errors/blockError.js +0 -1
  92. package/lib/chain/errors/blockError.js.map +1 -1
  93. package/lib/chain/errors/dataColumnSidecarError.d.ts +0 -42
  94. package/lib/chain/errors/dataColumnSidecarError.js +0 -14
  95. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
  96. package/lib/chain/errors/gossipValidation.js +0 -1
  97. package/lib/chain/errors/gossipValidation.js.map +1 -1
  98. package/lib/chain/errors/index.d.ts +2 -2
  99. package/lib/chain/errors/index.js +2 -2
  100. package/lib/chain/errors/index.js.map +1 -1
  101. package/lib/chain/forkChoice/index.js.map +1 -1
  102. package/lib/chain/genesis/genesis.js +5 -16
  103. package/lib/chain/genesis/genesis.js.map +1 -1
  104. package/lib/chain/index.d.ts +2 -2
  105. package/lib/chain/index.js +2 -2
  106. package/lib/chain/index.js.map +1 -1
  107. package/lib/chain/interface.d.ts +9 -12
  108. package/lib/chain/interface.js.map +1 -1
  109. package/lib/chain/lightClient/index.js +9 -16
  110. package/lib/chain/lightClient/index.js.map +1 -1
  111. package/lib/chain/lightClient/proofs.js.map +1 -1
  112. package/lib/chain/opPools/aggregatedAttestationPool.js +9 -14
  113. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  114. package/lib/chain/opPools/attestationPool.js +3 -8
  115. package/lib/chain/opPools/attestationPool.js.map +1 -1
  116. package/lib/chain/opPools/index.d.ts +1 -1
  117. package/lib/chain/opPools/index.js +1 -1
  118. package/lib/chain/opPools/index.js.map +1 -1
  119. package/lib/chain/opPools/opPool.js +12 -10
  120. package/lib/chain/opPools/opPool.js.map +1 -1
  121. package/lib/chain/opPools/syncCommitteeMessagePool.js +6 -9
  122. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  123. package/lib/chain/opPools/syncContributionAndProofPool.js +2 -5
  124. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  125. package/lib/chain/options.js +1 -1
  126. package/lib/chain/options.js.map +1 -1
  127. package/lib/chain/prepareNextSlot.js +136 -141
  128. package/lib/chain/prepareNextSlot.js.map +1 -1
  129. package/lib/chain/produceBlock/produceBlockBody.d.ts +1 -1
  130. package/lib/chain/produceBlock/produceBlockBody.js +10 -3
  131. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  132. package/lib/chain/regen/errors.js +0 -1
  133. package/lib/chain/regen/errors.js.map +1 -1
  134. package/lib/chain/regen/index.d.ts +1 -1
  135. package/lib/chain/regen/index.js +1 -1
  136. package/lib/chain/regen/index.js.map +1 -1
  137. package/lib/chain/regen/queued.js +28 -35
  138. package/lib/chain/regen/queued.js.map +1 -1
  139. package/lib/chain/regen/regen.js +0 -1
  140. package/lib/chain/regen/regen.js.map +1 -1
  141. package/lib/chain/reprocess.js +1 -3
  142. package/lib/chain/reprocess.js.map +1 -1
  143. package/lib/chain/seenCache/index.d.ts +1 -1
  144. package/lib/chain/seenCache/index.js +1 -1
  145. package/lib/chain/seenCache/index.js.map +1 -1
  146. package/lib/chain/seenCache/seenAggregateAndProof.js +6 -7
  147. package/lib/chain/seenCache/seenAggregateAndProof.js.map +1 -1
  148. package/lib/chain/seenCache/seenAttestationData.js +2 -5
  149. package/lib/chain/seenCache/seenAttestationData.js.map +1 -1
  150. package/lib/chain/seenCache/seenAttesters.js +4 -2
  151. package/lib/chain/seenCache/seenAttesters.js.map +1 -1
  152. package/lib/chain/seenCache/seenBlockInput.d.ts +84 -0
  153. package/lib/chain/seenCache/seenBlockInput.js +225 -0
  154. package/lib/chain/seenCache/seenBlockInput.js.map +1 -0
  155. package/lib/chain/seenCache/seenBlockProposers.js +4 -2
  156. package/lib/chain/seenCache/seenBlockProposers.js.map +1 -1
  157. package/lib/chain/seenCache/seenCommittee.js +3 -1
  158. package/lib/chain/seenCache/seenCommittee.js.map +1 -1
  159. package/lib/chain/seenCache/seenCommitteeContribution.js +2 -3
  160. package/lib/chain/seenCache/seenCommitteeContribution.js.map +1 -1
  161. package/lib/chain/seenCache/seenGossipBlockInput.d.ts +78 -74
  162. package/lib/chain/seenCache/seenGossipBlockInput.js +369 -235
  163. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  164. package/lib/chain/shufflingCache.js +2 -5
  165. package/lib/chain/shufflingCache.js.map +1 -1
  166. package/lib/chain/stateCache/blockStateCacheImpl.js +7 -13
  167. package/lib/chain/stateCache/blockStateCacheImpl.js.map +1 -1
  168. package/lib/chain/stateCache/datastore/db.js +0 -1
  169. package/lib/chain/stateCache/datastore/db.js.map +1 -1
  170. package/lib/chain/stateCache/datastore/file.js +0 -1
  171. package/lib/chain/stateCache/datastore/file.js.map +1 -1
  172. package/lib/chain/stateCache/datastore/index.d.ts +1 -1
  173. package/lib/chain/stateCache/datastore/index.js +1 -1
  174. package/lib/chain/stateCache/datastore/index.js.map +1 -1
  175. package/lib/chain/stateCache/fifoBlockStateCache.js +0 -10
  176. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  177. package/lib/chain/stateCache/inMemoryCheckpointsCache.js +4 -10
  178. package/lib/chain/stateCache/inMemoryCheckpointsCache.js.map +1 -1
  179. package/lib/chain/stateCache/index.d.ts +1 -1
  180. package/lib/chain/stateCache/index.js +1 -1
  181. package/lib/chain/stateCache/index.js.map +1 -1
  182. package/lib/chain/stateCache/mapMetrics.js +4 -4
  183. package/lib/chain/stateCache/mapMetrics.js.map +1 -1
  184. package/lib/chain/stateCache/persistentCheckpointsCache.js +6 -14
  185. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  186. package/lib/chain/validation/blobSidecar.d.ts +3 -8
  187. package/lib/chain/validation/blobSidecar.js +31 -73
  188. package/lib/chain/validation/blobSidecar.js.map +1 -1
  189. package/lib/chain/validation/dataColumnSidecar.d.ts +9 -7
  190. package/lib/chain/validation/dataColumnSidecar.js +57 -95
  191. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  192. package/lib/chain/validation/index.d.ts +1 -1
  193. package/lib/chain/validation/index.js +1 -1
  194. package/lib/chain/validation/index.js.map +1 -1
  195. package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts +2 -1
  196. package/lib/chain/validation/signatureSets/aggregateAndProof.js.map +1 -1
  197. package/lib/chain/validatorMonitor.d.ts +3 -1
  198. package/lib/chain/validatorMonitor.js +1 -2
  199. package/lib/chain/validatorMonitor.js.map +1 -1
  200. package/lib/db/beacon.js +0 -24
  201. package/lib/db/beacon.js.map +1 -1
  202. package/lib/db/index.d.ts +1 -1
  203. package/lib/db/index.js.map +1 -1
  204. package/lib/db/repositories/blobSidecars.js.map +1 -1
  205. package/lib/db/repositories/blockArchive.js +3 -2
  206. package/lib/db/repositories/blockArchive.js.map +1 -1
  207. package/lib/db/repositories/dataColumnSidecar.js +1 -1
  208. package/lib/db/repositories/dataColumnSidecar.js.map +1 -1
  209. package/lib/db/repositories/dataColumnSidecarArchive.js +1 -1
  210. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  211. package/lib/db/repositories/depositDataRoot.js +0 -1
  212. package/lib/db/repositories/depositDataRoot.js.map +1 -1
  213. package/lib/db/repositories/index.d.ts +10 -10
  214. package/lib/db/repositories/index.js +9 -9
  215. package/lib/db/repositories/index.js.map +1 -1
  216. package/lib/db/single/preGenesisState.js +0 -6
  217. package/lib/db/single/preGenesisState.js.map +1 -1
  218. package/lib/db/single/preGenesisStateLastProcessedBlock.js +0 -5
  219. package/lib/db/single/preGenesisStateLastProcessedBlock.js.map +1 -1
  220. package/lib/eth1/eth1DataCache.js +0 -2
  221. package/lib/eth1/eth1DataCache.js.map +1 -1
  222. package/lib/eth1/eth1DepositDataTracker.js +5 -18
  223. package/lib/eth1/eth1DepositDataTracker.js.map +1 -1
  224. package/lib/eth1/eth1DepositsCache.js +0 -3
  225. package/lib/eth1/eth1DepositsCache.js.map +1 -1
  226. package/lib/eth1/eth1MergeBlockTracker.js +4 -10
  227. package/lib/eth1/eth1MergeBlockTracker.js.map +1 -1
  228. package/lib/eth1/index.js +0 -2
  229. package/lib/eth1/index.js.map +1 -1
  230. package/lib/eth1/provider/eth1Provider.d.ts +2 -1
  231. package/lib/eth1/provider/eth1Provider.js +2 -6
  232. package/lib/eth1/provider/eth1Provider.js.map +1 -1
  233. package/lib/eth1/provider/jsonRpcHttpClient.d.ts +1 -1
  234. package/lib/eth1/provider/jsonRpcHttpClient.js +2 -16
  235. package/lib/eth1/provider/jsonRpcHttpClient.js.map +1 -1
  236. package/lib/eth1/provider/jwt.js.map +1 -1
  237. package/lib/eth1/provider/utils.d.ts +0 -5
  238. package/lib/eth1/provider/utils.js +1 -9
  239. package/lib/eth1/provider/utils.js.map +1 -1
  240. package/lib/eth1/utils/deposits.js.map +1 -1
  241. package/lib/execution/builder/cache.js +0 -6
  242. package/lib/execution/builder/cache.js.map +1 -1
  243. package/lib/execution/builder/http.js +8 -14
  244. package/lib/execution/builder/http.js.map +1 -1
  245. package/lib/execution/builder/index.js.map +1 -1
  246. package/lib/execution/engine/disabled.js +4 -2
  247. package/lib/execution/engine/disabled.js.map +1 -1
  248. package/lib/execution/engine/http.d.ts +2 -2
  249. package/lib/execution/engine/http.js +11 -43
  250. package/lib/execution/engine/http.js.map +1 -1
  251. package/lib/execution/engine/interface.d.ts +2 -2
  252. package/lib/execution/engine/interface.js.map +1 -1
  253. package/lib/execution/engine/mock.d.ts +0 -1
  254. package/lib/execution/engine/mock.js +12 -16
  255. package/lib/execution/engine/mock.js.map +1 -1
  256. package/lib/execution/engine/payloadIdCache.js +3 -1
  257. package/lib/execution/engine/payloadIdCache.js.map +1 -1
  258. package/lib/execution/engine/types.d.ts +1 -5
  259. package/lib/execution/engine/types.js +8 -37
  260. package/lib/execution/engine/types.js.map +1 -1
  261. package/lib/execution/engine/utils.js +3 -3
  262. package/lib/execution/engine/utils.js.map +1 -1
  263. package/lib/execution/index.d.ts +2 -2
  264. package/lib/execution/index.js +2 -2
  265. package/lib/execution/index.js.map +1 -1
  266. package/lib/index.d.ts +6 -6
  267. package/lib/index.js +5 -5
  268. package/lib/index.js.map +1 -1
  269. package/lib/metrics/index.d.ts +1 -1
  270. package/lib/metrics/index.js +1 -1
  271. package/lib/metrics/index.js.map +1 -1
  272. package/lib/metrics/metrics/beacon.d.ts +5 -11
  273. package/lib/metrics/metrics/beacon.js +20 -40
  274. package/lib/metrics/metrics/beacon.js.map +1 -1
  275. package/lib/metrics/metrics/lodestar.d.ts +13 -55
  276. package/lib/metrics/metrics/lodestar.js +7 -84
  277. package/lib/metrics/metrics/lodestar.js.map +1 -1
  278. package/lib/metrics/metrics.d.ts +1 -1
  279. package/lib/metrics/metrics.js.map +1 -1
  280. package/lib/metrics/nodeJsMetrics.js +1 -1
  281. package/lib/metrics/nodeJsMetrics.js.map +1 -1
  282. package/lib/metrics/server/http.d.ts +1 -1
  283. package/lib/metrics/utils/avgMinMax.d.ts +1 -1
  284. package/lib/metrics/utils/avgMinMax.js +6 -10
  285. package/lib/metrics/utils/avgMinMax.js.map +1 -1
  286. package/lib/metrics/utils/gauge.d.ts +1 -1
  287. package/lib/metrics/utils/gauge.js +4 -1
  288. package/lib/metrics/utils/gauge.js.map +1 -1
  289. package/lib/metrics/utils/registryMetricCreator.d.ts +1 -1
  290. package/lib/metrics/utils/registryMetricCreator.js.map +1 -1
  291. package/lib/monitoring/properties.js +0 -4
  292. package/lib/monitoring/properties.js.map +1 -1
  293. package/lib/monitoring/service.js +1 -13
  294. package/lib/monitoring/service.js.map +1 -1
  295. package/lib/monitoring/system.js +27 -25
  296. package/lib/monitoring/system.js.map +1 -1
  297. package/lib/network/core/networkCore.d.ts +2 -2
  298. package/lib/network/core/networkCore.js +50 -67
  299. package/lib/network/core/networkCore.js.map +1 -1
  300. package/lib/network/core/networkCoreWorker.js +1 -1
  301. package/lib/network/core/networkCoreWorker.js.map +1 -1
  302. package/lib/network/core/networkCoreWorkerHandler.d.ts +2 -2
  303. package/lib/network/core/networkCoreWorkerHandler.js +5 -14
  304. package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
  305. package/lib/network/discv5/index.d.ts +2 -2
  306. package/lib/network/discv5/index.js +3 -9
  307. package/lib/network/discv5/index.js.map +1 -1
  308. package/lib/network/discv5/worker.js +3 -3
  309. package/lib/network/discv5/worker.js.map +1 -1
  310. package/lib/network/events.d.ts +18 -2
  311. package/lib/network/events.js +7 -0
  312. package/lib/network/events.js.map +1 -1
  313. package/lib/network/gossip/encoding.d.ts +1 -1
  314. package/lib/network/gossip/encoding.js +2 -4
  315. package/lib/network/gossip/encoding.js.map +1 -1
  316. package/lib/network/gossip/errors.js +0 -1
  317. package/lib/network/gossip/errors.js.map +1 -1
  318. package/lib/network/gossip/gossipsub.d.ts +1 -2
  319. package/lib/network/gossip/gossipsub.js +17 -43
  320. package/lib/network/gossip/gossipsub.js.map +1 -1
  321. package/lib/network/gossip/index.d.ts +2 -2
  322. package/lib/network/gossip/index.js +2 -2
  323. package/lib/network/gossip/index.js.map +1 -1
  324. package/lib/network/gossip/interface.d.ts +2 -2
  325. package/lib/network/gossip/metrics.d.ts +7 -15
  326. package/lib/network/gossip/metrics.js +6 -16
  327. package/lib/network/gossip/metrics.js.map +1 -1
  328. package/lib/network/gossip/topic.d.ts +105 -125
  329. package/lib/network/gossip/topic.js +1 -2
  330. package/lib/network/gossip/topic.js.map +1 -1
  331. package/lib/network/index.d.ts +3 -3
  332. package/lib/network/index.js +3 -3
  333. package/lib/network/index.js.map +1 -1
  334. package/lib/network/interface.d.ts +2 -3
  335. package/lib/network/libp2p/index.js +3 -11
  336. package/lib/network/libp2p/index.js.map +1 -1
  337. package/lib/network/metadata.js +1 -6
  338. package/lib/network/metadata.js.map +1 -1
  339. package/lib/network/network.d.ts +2 -4
  340. package/lib/network/network.js +71 -89
  341. package/lib/network/network.js.map +1 -1
  342. package/lib/network/options.js +1 -1
  343. package/lib/network/options.js.map +1 -1
  344. package/lib/network/peers/datastore.d.ts +2 -2
  345. package/lib/network/peers/datastore.js +4 -10
  346. package/lib/network/peers/datastore.js.map +1 -1
  347. package/lib/network/peers/discover.js +66 -75
  348. package/lib/network/peers/discover.js.map +1 -1
  349. package/lib/network/peers/peerManager.js +130 -154
  350. package/lib/network/peers/peerManager.js.map +1 -1
  351. package/lib/network/peers/peersData.d.ts +1 -1
  352. package/lib/network/peers/peersData.js +3 -1
  353. package/lib/network/peers/peersData.js.map +1 -1
  354. package/lib/network/peers/score/index.d.ts +1 -1
  355. package/lib/network/peers/score/index.js +1 -1
  356. package/lib/network/peers/score/index.js.map +1 -1
  357. package/lib/network/peers/score/score.js +0 -6
  358. package/lib/network/peers/score/score.js.map +1 -1
  359. package/lib/network/peers/score/store.js +0 -3
  360. package/lib/network/peers/score/store.js.map +1 -1
  361. package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
  362. package/lib/network/peers/utils/subnetMap.js +4 -2
  363. package/lib/network/peers/utils/subnetMap.js.map +1 -1
  364. package/lib/network/processor/aggregatorTracker.js +3 -1
  365. package/lib/network/processor/aggregatorTracker.js.map +1 -1
  366. package/lib/network/processor/gossipHandlers.js +164 -145
  367. package/lib/network/processor/gossipHandlers.js.map +1 -1
  368. package/lib/network/processor/gossipQueues/indexed.js +9 -11
  369. package/lib/network/processor/gossipQueues/indexed.js.map +1 -1
  370. package/lib/network/processor/gossipQueues/linear.js +8 -9
  371. package/lib/network/processor/gossipQueues/linear.js.map +1 -1
  372. package/lib/network/processor/index.d.ts +1 -2
  373. package/lib/network/processor/index.js +8 -22
  374. package/lib/network/processor/index.js.map +1 -1
  375. package/lib/network/reqresp/ReqRespBeaconNode.d.ts +1 -1
  376. package/lib/network/reqresp/ReqRespBeaconNode.js +2 -11
  377. package/lib/network/reqresp/ReqRespBeaconNode.js.map +1 -1
  378. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRange.d.ts +28 -0
  379. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRange.js +328 -0
  380. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRange.js.map +1 -0
  381. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.d.ts +49 -0
  382. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.js +499 -0
  383. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.js.map +1 -0
  384. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  385. package/lib/network/reqresp/index.d.ts +3 -1
  386. package/lib/network/reqresp/index.js +3 -1
  387. package/lib/network/reqresp/index.js.map +1 -1
  388. package/lib/network/reqresp/rateLimit.js +2 -2
  389. package/lib/network/reqresp/rateLimit.js.map +1 -1
  390. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  391. package/lib/network/statusCache.js +0 -1
  392. package/lib/network/statusCache.js.map +1 -1
  393. package/lib/network/subnets/attnetsService.js +65 -73
  394. package/lib/network/subnets/attnetsService.js.map +1 -1
  395. package/lib/network/subnets/interface.js +1 -1
  396. package/lib/network/subnets/interface.js.map +1 -1
  397. package/lib/network/subnets/syncnetsService.js +22 -29
  398. package/lib/network/subnets/syncnetsService.js.map +1 -1
  399. package/lib/network/subnets/util.js +2 -1
  400. package/lib/network/subnets/util.js.map +1 -1
  401. package/lib/node/nodejs.d.ts +1 -1
  402. package/lib/node/nodejs.js +0 -15
  403. package/lib/node/nodejs.js.map +1 -1
  404. package/lib/node/notifier.js +2 -1
  405. package/lib/node/notifier.js.map +1 -1
  406. package/lib/node/options.js.map +1 -1
  407. package/lib/node/utils/interop/state.js +2 -1
  408. package/lib/node/utils/interop/state.js.map +1 -1
  409. package/lib/sync/backfill/backfill.d.ts +1 -1
  410. package/lib/sync/backfill/backfill.js +16 -58
  411. package/lib/sync/backfill/backfill.js.map +1 -1
  412. package/lib/sync/constants.d.ts +1 -2
  413. package/lib/sync/constants.js +1 -2
  414. package/lib/sync/constants.js.map +1 -1
  415. package/lib/sync/interface.d.ts +59 -1
  416. package/lib/sync/interface.js +20 -0
  417. package/lib/sync/interface.js.map +1 -1
  418. package/lib/sync/options.d.ts +1 -1
  419. package/lib/sync/range/batch.d.ts +32 -48
  420. package/lib/sync/range/batch.js +55 -201
  421. package/lib/sync/range/batch.js.map +1 -1
  422. package/lib/sync/range/chain.d.ts +10 -15
  423. package/lib/sync/range/chain.js +70 -141
  424. package/lib/sync/range/chain.js.map +1 -1
  425. package/lib/sync/range/range.d.ts +3 -3
  426. package/lib/sync/range/range.js +54 -86
  427. package/lib/sync/range/range.js.map +1 -1
  428. package/lib/sync/range/utils/hashBlocks.d.ts +2 -2
  429. package/lib/sync/range/utils/hashBlocks.js +4 -6
  430. package/lib/sync/range/utils/hashBlocks.js.map +1 -1
  431. package/lib/sync/range/utils/peerBalancer.js +13 -18
  432. package/lib/sync/range/utils/peerBalancer.js.map +1 -1
  433. package/lib/sync/sync.d.ts +2 -1
  434. package/lib/sync/sync.js +87 -95
  435. package/lib/sync/sync.js.map +1 -1
  436. package/lib/sync/unknownBlock.d.ts +28 -42
  437. package/lib/sync/unknownBlock.js +458 -405
  438. package/lib/sync/unknownBlock.js.map +1 -1
  439. package/lib/sync/utils/pendingBlocksTree.d.ts +6 -14
  440. package/lib/sync/utils/pendingBlocksTree.js +18 -24
  441. package/lib/sync/utils/pendingBlocksTree.js.map +1 -1
  442. package/lib/util/array.js +3 -7
  443. package/lib/util/array.js.map +1 -1
  444. package/lib/util/asyncIterableToEvents.js +3 -6
  445. package/lib/util/asyncIterableToEvents.js.map +1 -1
  446. package/lib/util/binarySearch.js +0 -2
  447. package/lib/util/binarySearch.js.map +1 -1
  448. package/lib/util/blobs.d.ts +2 -2
  449. package/lib/util/blobs.js +4 -4
  450. package/lib/util/blobs.js.map +1 -1
  451. package/lib/util/bufferPool.js +2 -7
  452. package/lib/util/bufferPool.js.map +1 -1
  453. package/lib/util/clock.d.ts +1 -1
  454. package/lib/util/clock.js +18 -23
  455. package/lib/util/clock.js.map +1 -1
  456. package/lib/util/dataColumns.d.ts +10 -13
  457. package/lib/util/dataColumns.js +132 -90
  458. package/lib/util/dataColumns.js.map +1 -1
  459. package/lib/util/itTrigger.js +4 -4
  460. package/lib/util/itTrigger.js.map +1 -1
  461. package/lib/util/map.js +0 -2
  462. package/lib/util/map.js.map +1 -1
  463. package/lib/util/queue/index.d.ts +1 -1
  464. package/lib/util/queue/index.js +1 -1
  465. package/lib/util/queue/index.js.map +1 -1
  466. package/lib/util/queue/itemQueue.js +49 -52
  467. package/lib/util/queue/itemQueue.js.map +1 -1
  468. package/lib/util/serializedCache.js +3 -1
  469. package/lib/util/serializedCache.js.map +1 -1
  470. package/lib/util/set.js +0 -2
  471. package/lib/util/set.js.map +1 -1
  472. package/lib/util/sszBytes.d.ts +0 -1
  473. package/lib/util/sszBytes.js +1 -1
  474. package/lib/util/sszBytes.js.map +1 -1
  475. package/lib/util/timeSeries.js +1 -3
  476. package/lib/util/timeSeries.js.map +1 -1
  477. package/lib/util/types.d.ts +1 -1
  478. package/lib/util/wrapError.d.ts +0 -7
  479. package/package.json +18 -36
  480. package/lib/bun-wrappers/prometheus-gc-stats.d.ts +0 -2
  481. package/lib/bun-wrappers/prometheus-gc-stats.js +0 -8
  482. package/lib/bun-wrappers/prometheus-gc-stats.js.map +0 -1
  483. package/lib/chain/ColumnReconstructionTracker.d.ts +0 -32
  484. package/lib/chain/ColumnReconstructionTracker.js +0 -71
  485. package/lib/chain/ColumnReconstructionTracker.js.map +0 -1
  486. package/lib/chain/GetBlobsTracker.d.ts +0 -31
  487. package/lib/chain/GetBlobsTracker.js +0 -82
  488. package/lib/chain/GetBlobsTracker.js.map +0 -1
  489. package/lib/sync/types.d.ts +0 -44
  490. package/lib/sync/types.js +0 -34
  491. package/lib/sync/types.js.map +0 -1
  492. package/lib/sync/utils/downloadByRange.d.ts +0 -186
  493. package/lib/sync/utils/downloadByRange.js +0 -457
  494. package/lib/sync/utils/downloadByRange.js.map +0 -1
  495. package/lib/sync/utils/downloadByRoot.d.ts +0 -121
  496. package/lib/sync/utils/downloadByRoot.js +0 -346
  497. package/lib/sync/utils/downloadByRoot.js.map +0 -1
  498. package/lib/util/execution.d.ts +0 -20
  499. package/lib/util/execution.js +0 -165
  500. package/lib/util/execution.js.map +0 -1
@@ -1,77 +1,21 @@
1
- import { ForkSeq, INTERVALS_PER_SLOT } from "@lodestar/params";
2
- import { RequestError, RequestErrorCode } from "@lodestar/reqresp";
3
- import { prettyBytes, prettyPrintIndices, pruneSetToMax, sleep } from "@lodestar/utils";
4
- import { isBlockInputBlobs, isBlockInputColumns } from "../chain/blocks/blockInput/blockInput.js";
5
- import { BlockInputSource } from "../chain/blocks/blockInput/types.js";
1
+ import { ForkName, ForkSeq, INTERVALS_PER_SLOT } from "@lodestar/params";
2
+ import { fromHex, pruneSetToMax, toRootHex } from "@lodestar/utils";
3
+ import { sleep } from "@lodestar/utils";
4
+ import { BlockInputType } from "../chain/blocks/types.js";
6
5
  import { BlockError, BlockErrorCode } from "../chain/errors/index.js";
7
- import { ChainEvent } from "../chain/index.js";
8
- import { NetworkEvent, prettyPrintPeerIdStr } from "../network/index.js";
6
+ import { NetworkEvent } from "../network/index.js";
7
+ import { beaconBlocksMaybeBlobsByRoot, unavailableBeaconBlobsByRoot, } from "../network/reqresp/beaconBlocksMaybeBlobsByRoot.js";
8
+ import { byteArrayEquals } from "../util/bytes.js";
9
9
  import { shuffle } from "../util/shuffle.js";
10
10
  import { sortBy } from "../util/sortBy.js";
11
11
  import { wrapError } from "../util/wrapError.js";
12
12
  import { MAX_CONCURRENT_REQUESTS } from "./constants.js";
13
- import { PendingBlockInputStatus, PendingBlockType, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, isPendingBlockInput, } from "./types.js";
14
- import { DownloadByRootError, downloadByRoot } from "./utils/downloadByRoot.js";
13
+ import { PendingBlockStatus, PendingBlockType } from "./interface.js";
15
14
  import { getAllDescendantBlocks, getDescendantBlocks, getUnknownAndAncestorBlocks } from "./utils/pendingBlocksTree.js";
16
15
  const MAX_ATTEMPTS_PER_BLOCK = 5;
17
16
  const MAX_KNOWN_BAD_BLOCKS = 500;
18
17
  const MAX_PENDING_BLOCKS = 100;
19
- var FetchResult;
20
- (function (FetchResult) {
21
- FetchResult["SuccessResolved"] = "success_resolved";
22
- FetchResult["SuccessMissingParent"] = "success_missing_parent";
23
- FetchResult["SuccessLate"] = "success_late";
24
- FetchResult["FailureTriedAllPeers"] = "failure_tried_all_peers";
25
- FetchResult["FailureMaxAttempts"] = "failure_max_attempts";
26
- })(FetchResult || (FetchResult = {}));
27
- /**
28
- * BlockInputSync is a class that handles ReqResp to find blocks and data related to a specific blockRoot. The
29
- * blockRoot may have been found via object gossip, or the API. Gossip objects that can trigger a search are block,
30
- * blobs, columns, attestations, etc. In the case of blocks and data this is generally during the current slot but
31
- * can also be for items that are received late but are not fully verified and thus not in fork-choice (old blocks on
32
- * an unknown fork). It can also be triggered via an attestation (or sync committee message or any other item that
33
- * gets gossiped) that references a blockRoot that is not in fork-choice. In rare (and realistically should not happen)
34
- * situations it can get triggered via the API when the validator attempts to publish a block, attestation, aggregate
35
- * and proof or a sync committee contribution that has unknown information included (parentRoot for instance).
36
- *
37
- * The goal of the class is to make sure that all information that is necessary for import into fork-choice is pulled
38
- * from peers so that the block and data can be processed, and thus the object that triggered the search can be
39
- * referenced and validated.
40
- *
41
- * The most common case for this search is a set of block/data that comes across gossip for the current slot, during
42
- * normal chain operation, but not everything was received before the gossip cutoff window happens so it is necessary
43
- * to pull remaining data via req/resp so that fork-choice can be updated prior to making an attestation for the
44
- * current slot.
45
- *
46
- * Event sources for old UnknownBlock
47
- *
48
- * - publishBlock
49
- * - gossipHandlers
50
- * - searchUnknownSlotRoot
51
- * = produceSyncCommitteeContribution
52
- * = validateGossipFnRetryUnknownRoot
53
- * * submitPoolAttestationsV2
54
- * * publishAggregateAndProofsV2
55
- * = onPendingGossipsubMessage
56
- * * NetworkEvent.pendingGossipsubMessage
57
- * - onGossipsubMessage
58
- */
59
- export class BlockInputSync {
60
- config;
61
- network;
62
- chain;
63
- logger;
64
- metrics;
65
- opts;
66
- /**
67
- * block RootHex -> PendingBlock. To avoid finding same root at the same time
68
- */
69
- pendingBlocks = new Map();
70
- knownBadBlocks = new Set();
71
- proposerBoostSecWindow;
72
- maxPendingBlocks;
73
- subscribedToNetworkEvents = false;
74
- peerBalancer;
18
+ export class UnknownBlockSync {
75
19
  constructor(config, network, chain, logger, metrics, opts) {
76
20
  this.config = config;
77
21
  this.network = network;
@@ -79,231 +23,306 @@ export class BlockInputSync {
79
23
  this.logger = logger;
80
24
  this.metrics = metrics;
81
25
  this.opts = opts;
26
+ /**
27
+ * block RootHex -> PendingBlock. To avoid finding same root at the same time
28
+ */
29
+ this.pendingBlocks = new Map();
30
+ this.knownBadBlocks = new Set();
31
+ this.subscribedToNetworkEvents = false;
32
+ this.engineGetBlobsCache = new Map();
33
+ this.blockInputsRetryTrackerCache = new Set();
34
+ /**
35
+ * Process an unknownBlock event and register the block in `pendingBlocks` Map.
36
+ */
37
+ this.onUnknownBlock = (data) => {
38
+ try {
39
+ const unknownBlockType = this.addUnknownBlock(data.rootHex, data.peer);
40
+ this.triggerUnknownBlockSearch();
41
+ this.metrics?.syncUnknownBlock.requests.inc({ type: unknownBlockType });
42
+ }
43
+ catch (e) {
44
+ this.logger.debug("Error handling unknownBlock event", {}, e);
45
+ }
46
+ };
47
+ /**
48
+ * Process an unknownBlockInput event and register the block in `pendingBlocks` Map.
49
+ */
50
+ this.onUnknownBlockInput = (data) => {
51
+ try {
52
+ const unknownBlockType = this.addUnknownBlock(data.blockInput, data.peer);
53
+ this.triggerUnknownBlockSearch();
54
+ this.metrics?.syncUnknownBlock.requests.inc({ type: unknownBlockType });
55
+ }
56
+ catch (e) {
57
+ this.logger.debug("Error handling unknownBlockInput event", {}, e);
58
+ }
59
+ };
60
+ /**
61
+ * Process an unknownBlockParent event and register the block in `pendingBlocks` Map.
62
+ */
63
+ this.onUnknownParent = (data) => {
64
+ try {
65
+ this.addUnknownParent(data.blockInput, data.peer);
66
+ this.triggerUnknownBlockSearch();
67
+ this.metrics?.syncUnknownBlock.requests.inc({ type: PendingBlockType.UNKNOWN_PARENT });
68
+ }
69
+ catch (e) {
70
+ this.logger.debug("Error handling unknownBlockParent event", {}, e);
71
+ }
72
+ };
73
+ this.onPeerConnected = (data) => {
74
+ try {
75
+ const peerId = data.peer;
76
+ const peerSyncMeta = this.network.getConnectedPeerSyncMeta(peerId);
77
+ this.peerBalancer.onPeerConnected(data.peer, peerSyncMeta);
78
+ this.triggerUnknownBlockSearch();
79
+ }
80
+ catch (e) {
81
+ this.logger.debug("Error handling peerConnected event", {}, e);
82
+ }
83
+ };
84
+ this.onPeerDisconnected = (data) => {
85
+ const peerId = data.peer;
86
+ this.peerBalancer.onPeerDisconnected(peerId);
87
+ };
88
+ /**
89
+ * Gather tip parent blocks with unknown parent and do a search for all of them
90
+ */
91
+ this.triggerUnknownBlockSearch = () => {
92
+ // Cheap early stop to prevent calling the network.getConnectedPeers()
93
+ if (this.pendingBlocks.size === 0) {
94
+ return;
95
+ }
96
+ // If the node loses all peers with pending unknown blocks, the sync will stall
97
+ const connectedPeers = this.network.getConnectedPeers();
98
+ if (connectedPeers.length === 0) {
99
+ this.logger.debug("No connected peers, skipping unknown block search.");
100
+ return;
101
+ }
102
+ const { unknowns, ancestors } = getUnknownAndAncestorBlocks(this.pendingBlocks);
103
+ // it's rare when there is no unknown block
104
+ // see https://github.com/ChainSafe/lodestar/issues/5649#issuecomment-1594213550
105
+ if (unknowns.length === 0) {
106
+ let processedBlocks = 0;
107
+ for (const block of ancestors) {
108
+ // when this happens, it's likely the block and parent block are processed by head sync
109
+ if (this.chain.forkChoice.hasBlockHex(block.parentBlockRootHex)) {
110
+ processedBlocks++;
111
+ this.processBlock(block).catch((e) => {
112
+ this.logger.debug("Unexpected error - process old downloaded block", {}, e);
113
+ });
114
+ }
115
+ }
116
+ this.logger.verbose("No unknown block, process ancestor downloaded blocks", {
117
+ pendingBlocks: this.pendingBlocks.size,
118
+ ancestorBlocks: ancestors.length,
119
+ processedBlocks,
120
+ });
121
+ return;
122
+ }
123
+ // most of the time there is exactly 1 unknown block
124
+ for (const block of unknowns) {
125
+ this.downloadBlock(block).catch((e) => {
126
+ this.logger.debug("Unexpected error - downloadBlock", { root: block.blockRootHex }, e);
127
+ });
128
+ }
129
+ };
82
130
  this.maxPendingBlocks = opts?.maxPendingBlocks ?? MAX_PENDING_BLOCKS;
83
131
  this.proposerBoostSecWindow = this.config.SECONDS_PER_SLOT / INTERVALS_PER_SLOT;
84
- this.peerBalancer = new UnknownBlockPeerBalancer();
132
+ this.peerBalancer = new UnknownBlockPeerBalancer(this.network.custodyConfig);
85
133
  if (metrics) {
86
- metrics.blockInputSync.pendingBlocks.addCollect(() => metrics.blockInputSync.pendingBlocks.set(this.pendingBlocks.size));
87
- metrics.blockInputSync.knownBadBlocks.addCollect(() => metrics.blockInputSync.knownBadBlocks.set(this.knownBadBlocks.size));
134
+ metrics.syncUnknownBlock.pendingBlocks.addCollect(() => {
135
+ metrics.syncUnknownBlock.pendingBlocks.set(this.pendingBlocks.size);
136
+ metrics.syncUnknownBlock.knownBadBlocks.set(this.knownBadBlocks.size);
137
+ metrics.syncUnknownBlock.peerBalancer.peersMetaCount.set(this.peerBalancer.peersMeta.size);
138
+ metrics.syncUnknownBlock.peerBalancer.peersActiveRequestCount.set(this.peerBalancer.activeRequests.size);
139
+ metrics.syncUnknownBlock.peerBalancer.totalActiveRequests.set(this.peerBalancer.getTotalActiveRequests());
140
+ });
88
141
  }
89
142
  }
90
143
  subscribeToNetwork() {
91
- if (this.opts?.disableBlockInputSync) {
92
- this.logger.verbose("BlockInputSync disabled by disableBlockInputSync option.");
93
- return;
144
+ if (!this.opts?.disableUnknownBlockSync) {
145
+ // cannot chain to the above if or the log will be incorrect
146
+ if (!this.subscribedToNetworkEvents) {
147
+ this.logger.verbose("UnknownBlockSync enabled.");
148
+ this.network.events.on(NetworkEvent.unknownBlock, this.onUnknownBlock);
149
+ this.network.events.on(NetworkEvent.unknownBlockInput, this.onUnknownBlockInput);
150
+ this.network.events.on(NetworkEvent.unknownBlockParent, this.onUnknownParent);
151
+ this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
152
+ this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
153
+ this.subscribedToNetworkEvents = true;
154
+ }
94
155
  }
95
- // cannot chain to the above if or the log will be incorrect
96
- if (!this.subscribedToNetworkEvents) {
97
- this.logger.verbose("BlockInputSync enabled.");
98
- this.chain.emitter.on(ChainEvent.unknownBlockRoot, this.onUnknownBlockRoot);
99
- this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
100
- this.chain.emitter.on(ChainEvent.unknownParent, this.onUnknownParent);
101
- this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
102
- this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
103
- this.subscribedToNetworkEvents = true;
156
+ else {
157
+ this.logger.verbose("UnknownBlockSync disabled by disableUnknownBlockSync option.");
104
158
  }
105
159
  }
106
160
  unsubscribeFromNetwork() {
107
- this.logger.verbose("BlockInputSync disabled.");
108
- this.chain.emitter.off(ChainEvent.unknownBlockRoot, this.onUnknownBlockRoot);
109
- this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
110
- this.chain.emitter.off(ChainEvent.unknownParent, this.onUnknownParent);
161
+ this.logger.verbose("UnknownBlockSync disabled.");
162
+ this.network.events.off(NetworkEvent.unknownBlock, this.onUnknownBlock);
163
+ this.network.events.off(NetworkEvent.unknownBlockInput, this.onUnknownBlockInput);
164
+ this.network.events.off(NetworkEvent.unknownBlockParent, this.onUnknownParent);
111
165
  this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
112
166
  this.network.events.off(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
113
167
  this.subscribedToNetworkEvents = false;
114
168
  }
115
169
  close() {
116
170
  this.unsubscribeFromNetwork();
171
+ // add more in the future if needed
117
172
  }
118
173
  isSubscribedToNetwork() {
119
174
  return this.subscribedToNetworkEvents;
120
175
  }
121
176
  /**
122
- * Process an unknownBlock event and register the block in `pendingBlocks` Map.
123
- */
124
- onUnknownBlockRoot = (data) => {
125
- try {
126
- const { root, slot } = data.rootSlot;
127
- this.addByRootHex(root, slot, data.peer);
128
- this.triggerUnknownBlockSearch();
129
- this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_BLOCK_ROOT });
130
- this.metrics?.blockInputSync.source.inc({ source: data.source });
131
- }
132
- catch (e) {
133
- this.logger.debug("Error handling unknownBlockRoot event", {}, e);
134
- }
135
- };
136
- /**
137
- * Process an unknownBlockInput event and register the block in `pendingBlocks` Map.
177
+ * When a blockInput comes with an unknown parent:
178
+ * - add the block to pendingBlocks with status downloaded or pending blockRootHex as key. This is similar to
179
+ * an `onUnknownBlock` event, but the blocks is downloaded.
180
+ * - add the parent root to pendingBlocks with status pending, parentBlockRootHex as key. This is
181
+ * the same to an `onUnknownBlock` event with parentBlockRootHex as root.
138
182
  */
139
- onIncompleteBlockInput = (data) => {
140
- try {
141
- this.addByBlockInput(data.blockInput, data.peer);
142
- this.triggerUnknownBlockSearch();
143
- this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.INCOMPLETE_BLOCK_INPUT });
144
- this.metrics?.blockInputSync.source.inc({ source: data.source });
145
- }
146
- catch (e) {
147
- this.logger.debug("Error handling incompleteBlockInput event", {}, e);
148
- }
149
- };
150
- /**
151
- * Process an unknownBlockParent event and register the block in `pendingBlocks` Map.
152
- */
153
- onUnknownParent = (data) => {
154
- try {
155
- // we don't know the slot of parent, hence make it undefined
156
- this.addByRootHex(data.blockInput.parentRootHex, undefined, data.peer);
157
- this.addByBlockInput(data.blockInput, data.peer);
158
- this.triggerUnknownBlockSearch();
159
- this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_PARENT });
160
- this.metrics?.blockInputSync.source.inc({ source: data.source });
161
- }
162
- catch (e) {
163
- this.logger.debug("Error handling unknownParent event", {}, e);
164
- }
165
- };
166
- addByRootHex = (rootHex, slot, peerIdStr) => {
167
- let pendingBlock = this.pendingBlocks.get(rootHex);
183
+ addUnknownParent(blockInput, peerIdStr) {
184
+ const block = blockInput.block.message;
185
+ const blockRoot = this.config.getForkTypes(block.slot).BeaconBlock.hashTreeRoot(block);
186
+ const blockRootHex = toRootHex(blockRoot);
187
+ const parentBlockRootHex = toRootHex(block.parentRoot);
188
+ // add 1 pending block with status downloaded
189
+ let pendingBlock = this.pendingBlocks.get(blockRootHex);
168
190
  if (!pendingBlock) {
169
- pendingBlock = {
170
- status: PendingBlockInputStatus.pending,
171
- slot,
172
- rootHex: rootHex,
173
- peerIdStrings: new Set(),
174
- timeAddedSec: Date.now() / 1000,
175
- };
176
- this.pendingBlocks.set(rootHex, pendingBlock);
177
- this.logger.verbose("Added new rootHex to BlockInputSync.pendingBlocks", {
178
- rootHex: prettyBytes(pendingBlock.rootHex),
179
- peerIdStr: peerIdStr ?? "unknown peer",
191
+ pendingBlock =
192
+ blockInput.type === BlockInputType.dataPromise
193
+ ? {
194
+ unknownBlockType: PendingBlockType.UNKNOWN_DATA,
195
+ blockRootHex,
196
+ // this will be set after we download block
197
+ parentBlockRootHex: null,
198
+ blockInput,
199
+ peerIdStrs: new Set(),
200
+ status: PendingBlockStatus.pending,
201
+ downloadAttempts: 0,
202
+ }
203
+ : {
204
+ blockRootHex,
205
+ parentBlockRootHex,
206
+ blockInput,
207
+ peerIdStrs: new Set(),
208
+ status: PendingBlockStatus.downloaded,
209
+ downloadAttempts: 0,
210
+ };
211
+ this.pendingBlocks.set(blockRootHex, pendingBlock);
212
+ this.logger.verbose("Added unknown block parent to pendingBlocks", {
213
+ root: blockRootHex,
214
+ parent: parentBlockRootHex,
180
215
  });
181
216
  }
182
- if (peerIdStr) {
183
- pendingBlock.peerIdStrings.add(peerIdStr);
217
+ pendingBlock.peerIdStrs.add(peerIdStr);
218
+ // add 1 pending block with status pending
219
+ this.addUnknownBlock(parentBlockRootHex, peerIdStr);
220
+ }
221
+ addUnknownBlock(blockInputOrRootHex, peerIdStr) {
222
+ let blockRootHex;
223
+ let blockInput;
224
+ let unknownBlockType;
225
+ if (typeof blockInputOrRootHex === "string") {
226
+ blockRootHex = blockInputOrRootHex;
227
+ blockInput = null;
228
+ unknownBlockType = PendingBlockType.UNKNOWN_BLOCK;
184
229
  }
185
- // TODO: check this prune methodology
186
- // Limit pending blocks to prevent DOS attacks that cause OOM
187
- const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
188
- if (prunedItemCount > 0) {
189
- this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
190
- }
191
- };
192
- addByBlockInput = (blockInput, peerIdStr) => {
193
- let pendingBlock = this.pendingBlocks.get(blockInput.blockRootHex);
194
- // if entry is missing or was added via rootHex and now we have more complete information overwrite
195
- // the existing information with the more complete cache entry
196
- if (!pendingBlock || !isPendingBlockInput(pendingBlock)) {
230
+ else {
231
+ if (blockInputOrRootHex.block !== null) {
232
+ const { block } = blockInputOrRootHex;
233
+ blockRootHex = toRootHex(this.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message));
234
+ unknownBlockType = PendingBlockType.UNKNOWN_DATA;
235
+ }
236
+ else {
237
+ unknownBlockType = PendingBlockType.UNKNOWN_BLOCKINPUT;
238
+ blockRootHex = blockInputOrRootHex.blockRootHex;
239
+ }
240
+ blockInput = blockInputOrRootHex;
241
+ }
242
+ let pendingBlock = this.pendingBlocks.get(blockRootHex);
243
+ if (!pendingBlock) {
197
244
  pendingBlock = {
198
- // can be added via unknown parent and we may already have full block input. need to check and set correctly
199
- // so we pull the data if its missing or handle the block correctly in getIncompleteAndAncestorBlocks
200
- status: blockInput.hasBlockAndAllData() ? PendingBlockInputStatus.downloaded : PendingBlockInputStatus.pending,
245
+ unknownBlockType,
246
+ blockRootHex,
247
+ // this will be set after we download block
248
+ parentBlockRootHex: null,
201
249
  blockInput,
202
- peerIdStrings: new Set(),
203
- timeAddedSec: Date.now() / 1000,
250
+ peerIdStrs: new Set(),
251
+ status: PendingBlockStatus.pending,
252
+ downloadAttempts: 0,
204
253
  };
205
- this.pendingBlocks.set(blockInput.blockRootHex, pendingBlock);
206
- this.logger.verbose("Added blockInput to BlockInputSync.pendingBlocks", pendingBlock.blockInput.getLogMeta());
254
+ this.pendingBlocks.set(blockRootHex, pendingBlock);
255
+ this.logger.verbose("Added unknown block to pendingBlocks", {
256
+ unknownBlockType,
257
+ root: blockRootHex,
258
+ slot: blockInput?.block?.message.slot ?? "unknown",
259
+ });
207
260
  }
208
261
  if (peerIdStr) {
209
- pendingBlock.peerIdStrings.add(peerIdStr);
262
+ pendingBlock.peerIdStrs.add(peerIdStr);
210
263
  }
211
- // TODO: check this prune methodology
212
264
  // Limit pending blocks to prevent DOS attacks that cause OOM
213
265
  const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
214
266
  if (prunedItemCount > 0) {
215
- this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
216
- }
217
- };
218
- onPeerConnected = (data) => {
219
- try {
220
- const peerId = data.peer;
221
- const peerSyncMeta = this.network.getConnectedPeerSyncMeta(peerId);
222
- this.peerBalancer.onPeerConnected(data.peer, peerSyncMeta);
223
- this.triggerUnknownBlockSearch();
224
- }
225
- catch (e) {
226
- this.logger.debug("Error handling peerConnected event", {}, e);
227
- }
228
- };
229
- onPeerDisconnected = (data) => {
230
- const peerId = data.peer;
231
- this.peerBalancer.onPeerDisconnected(peerId);
232
- };
233
- /**
234
- * Gather tip parent blocks with unknown parent and do a search for all of them
235
- */
236
- triggerUnknownBlockSearch = () => {
237
- // Cheap early stop to prevent calling the network.getConnectedPeers()
238
- if (this.pendingBlocks.size === 0) {
239
- return;
267
+ this.logger.warn(`Pruned ${prunedItemCount} pending blocks from UnknownBlockSync`);
240
268
  }
241
- // If the node loses all peers with pending unknown blocks, the sync will stall
242
- const connectedPeers = this.network.getConnectedPeers();
243
- if (connectedPeers.length === 0) {
244
- this.logger.debug("No connected peers, skipping unknown block search.");
245
- return;
246
- }
247
- const { unknowns, ancestors } = getUnknownAndAncestorBlocks(this.pendingBlocks);
248
- // it's rare when there is no unknown block
249
- // see https://github.com/ChainSafe/lodestar/issues/5649#issuecomment-1594213550
250
- if (unknowns.length === 0) {
251
- let processedBlocks = 0;
252
- for (const block of ancestors) {
253
- // when this happens, it's likely the block and parent block are processed by head sync
254
- if (this.chain.forkChoice.hasBlockHex(block.blockInput.parentRootHex)) {
255
- processedBlocks++;
256
- this.processBlock(block).catch((e) => {
257
- this.logger.debug("Unexpected error - process old downloaded block", {}, e);
258
- });
259
- }
260
- }
261
- this.logger.verbose("No unknown block, process ancestor downloaded blocks", {
262
- pendingBlocks: this.pendingBlocks.size,
263
- ancestorBlocks: ancestors.length,
264
- processedBlocks,
265
- });
266
- return;
267
- }
268
- // most of the time there is exactly 1 unknown block
269
- for (const block of unknowns) {
270
- this.downloadBlock(block).catch((e) => {
271
- this.logger.debug("Unexpected error - downloadBlock", { root: getBlockInputSyncCacheItemRootHex(block) }, e);
272
- });
273
- }
274
- };
269
+ return unknownBlockType;
270
+ }
275
271
  async downloadBlock(block) {
276
- if (block.status !== PendingBlockInputStatus.pending) {
272
+ if (block.status !== PendingBlockStatus.pending) {
277
273
  return;
278
274
  }
279
- const rootHex = getBlockInputSyncCacheItemRootHex(block);
275
+ const unknownBlockType = block.unknownBlockType;
280
276
  const logCtx = {
281
- slot: getBlockInputSyncCacheItemSlot(block),
282
- blockRoot: prettyBytes(rootHex),
277
+ root: block.blockRootHex,
283
278
  pendingBlocks: this.pendingBlocks.size,
279
+ slot: block.blockInput?.block?.message.slot ?? "unknown",
280
+ unknownBlockType,
284
281
  };
285
- this.logger.verbose("BlockInputSync.downloadBlock()", logCtx);
286
- block.status = PendingBlockInputStatus.fetching;
287
- const res = await wrapError(this.fetchBlockInput(block));
282
+ this.logger.verbose("Downloading unknown block", logCtx);
283
+ block.status = PendingBlockStatus.fetching;
284
+ let res;
285
+ if (block.blockInput === null) {
286
+ // we only have block root, and nothing else
287
+ res = await wrapError(this.fetchUnknownBlockRoot(fromHex(block.blockRootHex)));
288
+ }
289
+ else {
290
+ res = await wrapError(this.fetchUnavailableBlockInput(block.blockInput));
291
+ }
292
+ if (res.err)
293
+ this.metrics?.syncUnknownBlock.downloadedBlocksError.inc();
294
+ else
295
+ this.metrics?.syncUnknownBlock.downloadedBlocksSuccess.inc();
288
296
  if (!res.err) {
289
- this.metrics?.blockInputSync.downloadedBlocksSuccess.inc();
290
- const pending = res.result;
291
- this.pendingBlocks.set(pending.blockInput.blockRootHex, pending);
292
- const blockSlot = pending.blockInput.slot;
297
+ const { blockInput, peerIdStr } = res.result;
298
+ // fetchUnknownBlockRoot and fetchUnavailableBlockInput should return available data BlockInput, throw error if not
299
+ if (blockInput.type === BlockInputType.dataPromise) {
300
+ // if there were any peers who would have had the missing datacolumns, it would have resulted in err
301
+ throw Error(`Expected BlockInput to be available, got dataPromise for ${block.blockRootHex}`);
302
+ }
303
+ block = {
304
+ ...block,
305
+ status: PendingBlockStatus.downloaded,
306
+ blockInput,
307
+ parentBlockRootHex: toRootHex(blockInput.block.message.parentRoot),
308
+ };
309
+ this.pendingBlocks.set(block.blockRootHex, block);
310
+ const blockSlot = blockInput.block.message.slot;
293
311
  const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
294
312
  const delaySec = Date.now() / 1000 - (this.chain.genesisTime + blockSlot * this.config.SECONDS_PER_SLOT);
295
- this.metrics?.blockInputSync.elapsedTimeTillReceived.observe(delaySec);
296
- const parentInForkChoice = this.chain.forkChoice.hasBlockHex(pending.blockInput.parentRootHex);
297
- const logCtx2 = {
298
- ...logCtx,
299
- slot: blockSlot,
300
- parentInForkChoice,
301
- };
302
- this.logger.verbose("Downloaded unknown block", logCtx2);
303
- if (parentInForkChoice) {
313
+ this.metrics?.syncUnknownBlock.elapsedTimeTillReceived.observe(delaySec);
314
+ const parentInForkchoice = this.chain.forkChoice.hasBlock(blockInput.block.message.parentRoot);
315
+ this.logger.verbose("Downloaded unknown block", {
316
+ root: block.blockRootHex,
317
+ pendingBlocks: this.pendingBlocks.size,
318
+ parentInForkchoice,
319
+ blockInputType: blockInput.type,
320
+ unknownBlockType,
321
+ });
322
+ if (parentInForkchoice) {
304
323
  // Bingo! Process block. Add to pending blocks anyway for recycle the cache that prevents duplicate processing
305
- this.processBlock(pending).catch((e) => {
306
- this.logger.debug("Unexpected error - process newly downloaded block", logCtx2, e);
324
+ this.processBlock(block).catch((e) => {
325
+ this.logger.debug("Unexpected error - process newly downloaded block", {}, e);
307
326
  });
308
327
  }
309
328
  else if (blockSlot <= finalizedSlot) {
@@ -312,20 +331,34 @@ export class BlockInputSync {
312
331
  // 0 - 1 - ... - n - finalizedSlot
313
332
  // \
314
333
  // parent 1 - parent 2 - ... - unknownParent block
334
+ const blockRoot = this.config.getForkTypes(blockSlot).BeaconBlock.hashTreeRoot(blockInput.block.message);
315
335
  this.logger.debug("Downloaded block is before finalized slot", {
316
- ...logCtx2,
317
336
  finalizedSlot,
337
+ blockSlot,
338
+ parentRoot: toRootHex(blockRoot),
339
+ unknownBlockType,
318
340
  });
319
- this.removeAndDownScoreAllDescendants(block);
341
+ this.removeAndDownscoreAllDescendants(block);
320
342
  }
321
343
  else {
322
- this.onUnknownBlockRoot({ rootSlot: { root: pending.blockInput.parentRootHex }, source: BlockInputSource.byRoot });
344
+ this.onUnknownParent({ blockInput, peer: peerIdStr });
323
345
  }
324
346
  }
325
347
  else {
326
- this.metrics?.blockInputSync.downloadedBlocksError.inc();
327
- this.logger.debug("Ignoring unknown block root after many failed downloads", logCtx, res.err);
328
- this.removeAndDownScoreAllDescendants(block);
348
+ // block download has error, this allows to retry the download of the block
349
+ block.status = PendingBlockStatus.pending;
350
+ // parentSlot > finalizedSlot, continue downloading parent of parent
351
+ block.downloadAttempts++;
352
+ const errorData = { root: block.blockRootHex, attempts: block.downloadAttempts, unknownBlockType };
353
+ if (block.downloadAttempts > MAX_ATTEMPTS_PER_BLOCK) {
354
+ // Give up on this block and assume it does not exist, penalizing all peers as if it was a bad block
355
+ this.logger.debug("Ignoring unknown block root after many failed downloads", errorData, res.err);
356
+ this.removeAndDownscoreAllDescendants(block);
357
+ }
358
+ else {
359
+ // Try again when a new peer connects, its status changes, or a new unknownBlockParent event happens
360
+ this.logger.debug("Error downloading unknown block root", errorData, res.err);
361
+ }
329
362
  }
330
363
  }
331
364
  /**
@@ -336,14 +369,11 @@ export class BlockInputSync {
336
369
  async processBlock(pendingBlock) {
337
370
  // pending block status is `downloaded` right after `downloadBlock`
338
371
  // but could be `pending` if added by `onUnknownBlockParent` event and this function is called recursively
339
- if (pendingBlock.status !== PendingBlockInputStatus.downloaded) {
340
- if (pendingBlock.status === PendingBlockInputStatus.pending) {
372
+ if (pendingBlock.status !== PendingBlockStatus.downloaded) {
373
+ if (pendingBlock.status === PendingBlockStatus.pending) {
341
374
  const connectedPeers = this.network.getConnectedPeers();
342
375
  if (connectedPeers.length === 0) {
343
- this.logger.debug("No connected peers, skipping download block", {
344
- slot: pendingBlock.blockInput.slot,
345
- blockRoot: pendingBlock.blockInput.blockRootHex,
346
- });
376
+ this.logger.debug("No connected peers, skipping download block", { blockRoot: pendingBlock.blockRootHex });
347
377
  return;
348
378
  }
349
379
  // if the download is a success we'll call `processBlock()` for this block
@@ -351,17 +381,20 @@ export class BlockInputSync {
351
381
  }
352
382
  return;
353
383
  }
354
- pendingBlock.status = PendingBlockInputStatus.processing;
384
+ pendingBlock.status = PendingBlockStatus.processing;
355
385
  // this prevents unbundling attack
356
386
  // see https://lighthouse-blog.sigmaprime.io/mev-unbundling-rpc.html
357
- const { slot: blockSlot, proposerIndex } = pendingBlock.blockInput.getBlock().message;
387
+ const { slot: blockSlot, proposerIndex } = pendingBlock.blockInput.block.message;
358
388
  if (this.chain.clock.secFromSlot(blockSlot) < this.proposerBoostSecWindow &&
359
389
  this.chain.seenBlockProposers.isKnown(blockSlot, proposerIndex)) {
360
390
  // proposer is known by a gossip block already, wait a bit to make sure this block is not
361
391
  // eligible for proposer boost to prevent unbundling attack
392
+ const blockRoot = this.config
393
+ .getForkTypes(blockSlot)
394
+ .BeaconBlock.hashTreeRoot(pendingBlock.blockInput.block.message);
362
395
  this.logger.verbose("Avoid proposer boost for this block of known proposer", {
363
- slot: blockSlot,
364
- blockRoot: prettyBytes(pendingBlock.blockInput.blockRootHex),
396
+ blockSlot,
397
+ blockRoot: toRootHex(blockRoot),
365
398
  proposerIndex,
366
399
  });
367
400
  await sleep(this.proposerBoostSecWindow * 1000);
@@ -382,24 +415,21 @@ export class BlockInputSync {
382
415
  eagerPersistBlock: true,
383
416
  }));
384
417
  if (res.err)
385
- this.metrics?.blockInputSync.processedBlocksError.inc();
418
+ this.metrics?.syncUnknownBlock.processedBlocksError.inc();
386
419
  else
387
- this.metrics?.blockInputSync.processedBlocksSuccess.inc();
420
+ this.metrics?.syncUnknownBlock.processedBlocksSuccess.inc();
388
421
  if (!res.err) {
389
422
  // no need to update status to "processed", delete anyway
390
- this.pendingBlocks.delete(pendingBlock.blockInput.blockRootHex);
391
- this.chain.seenBlockInputCache.prune(pendingBlock.blockInput.blockRootHex);
423
+ this.pendingBlocks.delete(pendingBlock.blockRootHex);
392
424
  // Send child blocks to the processor
393
- for (const descendantBlock of getDescendantBlocks(pendingBlock.blockInput.blockRootHex, this.pendingBlocks)) {
394
- if (isPendingBlockInput(descendantBlock)) {
395
- this.processBlock(descendantBlock).catch((e) => {
396
- this.logger.debug("Unexpected error - process descendant block", {}, e);
397
- });
398
- }
425
+ for (const descendantBlock of getDescendantBlocks(pendingBlock.blockRootHex, this.pendingBlocks)) {
426
+ this.processBlock(descendantBlock).catch((e) => {
427
+ this.logger.debug("Unexpected error - process descendant block", {}, e);
428
+ });
399
429
  }
400
430
  }
401
431
  else {
402
- const errorData = { slot: pendingBlock.blockInput.slot, root: pendingBlock.blockInput.blockRootHex };
432
+ const errorData = { root: pendingBlock.blockRootHex, slot: pendingBlock.blockInput.block.message.slot };
403
433
  if (res.err instanceof BlockError) {
404
434
  switch (res.err.type.code) {
405
435
  // This cases are already handled with `{ignoreIfKnown: true}`
@@ -409,7 +439,7 @@ export class BlockInputSync {
409
439
  case BlockErrorCode.PRESTATE_MISSING:
410
440
  // Should not happen, mark as downloaded to try again latter
411
441
  this.logger.debug("Attempted to process block but its parent was still unknown", errorData, res.err);
412
- pendingBlock.status = PendingBlockInputStatus.downloaded;
442
+ pendingBlock.status = PendingBlockStatus.downloaded;
413
443
  break;
414
444
  case BlockErrorCode.EXECUTION_ENGINE_ERROR:
415
445
  // Removing the block(s) without penalizing the peers, hoping for EL to
@@ -419,13 +449,13 @@ export class BlockInputSync {
419
449
  default:
420
450
  // Block is not correct with respect to our chain. Log error loudly
421
451
  this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
422
- this.removeAndDownScoreAllDescendants(pendingBlock);
452
+ this.removeAndDownscoreAllDescendants(pendingBlock);
423
453
  }
424
454
  }
425
455
  // Probably a queue error or something unwanted happened, mark as pending to try again latter
426
456
  else {
427
457
  this.logger.debug("Unknown error processing block from unknown block sync", errorData, res.err);
428
- pendingBlock.status = PendingBlockInputStatus.downloaded;
458
+ pendingBlock.status = PendingBlockStatus.downloaded;
429
459
  }
430
460
  }
431
461
  }
@@ -438,129 +468,144 @@ export class BlockInputSync {
438
468
  * prefulu, will attempt a max of `MAX_ATTEMPTS_PER_BLOCK` on different peers, postfulu we may attempt more as defined in `getMaxDownloadAttempts()` function
439
469
  * Also verifies the received block root + returns the peer that provided the block for future downscoring.
440
470
  */
441
- async fetchBlockInput(cacheItem) {
442
- const rootHex = getBlockInputSyncCacheItemRootHex(cacheItem);
471
+ async fetchUnknownBlockRoot(blockRoot) {
472
+ const blockRootHex = toRootHex(blockRoot);
443
473
  const excludedPeers = new Set();
474
+ let partialDownload = null;
444
475
  const defaultPendingColumns = this.config.getForkSeq(this.chain.clock.currentSlot) >= ForkSeq.fulu
445
- ? new Set(this.network.custodyConfig.sampledColumns)
476
+ ? new Set(this.network.custodyConfig.sampleGroups)
446
477
  : null;
447
- const fetchStartSec = Date.now() / 1000;
448
- let slot = isPendingBlockInput(cacheItem) ? cacheItem.blockInput.slot : cacheItem.slot;
449
- if (slot !== undefined) {
450
- this.metrics?.blockInputSync.fetchBegin.observe(this.chain.clock.secFromSlot(slot, fetchStartSec));
451
- }
478
+ let lastError = null;
452
479
  let i = 0;
453
480
  while (i++ < this.getMaxDownloadAttempts()) {
454
- const pendingColumns = isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
455
- ? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
456
- : defaultPendingColumns;
457
- // pendingDataColumns is null pre-fulu
458
- const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers);
459
- if (peerMeta === null) {
481
+ // pendingDataColumns is null prefulu
482
+ const peer = this.peerBalancer.bestPeerForPendingColumns(partialDownload ? new Set(partialDownload.pendingDataColumns) : defaultPendingColumns, excludedPeers);
483
+ if (peer === null) {
460
484
  // no more peer with needed columns to try, throw error
461
- let message = `Error fetching UnknownBlockRoot slot=${slot} blockRoot=${prettyBytes(rootHex)} after ${i}: cannot find peer`;
462
- if (pendingColumns) {
463
- message += ` with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`;
464
- }
465
- this.metrics?.blockInputSync.fetchTimeSec.observe({ result: FetchResult.FailureTriedAllPeers }, Date.now() / 1000 - fetchStartSec);
466
- this.metrics?.blockInputSync.fetchPeers.set({ result: FetchResult.FailureTriedAllPeers }, i);
467
- throw Error(message);
485
+ throw Error(`Error fetching UnknownBlockRoot after ${i}: cannot find peer with needed columns ${partialDownload?.pendingDataColumns.join(", ")}`);
468
486
  }
469
- const { peerId, client: peerClient } = peerMeta;
470
- cacheItem.peerIdStrings.add(peerId);
487
+ const { peerId, client: peerClient } = peer;
488
+ excludedPeers.add(peerId);
471
489
  try {
472
- const downloadResult = await downloadByRoot({
473
- config: this.config,
474
- network: this.network,
475
- seenCache: this.chain.seenBlockInputCache,
476
- emitter: this.chain.emitter,
477
- peerMeta,
478
- cacheItem,
479
- });
480
- cacheItem = downloadResult.result;
481
- if (slot === undefined) {
482
- slot = cacheItem.blockInput.slot;
483
- // we were not able to observe the time into slot when starting the fetch, do it now
484
- this.metrics?.blockInputSync.fetchBegin.observe(this.chain.clock.secFromSlot(slot, fetchStartSec));
490
+ const { blocks: [blockInput], pendingDataColumns, } = await beaconBlocksMaybeBlobsByRoot(this.config, this.network, peerId, [blockRoot], partialDownload, peerClient, this.metrics, this.logger);
491
+ // Peer does not have the block, try with next peer
492
+ if (blockInput === undefined) {
493
+ continue;
485
494
  }
486
- const logCtx = { slot, rootHex, peerId, peerClient };
487
- this.logger.verbose("BlockInputSync.fetchBlockInput: successful download", logCtx);
488
- this.metrics?.blockInputSync.downloadByRoot.success.inc();
489
- const warnings = downloadResult.warnings;
490
- if (warnings) {
491
- for (const warning of warnings) {
492
- this.logger.debug("BlockInputSync.fetchBlockInput: downloaded with warning", logCtx, warning);
493
- this.metrics?.blockInputSync.downloadByRoot.warn.inc({ code: warning.type.code, client: peerClient });
494
- }
495
- // TODO: penalize peer?
495
+ if (pendingDataColumns !== null) {
496
+ partialDownload = { blocks: [blockInput], pendingDataColumns };
497
+ continue;
496
498
  }
499
+ // data is available, verify block root is correct
500
+ const block = blockInput.block.message;
501
+ const receivedBlockRoot = this.config.getForkTypes(block.slot).BeaconBlock.hashTreeRoot(block);
502
+ if (!byteArrayEquals(receivedBlockRoot, blockRoot)) {
503
+ throw Error(`Wrong block received by peer, got ${toRootHex(receivedBlockRoot)} expected ${blockRootHex}`);
504
+ }
505
+ return { blockInput, peerIdStr: peerId };
497
506
  }
498
507
  catch (e) {
499
- this.logger.debug("Error downloading in BlockInputSync.fetchBlockInput", { slot, rootHex, attempt: i, peer: peerId, peerClient }, e);
500
- const downloadByRootMetrics = this.metrics?.blockInputSync.downloadByRoot;
501
- // TODO: penalize peer?
502
- if (e instanceof DownloadByRootError) {
503
- const errorCode = e.type.code;
504
- downloadByRootMetrics?.error.inc({ code: errorCode, client: peerClient });
505
- excludedPeers.add(peerId);
508
+ this.logger.debug("Error fetching UnknownBlockRoot", { attempt: i, blockRootHex, peer: peerId }, e);
509
+ lastError = e;
510
+ }
511
+ finally {
512
+ this.peerBalancer.onRequestCompleted(peerId);
513
+ }
514
+ }
515
+ if (lastError) {
516
+ lastError.message = `Error fetching UnknownBlockRoot after ${i} attempts: ${lastError.message}`;
517
+ throw lastError;
518
+ }
519
+ throw Error(`Error fetching UnknownBlockRoot after ${i}: cannot download all blobs or data columns for block ${blockRootHex}`);
520
+ }
521
+ /**
522
+ * We have partial block input:
523
+ * - we have block but not have all blobs (deneb) or needed columns (fulu)
524
+ * - we don't have block and have some blobs (deneb) or some columns (fulu)
525
+ * Fetches missing block/data columns/block for the blockinput. This function returns either preData or availableData BlockInput.
526
+ */
527
+ async fetchUnavailableBlockInput(unavailableBlockInput) {
528
+ if (unavailableBlockInput.block !== null && unavailableBlockInput.type !== BlockInputType.dataPromise) {
529
+ return { blockInput: unavailableBlockInput, peerIdStr: "" };
530
+ }
531
+ let blockRootHex;
532
+ let blobKzgCommitmentsLen;
533
+ let blockRoot;
534
+ const dataMeta = {};
535
+ let sampledColumns = [];
536
+ if (unavailableBlockInput.block === null) {
537
+ blockRootHex = unavailableBlockInput.blockRootHex;
538
+ blockRoot = fromHex(blockRootHex);
539
+ }
540
+ else {
541
+ const { cachedData, block: unavailableBlock } = unavailableBlockInput;
542
+ blockRoot = this.config
543
+ .getForkTypes(unavailableBlock.message.slot)
544
+ .BeaconBlock.hashTreeRoot(unavailableBlock.message);
545
+ blockRootHex = toRootHex(blockRoot);
546
+ blobKzgCommitmentsLen = unavailableBlock.message.body.blobKzgCommitments.length;
547
+ if (cachedData.fork === ForkName.deneb || cachedData.fork === ForkName.electra) {
548
+ const pendingBlobs = blobKzgCommitmentsLen - cachedData.blobsCache.size;
549
+ Object.assign(dataMeta, { pendingBlobs });
550
+ }
551
+ else if (cachedData.fork === ForkName.fulu) {
552
+ sampledColumns = this.network.custodyConfig.sampledColumns;
553
+ const pendingColumns = sampledColumns.length - cachedData.dataColumnsCache.size;
554
+ Object.assign(dataMeta, { pendingColumns });
555
+ }
556
+ }
557
+ let lastError = null;
558
+ let i = 0;
559
+ const excludedPeers = new Set();
560
+ while (i++ < this.getMaxDownloadAttempts()) {
561
+ const bestPeer = this.peerBalancer.bestPeerForBlockInput(unavailableBlockInput, excludedPeers);
562
+ if (bestPeer === null) {
563
+ // no more peer to try, throw error
564
+ throw Error(`Error fetching UnavailableBlockInput after ${i}: cannot find peer with needed columns ${sampledColumns.join(", ")}`);
565
+ }
566
+ const { peerId, client: peerClient } = bestPeer;
567
+ excludedPeers.add(peerId);
568
+ try {
569
+ const blockInput = await unavailableBeaconBlobsByRoot(this.config, this.network, peerId, peerClient, unavailableBlockInput, {
570
+ metrics: this.metrics,
571
+ logger: this.logger,
572
+ executionEngine: this.chain.executionEngine,
573
+ emitter: this.chain.emitter,
574
+ blockInputsRetryTrackerCache: this.blockInputsRetryTrackerCache,
575
+ engineGetBlobsCache: this.engineGetBlobsCache,
576
+ });
577
+ if (unavailableBlockInput.block !== null && blockInput.type === BlockInputType.dataPromise) {
578
+ // all datacolumns were not downloaded we can continue with other peers
579
+ // as unavailableBlockInput.block's dataColumnsCache would be updated
580
+ continue;
506
581
  }
507
- else if (e instanceof RequestError) {
508
- // should look into req_resp metrics in this case
509
- downloadByRootMetrics?.error.inc({ code: "req_resp", client: peerClient });
510
- switch (e.type.code) {
511
- case RequestErrorCode.REQUEST_RATE_LIMITED:
512
- case RequestErrorCode.REQUEST_TIMEOUT:
513
- // do not exclude peer for these errors
514
- break;
515
- default:
516
- excludedPeers.add(peerId);
517
- break;
518
- }
582
+ // data is available, verify block root is correct
583
+ const block = blockInput.block.message;
584
+ const receivedBlockRoot = this.config.getForkTypes(block.slot).BeaconBlock.hashTreeRoot(block);
585
+ if (!byteArrayEquals(receivedBlockRoot, blockRoot)) {
586
+ throw Error(`Wrong block received by peer, got ${toRootHex(receivedBlockRoot)} expected ${blockRootHex}`);
587
+ }
588
+ if (unavailableBlockInput.block === null) {
589
+ this.logger.debug("Fetched NullBlockInput", { attempts: i, blockRootHex });
519
590
  }
520
591
  else {
521
- // investigate if this happens
522
- downloadByRootMetrics?.error.inc({ code: "unknown", client: peerClient });
523
- excludedPeers.add(peerId);
592
+ this.logger.debug("Fetched UnavailableBlockInput", { attempts: i, ...dataMeta, blobKzgCommitmentsLen });
524
593
  }
594
+ return { blockInput, peerIdStr: peerId };
595
+ }
596
+ catch (e) {
597
+ this.logger.debug("Error fetching UnavailableBlockInput", { attempt: i, blockRootHex, peer: peerId }, e);
598
+ lastError = e;
525
599
  }
526
600
  finally {
527
601
  this.peerBalancer.onRequestCompleted(peerId);
528
602
  }
529
- this.pendingBlocks.set(getBlockInputSyncCacheItemRootHex(cacheItem), cacheItem);
530
- if (cacheItem.status === PendingBlockInputStatus.downloaded) {
531
- // download was successful, no need to go with another peer, return
532
- const result = this.chain.forkChoice.hasBlockHex(cacheItem.blockInput.blockRootHex)
533
- ? FetchResult.SuccessLate
534
- : this.chain.forkChoice.hasBlockHex(cacheItem.blockInput.parentRootHex)
535
- ? FetchResult.SuccessResolved
536
- : FetchResult.SuccessMissingParent;
537
- this.metrics?.blockInputSync.fetchTimeSec.observe({ result }, Date.now() / 1000 - fetchStartSec);
538
- this.metrics?.blockInputSync.fetchPeers.set({ result }, i);
539
- return cacheItem;
540
- }
541
- } // end while loop over peers
542
- const message = `Error fetching BlockInput with slot=${slot} blockRoot=${prettyBytes(rootHex)} after ${i - 1} attempts.`;
543
- if (!isPendingBlockInput(cacheItem)) {
544
- throw Error(`${message} No block and no data was found.`);
545
- }
546
- if (!cacheItem.blockInput.hasBlock()) {
547
- throw new Error(`${message} Block was not found.`);
548
- }
549
- if (isBlockInputBlobs(cacheItem.blockInput)) {
550
- const missing = cacheItem.blockInput.getMissingBlobMeta().map((b) => b.index);
551
- if (missing.length) {
552
- throw new Error(`${message} Missing blob indices=${prettyPrintIndices(missing)}.`);
553
- }
554
- }
555
- if (isBlockInputColumns(cacheItem.blockInput)) {
556
- const missing = cacheItem.blockInput.getMissingSampledColumnMeta().missing;
557
- if (missing.length) {
558
- throw new Error(`${message} Missing column indices=${prettyPrintIndices(missing)}.`);
559
- }
560
- }
561
- this.metrics?.blockInputSync.fetchTimeSec.observe({ result: FetchResult.FailureMaxAttempts }, Date.now() / 1000 - fetchStartSec);
562
- this.metrics?.blockInputSync.fetchPeers.set({ result: FetchResult.FailureMaxAttempts }, i - 1);
563
- throw Error(message);
603
+ }
604
+ if (lastError) {
605
+ lastError.message = `Error fetching UnavailableBlockInput after ${i} attempts: ${lastError.message}`;
606
+ throw lastError;
607
+ }
608
+ throw Error(`Error fetching UnavailableBlockInput after ${i}: unknown error`);
564
609
  }
565
610
  /**
566
611
  * Gets all descendant blocks of `block` recursively from `pendingBlocks`.
@@ -568,44 +613,33 @@ export class BlockInputSync {
568
613
  * Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
569
614
  * referenced more than one bad block.
570
615
  */
571
- removeAndDownScoreAllDescendants(block) {
616
+ removeAndDownscoreAllDescendants(block) {
572
617
  // Get all blocks that are a descendant of this one
573
618
  const badPendingBlocks = this.removeAllDescendants(block);
574
619
  // just console log and do not penalize on pending/bad blocks for debugging
575
620
  // console.log("removeAndDownscoreAllDescendants", {block});
576
621
  for (const block of badPendingBlocks) {
577
- //
578
- // TODO(fulu): why is this commented out here?
579
- //
580
622
  // this.knownBadBlocks.add(block.blockRootHex);
581
623
  // for (const peerIdStr of block.peerIdStrs) {
582
624
  // // TODO: Refactor peerRpcScores to work with peerIdStr only
583
625
  // this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadBlockByRoot");
584
626
  // }
585
627
  this.logger.debug("ignored Banning unknown block", {
586
- slot: getBlockInputSyncCacheItemSlot(block),
587
- root: getBlockInputSyncCacheItemRootHex(block),
588
- peerIdStrings: Array.from(block.peerIdStrings)
589
- .map((id) => prettyPrintPeerIdStr(id))
590
- .join(","),
628
+ root: block.blockRootHex,
629
+ peerIdStrs: Array.from(block.peerIdStrs).join(","),
591
630
  });
592
631
  }
593
632
  // Prune knownBadBlocks
594
633
  pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
595
634
  }
596
635
  removeAllDescendants(block) {
597
- const rootHex = getBlockInputSyncCacheItemRootHex(block);
598
- const slot = getBlockInputSyncCacheItemSlot(block);
599
636
  // Get all blocks that are a descendant of this one
600
- const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
601
- this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
637
+ const badPendingBlocks = [block, ...getAllDescendantBlocks(block.blockRootHex, this.pendingBlocks)];
638
+ this.metrics?.syncUnknownBlock.removedBlocks.inc(badPendingBlocks.length);
602
639
  for (const block of badPendingBlocks) {
603
- const rootHex = getBlockInputSyncCacheItemRootHex(block);
604
- this.pendingBlocks.delete(rootHex);
605
- this.chain.seenBlockInputCache.prune(rootHex);
606
- this.logger.debug("Removing bad/unknown/incomplete BlockInputSyncCacheItem", {
607
- slot,
608
- blockRoot: rootHex,
640
+ this.pendingBlocks.delete(block.blockRootHex);
641
+ this.logger.debug("Removing unknown parent block", {
642
+ root: block.blockRootHex,
609
643
  });
610
644
  }
611
645
  return badPendingBlocks;
@@ -623,11 +657,10 @@ export class BlockInputSync {
623
657
  * Class to track active byRoots requests and balance them across eligible peers.
624
658
  */
625
659
  export class UnknownBlockPeerBalancer {
626
- peersMeta;
627
- activeRequests;
628
- constructor() {
660
+ constructor(custodyConfig) {
629
661
  this.peersMeta = new Map();
630
662
  this.activeRequests = new Map();
663
+ this.custodyConfig = custodyConfig;
631
664
  }
632
665
  /** Trigger on each peer re-status */
633
666
  onPeerConnected(peerId, syncMeta) {
@@ -661,16 +694,36 @@ export class UnknownBlockPeerBalancer {
661
694
  * called from fetchUnavailableBlockInput() where we have either BlockInput or NullBlockInput
662
695
  * excludedPeers are the peers that we requested already so we don't want to try again
663
696
  */
664
- bestPeerForBlockInput(blockInput, excludedPeers) {
697
+ bestPeerForBlockInput(unavailableBlockInput, excludedPeers) {
698
+ let cachedData = undefined;
699
+ if (unavailableBlockInput.block === null) {
700
+ // NullBlockInput
701
+ cachedData = unavailableBlockInput.cachedData;
702
+ }
703
+ else {
704
+ // BlockInput
705
+ if (unavailableBlockInput.type !== BlockInputType.dataPromise) {
706
+ throw Error(`bestPeerForBlockInput called with BlockInput type ${unavailableBlockInput.type}, expected dataPromise`);
707
+ }
708
+ cachedData = unavailableBlockInput.cachedData;
709
+ }
665
710
  const eligiblePeers = [];
666
- if (isBlockInputColumns(blockInput)) {
667
- const pendingDataColumns = new Set(blockInput.getMissingSampledColumnMeta().missing);
668
- // there could be no pending column in case when block is still missing
711
+ if (cachedData.fork === ForkName.fulu) {
712
+ // cached data is CachedDataColumns
713
+ const { dataColumnsCache } = cachedData;
714
+ const pendingDataColumns = new Set();
715
+ for (const column of this.custodyConfig.sampledColumns) {
716
+ if (!dataColumnsCache.has(column)) {
717
+ pendingDataColumns.add(column);
718
+ }
719
+ }
720
+ // there could be no pending column in case of NullBlockInput
669
721
  eligiblePeers.push(...this.filterPeers(pendingDataColumns, excludedPeers));
670
722
  }
671
723
  else {
672
724
  // prefulu
673
- eligiblePeers.push(...this.filterPeers(null, excludedPeers));
725
+ const pendingDataColumns = null;
726
+ eligiblePeers.push(...this.filterPeers(pendingDataColumns, excludedPeers));
674
727
  }
675
728
  if (eligiblePeers.length === 0) {
676
729
  return null;
@@ -722,7 +775,7 @@ export class UnknownBlockPeerBalancer {
722
775
  continue;
723
776
  }
724
777
  // postfulu, find peers that have custody columns that we need
725
- const { custodyColumns: peerColumns } = syncMeta;
778
+ const { custodyGroups: peerColumns } = syncMeta;
726
779
  // check if the peer has all needed columns
727
780
  // get match
728
781
  const columns = peerColumns.reduce((acc, elem) => {