@lodestar/beacon-node 1.50.0-dev.fef46df5ba → 1.50.0

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 (349) 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 -1
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/index.js +13 -0
  6. package/lib/api/impl/beacon/index.js.map +1 -1
  7. package/lib/api/impl/beacon/pool/index.js +4 -4
  8. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  9. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  10. package/lib/api/impl/beacon/state/index.js +28 -6
  11. package/lib/api/impl/beacon/state/index.js.map +1 -1
  12. package/lib/api/impl/debug/index.d.ts.map +1 -1
  13. package/lib/api/impl/debug/index.js +26 -18
  14. package/lib/api/impl/debug/index.js.map +1 -1
  15. package/lib/api/impl/validator/index.d.ts +1 -0
  16. package/lib/api/impl/validator/index.d.ts.map +1 -1
  17. package/lib/api/impl/validator/index.js +49 -1
  18. package/lib/api/impl/validator/index.js.map +1 -1
  19. package/lib/chain/archiveStore/archiveStore.d.ts +5 -0
  20. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  21. package/lib/chain/archiveStore/archiveStore.js +14 -24
  22. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  23. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +2 -2
  24. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
  25. package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -7
  26. package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
  27. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -1
  28. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  29. package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
  30. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  31. package/lib/chain/archiveStore/historicalState/worker.js +1 -1
  32. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  33. package/lib/chain/archiveStore/interface.d.ts +1 -1
  34. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  35. package/lib/chain/archiveStore/utils/pruneHistory.d.ts +1 -1
  36. package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
  37. package/lib/chain/archiveStore/utils/pruneHistory.js +2 -1
  38. package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
  39. package/lib/chain/beaconProposerCache.d.ts +6 -0
  40. package/lib/chain/beaconProposerCache.d.ts.map +1 -1
  41. package/lib/chain/beaconProposerCache.js +16 -0
  42. package/lib/chain/beaconProposerCache.js.map +1 -1
  43. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  44. package/lib/chain/blocks/importBlock.js +18 -8
  45. package/lib/chain/blocks/importBlock.js.map +1 -1
  46. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
  47. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  48. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  49. package/lib/chain/blocks/verifyBlock.js +7 -7
  50. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  51. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -1
  52. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  53. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +5 -4
  54. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  55. package/lib/chain/chain.d.ts +10 -5
  56. package/lib/chain/chain.d.ts.map +1 -1
  57. package/lib/chain/chain.js +105 -36
  58. package/lib/chain/chain.js.map +1 -1
  59. package/lib/chain/emitter.d.ts +7 -1
  60. package/lib/chain/emitter.d.ts.map +1 -1
  61. package/lib/chain/emitter.js +5 -0
  62. package/lib/chain/emitter.js.map +1 -1
  63. package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
  64. package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
  65. package/lib/chain/errors/envelopeReconstructionError.js +2 -0
  66. package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
  67. package/lib/chain/errors/gossipValidation.d.ts +3 -1
  68. package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
  69. package/lib/chain/errors/gossipValidation.js +2 -0
  70. package/lib/chain/errors/gossipValidation.js.map +1 -1
  71. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  72. package/lib/chain/forkChoice/index.js +2 -0
  73. package/lib/chain/forkChoice/index.js.map +1 -1
  74. package/lib/chain/interface.d.ts +5 -2
  75. package/lib/chain/interface.d.ts.map +1 -1
  76. package/lib/chain/opPools/aggregatedAttestationPool.d.ts +4 -2
  77. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  78. package/lib/chain/opPools/aggregatedAttestationPool.js +30 -8
  79. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  80. package/lib/chain/opPools/opPool.d.ts +5 -4
  81. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  82. package/lib/chain/opPools/opPool.js +60 -38
  83. package/lib/chain/opPools/opPool.js.map +1 -1
  84. package/lib/chain/opPools/proposerPreferencesPool.d.ts +8 -4
  85. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  86. package/lib/chain/opPools/proposerPreferencesPool.js +27 -12
  87. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  88. package/lib/chain/opPools/utils.d.ts +15 -1
  89. package/lib/chain/opPools/utils.d.ts.map +1 -1
  90. package/lib/chain/opPools/utils.js +33 -1
  91. package/lib/chain/opPools/utils.js.map +1 -1
  92. package/lib/chain/options.d.ts +2 -1
  93. package/lib/chain/options.d.ts.map +1 -1
  94. package/lib/chain/options.js +1 -1
  95. package/lib/chain/options.js.map +1 -1
  96. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  97. package/lib/chain/prepareNextSlot.js +16 -5
  98. package/lib/chain/prepareNextSlot.js.map +1 -1
  99. package/lib/chain/produceBlock/computeNewStateRoot.js +1 -1
  100. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  101. package/lib/chain/produceBlock/produceBlockBody.d.ts +11 -1
  102. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  103. package/lib/chain/produceBlock/produceBlockBody.js +46 -30
  104. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  105. package/lib/chain/regen/regen.d.ts +2 -0
  106. package/lib/chain/regen/regen.d.ts.map +1 -1
  107. package/lib/chain/regen/regen.js +10 -4
  108. package/lib/chain/regen/regen.js.map +1 -1
  109. package/lib/chain/seenCache/seenGossipBlockInput.d.ts +7 -0
  110. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  111. package/lib/chain/seenCache/seenGossipBlockInput.js +21 -0
  112. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  113. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
  114. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  115. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
  116. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  117. package/lib/chain/shufflingCache.d.ts +18 -8
  118. package/lib/chain/shufflingCache.d.ts.map +1 -1
  119. package/lib/chain/shufflingCache.js +72 -18
  120. package/lib/chain/shufflingCache.js.map +1 -1
  121. package/lib/chain/validation/attesterSlashing.d.ts +4 -4
  122. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  123. package/lib/chain/validation/attesterSlashing.js +3 -0
  124. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  125. package/lib/chain/validation/proposerSlashing.d.ts +3 -3
  126. package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
  127. package/lib/chain/validation/proposerSlashing.js +3 -0
  128. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  129. package/lib/chain/validatorMonitor.d.ts +5 -1
  130. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  131. package/lib/chain/validatorMonitor.js +64 -54
  132. package/lib/chain/validatorMonitor.js.map +1 -1
  133. package/lib/db/beacon.d.ts +4 -1
  134. package/lib/db/beacon.d.ts.map +1 -1
  135. package/lib/db/beacon.js +6 -1
  136. package/lib/db/beacon.js.map +1 -1
  137. package/lib/db/buckets.d.ts +3 -1
  138. package/lib/db/buckets.d.ts.map +1 -1
  139. package/lib/db/buckets.js +2 -0
  140. package/lib/db/buckets.js.map +1 -1
  141. package/lib/db/interface.d.ts +4 -1
  142. package/lib/db/interface.d.ts.map +1 -1
  143. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  144. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  145. package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
  146. package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
  147. package/lib/db/repositories/earliestAvailableSlot.js +19 -0
  148. package/lib/db/repositories/earliestAvailableSlot.js.map +1 -0
  149. package/lib/db/repositories/index.d.ts +1 -0
  150. package/lib/db/repositories/index.d.ts.map +1 -1
  151. package/lib/db/repositories/index.js +1 -0
  152. package/lib/db/repositories/index.js.map +1 -1
  153. package/lib/db/repositories/proposerPreferences.d.ts +14 -0
  154. package/lib/db/repositories/proposerPreferences.d.ts.map +1 -0
  155. package/lib/db/repositories/proposerPreferences.js +21 -0
  156. package/lib/db/repositories/proposerPreferences.js.map +1 -0
  157. package/lib/db/repositories/proposerSlashing.d.ts +1 -0
  158. package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
  159. package/lib/db/repositories/proposerSlashing.js +5 -1
  160. package/lib/db/repositories/proposerSlashing.js.map +1 -1
  161. package/lib/execution/engine/blobVersionedHashes.d.ts +9 -0
  162. package/lib/execution/engine/blobVersionedHashes.d.ts.map +1 -0
  163. package/lib/execution/engine/blobVersionedHashes.js +74 -0
  164. package/lib/execution/engine/blobVersionedHashes.js.map +1 -0
  165. package/lib/execution/engine/disabled.d.ts +0 -2
  166. package/lib/execution/engine/disabled.d.ts.map +1 -1
  167. package/lib/execution/engine/disabled.js +0 -6
  168. package/lib/execution/engine/disabled.js.map +1 -1
  169. package/lib/execution/engine/http.d.ts +46 -16
  170. package/lib/execution/engine/http.d.ts.map +1 -1
  171. package/lib/execution/engine/http.js +236 -213
  172. package/lib/execution/engine/http.js.map +1 -1
  173. package/lib/execution/engine/index.d.ts +3 -2
  174. package/lib/execution/engine/index.d.ts.map +1 -1
  175. package/lib/execution/engine/index.js +33 -8
  176. package/lib/execution/engine/index.js.map +1 -1
  177. package/lib/execution/engine/interface.d.ts +2 -4
  178. package/lib/execution/engine/interface.d.ts.map +1 -1
  179. package/lib/execution/engine/jsonRpcTransport.d.ts +25 -0
  180. package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -0
  181. package/lib/execution/engine/jsonRpcTransport.js +158 -0
  182. package/lib/execution/engine/jsonRpcTransport.js.map +1 -0
  183. package/lib/execution/engine/mock.d.ts +0 -2
  184. package/lib/execution/engine/mock.d.ts.map +1 -1
  185. package/lib/execution/engine/mock.js +0 -8
  186. package/lib/execution/engine/mock.js.map +1 -1
  187. package/lib/execution/engine/restHttpClient.d.ts +71 -0
  188. package/lib/execution/engine/restHttpClient.d.ts.map +1 -0
  189. package/lib/execution/engine/restHttpClient.js +201 -0
  190. package/lib/execution/engine/restHttpClient.js.map +1 -0
  191. package/lib/execution/engine/restTransport.d.ts +46 -0
  192. package/lib/execution/engine/restTransport.d.ts.map +1 -0
  193. package/lib/execution/engine/restTransport.js +399 -0
  194. package/lib/execution/engine/restTransport.js.map +1 -0
  195. package/lib/execution/engine/sszTypes.d.ts +1261 -0
  196. package/lib/execution/engine/sszTypes.d.ts.map +1 -0
  197. package/lib/execution/engine/sszTypes.js +216 -0
  198. package/lib/execution/engine/sszTypes.js.map +1 -0
  199. package/lib/execution/engine/transport.d.ts +39 -0
  200. package/lib/execution/engine/transport.d.ts.map +1 -0
  201. package/lib/execution/engine/transport.js +2 -0
  202. package/lib/execution/engine/transport.js.map +1 -0
  203. package/lib/execution/engine/types.d.ts +2 -8
  204. package/lib/execution/engine/types.d.ts.map +1 -1
  205. package/lib/execution/engine/types.js +24 -57
  206. package/lib/execution/engine/types.js.map +1 -1
  207. package/lib/execution/engine/utils.d.ts.map +1 -1
  208. package/lib/execution/engine/utils.js +9 -2
  209. package/lib/execution/engine/utils.js.map +1 -1
  210. package/lib/metrics/metrics/lodestar.d.ts +10 -0
  211. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  212. package/lib/metrics/metrics/lodestar.js +22 -0
  213. package/lib/metrics/metrics/lodestar.js.map +1 -1
  214. package/lib/metrics/metrics.d.ts +7 -3
  215. package/lib/metrics/metrics.d.ts.map +1 -1
  216. package/lib/metrics/metrics.js +4 -4
  217. package/lib/metrics/metrics.js.map +1 -1
  218. package/lib/network/core/events.d.ts +5 -1
  219. package/lib/network/core/events.d.ts.map +1 -1
  220. package/lib/network/core/events.js +8 -0
  221. package/lib/network/core/events.js.map +1 -1
  222. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  223. package/lib/network/processor/gossipHandlers.js +67 -68
  224. package/lib/network/processor/gossipHandlers.js.map +1 -1
  225. package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
  226. package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
  227. package/lib/network/processor/gossipValidatorFn.js +7 -3
  228. package/lib/network/processor/gossipValidatorFn.js.map +1 -1
  229. package/lib/network/processor/index.d.ts +9 -0
  230. package/lib/network/processor/index.d.ts.map +1 -1
  231. package/lib/network/processor/index.js +77 -10
  232. package/lib/network/processor/index.js.map +1 -1
  233. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +1 -1
  234. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  235. package/lib/node/nodejs.d.ts +3 -1
  236. package/lib/node/nodejs.d.ts.map +1 -1
  237. package/lib/node/nodejs.js +27 -4
  238. package/lib/node/nodejs.js.map +1 -1
  239. package/lib/node/options.d.ts +3 -2
  240. package/lib/node/options.d.ts.map +1 -1
  241. package/lib/node/options.js +2 -2
  242. package/lib/node/options.js.map +1 -1
  243. package/lib/sync/range/batch.d.ts +11 -5
  244. package/lib/sync/range/batch.d.ts.map +1 -1
  245. package/lib/sync/range/batch.js +21 -17
  246. package/lib/sync/range/batch.js.map +1 -1
  247. package/lib/sync/range/chain.d.ts +4 -4
  248. package/lib/sync/range/chain.d.ts.map +1 -1
  249. package/lib/sync/range/chain.js +12 -6
  250. package/lib/sync/range/chain.js.map +1 -1
  251. package/lib/sync/range/range.d.ts.map +1 -1
  252. package/lib/sync/range/range.js +75 -29
  253. package/lib/sync/range/range.js.map +1 -1
  254. package/lib/sync/unknownBlock.d.ts +5 -2
  255. package/lib/sync/unknownBlock.d.ts.map +1 -1
  256. package/lib/sync/unknownBlock.js +49 -24
  257. package/lib/sync/unknownBlock.js.map +1 -1
  258. package/lib/sync/utils/downloadByRange.d.ts +38 -14
  259. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  260. package/lib/sync/utils/downloadByRange.js +84 -39
  261. package/lib/sync/utils/downloadByRange.js.map +1 -1
  262. package/lib/util/asyncIterableToEvents.d.ts +10 -2
  263. package/lib/util/asyncIterableToEvents.d.ts.map +1 -1
  264. package/lib/util/asyncIterableToEvents.js +29 -3
  265. package/lib/util/asyncIterableToEvents.js.map +1 -1
  266. package/lib/util/execution.d.ts +9 -11
  267. package/lib/util/execution.d.ts.map +1 -1
  268. package/lib/util/execution.js +5 -1
  269. package/lib/util/execution.js.map +1 -1
  270. package/lib/util/metadata.d.ts.map +1 -1
  271. package/lib/util/metadata.js +2 -1
  272. package/lib/util/metadata.js.map +1 -1
  273. package/package.json +18 -17
  274. package/src/api/impl/beacon/blocks/index.ts +7 -4
  275. package/src/api/impl/beacon/index.ts +15 -0
  276. package/src/api/impl/beacon/pool/index.ts +4 -4
  277. package/src/api/impl/beacon/state/index.ts +43 -6
  278. package/src/api/impl/debug/index.ts +28 -19
  279. package/src/api/impl/validator/index.ts +61 -2
  280. package/src/chain/archiveStore/archiveStore.ts +19 -25
  281. package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -7
  282. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -1
  283. package/src/chain/archiveStore/historicalState/types.ts +2 -2
  284. package/src/chain/archiveStore/historicalState/worker.ts +1 -2
  285. package/src/chain/archiveStore/interface.ts +1 -1
  286. package/src/chain/archiveStore/utils/pruneHistory.ts +4 -1
  287. package/src/chain/beaconProposerCache.ts +19 -1
  288. package/src/chain/blocks/importBlock.ts +21 -7
  289. package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
  290. package/src/chain/blocks/verifyBlock.ts +8 -6
  291. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +6 -3
  292. package/src/chain/chain.ts +135 -38
  293. package/src/chain/emitter.ts +7 -1
  294. package/src/chain/errors/envelopeReconstructionError.ts +3 -0
  295. package/src/chain/errors/gossipValidation.ts +2 -0
  296. package/src/chain/forkChoice/index.ts +2 -0
  297. package/src/chain/interface.ts +5 -2
  298. package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
  299. package/src/chain/opPools/opPool.ts +89 -46
  300. package/src/chain/opPools/proposerPreferencesPool.ts +34 -11
  301. package/src/chain/opPools/utils.ts +62 -3
  302. package/src/chain/options.ts +3 -3
  303. package/src/chain/prepareNextSlot.ts +21 -6
  304. package/src/chain/produceBlock/computeNewStateRoot.ts +1 -1
  305. package/src/chain/produceBlock/produceBlockBody.ts +76 -35
  306. package/src/chain/regen/regen.ts +12 -4
  307. package/src/chain/seenCache/seenGossipBlockInput.ts +23 -0
  308. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
  309. package/src/chain/shufflingCache.ts +84 -22
  310. package/src/chain/validation/attesterSlashing.ts +7 -4
  311. package/src/chain/validation/proposerSlashing.ts +7 -4
  312. package/src/chain/validatorMonitor.ts +75 -57
  313. package/src/db/beacon.ts +6 -0
  314. package/src/db/buckets.ts +2 -0
  315. package/src/db/interface.ts +4 -0
  316. package/src/db/repositories/earliestAvailableSlot.ts +19 -0
  317. package/src/db/repositories/index.ts +1 -0
  318. package/src/db/repositories/proposerPreferences.ts +23 -0
  319. package/src/db/repositories/proposerSlashing.ts +6 -1
  320. package/src/execution/engine/blobVersionedHashes.ts +72 -0
  321. package/src/execution/engine/disabled.ts +0 -8
  322. package/src/execution/engine/http.ts +304 -300
  323. package/src/execution/engine/index.ts +41 -7
  324. package/src/execution/engine/interface.ts +2 -6
  325. package/src/execution/engine/jsonRpcTransport.ts +247 -0
  326. package/src/execution/engine/mock.ts +1 -19
  327. package/src/execution/engine/restHttpClient.ts +269 -0
  328. package/src/execution/engine/restTransport.ts +544 -0
  329. package/src/execution/engine/sszTypes.ts +354 -0
  330. package/src/execution/engine/transport.ts +61 -0
  331. package/src/execution/engine/types.ts +34 -84
  332. package/src/execution/engine/utils.ts +12 -2
  333. package/src/metrics/metrics/lodestar.ts +23 -0
  334. package/src/metrics/metrics.ts +21 -6
  335. package/src/network/core/events.ts +16 -1
  336. package/src/network/processor/gossipHandlers.ts +98 -81
  337. package/src/network/processor/gossipValidatorFn.ts +13 -3
  338. package/src/network/processor/index.ts +89 -10
  339. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +1 -1
  340. package/src/node/nodejs.ts +35 -3
  341. package/src/node/options.ts +10 -1
  342. package/src/sync/range/batch.ts +25 -19
  343. package/src/sync/range/chain.ts +15 -8
  344. package/src/sync/range/range.ts +76 -30
  345. package/src/sync/unknownBlock.ts +62 -25
  346. package/src/sync/utils/downloadByRange.ts +123 -58
  347. package/src/util/asyncIterableToEvents.ts +44 -5
  348. package/src/util/execution.ts +16 -8
  349. package/src/util/metadata.ts +2 -1
@@ -22,6 +22,7 @@ import {
22
22
  getParentRootFromSignedBeaconBlockSerialized,
23
23
  getPayloadPresentFromPayloadAttestationMessageSerialized,
24
24
  } from "../../util/sszBytes.js";
25
+ import {INetworkCore} from "../core/index.js";
25
26
  import {NetworkEvent, NetworkEventBus} from "../events.js";
26
27
  import {
27
28
  GossipHandlers,
@@ -34,7 +35,12 @@ import {MAX_PEERS_PER_ROOT} from "./constants.js";
34
35
  import {createExtractBlockSlotRootFns} from "./extractSlotRootFns.js";
35
36
  import {GossipHandlerOpts, ValidatorFnsModules, getGossipHandlers} from "./gossipHandlers.js";
36
37
  import {createGossipQueues} from "./gossipQueues/index.js";
37
- import {ValidatorFnModules, getGossipValidatorBatchFn, getGossipValidatorFn} from "./gossipValidatorFn.js";
38
+ import {
39
+ ValidatorFnModules,
40
+ getGossipValidatorBatchFn,
41
+ getGossipValidatorFn,
42
+ rejectPeerAction,
43
+ } from "./gossipValidatorFn.js";
38
44
  import {PendingGossipsubMessage} from "./types.js";
39
45
 
40
46
  export * from "./types.js";
@@ -102,6 +108,12 @@ type SearchedRootEntry = {
102
108
  */
103
109
  const DEFAULT_EARLIEST_PERMISSIBLE_SLOT_DISTANCE = 32;
104
110
 
111
+ /**
112
+ * The network worker bans a Fatal peer only on its next PeerManager heartbeat (30s). Until then the main thread
113
+ * would keep validating that peer's messages, so drop them here. 3 slots (36s) outlasts one heartbeat.
114
+ */
115
+ export const FATAL_PEER_EXPIRY_SLOTS = 3;
116
+
105
117
  type WorkOpts = {
106
118
  bypassQueue?: boolean;
107
119
  };
@@ -232,6 +244,7 @@ type SearchUnknownRootTarget =
232
244
  */
233
245
  export class NetworkProcessor {
234
246
  private readonly chain: IBeaconChain;
247
+ private readonly core: INetworkCore;
235
248
  private readonly events: NetworkEventBus;
236
249
  private readonly logger: Logger;
237
250
  private readonly metrics: Metrics | null;
@@ -256,23 +269,31 @@ export class NetworkProcessor {
256
269
  () => new MapDef<RootHex, SearchedRootEntry>(() => ({}))
257
270
  );
258
271
  private bufferedRootsBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
272
+ /** peer -> clock slot when it was Fatal'ed */
273
+ private readonly fatalPeers = new Map<PeerIdStr, Slot>();
259
274
 
260
275
  constructor(
261
276
  modules: NetworkProcessorModules,
262
277
  private readonly opts: NetworkProcessorOpts
263
278
  ) {
264
- const {chain, events, logger, metrics} = modules;
279
+ const {chain, core, events, logger, metrics} = modules;
265
280
  this.chain = chain;
281
+ this.core = core;
266
282
  this.events = events;
267
283
  this.metrics = metrics;
268
284
  this.logger = logger;
269
285
  this.events = events;
270
286
  this.gossipQueues = createGossipQueues();
271
287
  this.gossipTopicConcurrency = mapValues(this.gossipQueues, () => 0);
272
- this.gossipValidatorFn = getGossipValidatorFn(modules.gossipHandlers ?? getGossipHandlers(modules, opts), modules);
288
+ this.gossipValidatorFn = getGossipValidatorFn(
289
+ modules.gossipHandlers ?? getGossipHandlers(modules, opts),
290
+ modules,
291
+ this.onFatalPeer
292
+ );
273
293
  this.gossipValidatorBatchFn = getGossipValidatorBatchFn(
274
294
  modules.gossipHandlers ?? getGossipHandlers(modules, opts),
275
- modules
295
+ modules,
296
+ this.onFatalPeer
276
297
  );
277
298
 
278
299
  events.on(NetworkEvent.pendingGossipsubMessage, this.onPendingGossipsubMessage);
@@ -406,19 +427,47 @@ export class NetworkProcessor {
406
427
  }
407
428
  }
408
429
 
430
+ private onFatalPeer = (peer: PeerIdStr): void => {
431
+ this.fatalPeers.set(peer, this.chain.clock.currentSlot);
432
+ };
433
+
409
434
  private onPendingGossipsubMessage = (message: PendingGossipsubMessage): void => {
410
435
  const topicType = message.topic.type;
436
+ if (this.fatalPeers.has(message.propagationSource)) {
437
+ this.metrics?.networkProcessor.gossipValidationError.inc({topic: topicType, error: GossipErrorCode.FATAL_PEER});
438
+ // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
439
+ return;
440
+ }
441
+
411
442
  const extractBlockSlotRootFn = this.extractBlockSlotRootFns[topicType];
443
+ if (extractBlockSlotRootFn === undefined) {
444
+ // some messages don't have slot and root
445
+ this.pushPendingGossipsubMessageToQueue(message);
446
+ return;
447
+ }
412
448
 
413
449
  // 1st extract round: make sure slot is in range and if block root is not available
414
450
  // proactively search for it + queue the message
415
- const slotRoot = extractBlockSlotRootFn
416
- ? extractBlockSlotRootFn(message.msg.data, message.topic.boundary.fork)
417
- : null;
451
+ const slotRoot = extractBlockSlotRootFn(message.msg.data, message.topic.boundary.fork);
418
452
  if (slotRoot === null) {
419
- // some messages don't have slot and root
420
- // if the msg.data is invalid, message will be rejected when deserializing data in later phase (gossipValidatorFn)
421
- this.pushPendingGossipsubMessageToQueue(message);
453
+ // DOS protection: null means malformed bytes or slot >= 2^32, neither is forwarded by honest peers.
454
+ const {propagationSource, clientAgent, clientVersion} = message;
455
+ const code = GossipErrorCode.INVALID_SLOT;
456
+ const peerAction = rejectPeerAction(topicType, code);
457
+ this.metrics?.networkProcessor.gossipValidationReject.inc({topic: topicType});
458
+ this.core.reportPeer(propagationSource, peerAction, code);
459
+ this.logger.debug(`Gossip validation ${topicType} rejected due to null slot`, {
460
+ peer: propagationSource,
461
+ clientAgent,
462
+ clientVersion,
463
+ peerAction,
464
+ code,
465
+ });
466
+ this.events.emit(NetworkEvent.gossipMessageValidationResult, {
467
+ msgId: message.msgId,
468
+ propagationSource,
469
+ acceptance: TopicValidatorResult.Reject,
470
+ });
422
471
  return;
423
472
  }
424
473
 
@@ -872,6 +921,10 @@ export class NetworkProcessor {
872
921
  for (const slot of this.bufferedRootsBySlot.keys()) {
873
922
  if (slot <= minSlot) this.bufferedRootsBySlot.delete(slot);
874
923
  }
924
+
925
+ for (const [peer, slot] of this.fatalPeers) {
926
+ if (clockSlot - slot > FATAL_PEER_EXPIRY_SLOTS) this.fatalPeers.delete(peer);
927
+ }
875
928
  };
876
929
 
877
930
  private executeWork(): void {
@@ -927,6 +980,32 @@ export class NetworkProcessor {
927
980
  private async processPendingGossipsubMessage(
928
981
  messageOrArray: PendingGossipsubMessage | PendingGossipsubMessage[]
929
982
  ): Promise<void> {
983
+ // drop messages from peers Fatal'ed after these were queued or started awaiting a block/envelope
984
+ // No need to report the dropped jobs to gossip. They will be eventually pruned from the mcache
985
+ if (this.fatalPeers.size > 0) {
986
+ if (Array.isArray(messageOrArray)) {
987
+ const allowed: PendingGossipsubMessage[] = [];
988
+ for (const msg of messageOrArray) {
989
+ if (this.fatalPeers.has(msg.propagationSource)) {
990
+ this.metrics?.networkProcessor.gossipValidationError.inc({
991
+ topic: msg.topic.type,
992
+ error: GossipErrorCode.FATAL_PEER,
993
+ });
994
+ } else {
995
+ allowed.push(msg);
996
+ }
997
+ }
998
+ if (allowed.length === 0) return;
999
+ messageOrArray = allowed;
1000
+ } else if (this.fatalPeers.has(messageOrArray.propagationSource)) {
1001
+ this.metrics?.networkProcessor.gossipValidationError.inc({
1002
+ topic: messageOrArray.topic.type,
1003
+ error: GossipErrorCode.FATAL_PEER,
1004
+ });
1005
+ return;
1006
+ }
1007
+ }
1008
+
930
1009
  const nowSec = Date.now() / 1000;
931
1010
  if (Array.isArray(messageOrArray)) {
932
1011
  for (const msg of messageOrArray) {
@@ -53,7 +53,7 @@ export async function* onExecutionPayloadEnvelopesByRoot(
53
53
  const batch = requests.slice(i, i + MAX_BODIES_PER_REQUEST);
54
54
  let envelopesBytes: (Uint8Array | null)[];
55
55
  try {
56
- // By-root allows omission, so a mismatched envelope is left out rather than failing the response.
56
+ // By-root allows omission, so an envelope that cannot be rebuilt is left out rather than failing the response.
57
57
  envelopesBytes = await chain.getSerializedExecutionPayloadEnvelopes(batch, "omit");
58
58
  } catch (e) {
59
59
  if (e instanceof EnvelopeReconstructionError) {
@@ -1,6 +1,12 @@
1
1
  import {setMaxListeners} from "node:events";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Registry} from "prom-client";
4
+ import {
5
+ init as initNativeMetrics,
6
+ registerLocalValidator as registerNativeLocalValidator,
7
+ scrapeMetrics as scrapeNativeMetrics,
8
+ unregisterLocalValidator as unregisterNativeLocalValidator,
9
+ } from "@chainsafe/lodestar-z/metrics";
4
10
  import {type PubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
5
11
  import {hasher} from "@chainsafe/persistent-merkle-tree";
6
12
  import {BeaconApiMethods} from "@lodestar/api/beacon/server";
@@ -8,6 +14,7 @@ import {BeaconConfig} from "@lodestar/config";
8
14
  import type {LoggerNode} from "@lodestar/logger/node";
9
15
  import {ZERO_HASH_HEX} from "@lodestar/params";
10
16
  import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
17
+ import {Slot} from "@lodestar/types";
11
18
  import {sleep, toRootHex} from "@lodestar/utils";
12
19
  import {ProcessShutdownCallback} from "@lodestar/validator";
13
20
  import {BeaconRestApiServer, getApi} from "../api/index.js";
@@ -55,6 +62,7 @@ export type BeaconNodeInitModules = {
55
62
  peerStoreDir?: string;
56
63
  anchorState: IBeaconStateView;
57
64
  isAnchorStateFinalized: boolean;
65
+ earliestAvailableSlot: Slot;
58
66
  metricsRegistries?: Registry[];
59
67
  };
60
68
 
@@ -153,6 +161,7 @@ export class BeaconNode {
153
161
  peerStoreDir,
154
162
  anchorState,
155
163
  isAnchorStateFinalized,
164
+ earliestAvailableSlot,
156
165
  metricsRegistries = [],
157
166
  }: BeaconNodeInitModules): Promise<T> {
158
167
  if (hasher.name !== "hashtree") {
@@ -171,7 +180,13 @@ export class BeaconNode {
171
180
  // monitoring relies on metrics data
172
181
  opts.monitoring.endpoint
173
182
  ) {
174
- metrics = createMetrics(opts.metrics, anchorState.genesisTime, metricsRegistries);
183
+ if (opts.chain.nativeStateTransition) {
184
+ initNativeMetrics();
185
+ }
186
+
187
+ metrics = createMetrics(opts.metrics, anchorState.genesisTime, metricsRegistries, {
188
+ includeStateTransitionMetrics: !opts.chain.nativeStateTransition,
189
+ });
175
190
  initBeaconMetrics(metrics, anchorState);
176
191
  // Since the db is instantiated before this, metrics must be injected manually afterwards
177
192
  db.setMetrics(metrics.db, metrics.flatFileStore);
@@ -185,7 +200,13 @@ export class BeaconNode {
185
200
  config,
186
201
  anchorState.genesisTime,
187
202
  logger.child({module: LoggerModule.vmon}),
188
- opts.validatorMonitor
203
+ opts.validatorMonitor,
204
+ opts.chain.nativeStateTransition
205
+ ? {
206
+ registerLocalValidator: registerNativeLocalValidator,
207
+ unregisterLocalValidator: unregisterNativeLocalValidator,
208
+ }
209
+ : null
189
210
  )
190
211
  : null;
191
212
 
@@ -248,6 +269,7 @@ export class BeaconNode {
248
269
  validatorMonitor,
249
270
  anchorState,
250
271
  isAnchorStateFinalized,
272
+ earliestAvailableSlot,
251
273
  executionEngine: initializeExecutionEngine(executionEngineOpts, {
252
274
  metrics,
253
275
  signal,
@@ -303,7 +325,17 @@ export class BeaconNode {
303
325
  const metricsServer = opts.metrics.enabled
304
326
  ? await getHttpMetricsServer(opts.metrics, {
305
327
  register: (metrics as Metrics).register,
306
- getOtherMetrics: async () => Promise.all([network.scrapeMetrics(), chain.archiveStore.scrapeMetrics()]),
328
+ getOtherMetrics: async () => {
329
+ const otherMetrics = await Promise.all([network.scrapeMetrics(), chain.archiveStore.scrapeMetrics()]);
330
+ if (opts.chain.nativeStateTransition) {
331
+ try {
332
+ otherMetrics.push(scrapeNativeMetrics());
333
+ } catch (e) {
334
+ logger.warn("Failed to scrape native state-transition metrics", {}, e as Error);
335
+ }
336
+ }
337
+ return otherMetrics;
338
+ },
307
339
  logger: logger.child({module: LoggerModule.metrics}),
308
340
  })
309
341
  : null;
@@ -3,12 +3,14 @@ import {ArchiveMode, DEFAULT_ARCHIVE_MODE, IChainOptions, defaultChainOptions} f
3
3
  import {ValidatorMonitorOpts, defaultValidatorMonitorOpts} from "../chain/validatorMonitor.js";
4
4
  import {DatabaseOptions, defaultDbOptions} from "../db/options.js";
5
5
  import {
6
+ EngineApiMode,
6
7
  ExecutionBuilderOpts,
7
8
  ExecutionEngineOpts,
8
9
  defaultExecutionBuilderHttpOpts,
9
10
  defaultExecutionBuilderOpts,
10
11
  defaultExecutionEngineHttpOpts,
11
12
  defaultExecutionEngineOpts,
13
+ engineApiModes,
12
14
  } from "../execution/index.js";
13
15
  import {MetricsOptions, defaultMetricsOptions} from "../metrics/options.js";
14
16
  import {MonitoringOptions, defaultMonitoringOptions} from "../monitoring/options.js";
@@ -19,7 +21,14 @@ import {SyncOptions, defaultSyncOptions} from "../sync/options.js";
19
21
  export {allNamespaces} from "../api/rest/index.js";
20
22
 
21
23
  // Re-export to use as default values in CLI args
22
- export {defaultExecutionEngineHttpOpts, defaultExecutionBuilderHttpOpts, ArchiveMode, DEFAULT_ARCHIVE_MODE};
24
+ export {
25
+ defaultExecutionEngineHttpOpts,
26
+ defaultExecutionBuilderHttpOpts,
27
+ engineApiModes,
28
+ ArchiveMode,
29
+ DEFAULT_ARCHIVE_MODE,
30
+ };
31
+ export type {EngineApiMode};
23
32
 
24
33
  export interface IBeaconNodeOptions {
25
34
  api: ApiOptions;
@@ -14,7 +14,7 @@ import {IClock} from "../../util/clock.js";
14
14
  import {CustodyConfig} from "../../util/dataColumns.js";
15
15
  import {PeerIdStr} from "../../util/peerId.js";
16
16
  import {MAX_BATCH_DOWNLOAD_ATTEMPTS, MAX_BATCH_PROCESSING_ATTEMPTS} from "../constants.js";
17
- import {DownloadByRangeRequests, ParentPayloadCommitments} from "../utils/downloadByRange.js";
17
+ import {DownloadByRangeRequests, ParentPayload} from "../utils/downloadByRange.js";
18
18
  import {getBatchSlotRange, hashBlocks} from "./utils/index.js";
19
19
 
20
20
  /**
@@ -175,7 +175,7 @@ export class Batch {
175
175
  private readonly clock: IClock;
176
176
  private readonly custodyConfig: CustodyConfig;
177
177
  private readonly isFirstBatchInChain: boolean;
178
- private readonly latestBid: gloas.ExecutionPayloadBid | undefined;
178
+ private readonly parentPayload: ParentPayload | undefined;
179
179
 
180
180
  constructor(
181
181
  startEpoch: Epoch,
@@ -183,7 +183,7 @@ export class Batch {
183
183
  clock: IClock,
184
184
  custodyConfig: CustodyConfig,
185
185
  isFirstBatchInChain: boolean,
186
- latestBid: gloas.ExecutionPayloadBid | undefined,
186
+ parentPayload: ParentPayload | undefined,
187
187
  targetSlot: Slot
188
188
  ) {
189
189
  this.config = config;
@@ -196,7 +196,7 @@ export class Batch {
196
196
  this.startSlot = startSlot;
197
197
  this.count = Math.min(count, targetSlot - startSlot + 1);
198
198
  this.isFirstBatchInChain = isFirstBatchInChain;
199
- this.latestBid = latestBid;
199
+ this.parentPayload = parentPayload;
200
200
  this.requests = this.getRequests([]);
201
201
  }
202
202
 
@@ -208,24 +208,19 @@ export class Batch {
208
208
  }
209
209
 
210
210
  // we only know if we should download parent envelope if firstBlock is downloaded
211
- if (firstBlock === undefined) return false;
212
- if (this.latestBid === undefined) return false;
213
- const firstBlockBidParentHash = (firstBlock.message.body as gloas.BeaconBlockBody).signedExecutionPayloadBid.message
214
- .parentBlockHash;
215
- return byteArrayEquals(firstBlockBidParentHash, this.latestBid.blockHash);
211
+ if (firstBlock === undefined || this.parentPayload === undefined) return false;
212
+ const firstBlockBid = (firstBlock.message.body as gloas.BeaconBlockBody).signedExecutionPayloadBid.message;
213
+ return (
214
+ byteArrayEquals(firstBlock.message.parentRoot, this.parentPayload.blockRoot) &&
215
+ byteArrayEquals(firstBlockBid.parentBlockHash, this.parentPayload.bid.blockHash)
216
+ );
216
217
  }
217
218
 
218
- getParentPayloadCommitments(parentBlockRoot: Uint8Array): ParentPayloadCommitments {
219
- if (this.latestBid === undefined) {
220
- throw new Error(
221
- `Coding error: getParentPayloadCommitments called without latestBid for parentBlockRoot=${toRootHex(parentBlockRoot)}`
222
- );
219
+ getParentPayload(): ParentPayload {
220
+ if (this.parentPayload === undefined) {
221
+ throw new Error("Coding error: getParentPayload called without parentPayload");
223
222
  }
224
- return {
225
- blockRoot: parentBlockRoot,
226
- blockRootHex: toRootHex(parentBlockRoot),
227
- kzgCommitments: this.latestBid.blobKzgCommitments,
228
- };
223
+ return this.parentPayload;
229
224
  }
230
225
 
231
226
  /**
@@ -773,6 +768,7 @@ export class Batch {
773
768
  // remove any downloaded blocks and re-attempt
774
769
  // TODO(fulu): need to remove the bad blocks from the SeenBlockInputCache
775
770
  this.state = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
771
+ this.resetRequests();
776
772
  }
777
773
 
778
774
  private onProcessingError(attempt: FailedAttempt): void {
@@ -784,6 +780,16 @@ export class Batch {
784
780
  // remove any downloaded blocks and re-attempt
785
781
  // TODO(fulu): need to remove the bad blocks from the SeenBlockInputCache
786
782
  this.state = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
783
+ this.resetRequests();
784
+ }
785
+
786
+ /**
787
+ * After a partial download `requests` only cover the data still missing from the downloaded blocks,
788
+ * e.g. a single column. Once those blocks are dropped, request the whole batch again, otherwise
789
+ * every peer fails the retry for lack of blocks to validate the data against.
790
+ */
791
+ private resetRequests(): void {
792
+ this.requests = this.getRequests([]);
787
793
  }
788
794
 
789
795
  /** Helper to construct typed BatchError. Stack traces are correct as the error is thrown above */
@@ -1,5 +1,5 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {Epoch, Root, Slot, gloas} from "@lodestar/types";
2
+ import {Epoch, Root, Slot} from "@lodestar/types";
3
3
  import {ErrorAborted, LodestarError, Logger, prettyPrintIndices, toRootHex} from "@lodestar/utils";
4
4
  import {isBlockInputBlobs, isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
5
5
  import {BlockInputErrorCode} from "../../chain/blocks/blockInput/errors.js";
@@ -16,7 +16,7 @@ import {ItTrigger} from "../../util/itTrigger.js";
16
16
  import {PeerIdStr} from "../../util/peerId.js";
17
17
  import {WarnResult, wrapError} from "../../util/wrapError.js";
18
18
  import {BATCH_BUFFER_SIZE, EPOCHS_PER_BATCH, MAX_LOOK_AHEAD_EPOCHS} from "../constants.js";
19
- import {DownloadByRangeError, DownloadByRangeErrorCode} from "../utils/downloadByRange.js";
19
+ import {DownloadByRangeError, DownloadByRangeErrorCode, ParentPayload} from "../utils/downloadByRange.js";
20
20
  import {getRateLimitedUntilMs} from "../utils/rateLimit.js";
21
21
  import {RangeSyncType} from "../utils/remoteSyncType.js";
22
22
  import {Batch, BatchError, BatchErrorCode, BatchMetadata, BatchStatus} from "./batch.js";
@@ -160,7 +160,7 @@ export class SyncChain {
160
160
  private readonly clock: IClock;
161
161
  private readonly metrics: Metrics | null;
162
162
  private readonly custodyConfig: CustodyConfig;
163
- private readonly latestBid: gloas.ExecutionPayloadBid | undefined;
163
+ private readonly parentPayload: ParentPayload | undefined;
164
164
 
165
165
  constructor(
166
166
  initialBatchEpoch: Epoch,
@@ -168,7 +168,7 @@ export class SyncChain {
168
168
  syncType: RangeSyncType,
169
169
  fns: SyncChainFns,
170
170
  modules: SyncChainModules,
171
- latestBid: gloas.ExecutionPayloadBid | undefined
171
+ parentPayload: ParentPayload | undefined
172
172
  ) {
173
173
  const {config, clock, custodyConfig, logger, metrics} = modules;
174
174
  this.firstBatchEpoch = initialBatchEpoch;
@@ -184,7 +184,7 @@ export class SyncChain {
184
184
  this.clock = clock;
185
185
  this.metrics = metrics;
186
186
  this.custodyConfig = custodyConfig;
187
- this.latestBid = latestBid;
187
+ this.parentPayload = parentPayload;
188
188
  this.logger = logger;
189
189
  this.logId = `${syncType}-${nextChainId++}`;
190
190
 
@@ -225,6 +225,9 @@ export class SyncChain {
225
225
  Math.floor((localFinalizedEpoch - this.lastEpochWithProcessBlocks) / EPOCHS_PER_BATCH) * EPOCHS_PER_BATCH;
226
226
  this.advanceChain(lastEpochWithProcessBlocksAligned);
227
227
 
228
+ // stopSyncing() cleared the retry timer, but peers can still be in rate limit backoff
229
+ this.scheduleRateLimitBackoffRetry();
230
+
228
231
  // Potentially download new batches and process pending
229
232
  this.triggerBatchDownloader();
230
233
  this.triggerBatchProcessor();
@@ -417,8 +420,10 @@ export class SyncChain {
417
420
  this.rateLimitBackoffTimeout = setTimeout(
418
421
  () => {
419
422
  this.rateLimitBackoffTimeout = undefined;
420
- this.triggerBatchDownloader();
423
+ // Must run before the downloader, it drops expired peers without triggering a download. If it ran
424
+ // after, a backoff expiring in between would leave the peer idle with no retry scheduled.
421
425
  this.scheduleRateLimitBackoffRetry();
426
+ this.triggerBatchDownloader();
422
427
  },
423
428
  Math.max(0, retryAt - now)
424
429
  );
@@ -531,8 +536,8 @@ export class SyncChain {
531
536
  this.clock,
532
537
  this.custodyConfig,
533
538
  this.isFirstBatch,
534
- // `latestBid` is only meaningful for the first batch's parent-payload check
535
- this.isFirstBatch ? this.latestBid : undefined,
539
+ // `parentPayload` is only meaningful for the first batch
540
+ this.isFirstBatch ? this.parentPayload : undefined,
536
541
  this.target.slot
537
542
  );
538
543
  this.isFirstBatch = false;
@@ -581,6 +586,8 @@ export class SyncChain {
581
586
  case DownloadByRangeErrorCode.OUT_OF_RANGE_BLOCKS:
582
587
  case DownloadByRangeErrorCode.PARENT_ROOT_MISMATCH:
583
588
  case DownloadByRangeErrorCode.INVALID_ENVELOPE_BEACON_BLOCK_ROOT:
589
+ case DownloadByRangeErrorCode.INVALID_ENVELOPE_BLOCK_HASH:
590
+ case DownloadByRangeErrorCode.INVALID_ENVELOPE_SLOT:
584
591
  case DownloadByRangeErrorCode.INVALID_CHAIN_SEGMENT:
585
592
  case BlobSidecarErrorCode.INCLUSION_PROOF_INVALID:
586
593
  case BlobSidecarErrorCode.INVALID_KZG_PROOF_BATCH:
@@ -2,9 +2,10 @@ import {EventEmitter} from "node:events";
2
2
  import {StrictEventEmitter} from "strict-event-emitter-types";
3
3
  import {BeaconConfig} from "@lodestar/config";
4
4
  import {IBeaconStateViewGloas, computeStartSlotAtEpoch, isStatePostGloas} from "@lodestar/state-transition";
5
- import {Epoch, Status, fulu} from "@lodestar/types";
6
- import {Logger, prettyPrintIndices, toRootHex} from "@lodestar/utils";
5
+ import {Epoch, Status, fulu, ssz} from "@lodestar/types";
6
+ import {Logger, byteArrayEquals, prettyPrintIndices, toRootHex} from "@lodestar/utils";
7
7
  import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
8
+ import {PayloadError, PayloadErrorCode} from "../../chain/blocks/importExecutionPayload.js";
8
9
  import {AttestationImportOpt, ImportBlockOpts} from "../../chain/blocks/index.js";
9
10
  import {assertLinearChainSegment} from "../../chain/blocks/utils/chainSegment.js";
10
11
  import {BlockError} from "../../chain/errors/index.js";
@@ -15,6 +16,7 @@ import {PeerIdStr} from "../../util/peerId.js";
15
16
  import {
16
17
  DownloadByRangeError,
17
18
  DownloadByRangeErrorCode,
19
+ ParentPayload,
18
20
  cacheByRangeResponses,
19
21
  downloadByRange,
20
22
  } from "../utils/downloadByRange.js";
@@ -207,22 +209,50 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
207
209
  blsVerifyOnMainThread: false,
208
210
  };
209
211
 
210
- if (this.opts?.disableProcessAsChainSegment) {
211
- // Should only be used for debugging or testing
212
- for (const block of blocks) {
213
- await this.chain.processBlock(block, flags);
214
- const payloadEnvelope = payloadEnvelopes?.get(block.slot);
215
- if (payloadEnvelope?.hasPayloadEnvelope()) {
216
- await this.chain.processExecutionPayload(payloadEnvelope);
212
+ try {
213
+ if (this.opts?.disableProcessAsChainSegment) {
214
+ // Should only be used for debugging or testing
215
+ for (const block of blocks) {
216
+ await this.chain.processBlock(block, flags);
217
+ const payloadEnvelope = payloadEnvelopes?.get(block.slot);
218
+ if (payloadEnvelope?.hasPayloadEnvelope()) {
219
+ await this.chain.processExecutionPayload(payloadEnvelope);
220
+ }
221
+ }
222
+ } else {
223
+ const {orphaned, skipped} = await this.chain.processChainSegment(blocks, payloadEnvelopes, flags);
224
+ // We log orphaned payloads to work with different clients
225
+ // in the future, consider applying penalties in certain conditions
226
+ // make sure the payload source was from range sync in that case
227
+ for (const {slot, payloadEnvelopeInput} of orphaned) {
228
+ const {peerIdStr, source} = payloadEnvelopeInput.getPayloadEnvelopeSource();
229
+ let client = "unknown";
230
+ if (peerIdStr !== undefined) {
231
+ try {
232
+ client = this.getConnectedPeerSyncMeta(peerIdStr).client;
233
+ } catch {
234
+ // peer disconnected since serving the envelope, keep "unknown"
235
+ }
236
+ }
237
+ this.logger.debug("Orphaned payload envelope in range sync batch", {
238
+ slot,
239
+ root: payloadEnvelopeInput.blockRootHex,
240
+ peer: peerIdStr ?? "unknown",
241
+ source,
242
+ client,
243
+ skipped,
244
+ });
217
245
  }
218
246
  }
219
- } else {
220
- const {orphaned, skipped} = await this.chain.processChainSegment(blocks, payloadEnvelopes, flags);
221
- // We log orphaned payloads to work with different clients
222
- // in the future, consider applying penalties in certain conditions
223
- // make sure the payload source was from range sync in that case
224
- for (const {slot, payloadEnvelopeInput} of orphaned) {
225
- const {peerIdStr, source} = payloadEnvelopeInput.getPayloadEnvelopeSource();
247
+ } catch (e) {
248
+ if (
249
+ e instanceof PayloadError &&
250
+ (e.type.code === PayloadErrorCode.INVALID_SIGNATURE ||
251
+ e.type.code === PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR ||
252
+ e.type.code === PayloadErrorCode.EXECUTION_ENGINE_INVALID)
253
+ ) {
254
+ const {payloadInput} = e;
255
+ const {peerIdStr, source} = payloadInput.getPayloadEnvelopeSource();
226
256
  let client = "unknown";
227
257
  if (peerIdStr !== undefined) {
228
258
  try {
@@ -231,30 +261,32 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
231
261
  // peer disconnected since serving the envelope, keep "unknown"
232
262
  }
233
263
  }
234
- this.logger.debug("Orphaned payload envelope in range sync batch", {
235
- slot,
236
- root: payloadEnvelopeInput.blockRootHex,
264
+ this.logger.debug("Removing invalid payload envelope in range sync batch", {
265
+ slot: payloadInput.slot,
266
+ root: payloadInput.blockRootHex,
237
267
  peer: peerIdStr ?? "unknown",
238
268
  source,
239
269
  client,
240
- skipped,
270
+ code: e.type.code,
241
271
  });
272
+ // The batch is downloaded again, it would otherwise reuse the cached envelope and fail the same way
273
+ this.chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
242
274
  }
275
+ throw e;
243
276
  }
244
277
  };
245
278
 
246
279
  private downloadByRange: SyncChainFns["downloadByRange"] = async (peer, batch) => {
247
280
  const batchBlocks = batch.getBlocks();
248
281
  const requests = batch.getRequestsForPeer(peer);
249
- const parentRoot = requests.parentPayloadRequest?.envelopeBlockRoot ?? requests.parentPayloadRequest?.blockRoot;
250
- const parentPayloadCommitments = parentRoot ? batch.getParentPayloadCommitments(parentRoot) : undefined;
282
+ const parentPayload = requests.parentPayloadRequest ? batch.getParentPayload() : undefined;
251
283
  const {result, warnings} = await downloadByRange({
252
284
  config: this.config,
253
285
  network: this.network,
254
286
  logger: this.logger,
255
287
  peerIdStr: peer.peerId,
256
288
  batchBlocks,
257
- parentPayloadCommitments,
289
+ parentPayload,
258
290
  peerDasMetrics: this.chain.metrics?.peerDas,
259
291
  ...requests,
260
292
  });
@@ -267,6 +299,7 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
267
299
  batchBlocks,
268
300
  downloadedPayloadEnvelopes,
269
301
  existingPayloadEnvelopes: batch.getPayloadEnvelopes(),
302
+ parentPayload,
270
303
  custodyConfig: this.chain.custodyConfig,
271
304
  seenTimestampSec: Date.now() / 1000,
272
305
  });
@@ -344,13 +377,26 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
344
377
  private addPeerOrCreateChain(startEpoch: Epoch, target: ChainTarget, peer: PeerIdStr, syncType: RangeSyncType): void {
345
378
  let syncChain = this.chains.get(syncType);
346
379
  if (!syncChain) {
347
- // The first batch of a new sync chain may need to detect whether the parent block was an
348
- // gloas "empty" block (no envelope produced). It does so by comparing the first
349
- // downloaded block's `bid.parentBlockHash` against the head state's `latestExecutionPayloadBid.blockHash`.
380
+ // The first block of the first batch may build on the head's FULL variant before its payload is imported,
381
+ // the batch then downloads the head's payload as dangling parent and validates it against the bid
350
382
  const headState = this.chain.getHeadState();
351
- const latestBid = isStatePostGloas(headState)
352
- ? (headState as IBeaconStateViewGloas).latestExecutionPayloadBid
353
- : undefined;
383
+ let parentPayload: ParentPayload | undefined;
384
+ if (isStatePostGloas(headState) && headState.latestBlockHeader.slot > 0) {
385
+ const header = ssz.phase0.BeaconBlockHeader.clone(headState.latestBlockHeader);
386
+ if (byteArrayEquals(header.stateRoot, ssz.Root.defaultValue())) {
387
+ header.stateRoot = headState.hashTreeRoot();
388
+ }
389
+ const headRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(header);
390
+ const headSlot = headState.latestBlockHeader.slot;
391
+ parentPayload = {
392
+ blockRoot: headRoot,
393
+ blockRootHex: toRootHex(headRoot),
394
+ slot: headSlot,
395
+ proposerIndex: headState.latestBlockHeader.proposerIndex,
396
+ forkName: this.config.getForkName(headSlot),
397
+ bid: (headState as IBeaconStateViewGloas).latestExecutionPayloadBid,
398
+ };
399
+ }
354
400
 
355
401
  syncChain = new SyncChain(
356
402
  startEpoch,
@@ -371,7 +417,7 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
371
417
  custodyConfig: this.chain.custodyConfig,
372
418
  metrics: this.metrics,
373
419
  },
374
- latestBid
420
+ parentPayload
375
421
  );
376
422
  this.chains.set(syncType, syncChain);
377
423