@lodestar/beacon-node 1.50.0-dev.fd4f342a40 → 1.50.0-dev.fe4e7408b4

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 (333) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +13 -3
  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/state/index.d.ts.map +1 -1
  8. package/lib/api/impl/beacon/state/index.js +28 -6
  9. package/lib/api/impl/beacon/state/index.js.map +1 -1
  10. package/lib/api/impl/debug/index.d.ts.map +1 -1
  11. package/lib/api/impl/debug/index.js +26 -18
  12. package/lib/api/impl/debug/index.js.map +1 -1
  13. package/lib/api/impl/validator/index.d.ts +1 -0
  14. package/lib/api/impl/validator/index.d.ts.map +1 -1
  15. package/lib/api/impl/validator/index.js +65 -4
  16. package/lib/api/impl/validator/index.js.map +1 -1
  17. package/lib/chain/archiveStore/archiveStore.d.ts +5 -0
  18. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  19. package/lib/chain/archiveStore/archiveStore.js +14 -24
  20. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  21. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +2 -2
  22. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
  23. package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -7
  24. package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
  25. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -1
  26. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  27. package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
  28. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  29. package/lib/chain/archiveStore/historicalState/worker.js +1 -1
  30. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  31. package/lib/chain/archiveStore/interface.d.ts +1 -1
  32. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  33. package/lib/chain/archiveStore/utils/pruneHistory.d.ts +1 -1
  34. package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
  35. package/lib/chain/archiveStore/utils/pruneHistory.js +2 -1
  36. package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
  37. package/lib/chain/beaconProposerCache.d.ts +6 -0
  38. package/lib/chain/beaconProposerCache.d.ts.map +1 -1
  39. package/lib/chain/beaconProposerCache.js +16 -0
  40. package/lib/chain/beaconProposerCache.js.map +1 -1
  41. package/lib/chain/blocks/blockInput/blockInput.d.ts +1 -1
  42. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  43. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  44. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  45. package/lib/chain/blocks/importBlock.js +14 -8
  46. package/lib/chain/blocks/importBlock.js.map +1 -1
  47. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  48. package/lib/chain/blocks/verifyBlock.js +7 -7
  49. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  50. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -1
  51. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  52. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +5 -4
  53. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  54. package/lib/chain/chain.d.ts +10 -5
  55. package/lib/chain/chain.d.ts.map +1 -1
  56. package/lib/chain/chain.js +102 -33
  57. package/lib/chain/chain.js.map +1 -1
  58. package/lib/chain/emitter.d.ts +7 -1
  59. package/lib/chain/emitter.d.ts.map +1 -1
  60. package/lib/chain/emitter.js +5 -0
  61. package/lib/chain/emitter.js.map +1 -1
  62. package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
  63. package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
  64. package/lib/chain/errors/envelopeReconstructionError.js +2 -0
  65. package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
  66. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  67. package/lib/chain/forkChoice/index.js +2 -0
  68. package/lib/chain/forkChoice/index.js.map +1 -1
  69. package/lib/chain/interface.d.ts +6 -3
  70. package/lib/chain/interface.d.ts.map +1 -1
  71. package/lib/chain/opPools/aggregatedAttestationPool.d.ts +4 -2
  72. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  73. package/lib/chain/opPools/aggregatedAttestationPool.js +30 -8
  74. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  75. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  76. package/lib/chain/opPools/opPool.js +1 -23
  77. package/lib/chain/opPools/opPool.js.map +1 -1
  78. package/lib/chain/opPools/proposerPreferencesPool.d.ts +8 -4
  79. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  80. package/lib/chain/opPools/proposerPreferencesPool.js +27 -12
  81. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  82. package/lib/chain/opPools/utils.d.ts +11 -0
  83. package/lib/chain/opPools/utils.d.ts.map +1 -1
  84. package/lib/chain/opPools/utils.js +22 -0
  85. package/lib/chain/opPools/utils.js.map +1 -1
  86. package/lib/chain/options.d.ts +2 -1
  87. package/lib/chain/options.d.ts.map +1 -1
  88. package/lib/chain/options.js +1 -1
  89. package/lib/chain/options.js.map +1 -1
  90. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  91. package/lib/chain/prepareNextSlot.js +11 -3
  92. package/lib/chain/prepareNextSlot.js.map +1 -1
  93. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  94. package/lib/chain/produceBlock/computeNewStateRoot.js +1 -1
  95. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  96. package/lib/chain/produceBlock/produceBlockBody.d.ts +8 -0
  97. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  98. package/lib/chain/produceBlock/produceBlockBody.js +48 -34
  99. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  100. package/lib/chain/regen/regen.d.ts +2 -0
  101. package/lib/chain/regen/regen.d.ts.map +1 -1
  102. package/lib/chain/regen/regen.js +10 -4
  103. package/lib/chain/regen/regen.js.map +1 -1
  104. package/lib/chain/seenCache/seenGossipBlockInput.d.ts +7 -0
  105. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  106. package/lib/chain/seenCache/seenGossipBlockInput.js +22 -1
  107. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  108. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +3 -2
  109. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  110. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +13 -13
  111. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  112. package/lib/chain/shufflingCache.d.ts +18 -8
  113. package/lib/chain/shufflingCache.d.ts.map +1 -1
  114. package/lib/chain/shufflingCache.js +72 -18
  115. package/lib/chain/shufflingCache.js.map +1 -1
  116. package/lib/chain/validatorMonitor.d.ts +5 -1
  117. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  118. package/lib/chain/validatorMonitor.js +64 -54
  119. package/lib/chain/validatorMonitor.js.map +1 -1
  120. package/lib/db/beacon.d.ts +4 -1
  121. package/lib/db/beacon.d.ts.map +1 -1
  122. package/lib/db/beacon.js +6 -1
  123. package/lib/db/beacon.js.map +1 -1
  124. package/lib/db/buckets.d.ts +3 -1
  125. package/lib/db/buckets.d.ts.map +1 -1
  126. package/lib/db/buckets.js +2 -0
  127. package/lib/db/buckets.js.map +1 -1
  128. package/lib/db/interface.d.ts +4 -1
  129. package/lib/db/interface.d.ts.map +1 -1
  130. package/lib/db/repositories/blockArchive.js +2 -2
  131. package/lib/db/repositories/blockArchive.js.map +1 -1
  132. package/lib/db/repositories/blockArchiveIndex.d.ts +3 -3
  133. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  134. package/lib/db/repositories/blockArchiveIndex.js +1 -2
  135. package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
  136. package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
  137. package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
  138. package/lib/db/repositories/earliestAvailableSlot.js +19 -0
  139. package/lib/db/repositories/earliestAvailableSlot.js.map +1 -0
  140. package/lib/db/repositories/index.d.ts +1 -0
  141. package/lib/db/repositories/index.d.ts.map +1 -1
  142. package/lib/db/repositories/index.js +1 -0
  143. package/lib/db/repositories/index.js.map +1 -1
  144. package/lib/db/repositories/proposerPreferences.d.ts +14 -0
  145. package/lib/db/repositories/proposerPreferences.d.ts.map +1 -0
  146. package/lib/db/repositories/proposerPreferences.js +21 -0
  147. package/lib/db/repositories/proposerPreferences.js.map +1 -0
  148. package/lib/execution/engine/blobVersionedHashes.d.ts +9 -0
  149. package/lib/execution/engine/blobVersionedHashes.d.ts.map +1 -0
  150. package/lib/execution/engine/blobVersionedHashes.js +74 -0
  151. package/lib/execution/engine/blobVersionedHashes.js.map +1 -0
  152. package/lib/execution/engine/disabled.d.ts +0 -2
  153. package/lib/execution/engine/disabled.d.ts.map +1 -1
  154. package/lib/execution/engine/disabled.js +0 -6
  155. package/lib/execution/engine/disabled.js.map +1 -1
  156. package/lib/execution/engine/http.d.ts +46 -16
  157. package/lib/execution/engine/http.d.ts.map +1 -1
  158. package/lib/execution/engine/http.js +236 -213
  159. package/lib/execution/engine/http.js.map +1 -1
  160. package/lib/execution/engine/index.d.ts +3 -2
  161. package/lib/execution/engine/index.d.ts.map +1 -1
  162. package/lib/execution/engine/index.js +33 -8
  163. package/lib/execution/engine/index.js.map +1 -1
  164. package/lib/execution/engine/interface.d.ts +2 -4
  165. package/lib/execution/engine/interface.d.ts.map +1 -1
  166. package/lib/execution/engine/jsonRpcTransport.d.ts +25 -0
  167. package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -0
  168. package/lib/execution/engine/jsonRpcTransport.js +158 -0
  169. package/lib/execution/engine/jsonRpcTransport.js.map +1 -0
  170. package/lib/execution/engine/mock.d.ts +0 -2
  171. package/lib/execution/engine/mock.d.ts.map +1 -1
  172. package/lib/execution/engine/mock.js +0 -8
  173. package/lib/execution/engine/mock.js.map +1 -1
  174. package/lib/execution/engine/restHttpClient.d.ts +71 -0
  175. package/lib/execution/engine/restHttpClient.d.ts.map +1 -0
  176. package/lib/execution/engine/restHttpClient.js +201 -0
  177. package/lib/execution/engine/restHttpClient.js.map +1 -0
  178. package/lib/execution/engine/restTransport.d.ts +46 -0
  179. package/lib/execution/engine/restTransport.d.ts.map +1 -0
  180. package/lib/execution/engine/restTransport.js +399 -0
  181. package/lib/execution/engine/restTransport.js.map +1 -0
  182. package/lib/execution/engine/sszTypes.d.ts +1261 -0
  183. package/lib/execution/engine/sszTypes.d.ts.map +1 -0
  184. package/lib/execution/engine/sszTypes.js +216 -0
  185. package/lib/execution/engine/sszTypes.js.map +1 -0
  186. package/lib/execution/engine/transport.d.ts +39 -0
  187. package/lib/execution/engine/transport.d.ts.map +1 -0
  188. package/lib/execution/engine/transport.js +2 -0
  189. package/lib/execution/engine/transport.js.map +1 -0
  190. package/lib/execution/engine/types.d.ts +2 -8
  191. package/lib/execution/engine/types.d.ts.map +1 -1
  192. package/lib/execution/engine/types.js +24 -57
  193. package/lib/execution/engine/types.js.map +1 -1
  194. package/lib/execution/engine/utils.d.ts.map +1 -1
  195. package/lib/execution/engine/utils.js +9 -2
  196. package/lib/execution/engine/utils.js.map +1 -1
  197. package/lib/metrics/metrics/lodestar.d.ts +10 -0
  198. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  199. package/lib/metrics/metrics/lodestar.js +22 -0
  200. package/lib/metrics/metrics/lodestar.js.map +1 -1
  201. package/lib/metrics/metrics.d.ts +7 -3
  202. package/lib/metrics/metrics.d.ts.map +1 -1
  203. package/lib/metrics/metrics.js +4 -4
  204. package/lib/metrics/metrics.js.map +1 -1
  205. package/lib/network/core/events.d.ts +5 -1
  206. package/lib/network/core/events.d.ts.map +1 -1
  207. package/lib/network/core/events.js +8 -0
  208. package/lib/network/core/events.js.map +1 -1
  209. package/lib/network/gossip/topic.d.ts +0 -14
  210. package/lib/network/gossip/topic.d.ts.map +1 -1
  211. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  212. package/lib/network/processor/gossipHandlers.js +60 -64
  213. package/lib/network/processor/gossipHandlers.js.map +1 -1
  214. package/lib/network/processor/index.js +3 -3
  215. package/lib/network/processor/index.js.map +1 -1
  216. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +1 -1
  217. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  218. package/lib/node/nodejs.d.ts +3 -1
  219. package/lib/node/nodejs.d.ts.map +1 -1
  220. package/lib/node/nodejs.js +27 -4
  221. package/lib/node/nodejs.js.map +1 -1
  222. package/lib/node/options.d.ts +3 -2
  223. package/lib/node/options.d.ts.map +1 -1
  224. package/lib/node/options.js +2 -2
  225. package/lib/node/options.js.map +1 -1
  226. package/lib/sync/range/batch.d.ts +11 -5
  227. package/lib/sync/range/batch.d.ts.map +1 -1
  228. package/lib/sync/range/batch.js +21 -17
  229. package/lib/sync/range/batch.js.map +1 -1
  230. package/lib/sync/range/chain.d.ts +4 -4
  231. package/lib/sync/range/chain.d.ts.map +1 -1
  232. package/lib/sync/range/chain.js +12 -6
  233. package/lib/sync/range/chain.js.map +1 -1
  234. package/lib/sync/range/range.d.ts.map +1 -1
  235. package/lib/sync/range/range.js +25 -11
  236. package/lib/sync/range/range.js.map +1 -1
  237. package/lib/sync/unknownBlock.d.ts +5 -2
  238. package/lib/sync/unknownBlock.d.ts.map +1 -1
  239. package/lib/sync/unknownBlock.js +44 -20
  240. package/lib/sync/unknownBlock.js.map +1 -1
  241. package/lib/sync/utils/downloadByRange.d.ts +38 -14
  242. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  243. package/lib/sync/utils/downloadByRange.js +84 -39
  244. package/lib/sync/utils/downloadByRange.js.map +1 -1
  245. package/lib/util/asyncIterableToEvents.d.ts +10 -2
  246. package/lib/util/asyncIterableToEvents.d.ts.map +1 -1
  247. package/lib/util/asyncIterableToEvents.js +29 -3
  248. package/lib/util/asyncIterableToEvents.js.map +1 -1
  249. package/lib/util/execution.d.ts +9 -11
  250. package/lib/util/execution.d.ts.map +1 -1
  251. package/lib/util/execution.js +5 -1
  252. package/lib/util/execution.js.map +1 -1
  253. package/lib/util/metadata.d.ts.map +1 -1
  254. package/lib/util/metadata.js +2 -1
  255. package/lib/util/metadata.js.map +1 -1
  256. package/lib/util/sszBytes.d.ts +2 -2
  257. package/lib/util/sszBytes.d.ts.map +1 -1
  258. package/lib/util/sszBytes.js +21 -14
  259. package/lib/util/sszBytes.js.map +1 -1
  260. package/package.json +18 -17
  261. package/src/api/impl/beacon/blocks/index.ts +17 -7
  262. package/src/api/impl/beacon/index.ts +15 -0
  263. package/src/api/impl/beacon/state/index.ts +43 -6
  264. package/src/api/impl/debug/index.ts +28 -19
  265. package/src/api/impl/validator/index.ts +81 -7
  266. package/src/chain/archiveStore/archiveStore.ts +19 -25
  267. package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -7
  268. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -1
  269. package/src/chain/archiveStore/historicalState/types.ts +2 -2
  270. package/src/chain/archiveStore/historicalState/worker.ts +1 -2
  271. package/src/chain/archiveStore/interface.ts +1 -1
  272. package/src/chain/archiveStore/utils/pruneHistory.ts +4 -1
  273. package/src/chain/beaconProposerCache.ts +19 -1
  274. package/src/chain/blocks/blockInput/blockInput.ts +1 -1
  275. package/src/chain/blocks/importBlock.ts +17 -7
  276. package/src/chain/blocks/verifyBlock.ts +8 -6
  277. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +6 -3
  278. package/src/chain/chain.ts +132 -35
  279. package/src/chain/emitter.ts +7 -1
  280. package/src/chain/errors/envelopeReconstructionError.ts +3 -0
  281. package/src/chain/forkChoice/index.ts +2 -0
  282. package/src/chain/interface.ts +7 -3
  283. package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
  284. package/src/chain/opPools/opPool.ts +1 -31
  285. package/src/chain/opPools/proposerPreferencesPool.ts +34 -11
  286. package/src/chain/opPools/utils.ts +31 -0
  287. package/src/chain/options.ts +3 -3
  288. package/src/chain/prepareNextSlot.ts +15 -3
  289. package/src/chain/produceBlock/computeNewStateRoot.ts +10 -3
  290. package/src/chain/produceBlock/produceBlockBody.ts +78 -39
  291. package/src/chain/regen/regen.ts +12 -4
  292. package/src/chain/seenCache/seenGossipBlockInput.ts +25 -1
  293. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +20 -13
  294. package/src/chain/shufflingCache.ts +84 -22
  295. package/src/chain/validatorMonitor.ts +75 -57
  296. package/src/db/beacon.ts +6 -0
  297. package/src/db/buckets.ts +2 -0
  298. package/src/db/interface.ts +4 -0
  299. package/src/db/repositories/blockArchive.ts +2 -2
  300. package/src/db/repositories/blockArchiveIndex.ts +2 -3
  301. package/src/db/repositories/earliestAvailableSlot.ts +19 -0
  302. package/src/db/repositories/index.ts +1 -0
  303. package/src/db/repositories/proposerPreferences.ts +23 -0
  304. package/src/execution/engine/blobVersionedHashes.ts +72 -0
  305. package/src/execution/engine/disabled.ts +0 -8
  306. package/src/execution/engine/http.ts +304 -300
  307. package/src/execution/engine/index.ts +41 -7
  308. package/src/execution/engine/interface.ts +2 -6
  309. package/src/execution/engine/jsonRpcTransport.ts +247 -0
  310. package/src/execution/engine/mock.ts +1 -19
  311. package/src/execution/engine/restHttpClient.ts +269 -0
  312. package/src/execution/engine/restTransport.ts +544 -0
  313. package/src/execution/engine/sszTypes.ts +354 -0
  314. package/src/execution/engine/transport.ts +61 -0
  315. package/src/execution/engine/types.ts +34 -84
  316. package/src/execution/engine/utils.ts +12 -2
  317. package/src/metrics/metrics/lodestar.ts +23 -0
  318. package/src/metrics/metrics.ts +21 -6
  319. package/src/network/core/events.ts +16 -1
  320. package/src/network/processor/gossipHandlers.ts +91 -77
  321. package/src/network/processor/index.ts +3 -3
  322. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +1 -1
  323. package/src/node/nodejs.ts +35 -3
  324. package/src/node/options.ts +10 -1
  325. package/src/sync/range/batch.ts +25 -19
  326. package/src/sync/range/chain.ts +15 -8
  327. package/src/sync/range/range.ts +26 -12
  328. package/src/sync/unknownBlock.ts +57 -21
  329. package/src/sync/utils/downloadByRange.ts +123 -58
  330. package/src/util/asyncIterableToEvents.ts +44 -5
  331. package/src/util/execution.ts +16 -8
  332. package/src/util/metadata.ts +2 -1
  333. package/src/util/sszBytes.ts +25 -12
@@ -1,13 +1,17 @@
1
- import { ForkName, ForkSeq, SLOTS_PER_EPOCH, isForkPostFulu } from "@lodestar/params";
2
- import { strip0xPrefix } from "@lodestar/utils";
1
+ import { ForkName, ForkSeq, SLOTS_PER_EPOCH, isForkPostBellatrix, isForkPostFulu, } from "@lodestar/params";
2
+ import { isErrorAborted } from "@lodestar/utils";
3
3
  import { EPOCHS_PER_BATCH } from "../../sync/constants.js";
4
4
  import { getLodestarClientVersion } from "../../util/metadata.js";
5
+ import { isQueueErrorAborted } from "../../util/queue/errors.js";
5
6
  import { JobItemQueue } from "../../util/queue/index.js";
6
- import { ClientCode, ExecutionEngineState, ExecutionPayloadStatus, } from "./interface.js";
7
- import { ErrorJsonRpcResponse, HttpRpcError, JsonRpcHttpClientEvent, } from "./jsonRpcHttpClient.js";
7
+ import { ExecutionEngineState, ExecutionPayloadStatus, } from "./interface.js";
8
+ import { ErrorJsonRpcResponse, HttpRpcError, JsonRpcHttpClientEvent } from "./jsonRpcHttpClient.js";
8
9
  import { PayloadIdCache } from "./payloadIdCache.js";
9
- import { BLOB_AND_PROOF_V2_RPC_BYTES, assertReqSizeLimit, deserializeBlobAndProofs, deserializeBlobAndProofsV2, deserializeBlobAndProofsV2IntoBytes, deserializeExecutionPayloadBody, deserializeExecutionPayloadBodyV2, parseExecutionPayload, serializeBeaconBlockRoot, serializeExecutionPayload, serializeExecutionRequests, serializePayloadAttributes, serializeVersionedHashes, } from "./types.js";
10
- import { bytesToData, getExecutionEngineState, numToQuantity } from "./utils.js";
10
+ import { EngineRestError, EngineRestResponseError, isRetryableEngineRestError } from "./restHttpClient.js";
11
+ import { executionForkName } from "./sszTypes.js";
12
+ import { serializePayloadAttributes } from "./types.js";
13
+ import { getExecutionEngineState } from "./utils.js";
14
+ export const engineApiModes = ["auto", "ssz", "json-rpc"];
11
15
  export const defaultExecutionEngineHttpOpts = {
12
16
  /**
13
17
  * By default ELs host engine api on an auth protected 8551 port, would need a jwt secret to be
@@ -18,38 +22,26 @@ export const defaultExecutionEngineHttpOpts = {
18
22
  retries: 2,
19
23
  retryDelay: 2000,
20
24
  timeout: 12000,
25
+ engineApi: "auto",
21
26
  };
22
27
  /**
23
28
  * Size for the serializing queue for fcUs and new payloads, the max length could be equal to
24
29
  * EPOCHS_PER_BATCH * 2 in case new payloads are also not awaited serially
25
30
  */
26
31
  const QUEUE_MAX_LENGTH = EPOCHS_PER_BATCH * SLOTS_PER_EPOCH * 2;
32
+ /** Minimum delay between capabilities probes while the execution client is unreachable */
33
+ const REST_PROBE_RETRY_MS = 12_000;
27
34
  /**
28
- * Maximum number of version hashes that can be sent in a getBlobs request
29
- * Clients must support at least 128 versionedHashes, so we avoid sending more
30
- * https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#specification-3
31
- */
32
- const MAX_VERSIONED_HASHES = 128;
33
- // Define static options once to prevent extra allocations
34
- const notifyNewPayloadOpts = { routeId: "notifyNewPayload" };
35
- const forkchoiceUpdatedV1Opts = { routeId: "forkchoiceUpdated" };
36
- const getPayloadOpts = { routeId: "getPayload" };
37
- const getPayloadBodiesByHashOpts = { routeId: "getPayloadBodiesByHash" };
38
- const getPayloadBodiesByRangeOpts = { routeId: "getPayloadBodiesByRange" };
39
- const getBlobsV1Opts = { routeId: "getBlobsV1" };
40
- const getBlobsV2Opts = { routeId: "getBlobsV2" };
41
- const getClientVersionOpts = { routeId: "getClientVersion" };
42
- /**
43
- * based on Ethereum JSON-RPC API and inherits the following properties of this standard:
44
- * - Supported communication protocols (HTTP and WebSocket)
45
- * - Message format and encoding notation
46
- * - Error codes improvement proposal
35
+ * Drives the execution client over the engine API, either the legacy `engine_*` JSON-RPC methods
36
+ * or the REST API with SSZ encoded bodies.
37
+ * https://github.com/ethereum/execution-apis/tree/main/src/engine
47
38
  *
48
- * Client software MUST expose Engine API at a port independent from JSON-RPC API. The default port for the Engine API is 8550 for HTTP and 8551 for WebSocket.
49
- * https://github.com/ethereum/execution-apis/blob/v1.0.0-alpha.1/src/engine/interop/specification.md
39
+ * In `auto` mode a capabilities response from a server without the REST API, a 4xx other than
40
+ * 401/403, selects JSON-RPC until the EL reconnects. Transient discovery failures use JSON-RPC while
41
+ * awaiting another probe; authentication failures fail visibly, malformed capabilities fall back to
42
+ * JSON-RPC with a warning. Forks and blob revisions the EL does not advertise also use JSON-RPC.
50
43
  */
51
44
  export class ExecutionEngineHttp {
52
- rpc;
53
45
  opts;
54
46
  logger;
55
47
  metrics;
@@ -69,29 +61,41 @@ export class ExecutionEngineHttp {
69
61
  * the order of new payloads and fcUs is pretty important to EL, this queue will serialize the calls in the
70
62
  * order with which we make them.
71
63
  */
72
- rpcFetchQueue;
73
- jobQueueProcessor = async ({ method, params, methodOpts }) => {
74
- return this.rpc.fetchWithRetries({ method, params }, methodOpts);
75
- };
76
- constructor(rpc, { metrics, signal, logger }, opts) {
77
- this.rpc = rpc;
64
+ queue;
65
+ jsonRpc;
66
+ rest;
67
+ engineApi;
68
+ restSupport = { state: "pending" };
69
+ restProbe = null;
70
+ lastRestProbeMs = Number.NEGATIVE_INFINITY;
71
+ loggedRestFallbacks = new Set();
72
+ constructor({ jsonRpc, rest }, { metrics, signal, logger }, opts) {
78
73
  this.opts = opts;
79
- this.rpcFetchQueue = new JobItemQueue(this.jobQueueProcessor, { maxLength: QUEUE_MAX_LENGTH, maxConcurrency: 1, noYieldIfOneItem: true, signal }, metrics?.engineHttpProcessorQueue);
74
+ this.queue = new JobItemQueue((job) => job(), { maxLength: QUEUE_MAX_LENGTH, maxConcurrency: 1, noYieldIfOneItem: true, signal }, metrics?.engineHttpProcessorQueue);
80
75
  this.logger = logger;
81
76
  this.metrics = metrics ?? null;
82
- this.rpc.emitter.on(JsonRpcHttpClientEvent.ERROR, ({ error }) => {
83
- this.updateEngineState(getExecutionEngineState({ payloadError: error, oldState: this.state }));
84
- });
85
- this.rpc.emitter.on(JsonRpcHttpClientEvent.RESPONSE, () => {
86
- if (this.clientVersion === undefined) {
87
- this.clientVersion = null;
88
- // This statement should only be called first time receiving response after startup
89
- this.getClientVersion(getLodestarClientVersion(this.opts)).catch((e) => {
90
- this.logger.debug("Unable to get execution client version", {}, e);
91
- });
92
- }
93
- this.updateEngineState(getExecutionEngineState({ targetState: ExecutionEngineState.ONLINE, oldState: this.state }));
77
+ this.jsonRpc = jsonRpc;
78
+ this.rest = rest ?? null;
79
+ this.engineApi = opts?.engineApi ?? "auto";
80
+ if (this.engineApi === "ssz" && this.rest === null) {
81
+ throw Error("REST transport is required for engineApi=ssz");
82
+ }
83
+ // REST errors are handled in withTransport after compatibility fallback.
84
+ this.jsonRpc.emitter.on(JsonRpcHttpClientEvent.ERROR, ({ error }) => {
85
+ this.updateEngineState(getExecutionEngineState({ payloadError: error, oldState: this.state }), error);
94
86
  });
87
+ for (const transport of [this.jsonRpc, this.rest]) {
88
+ transport?.emitter.on(JsonRpcHttpClientEvent.RESPONSE, () => {
89
+ if (this.clientVersion === undefined) {
90
+ this.clientVersion = null;
91
+ // This statement should only be called first time receiving response after startup
92
+ this.getClientVersion(getLodestarClientVersion(this.opts)).catch((e) => {
93
+ this.logger.debug("Unable to get execution client version", {}, e);
94
+ });
95
+ }
96
+ this.updateEngineState(getExecutionEngineState({ targetState: ExecutionEngineState.ONLINE, oldState: this.state }));
97
+ });
98
+ }
95
99
  }
96
100
  /**
97
101
  * `engine_newPayloadV1`
@@ -116,17 +120,7 @@ export class ExecutionEngineHttp {
116
120
  * If any of the above fails due to errors unrelated to the normal processing flow of the method, client software MUST respond with an error object.
117
121
  */
118
122
  async notifyNewPayload(fork, executionPayload, versionedHashes, parentBlockRoot, executionRequests) {
119
- const method = ForkSeq[fork] >= ForkSeq.gloas
120
- ? "engine_newPayloadV5"
121
- : ForkSeq[fork] >= ForkSeq.electra
122
- ? "engine_newPayloadV4"
123
- : ForkSeq[fork] >= ForkSeq.deneb
124
- ? "engine_newPayloadV3"
125
- : ForkSeq[fork] >= ForkSeq.capella
126
- ? "engine_newPayloadV2"
127
- : "engine_newPayloadV1";
128
- const serializedExecutionPayload = serializeExecutionPayload(fork, executionPayload);
129
- let engineRequest;
123
+ // Validate before queueing, argument errors must not be reported as an unavailable execution client
130
124
  if (ForkSeq[fork] >= ForkSeq.deneb) {
131
125
  if (versionedHashes === undefined) {
132
126
  throw Error(`versionedHashes required in notifyNewPayload for fork=${fork}`);
@@ -134,46 +128,26 @@ export class ExecutionEngineHttp {
134
128
  if (parentBlockRoot === undefined) {
135
129
  throw Error(`parentBlockRoot required in notifyNewPayload for fork=${fork}`);
136
130
  }
137
- const serializedVersionedHashes = serializeVersionedHashes(versionedHashes);
138
- const parentBeaconBlockRoot = serializeBeaconBlockRoot(parentBlockRoot);
139
- if (ForkSeq[fork] >= ForkSeq.electra) {
140
- if (executionRequests === undefined) {
141
- throw Error(`executionRequests required in notifyNewPayload for fork=${fork}`);
142
- }
143
- const serializedExecutionRequests = serializeExecutionRequests(fork, executionRequests);
144
- engineRequest = {
145
- method: ForkSeq[fork] >= ForkSeq.gloas ? "engine_newPayloadV5" : "engine_newPayloadV4",
146
- params: [
147
- serializedExecutionPayload,
148
- serializedVersionedHashes,
149
- parentBeaconBlockRoot,
150
- serializedExecutionRequests,
151
- ],
152
- methodOpts: notifyNewPayloadOpts,
153
- };
154
- }
155
- else {
156
- engineRequest = {
157
- method: "engine_newPayloadV3",
158
- params: [serializedExecutionPayload, serializedVersionedHashes, parentBeaconBlockRoot],
159
- methodOpts: notifyNewPayloadOpts,
160
- };
131
+ if (ForkSeq[fork] >= ForkSeq.electra && executionRequests === undefined) {
132
+ throw Error(`executionRequests required in notifyNewPayload for fork=${fork}`);
161
133
  }
162
134
  }
163
- else {
164
- const method = ForkSeq[fork] >= ForkSeq.capella ? "engine_newPayloadV2" : "engine_newPayloadV1";
165
- engineRequest = {
166
- method,
167
- params: [serializedExecutionPayload],
168
- methodOpts: notifyNewPayloadOpts,
169
- };
135
+ let result;
136
+ try {
137
+ result = await this.enqueue(() => this.withTransport(fork, undefined, (transport) => transport.newPayload(fork, executionPayload, versionedHashes, parentBlockRoot, executionRequests)));
170
138
  }
171
- const { status, latestValidHash, validationError } = await this.rpcFetchQueue.push(engineRequest).catch((e) => {
172
- if (e instanceof HttpRpcError || e instanceof ErrorJsonRpcResponse) {
173
- return { status: ExecutionPayloadStatus.ELERROR, latestValidHash: null, validationError: e.message };
174
- }
175
- return { status: ExecutionPayloadStatus.UNAVAILABLE, latestValidHash: null, validationError: e.message };
176
- });
139
+ catch (e) {
140
+ const status = isEngineResponseError(e)
141
+ ? ExecutionPayloadStatus.ELERROR
142
+ : ExecutionPayloadStatus.UNAVAILABLE;
143
+ // Only newPayload treats an unreachable execution client, including a timeout, as offline
144
+ const newState = status === ExecutionPayloadStatus.UNAVAILABLE && !isErrorAborted(e) && !isQueueErrorAborted(e)
145
+ ? getExecutionEngineState({ payloadStatus: status, oldState: this.state })
146
+ : getExecutionEngineState({ payloadError: e, oldState: this.state });
147
+ this.updateEngineState(newState, e);
148
+ return { status, latestValidHash: null, validationError: e.message };
149
+ }
150
+ const { status, latestValidHash, validationError } = result;
177
151
  this.updateEngineState(getExecutionEngineState({ payloadStatus: status, oldState: this.state }));
178
152
  switch (status) {
179
153
  case ExecutionPayloadStatus.VALID:
@@ -228,38 +202,21 @@ export class ExecutionEngineHttp {
228
202
  * If any of the above fails due to errors unrelated to the normal processing flow of the method, client software MUST respond with an error object.
229
203
  */
230
204
  async notifyForkchoiceUpdate(fork, headBlockHash, safeBlockHash, finalizedBlockHash, payloadAttributes) {
231
- // Once on capella, should this need to be permanently switched to v2 when payload attrs
232
- // not provided
233
- const method = ForkSeq[fork] >= ForkSeq.gloas
234
- ? "engine_forkchoiceUpdatedV4"
235
- : ForkSeq[fork] >= ForkSeq.deneb
236
- ? "engine_forkchoiceUpdatedV3"
237
- : ForkSeq[fork] >= ForkSeq.capella
238
- ? "engine_forkchoiceUpdatedV2"
239
- : "engine_forkchoiceUpdatedV1";
240
- const payloadAttributesRpc = payloadAttributes ? serializePayloadAttributes(payloadAttributes) : undefined;
241
- // If we are just fcUing and not asking execution for payload, retry is not required
242
- // and we can move on, as the next fcU will be issued soon on the new slot
243
- const fcUReqOpts = payloadAttributes !== undefined ? forkchoiceUpdatedV1Opts : { ...forkchoiceUpdatedV1Opts, retries: 0 };
244
- const request = this.rpcFetchQueue.push({
245
- method,
246
- params: [{ headBlockHash, safeBlockHash, finalizedBlockHash }, payloadAttributesRpc],
247
- methodOpts: fcUReqOpts,
248
- });
249
- const { payloadStatus: { status, latestValidHash: _latestValidHash, validationError }, payloadId, } = await request;
205
+ const { payloadStatus: { status, validationError }, payloadId, } = await this.enqueue(() => this.withTransport(fork, undefined, (transport) => transport.forkchoiceUpdated(fork, headBlockHash, safeBlockHash, finalizedBlockHash, payloadAttributes)));
250
206
  this.updateEngineState(getExecutionEngineState({ payloadStatus: status, oldState: this.state }));
251
207
  this.metrics?.engineNotifyForkchoiceUpdateResult.inc({ result: status });
252
208
  switch (status) {
253
209
  case ExecutionPayloadStatus.VALID:
254
210
  // if payloadAttributes are provided, a valid payloadId is expected
255
- if (payloadAttributesRpc) {
256
- if (!payloadId || payloadId === "0x") {
211
+ if (payloadAttributes) {
212
+ if (payloadId === null) {
257
213
  throw Error(`Received invalid payloadId=${payloadId}`);
258
214
  }
215
+ const payloadAttributesRpc = serializePayloadAttributes(payloadAttributes);
259
216
  this.payloadIdCache.add({ headBlockHash, finalizedBlockHash, ...payloadAttributesRpc }, payloadId);
260
217
  void this.prunePayloadIdCache();
261
218
  }
262
- return payloadId !== "0x" ? payloadId : null;
219
+ return payloadId;
263
220
  case ExecutionPayloadStatus.SYNCING:
264
221
  // Throw error on syncing if requested to produce a block, else silently ignore
265
222
  if (payloadAttributes) {
@@ -280,118 +237,154 @@ export class ExecutionEngineHttp {
280
237
  * 3. Client software MAY stop the corresponding building process after serving this call.
281
238
  */
282
239
  async getPayload(fork, payloadId) {
283
- let method;
284
- switch (fork) {
285
- case ForkName.phase0:
286
- case ForkName.altair:
287
- case ForkName.bellatrix:
288
- method = "engine_getPayloadV1";
289
- break;
290
- case ForkName.capella:
291
- method = "engine_getPayloadV2";
292
- break;
293
- case ForkName.deneb:
294
- method = "engine_getPayloadV3";
295
- break;
296
- case ForkName.electra:
297
- method = "engine_getPayloadV4";
298
- break;
299
- case ForkName.fulu:
300
- method = "engine_getPayloadV5";
301
- break;
302
- default:
303
- method = "engine_getPayloadV6";
304
- break;
305
- }
306
- const payloadResponse = await this.rpc.fetchWithRetries({
307
- method,
308
- params: [payloadId],
309
- }, getPayloadOpts);
310
- return parseExecutionPayload(fork, payloadResponse);
240
+ return this.withTransport(fork, undefined, (transport) => transport.getPayload(fork, payloadId));
311
241
  }
312
242
  async prunePayloadIdCache() {
313
243
  this.payloadIdCache.prune();
314
244
  }
315
- async getPayloadBodiesByHash(_fork, blockHashes) {
316
- const method = "engine_getPayloadBodiesByHashV1";
317
- assertReqSizeLimit(blockHashes.length, 32);
318
- const response = await this.rpc.fetchWithRetries({ method, params: [blockHashes] }, getPayloadBodiesByHashOpts);
319
- return response.map(deserializeExecutionPayloadBody);
320
- }
321
245
  async getPayloadBodiesByHashV2(blockHashes) {
322
- const method = "engine_getPayloadBodiesByHashV2";
323
- assertReqSizeLimit(blockHashes.length, 32);
324
- const response = await this.rpc.fetchWithRetries({ method, params: [blockHashes] }, getPayloadBodiesByHashOpts);
325
- return response.map(deserializeExecutionPayloadBodyV2);
326
- }
327
- async getPayloadBodiesByRange(_fork, startBlockNumber, blockCount) {
328
- const method = "engine_getPayloadBodiesByRangeV1";
329
- assertReqSizeLimit(blockCount, 32);
330
- const start = numToQuantity(startBlockNumber);
331
- const count = numToQuantity(blockCount);
332
- const response = await this.rpc.fetchWithRetries({ method, params: [start, count] }, getPayloadBodiesByRangeOpts);
333
- return response.map(deserializeExecutionPayloadBody);
246
+ return this.withTransport(ForkName.gloas, undefined, (transport) => transport.getPayloadBodiesByHashV2(blockHashes));
334
247
  }
335
248
  async getBlobs(fork, versionedHashes, buffers) {
336
- assertReqSizeLimit(versionedHashes.length, MAX_VERSIONED_HASHES);
337
- const versionedHashesHex = versionedHashes.map(bytesToData);
338
249
  if (isForkPostFulu(fork)) {
339
- return await this.getBlobsV2(versionedHashesHex, buffers);
250
+ return this.withTransport(undefined, "v2", (transport) => transport.getBlobsV2(versionedHashes, buffers));
340
251
  }
341
- return await this.getBlobsV1(versionedHashesHex);
252
+ return this.withTransport(undefined, "v1", (transport) => transport.getBlobsV1(versionedHashes));
342
253
  }
343
- async getBlobsV1(versionedHashesHex) {
344
- const response = await this.rpc.fetchWithRetries({
345
- method: "engine_getBlobsV1",
346
- params: [versionedHashesHex],
347
- }, getBlobsV1Opts);
348
- const invalidLength = response.length !== versionedHashesHex.length;
349
- if (invalidLength) {
350
- const error = `Invalid engine_getBlobsV1 response length=${response.length} versionedHashes=${versionedHashesHex.length}`;
351
- this.logger.error(error);
352
- throw Error(error);
353
- }
354
- return response.map(deserializeBlobAndProofs);
254
+ enqueue(job) {
255
+ return this.queue.push(job);
355
256
  }
356
- async getBlobsV2(versionedHashesHex, buffers) {
357
- if (buffers) {
358
- // Callers preallocate one buffer per max blobs of the epoch, only the first entries are used
359
- if (buffers.length < versionedHashesHex.length) {
360
- throw Error(`Not enough buffers length=${buffers.length} versionedHashes=${versionedHashesHex.length}`);
257
+ /**
258
+ * Run a call on the selected transport. An execution client that advertises a fork in its
259
+ * capabilities but rejects it with `unsupported-fork` is not spec compliant, keep the node
260
+ * functional by serving that fork over JSON-RPC from then on.
261
+ */
262
+ async withTransport(fork, blobsRevision, fn) {
263
+ const transport = await this.getTransport(fork, blobsRevision);
264
+ this.metrics?.engineApiRequests.inc({ transport: transport === this.rest ? "ssz" : "json-rpc" });
265
+ try {
266
+ return await fn(transport);
267
+ }
268
+ catch (e) {
269
+ if (fork !== undefined && transport === this.rest && this.engineApi === "auto" && isUnsupportedForkError(e)) {
270
+ this.disableRestForFork(fork, e);
271
+ this.metrics?.engineApiRequests.inc({ transport: "json-rpc" });
272
+ return fn(this.jsonRpc);
273
+ }
274
+ if (transport === this.rest) {
275
+ this.updateEngineState(getExecutionEngineState({ payloadError: e, oldState: this.state }), e);
361
276
  }
362
- for (const [i, buffer] of buffers.entries()) {
363
- if (buffer.length !== BLOB_AND_PROOF_V2_RPC_BYTES) {
364
- throw Error(`Invalid buffer[${i}] length=${buffer.length} expected=${BLOB_AND_PROOF_V2_RPC_BYTES}`);
277
+ throw e;
278
+ }
279
+ }
280
+ disableRestForFork(fork, e) {
281
+ const executionFork = isForkPostBellatrix(fork) ? executionForkName[fork] : null;
282
+ if (this.restSupport.state === "supported" && executionFork !== null) {
283
+ this.restSupport.capabilities.supportedForks.delete(executionFork);
284
+ }
285
+ if (!this.loggedRestFallbacks.has(fork)) {
286
+ this.loggedRestFallbacks.add(fork);
287
+ this.logger.debug("REST engine API rejected an advertised fork, using JSON-RPC until reconnect", {
288
+ fork,
289
+ executionFork,
290
+ status: e.status,
291
+ type: e.type,
292
+ detail: e.detail,
293
+ });
294
+ }
295
+ }
296
+ /**
297
+ * Pick the transport for a call. Fork-scoped calls only go over REST if the execution client
298
+ * advertises that fork, blob requests only if it serves the needed `/blobs/vN` revision.
299
+ */
300
+ async getTransport(fork, blobsRevision) {
301
+ if (this.rest === null || this.engineApi === "json-rpc") {
302
+ return this.jsonRpc;
303
+ }
304
+ const restSupport = await this.probeRestSupport(this.rest);
305
+ if (restSupport.state === "pending" && restSupport.error) {
306
+ this.updateEngineState(getExecutionEngineState({ payloadError: restSupport.error, oldState: this.state }), restSupport.error);
307
+ throw restSupport.error;
308
+ }
309
+ if (this.engineApi === "ssz") {
310
+ return this.rest;
311
+ }
312
+ if (restSupport.state !== "supported") {
313
+ return this.jsonRpc;
314
+ }
315
+ if (fork !== undefined) {
316
+ const executionFork = isForkPostBellatrix(fork) ? executionForkName[fork] : null;
317
+ if (executionFork === null || !restSupport.capabilities.supportedForks.has(executionFork)) {
318
+ if (!this.loggedRestFallbacks.has(fork)) {
319
+ this.loggedRestFallbacks.add(fork);
320
+ this.logger.debug("Using JSON-RPC for a fork not advertised by the REST engine API", { fork, executionFork });
365
321
  }
322
+ return this.jsonRpc;
366
323
  }
367
324
  }
368
- const response = await this.rpc.fetchWithRetries({
369
- method: "engine_getBlobsV2",
370
- params: [versionedHashesHex],
371
- }, getBlobsV2Opts);
372
- // engine_getBlobsV2 does not return partial responses. It returns null if any blob is not found
373
- const invalidLength = !!response && response.length !== versionedHashesHex.length;
374
- if (invalidLength) {
375
- const error = `Invalid engine_getBlobsV2 response length=${response?.length ?? "null"} versionedHashes=${versionedHashesHex.length}`;
376
- this.logger.error(error);
377
- throw Error(error);
325
+ if (blobsRevision !== undefined && !restSupport.capabilities.blobsRevisions.has(blobsRevision)) {
326
+ if (!this.loggedRestFallbacks.has(blobsRevision)) {
327
+ this.loggedRestFallbacks.add(blobsRevision);
328
+ this.logger.debug("Using JSON-RPC for a blob revision not advertised by the REST engine API", { blobsRevision });
329
+ }
330
+ return this.jsonRpc;
378
331
  }
379
- if (response == null) {
380
- return null;
332
+ return this.rest;
333
+ }
334
+ probeRestSupport(rest) {
335
+ if (this.restSupport.state !== "pending") {
336
+ return Promise.resolve(this.restSupport);
337
+ }
338
+ if (this.restProbe !== null) {
339
+ return this.restProbe;
381
340
  }
382
- if (buffers) {
383
- // getBlobsV2() is designed to called once per slot so we expect to have buffers
384
- return response.map((data, i) => deserializeBlobAndProofsV2IntoBytes(data, buffers[i]));
341
+ // Transport errors keep the probe pending, do not retry on every call while the EL is down
342
+ if (Date.now() - this.lastRestProbeMs < REST_PROBE_RETRY_MS) {
343
+ return Promise.resolve(this.restSupport);
385
344
  }
386
- return response.map(deserializeBlobAndProofsV2);
345
+ this.restProbe = rest
346
+ .getCapabilities()
347
+ .then((capabilities) => {
348
+ this.restSupport = { state: "supported", capabilities };
349
+ this.loggedRestFallbacks.clear();
350
+ this.logger.debug("Discovered REST engine API capabilities", {
351
+ supportedForks: Array.from(capabilities.supportedForks).join(","),
352
+ blobsRevisions: Array.from(capabilities.blobsRevisions).join(","),
353
+ ...capabilities.limits,
354
+ });
355
+ return this.restSupport;
356
+ }, (e) => {
357
+ if (this.engineApi === "auto" && isRestApiAbsent(e)) {
358
+ this.restSupport = { state: "unsupported" };
359
+ this.logger.debug("Execution client does not support engine API over REST, using JSON-RPC", {
360
+ status: e.status,
361
+ type: e.type ?? "unknown",
362
+ });
363
+ }
364
+ else if (this.engineApi === "auto" && e instanceof EngineRestResponseError) {
365
+ // The REST API answered but cannot be trusted, auto mode must keep the node on a working transport
366
+ this.restSupport = { state: "unsupported" };
367
+ this.logger.warn("Invalid engine API capabilities, using JSON-RPC until reconnect", {}, e);
368
+ }
369
+ else {
370
+ const transient = isRetryableEngineRestError(e);
371
+ this.restSupport = { state: "pending", error: this.engineApi === "auto" && transient ? undefined : e };
372
+ this.logger.debug("Unable to probe engine API capabilities", {
373
+ engineApi: this.engineApi,
374
+ fallback: this.restSupport.error ? "none" : "json-rpc",
375
+ retryAfterMs: REST_PROBE_RETRY_MS,
376
+ }, e);
377
+ }
378
+ return this.restSupport;
379
+ })
380
+ .finally(() => {
381
+ this.restProbe = null;
382
+ this.lastRestProbeMs = Date.now();
383
+ });
384
+ return this.restProbe;
387
385
  }
388
386
  async getClientVersion(clientVersion) {
389
- const method = "engine_getClientVersionV1";
390
- const response = await this.rpc.fetchWithRetries({ method, params: [{ ...clientVersion, commit: `0x${clientVersion.commit}` }] }, getClientVersionOpts);
391
- const clientVersions = response.map((cv) => {
392
- const code = cv.code in ClientCode ? ClientCode[cv.code] : ClientCode.XX;
393
- return { code, name: cv.name, version: cv.version, commit: strip0xPrefix(cv.commit) };
394
- });
387
+ const clientVersions = await this.withTransport(undefined, undefined, (transport) => transport.getClientVersion(clientVersion));
395
388
  if (clientVersions.length === 0) {
396
389
  throw Error("Received empty client versions array");
397
390
  }
@@ -399,32 +392,62 @@ export class ExecutionEngineHttp {
399
392
  this.logger.debug("Execution client version updated", this.clientVersion);
400
393
  return clientVersions;
401
394
  }
402
- updateEngineState(newState) {
395
+ updateEngineState(newState, error) {
403
396
  const oldState = this.state;
404
397
  if (oldState === newState)
405
398
  return;
406
399
  switch (newState) {
407
400
  case ExecutionEngineState.ONLINE:
408
401
  this.logger.info("Execution client became online", { oldState, newState });
402
+ if (oldState === ExecutionEngineState.AUTH_FAILED) {
403
+ this.restSupport = { state: "pending" };
404
+ this.lastRestProbeMs = Number.NEGATIVE_INFINITY;
405
+ }
409
406
  this.getClientVersion(getLodestarClientVersion(this.opts)).catch((e) => {
410
407
  this.logger.debug("Unable to get execution client version", {}, e);
411
408
  this.clientVersion = null;
412
409
  });
413
410
  break;
414
411
  case ExecutionEngineState.OFFLINE:
415
- this.logger.error("Execution client went offline", { oldState, newState });
412
+ // Reprobe before choosing a transport for the next call after a disconnect.
413
+ this.restSupport = { state: "pending" };
414
+ this.lastRestProbeMs = Number.NEGATIVE_INFINITY;
415
+ this.logger.error("Execution client went offline", { oldState, newState }, error);
416
416
  break;
417
417
  case ExecutionEngineState.SYNCED:
418
418
  this.logger.info("Execution client is synced", { oldState, newState });
419
419
  break;
420
420
  case ExecutionEngineState.SYNCING:
421
- this.logger.warn("Execution client is syncing", { oldState, newState });
421
+ this.logger.warn(error ? "Execution client request failed" : "Execution client is syncing", { oldState, newState }, error);
422
422
  break;
423
423
  case ExecutionEngineState.AUTH_FAILED:
424
- this.logger.error("Execution client authentication failed", { oldState, newState });
424
+ this.logger.error("Execution client authentication failed", { oldState, newState }, error);
425
425
  break;
426
426
  }
427
427
  this.state = newState;
428
428
  }
429
429
  }
430
+ /** The execution client answered, as opposed to being unreachable */
431
+ function isEngineResponseError(e) {
432
+ return (e instanceof HttpRpcError ||
433
+ e instanceof ErrorJsonRpcResponse ||
434
+ e instanceof EngineRestError ||
435
+ e instanceof EngineRestResponseError);
436
+ }
437
+ /**
438
+ * Legacy JSON-RPC servers and proxies reject the capabilities probe in different ways, a 404 or
439
+ * another client error such as 405. Neither serves the REST API. Authentication failures and
440
+ * transient client errors say nothing about REST support.
441
+ */
442
+ function isRestApiAbsent(e) {
443
+ return (e instanceof EngineRestError &&
444
+ e.status >= 400 &&
445
+ e.status < 500 &&
446
+ e.status !== 401 &&
447
+ e.status !== 403 &&
448
+ !isRetryableEngineRestError(e));
449
+ }
450
+ function isUnsupportedForkError(e) {
451
+ return e instanceof EngineRestError && e.status === 400 && e.type === "/engine-api/errors/unsupported-fork";
452
+ }
430
453
  //# sourceMappingURL=http.js.map