@lodestar/beacon-node 1.50.0-dev.fef46df5ba → 1.50.0-rc.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.
- package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +6 -1
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/index.js +13 -0
- package/lib/api/impl/beacon/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +4 -4
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +28 -6
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +26 -18
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts +1 -0
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +49 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +5 -0
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +14 -24
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +2 -2
- package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -7
- package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
- package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
- package/lib/chain/archiveStore/interface.d.ts +1 -1
- package/lib/chain/archiveStore/interface.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +2 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/beaconProposerCache.d.ts +6 -0
- package/lib/chain/beaconProposerCache.d.ts.map +1 -1
- package/lib/chain/beaconProposerCache.js +16 -0
- package/lib/chain/beaconProposerCache.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +18 -8
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +7 -7
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +5 -4
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/chain.d.ts +10 -5
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +105 -36
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +7 -1
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +5 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
- package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.js +2 -0
- package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
- package/lib/chain/errors/gossipValidation.d.ts +3 -1
- package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.js +2 -0
- package/lib/chain/errors/gossipValidation.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +2 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +5 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts +4 -2
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +30 -8
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/opPool.d.ts +5 -4
- package/lib/chain/opPools/opPool.d.ts.map +1 -1
- package/lib/chain/opPools/opPool.js +60 -38
- package/lib/chain/opPools/opPool.js.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +8 -4
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +27 -12
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +15 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +33 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/options.d.ts +2 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -1
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +16 -5
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +11 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +46 -30
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +2 -0
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +10 -4
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts +7 -0
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.js +21 -0
- package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/shufflingCache.d.ts +18 -8
- package/lib/chain/shufflingCache.d.ts.map +1 -1
- package/lib/chain/shufflingCache.js +72 -18
- package/lib/chain/shufflingCache.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts +4 -4
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +3 -0
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.d.ts +3 -3
- package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +3 -0
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +5 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +64 -54
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/db/beacon.d.ts +4 -1
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +6 -1
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/buckets.d.ts +3 -1
- package/lib/db/buckets.d.ts.map +1 -1
- package/lib/db/buckets.js +2 -0
- package/lib/db/buckets.js.map +1 -1
- package/lib/db/interface.d.ts +4 -1
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
- package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
- package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
- package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
- package/lib/db/repositories/earliestAvailableSlot.js +19 -0
- package/lib/db/repositories/earliestAvailableSlot.js.map +1 -0
- package/lib/db/repositories/index.d.ts +1 -0
- package/lib/db/repositories/index.d.ts.map +1 -1
- package/lib/db/repositories/index.js +1 -0
- package/lib/db/repositories/index.js.map +1 -1
- package/lib/db/repositories/proposerPreferences.d.ts +14 -0
- package/lib/db/repositories/proposerPreferences.d.ts.map +1 -0
- package/lib/db/repositories/proposerPreferences.js +21 -0
- package/lib/db/repositories/proposerPreferences.js.map +1 -0
- package/lib/db/repositories/proposerSlashing.d.ts +1 -0
- package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
- package/lib/db/repositories/proposerSlashing.js +5 -1
- package/lib/db/repositories/proposerSlashing.js.map +1 -1
- package/lib/execution/engine/blobVersionedHashes.d.ts +9 -0
- package/lib/execution/engine/blobVersionedHashes.d.ts.map +1 -0
- package/lib/execution/engine/blobVersionedHashes.js +74 -0
- package/lib/execution/engine/blobVersionedHashes.js.map +1 -0
- package/lib/execution/engine/disabled.d.ts +0 -2
- package/lib/execution/engine/disabled.d.ts.map +1 -1
- package/lib/execution/engine/disabled.js +0 -6
- package/lib/execution/engine/disabled.js.map +1 -1
- package/lib/execution/engine/http.d.ts +46 -16
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +236 -213
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/index.d.ts +3 -2
- package/lib/execution/engine/index.d.ts.map +1 -1
- package/lib/execution/engine/index.js +33 -8
- package/lib/execution/engine/index.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +2 -4
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/jsonRpcTransport.d.ts +25 -0
- package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -0
- package/lib/execution/engine/jsonRpcTransport.js +158 -0
- package/lib/execution/engine/jsonRpcTransport.js.map +1 -0
- package/lib/execution/engine/mock.d.ts +0 -2
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +0 -8
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/restHttpClient.d.ts +71 -0
- package/lib/execution/engine/restHttpClient.d.ts.map +1 -0
- package/lib/execution/engine/restHttpClient.js +201 -0
- package/lib/execution/engine/restHttpClient.js.map +1 -0
- package/lib/execution/engine/restTransport.d.ts +46 -0
- package/lib/execution/engine/restTransport.d.ts.map +1 -0
- package/lib/execution/engine/restTransport.js +399 -0
- package/lib/execution/engine/restTransport.js.map +1 -0
- package/lib/execution/engine/sszTypes.d.ts +1261 -0
- package/lib/execution/engine/sszTypes.d.ts.map +1 -0
- package/lib/execution/engine/sszTypes.js +216 -0
- package/lib/execution/engine/sszTypes.js.map +1 -0
- package/lib/execution/engine/transport.d.ts +39 -0
- package/lib/execution/engine/transport.d.ts.map +1 -0
- package/lib/execution/engine/transport.js +2 -0
- package/lib/execution/engine/transport.js.map +1 -0
- package/lib/execution/engine/types.d.ts +2 -8
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +24 -57
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/execution/engine/utils.d.ts.map +1 -1
- package/lib/execution/engine/utils.js +9 -2
- package/lib/execution/engine/utils.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +10 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +22 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/metrics/metrics.d.ts +7 -3
- package/lib/metrics/metrics.d.ts.map +1 -1
- package/lib/metrics/metrics.js +4 -4
- package/lib/metrics/metrics.js.map +1 -1
- package/lib/network/core/events.d.ts +5 -1
- package/lib/network/core/events.d.ts.map +1 -1
- package/lib/network/core/events.js +8 -0
- package/lib/network/core/events.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +67 -68
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +7 -3
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/lib/network/processor/index.d.ts +9 -0
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +77 -10
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
- package/lib/node/nodejs.d.ts +3 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +27 -4
- package/lib/node/nodejs.js.map +1 -1
- package/lib/node/options.d.ts +3 -2
- package/lib/node/options.d.ts.map +1 -1
- package/lib/node/options.js +2 -2
- package/lib/node/options.js.map +1 -1
- package/lib/sync/range/batch.d.ts +11 -5
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +21 -17
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +4 -4
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +12 -6
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +75 -29
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +5 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +49 -24
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +38 -14
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +84 -39
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/util/asyncIterableToEvents.d.ts +10 -2
- package/lib/util/asyncIterableToEvents.d.ts.map +1 -1
- package/lib/util/asyncIterableToEvents.js +29 -3
- package/lib/util/asyncIterableToEvents.js.map +1 -1
- package/lib/util/execution.d.ts +9 -11
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +5 -1
- package/lib/util/execution.js.map +1 -1
- package/lib/util/metadata.d.ts.map +1 -1
- package/lib/util/metadata.js +2 -1
- package/lib/util/metadata.js.map +1 -1
- package/package.json +18 -17
- package/src/api/impl/beacon/blocks/index.ts +7 -4
- package/src/api/impl/beacon/index.ts +15 -0
- package/src/api/impl/beacon/pool/index.ts +4 -4
- package/src/api/impl/beacon/state/index.ts +43 -6
- package/src/api/impl/debug/index.ts +28 -19
- package/src/api/impl/validator/index.ts +61 -2
- package/src/chain/archiveStore/archiveStore.ts +19 -25
- package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -7
- package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -1
- package/src/chain/archiveStore/historicalState/types.ts +2 -2
- package/src/chain/archiveStore/historicalState/worker.ts +1 -2
- package/src/chain/archiveStore/interface.ts +1 -1
- package/src/chain/archiveStore/utils/pruneHistory.ts +4 -1
- package/src/chain/beaconProposerCache.ts +19 -1
- package/src/chain/blocks/importBlock.ts +21 -7
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
- package/src/chain/blocks/verifyBlock.ts +8 -6
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +6 -3
- package/src/chain/chain.ts +135 -38
- package/src/chain/emitter.ts +7 -1
- package/src/chain/errors/envelopeReconstructionError.ts +3 -0
- package/src/chain/errors/gossipValidation.ts +2 -0
- package/src/chain/forkChoice/index.ts +2 -0
- package/src/chain/interface.ts +5 -2
- package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
- package/src/chain/opPools/opPool.ts +89 -46
- package/src/chain/opPools/proposerPreferencesPool.ts +34 -11
- package/src/chain/opPools/utils.ts +62 -3
- package/src/chain/options.ts +3 -3
- package/src/chain/prepareNextSlot.ts +21 -6
- package/src/chain/produceBlock/computeNewStateRoot.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +76 -35
- package/src/chain/regen/regen.ts +12 -4
- package/src/chain/seenCache/seenGossipBlockInput.ts +23 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
- package/src/chain/shufflingCache.ts +84 -22
- package/src/chain/validation/attesterSlashing.ts +7 -4
- package/src/chain/validation/proposerSlashing.ts +7 -4
- package/src/chain/validatorMonitor.ts +75 -57
- package/src/db/beacon.ts +6 -0
- package/src/db/buckets.ts +2 -0
- package/src/db/interface.ts +4 -0
- package/src/db/repositories/earliestAvailableSlot.ts +19 -0
- package/src/db/repositories/index.ts +1 -0
- package/src/db/repositories/proposerPreferences.ts +23 -0
- package/src/db/repositories/proposerSlashing.ts +6 -1
- package/src/execution/engine/blobVersionedHashes.ts +72 -0
- package/src/execution/engine/disabled.ts +0 -8
- package/src/execution/engine/http.ts +304 -300
- package/src/execution/engine/index.ts +41 -7
- package/src/execution/engine/interface.ts +2 -6
- package/src/execution/engine/jsonRpcTransport.ts +247 -0
- package/src/execution/engine/mock.ts +1 -19
- package/src/execution/engine/restHttpClient.ts +269 -0
- package/src/execution/engine/restTransport.ts +544 -0
- package/src/execution/engine/sszTypes.ts +354 -0
- package/src/execution/engine/transport.ts +61 -0
- package/src/execution/engine/types.ts +34 -84
- package/src/execution/engine/utils.ts +12 -2
- package/src/metrics/metrics/lodestar.ts +23 -0
- package/src/metrics/metrics.ts +21 -6
- package/src/network/core/events.ts +16 -1
- package/src/network/processor/gossipHandlers.ts +98 -81
- package/src/network/processor/gossipValidatorFn.ts +13 -3
- package/src/network/processor/index.ts +89 -10
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +1 -1
- package/src/node/nodejs.ts +35 -3
- package/src/node/options.ts +10 -1
- package/src/sync/range/batch.ts +25 -19
- package/src/sync/range/chain.ts +15 -8
- package/src/sync/range/range.ts +76 -30
- package/src/sync/unknownBlock.ts +62 -25
- package/src/sync/utils/downloadByRange.ts +123 -58
- package/src/util/asyncIterableToEvents.ts +44 -5
- package/src/util/execution.ts +16 -8
- package/src/util/metadata.ts +2 -1
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import {Logger} from "@lodestar/logger";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ForkName,
|
|
4
|
+
ForkPostFulu,
|
|
5
|
+
ForkPreFulu,
|
|
6
|
+
ForkSeq,
|
|
7
|
+
SLOTS_PER_EPOCH,
|
|
8
|
+
isForkPostBellatrix,
|
|
9
|
+
isForkPostFulu,
|
|
10
|
+
} from "@lodestar/params";
|
|
3
11
|
import {BlobsBundle, ExecutionPayload, ExecutionRequests, Root, RootHex, Wei} from "@lodestar/types";
|
|
4
12
|
import {BlobAndProof} from "@lodestar/types/deneb";
|
|
5
13
|
import {BlobAndProofV2} from "@lodestar/types/fulu";
|
|
6
|
-
import {
|
|
14
|
+
import {isErrorAborted} from "@lodestar/utils";
|
|
7
15
|
import {Metrics} from "../../metrics/index.js";
|
|
8
16
|
import {EPOCHS_PER_BATCH} from "../../sync/constants.js";
|
|
9
17
|
import {getLodestarClientVersion} from "../../util/metadata.js";
|
|
18
|
+
import {isQueueErrorAborted} from "../../util/queue/errors.js";
|
|
10
19
|
import {JobItemQueue} from "../../util/queue/index.js";
|
|
11
20
|
import {
|
|
12
|
-
ClientCode,
|
|
13
21
|
ClientVersion,
|
|
14
22
|
ExecutePayloadResponse,
|
|
15
23
|
ExecutionEngineState,
|
|
@@ -19,34 +27,14 @@ import {
|
|
|
19
27
|
PayloadId,
|
|
20
28
|
VersionedHashes,
|
|
21
29
|
} from "./interface.js";
|
|
22
|
-
import {
|
|
23
|
-
ErrorJsonRpcResponse,
|
|
24
|
-
HttpRpcError,
|
|
25
|
-
IJsonRpcHttpClient,
|
|
26
|
-
JsonRpcHttpClientEvent,
|
|
27
|
-
ReqOpts,
|
|
28
|
-
} from "./jsonRpcHttpClient.js";
|
|
30
|
+
import {ErrorJsonRpcResponse, HttpRpcError, JsonRpcHttpClientEvent} from "./jsonRpcHttpClient.js";
|
|
29
31
|
import {PayloadIdCache} from "./payloadIdCache.js";
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
assertReqSizeLimit,
|
|
37
|
-
deserializeBlobAndProofs,
|
|
38
|
-
deserializeBlobAndProofsV2,
|
|
39
|
-
deserializeBlobAndProofsV2IntoBytes,
|
|
40
|
-
deserializeExecutionPayloadBody,
|
|
41
|
-
deserializeExecutionPayloadBodyV2,
|
|
42
|
-
parseExecutionPayload,
|
|
43
|
-
serializeBeaconBlockRoot,
|
|
44
|
-
serializeExecutionPayload,
|
|
45
|
-
serializeExecutionRequests,
|
|
46
|
-
serializePayloadAttributes,
|
|
47
|
-
serializeVersionedHashes,
|
|
48
|
-
} from "./types.js";
|
|
49
|
-
import {bytesToData, getExecutionEngineState, numToQuantity} from "./utils.js";
|
|
32
|
+
import {EngineRestError, EngineRestResponseError, isRetryableEngineRestError} from "./restHttpClient.js";
|
|
33
|
+
import {EngineCapabilities, RestEngineTransport} from "./restTransport.js";
|
|
34
|
+
import {executionForkName} from "./sszTypes.js";
|
|
35
|
+
import {IEngineTransport, PayloadStatusResult} from "./transport.js";
|
|
36
|
+
import {ExecutionPayloadBodyV2, serializePayloadAttributes} from "./types.js";
|
|
37
|
+
import {getExecutionEngineState} from "./utils.js";
|
|
50
38
|
|
|
51
39
|
export type ExecutionEngineModules = {
|
|
52
40
|
signal: AbortSignal;
|
|
@@ -54,11 +42,21 @@ export type ExecutionEngineModules = {
|
|
|
54
42
|
logger: Logger;
|
|
55
43
|
};
|
|
56
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Engine API transport selection
|
|
47
|
+
* - `auto`: negotiate REST with one execution URL; use JSON-RPC for multiple URLs or unadvertised features
|
|
48
|
+
* - `ssz`: always use the REST API with SSZ encoding
|
|
49
|
+
* - `json-rpc`: always use the legacy JSON-RPC API
|
|
50
|
+
*/
|
|
51
|
+
export type EngineApiMode = "auto" | "ssz" | "json-rpc";
|
|
52
|
+
export const engineApiModes: EngineApiMode[] = ["auto", "ssz", "json-rpc"];
|
|
53
|
+
|
|
57
54
|
export type ExecutionEngineHttpOpts = {
|
|
58
55
|
urls: string[];
|
|
59
56
|
retries: number;
|
|
60
57
|
retryDelay: number;
|
|
61
58
|
timeout?: number;
|
|
59
|
+
engineApi?: EngineApiMode;
|
|
62
60
|
/**
|
|
63
61
|
* 256 bit jwt secret in hex format without the leading 0x. If provided, the execution engine
|
|
64
62
|
* rpc requests will be bundled by an authorization header having a fresh jwt token on each
|
|
@@ -94,39 +92,37 @@ export const defaultExecutionEngineHttpOpts: ExecutionEngineHttpOpts = {
|
|
|
94
92
|
retries: 2,
|
|
95
93
|
retryDelay: 2000,
|
|
96
94
|
timeout: 12000,
|
|
95
|
+
engineApi: "auto",
|
|
97
96
|
};
|
|
98
97
|
|
|
98
|
+
export type ExecutionEngineTransports = {
|
|
99
|
+
jsonRpc: IEngineTransport;
|
|
100
|
+
rest?: RestEngineTransport;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type RestSupport =
|
|
104
|
+
| {state: "pending"; error?: Error}
|
|
105
|
+
| {state: "unsupported"}
|
|
106
|
+
| {state: "supported"; capabilities: EngineCapabilities};
|
|
107
|
+
|
|
99
108
|
/**
|
|
100
109
|
* Size for the serializing queue for fcUs and new payloads, the max length could be equal to
|
|
101
110
|
* EPOCHS_PER_BATCH * 2 in case new payloads are also not awaited serially
|
|
102
111
|
*/
|
|
103
112
|
const QUEUE_MAX_LENGTH = EPOCHS_PER_BATCH * SLOTS_PER_EPOCH * 2;
|
|
104
113
|
|
|
105
|
-
/**
|
|
106
|
-
|
|
107
|
-
* Clients must support at least 128 versionedHashes, so we avoid sending more
|
|
108
|
-
* https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#specification-3
|
|
109
|
-
*/
|
|
110
|
-
const MAX_VERSIONED_HASHES = 128;
|
|
111
|
-
|
|
112
|
-
// Define static options once to prevent extra allocations
|
|
113
|
-
const notifyNewPayloadOpts: ReqOpts = {routeId: "notifyNewPayload"};
|
|
114
|
-
const forkchoiceUpdatedV1Opts: ReqOpts = {routeId: "forkchoiceUpdated"};
|
|
115
|
-
const getPayloadOpts: ReqOpts = {routeId: "getPayload"};
|
|
116
|
-
const getPayloadBodiesByHashOpts: ReqOpts = {routeId: "getPayloadBodiesByHash"};
|
|
117
|
-
const getPayloadBodiesByRangeOpts: ReqOpts = {routeId: "getPayloadBodiesByRange"};
|
|
118
|
-
const getBlobsV1Opts: ReqOpts = {routeId: "getBlobsV1"};
|
|
119
|
-
const getBlobsV2Opts: ReqOpts = {routeId: "getBlobsV2"};
|
|
120
|
-
const getClientVersionOpts: ReqOpts = {routeId: "getClientVersion"};
|
|
114
|
+
/** Minimum delay between capabilities probes while the execution client is unreachable */
|
|
115
|
+
const REST_PROBE_RETRY_MS = 12_000;
|
|
121
116
|
|
|
122
117
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* -
|
|
126
|
-
* - Error codes improvement proposal
|
|
118
|
+
* Drives the execution client over the engine API, either the legacy `engine_*` JSON-RPC methods
|
|
119
|
+
* or the REST API with SSZ encoded bodies.
|
|
120
|
+
* https://github.com/ethereum/execution-apis/tree/main/src/engine
|
|
127
121
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
122
|
+
* In `auto` mode a capabilities response from a server without the REST API, a 4xx other than
|
|
123
|
+
* 401/403, selects JSON-RPC until the EL reconnects. Transient discovery failures use JSON-RPC while
|
|
124
|
+
* awaiting another probe; authentication failures fail visibly, malformed capabilities fall back to
|
|
125
|
+
* JSON-RPC with a warning. Forks and blob revisions the EL does not advertise also use JSON-RPC.
|
|
130
126
|
*/
|
|
131
127
|
export class ExecutionEngineHttp implements IExecutionEngine {
|
|
132
128
|
private logger: Logger;
|
|
@@ -150,42 +146,55 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
150
146
|
* the order of new payloads and fcUs is pretty important to EL, this queue will serialize the calls in the
|
|
151
147
|
* order with which we make them.
|
|
152
148
|
*/
|
|
153
|
-
private readonly
|
|
149
|
+
private readonly queue: JobItemQueue<[() => Promise<unknown>], unknown>;
|
|
154
150
|
|
|
155
|
-
private
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
151
|
+
private readonly jsonRpc: IEngineTransport;
|
|
152
|
+
private readonly rest: RestEngineTransport | null;
|
|
153
|
+
private readonly engineApi: EngineApiMode;
|
|
154
|
+
private restSupport: RestSupport = {state: "pending"};
|
|
155
|
+
private restProbe: Promise<RestSupport> | null = null;
|
|
156
|
+
private lastRestProbeMs = Number.NEGATIVE_INFINITY;
|
|
157
|
+
private readonly loggedRestFallbacks = new Set<ForkName | "v1" | "v2">();
|
|
161
158
|
|
|
162
159
|
constructor(
|
|
163
|
-
|
|
160
|
+
{jsonRpc, rest}: ExecutionEngineTransports,
|
|
164
161
|
{metrics, signal, logger}: ExecutionEngineModules,
|
|
165
162
|
private readonly opts?: ExecutionEngineHttpOpts
|
|
166
163
|
) {
|
|
167
|
-
this.
|
|
168
|
-
|
|
164
|
+
this.queue = new JobItemQueue<[() => Promise<unknown>], unknown>(
|
|
165
|
+
(job) => job(),
|
|
169
166
|
{maxLength: QUEUE_MAX_LENGTH, maxConcurrency: 1, noYieldIfOneItem: true, signal},
|
|
170
167
|
metrics?.engineHttpProcessorQueue
|
|
171
168
|
);
|
|
172
169
|
this.logger = logger;
|
|
173
170
|
this.metrics = metrics ?? null;
|
|
171
|
+
this.jsonRpc = jsonRpc;
|
|
172
|
+
this.rest = rest ?? null;
|
|
173
|
+
this.engineApi = opts?.engineApi ?? "auto";
|
|
174
174
|
|
|
175
|
-
this.
|
|
176
|
-
|
|
177
|
-
}
|
|
175
|
+
if (this.engineApi === "ssz" && this.rest === null) {
|
|
176
|
+
throw Error("REST transport is required for engineApi=ssz");
|
|
177
|
+
}
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
// This statement should only be called first time receiving response after startup
|
|
183
|
-
this.getClientVersion(getLodestarClientVersion(this.opts)).catch((e) => {
|
|
184
|
-
this.logger.debug("Unable to get execution client version", {}, e);
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
this.updateEngineState(getExecutionEngineState({targetState: ExecutionEngineState.ONLINE, oldState: this.state}));
|
|
179
|
+
// REST errors are handled in withTransport after compatibility fallback.
|
|
180
|
+
this.jsonRpc.emitter.on(JsonRpcHttpClientEvent.ERROR, ({error}) => {
|
|
181
|
+
this.updateEngineState(getExecutionEngineState({payloadError: error, oldState: this.state}), error);
|
|
188
182
|
});
|
|
183
|
+
|
|
184
|
+
for (const transport of [this.jsonRpc, this.rest]) {
|
|
185
|
+
transport?.emitter.on(JsonRpcHttpClientEvent.RESPONSE, () => {
|
|
186
|
+
if (this.clientVersion === undefined) {
|
|
187
|
+
this.clientVersion = null;
|
|
188
|
+
// This statement should only be called first time receiving response after startup
|
|
189
|
+
this.getClientVersion(getLodestarClientVersion(this.opts)).catch((e) => {
|
|
190
|
+
this.logger.debug("Unable to get execution client version", {}, e);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
this.updateEngineState(
|
|
194
|
+
getExecutionEngineState({targetState: ExecutionEngineState.ONLINE, oldState: this.state})
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
189
198
|
}
|
|
190
199
|
|
|
191
200
|
/**
|
|
@@ -217,20 +226,7 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
217
226
|
parentBlockRoot?: Root,
|
|
218
227
|
executionRequests?: ExecutionRequests
|
|
219
228
|
): Promise<ExecutePayloadResponse> {
|
|
220
|
-
|
|
221
|
-
ForkSeq[fork] >= ForkSeq.gloas
|
|
222
|
-
? "engine_newPayloadV5"
|
|
223
|
-
: ForkSeq[fork] >= ForkSeq.electra
|
|
224
|
-
? "engine_newPayloadV4"
|
|
225
|
-
: ForkSeq[fork] >= ForkSeq.deneb
|
|
226
|
-
? "engine_newPayloadV3"
|
|
227
|
-
: ForkSeq[fork] >= ForkSeq.capella
|
|
228
|
-
? "engine_newPayloadV2"
|
|
229
|
-
: "engine_newPayloadV1";
|
|
230
|
-
|
|
231
|
-
const serializedExecutionPayload = serializeExecutionPayload(fork, executionPayload);
|
|
232
|
-
|
|
233
|
-
let engineRequest: EngineRequest;
|
|
229
|
+
// Validate before queueing, argument errors must not be reported as an unavailable execution client
|
|
234
230
|
if (ForkSeq[fork] >= ForkSeq.deneb) {
|
|
235
231
|
if (versionedHashes === undefined) {
|
|
236
232
|
throw Error(`versionedHashes required in notifyNewPayload for fork=${fork}`);
|
|
@@ -238,49 +234,31 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
238
234
|
if (parentBlockRoot === undefined) {
|
|
239
235
|
throw Error(`parentBlockRoot required in notifyNewPayload for fork=${fork}`);
|
|
240
236
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const parentBeaconBlockRoot = serializeBeaconBlockRoot(parentBlockRoot);
|
|
244
|
-
|
|
245
|
-
if (ForkSeq[fork] >= ForkSeq.electra) {
|
|
246
|
-
if (executionRequests === undefined) {
|
|
247
|
-
throw Error(`executionRequests required in notifyNewPayload for fork=${fork}`);
|
|
248
|
-
}
|
|
249
|
-
const serializedExecutionRequests = serializeExecutionRequests(fork, executionRequests);
|
|
250
|
-
engineRequest = {
|
|
251
|
-
method: ForkSeq[fork] >= ForkSeq.gloas ? "engine_newPayloadV5" : "engine_newPayloadV4",
|
|
252
|
-
params: [
|
|
253
|
-
serializedExecutionPayload,
|
|
254
|
-
serializedVersionedHashes,
|
|
255
|
-
parentBeaconBlockRoot,
|
|
256
|
-
serializedExecutionRequests,
|
|
257
|
-
],
|
|
258
|
-
methodOpts: notifyNewPayloadOpts,
|
|
259
|
-
};
|
|
260
|
-
} else {
|
|
261
|
-
engineRequest = {
|
|
262
|
-
method: "engine_newPayloadV3",
|
|
263
|
-
params: [serializedExecutionPayload, serializedVersionedHashes, parentBeaconBlockRoot],
|
|
264
|
-
methodOpts: notifyNewPayloadOpts,
|
|
265
|
-
};
|
|
237
|
+
if (ForkSeq[fork] >= ForkSeq.electra && executionRequests === undefined) {
|
|
238
|
+
throw Error(`executionRequests required in notifyNewPayload for fork=${fork}`);
|
|
266
239
|
}
|
|
267
|
-
} else {
|
|
268
|
-
const method = ForkSeq[fork] >= ForkSeq.capella ? "engine_newPayloadV2" : "engine_newPayloadV1";
|
|
269
|
-
engineRequest = {
|
|
270
|
-
method,
|
|
271
|
-
params: [serializedExecutionPayload],
|
|
272
|
-
methodOpts: notifyNewPayloadOpts,
|
|
273
|
-
};
|
|
274
240
|
}
|
|
275
241
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
})
|
|
242
|
+
let result: PayloadStatusResult;
|
|
243
|
+
try {
|
|
244
|
+
result = await this.enqueue(() =>
|
|
245
|
+
this.withTransport(fork, undefined, (transport) =>
|
|
246
|
+
transport.newPayload(fork, executionPayload, versionedHashes, parentBlockRoot, executionRequests)
|
|
247
|
+
)
|
|
248
|
+
);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
const status = isEngineResponseError(e as Error)
|
|
251
|
+
? ExecutionPayloadStatus.ELERROR
|
|
252
|
+
: ExecutionPayloadStatus.UNAVAILABLE;
|
|
253
|
+
// Only newPayload treats an unreachable execution client, including a timeout, as offline
|
|
254
|
+
const newState =
|
|
255
|
+
status === ExecutionPayloadStatus.UNAVAILABLE && !isErrorAborted(e) && !isQueueErrorAborted(e)
|
|
256
|
+
? getExecutionEngineState({payloadStatus: status, oldState: this.state})
|
|
257
|
+
: getExecutionEngineState({payloadError: e, oldState: this.state});
|
|
258
|
+
this.updateEngineState(newState, e as Error);
|
|
259
|
+
return {status, latestValidHash: null, validationError: (e as Error).message};
|
|
260
|
+
}
|
|
261
|
+
const {status, latestValidHash, validationError} = result;
|
|
284
262
|
|
|
285
263
|
this.updateEngineState(getExecutionEngineState({payloadStatus: status, oldState: this.state}));
|
|
286
264
|
|
|
@@ -349,32 +327,14 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
349
327
|
finalizedBlockHash: RootHex,
|
|
350
328
|
payloadAttributes?: PayloadAttributes
|
|
351
329
|
): Promise<PayloadId | null> {
|
|
352
|
-
// Once on capella, should this need to be permanently switched to v2 when payload attrs
|
|
353
|
-
// not provided
|
|
354
|
-
const method =
|
|
355
|
-
ForkSeq[fork] >= ForkSeq.gloas
|
|
356
|
-
? "engine_forkchoiceUpdatedV4"
|
|
357
|
-
: ForkSeq[fork] >= ForkSeq.deneb
|
|
358
|
-
? "engine_forkchoiceUpdatedV3"
|
|
359
|
-
: ForkSeq[fork] >= ForkSeq.capella
|
|
360
|
-
? "engine_forkchoiceUpdatedV2"
|
|
361
|
-
: "engine_forkchoiceUpdatedV1";
|
|
362
|
-
const payloadAttributesRpc = payloadAttributes ? serializePayloadAttributes(payloadAttributes) : undefined;
|
|
363
|
-
// If we are just fcUing and not asking execution for payload, retry is not required
|
|
364
|
-
// and we can move on, as the next fcU will be issued soon on the new slot
|
|
365
|
-
const fcUReqOpts =
|
|
366
|
-
payloadAttributes !== undefined ? forkchoiceUpdatedV1Opts : {...forkchoiceUpdatedV1Opts, retries: 0};
|
|
367
|
-
|
|
368
|
-
const request = this.rpcFetchQueue.push({
|
|
369
|
-
method,
|
|
370
|
-
params: [{headBlockHash, safeBlockHash, finalizedBlockHash}, payloadAttributesRpc],
|
|
371
|
-
methodOpts: fcUReqOpts,
|
|
372
|
-
}) as Promise<EngineApiRpcReturnTypes[typeof method]>;
|
|
373
|
-
|
|
374
330
|
const {
|
|
375
|
-
payloadStatus: {status,
|
|
331
|
+
payloadStatus: {status, validationError},
|
|
376
332
|
payloadId,
|
|
377
|
-
} = await
|
|
333
|
+
} = await this.enqueue(() =>
|
|
334
|
+
this.withTransport(fork, undefined, (transport) =>
|
|
335
|
+
transport.forkchoiceUpdated(fork, headBlockHash, safeBlockHash, finalizedBlockHash, payloadAttributes)
|
|
336
|
+
)
|
|
337
|
+
);
|
|
378
338
|
|
|
379
339
|
this.updateEngineState(getExecutionEngineState({payloadStatus: status, oldState: this.state}));
|
|
380
340
|
this.metrics?.engineNotifyForkchoiceUpdateResult.inc({result: status});
|
|
@@ -382,15 +342,16 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
382
342
|
switch (status) {
|
|
383
343
|
case ExecutionPayloadStatus.VALID:
|
|
384
344
|
// if payloadAttributes are provided, a valid payloadId is expected
|
|
385
|
-
if (
|
|
386
|
-
if (
|
|
345
|
+
if (payloadAttributes) {
|
|
346
|
+
if (payloadId === null) {
|
|
387
347
|
throw Error(`Received invalid payloadId=${payloadId}`);
|
|
388
348
|
}
|
|
389
349
|
|
|
350
|
+
const payloadAttributesRpc = serializePayloadAttributes(payloadAttributes);
|
|
390
351
|
this.payloadIdCache.add({headBlockHash, finalizedBlockHash, ...payloadAttributesRpc}, payloadId);
|
|
391
352
|
void this.prunePayloadIdCache();
|
|
392
353
|
}
|
|
393
|
-
return payloadId
|
|
354
|
+
return payloadId;
|
|
394
355
|
|
|
395
356
|
case ExecutionPayloadStatus.SYNCING:
|
|
396
357
|
// Throw error on syncing if requested to produce a block, else silently ignore
|
|
@@ -428,80 +389,17 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
428
389
|
executionRequests?: ExecutionRequests;
|
|
429
390
|
shouldOverrideBuilder?: boolean;
|
|
430
391
|
}> {
|
|
431
|
-
|
|
432
|
-
switch (fork) {
|
|
433
|
-
case ForkName.phase0:
|
|
434
|
-
case ForkName.altair:
|
|
435
|
-
case ForkName.bellatrix:
|
|
436
|
-
method = "engine_getPayloadV1";
|
|
437
|
-
break;
|
|
438
|
-
case ForkName.capella:
|
|
439
|
-
method = "engine_getPayloadV2";
|
|
440
|
-
break;
|
|
441
|
-
case ForkName.deneb:
|
|
442
|
-
method = "engine_getPayloadV3";
|
|
443
|
-
break;
|
|
444
|
-
case ForkName.electra:
|
|
445
|
-
method = "engine_getPayloadV4";
|
|
446
|
-
break;
|
|
447
|
-
case ForkName.fulu:
|
|
448
|
-
method = "engine_getPayloadV5";
|
|
449
|
-
break;
|
|
450
|
-
default:
|
|
451
|
-
method = "engine_getPayloadV6";
|
|
452
|
-
break;
|
|
453
|
-
}
|
|
454
|
-
const payloadResponse = await this.rpc.fetchWithRetries<
|
|
455
|
-
EngineApiRpcReturnTypes[typeof method],
|
|
456
|
-
EngineApiRpcParamTypes[typeof method]
|
|
457
|
-
>(
|
|
458
|
-
{
|
|
459
|
-
method,
|
|
460
|
-
params: [payloadId],
|
|
461
|
-
},
|
|
462
|
-
getPayloadOpts
|
|
463
|
-
);
|
|
464
|
-
return parseExecutionPayload(fork, payloadResponse);
|
|
392
|
+
return this.withTransport(fork, undefined, (transport) => transport.getPayload(fork, payloadId));
|
|
465
393
|
}
|
|
466
394
|
|
|
467
395
|
async prunePayloadIdCache(): Promise<void> {
|
|
468
396
|
this.payloadIdCache.prune();
|
|
469
397
|
}
|
|
470
398
|
|
|
471
|
-
async getPayloadBodiesByHash(_fork: ForkName, blockHashes: RootHex[]): Promise<(ExecutionPayloadBody | null)[]> {
|
|
472
|
-
const method = "engine_getPayloadBodiesByHashV1";
|
|
473
|
-
assertReqSizeLimit(blockHashes.length, 32);
|
|
474
|
-
const response = await this.rpc.fetchWithRetries<
|
|
475
|
-
EngineApiRpcReturnTypes[typeof method],
|
|
476
|
-
EngineApiRpcParamTypes[typeof method]
|
|
477
|
-
>({method, params: [blockHashes]}, getPayloadBodiesByHashOpts);
|
|
478
|
-
return response.map(deserializeExecutionPayloadBody);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
399
|
async getPayloadBodiesByHashV2(blockHashes: RootHex[]): Promise<(ExecutionPayloadBodyV2 | null)[]> {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
EngineApiRpcReturnTypes[typeof method],
|
|
486
|
-
EngineApiRpcParamTypes[typeof method]
|
|
487
|
-
>({method, params: [blockHashes]}, getPayloadBodiesByHashOpts);
|
|
488
|
-
return response.map(deserializeExecutionPayloadBodyV2);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
async getPayloadBodiesByRange(
|
|
492
|
-
_fork: ForkName,
|
|
493
|
-
startBlockNumber: number,
|
|
494
|
-
blockCount: number
|
|
495
|
-
): Promise<(ExecutionPayloadBody | null)[]> {
|
|
496
|
-
const method = "engine_getPayloadBodiesByRangeV1";
|
|
497
|
-
assertReqSizeLimit(blockCount, 32);
|
|
498
|
-
const start = numToQuantity(startBlockNumber);
|
|
499
|
-
const count = numToQuantity(blockCount);
|
|
500
|
-
const response = await this.rpc.fetchWithRetries<
|
|
501
|
-
EngineApiRpcReturnTypes[typeof method],
|
|
502
|
-
EngineApiRpcParamTypes[typeof method]
|
|
503
|
-
>({method, params: [start, count]}, getPayloadBodiesByRangeOpts);
|
|
504
|
-
return response.map(deserializeExecutionPayloadBody);
|
|
400
|
+
return this.withTransport(ForkName.gloas, undefined, (transport) =>
|
|
401
|
+
transport.getPayloadBodiesByHashV2(blockHashes)
|
|
402
|
+
);
|
|
505
403
|
}
|
|
506
404
|
|
|
507
405
|
async getBlobs(
|
|
@@ -519,95 +417,168 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
519
417
|
versionedHashes: VersionedHashes,
|
|
520
418
|
buffers?: Uint8Array[]
|
|
521
419
|
): Promise<BlobAndProofV2[] | (BlobAndProof | null)[] | null> {
|
|
522
|
-
assertReqSizeLimit(versionedHashes.length, MAX_VERSIONED_HASHES);
|
|
523
|
-
const versionedHashesHex = versionedHashes.map(bytesToData);
|
|
524
420
|
if (isForkPostFulu(fork)) {
|
|
525
|
-
return
|
|
421
|
+
return this.withTransport(undefined, "v2", (transport) => transport.getBlobsV2(versionedHashes, buffers));
|
|
526
422
|
}
|
|
527
|
-
return
|
|
423
|
+
return this.withTransport(undefined, "v1", (transport) => transport.getBlobsV1(versionedHashes));
|
|
528
424
|
}
|
|
529
425
|
|
|
530
|
-
private
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
EngineApiRpcParamTypes["engine_getBlobsV1"]
|
|
534
|
-
>(
|
|
535
|
-
{
|
|
536
|
-
method: "engine_getBlobsV1",
|
|
537
|
-
params: [versionedHashesHex],
|
|
538
|
-
},
|
|
539
|
-
getBlobsV1Opts
|
|
540
|
-
);
|
|
541
|
-
|
|
542
|
-
const invalidLength = response.length !== versionedHashesHex.length;
|
|
426
|
+
private enqueue<T>(job: () => Promise<T>): Promise<T> {
|
|
427
|
+
return this.queue.push(job) as Promise<T>;
|
|
428
|
+
}
|
|
543
429
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
430
|
+
/**
|
|
431
|
+
* Run a call on the selected transport. An execution client that advertises a fork in its
|
|
432
|
+
* capabilities but rejects it with `unsupported-fork` is not spec compliant, keep the node
|
|
433
|
+
* functional by serving that fork over JSON-RPC from then on.
|
|
434
|
+
*/
|
|
435
|
+
private async withTransport<T>(
|
|
436
|
+
fork: ForkName | undefined,
|
|
437
|
+
blobsRevision: "v1" | "v2" | undefined,
|
|
438
|
+
fn: (transport: IEngineTransport) => Promise<T>
|
|
439
|
+
): Promise<T> {
|
|
440
|
+
const transport = await this.getTransport(fork, blobsRevision);
|
|
441
|
+
this.metrics?.engineApiRequests.inc({transport: transport === this.rest ? "ssz" : "json-rpc"});
|
|
442
|
+
try {
|
|
443
|
+
return await fn(transport);
|
|
444
|
+
} catch (e) {
|
|
445
|
+
if (fork !== undefined && transport === this.rest && this.engineApi === "auto" && isUnsupportedForkError(e)) {
|
|
446
|
+
this.disableRestForFork(fork, e);
|
|
447
|
+
this.metrics?.engineApiRequests.inc({transport: "json-rpc"});
|
|
448
|
+
return fn(this.jsonRpc);
|
|
449
|
+
}
|
|
450
|
+
if (transport === this.rest) {
|
|
451
|
+
this.updateEngineState(getExecutionEngineState({payloadError: e, oldState: this.state}), e as Error);
|
|
452
|
+
}
|
|
453
|
+
throw e;
|
|
548
454
|
}
|
|
455
|
+
}
|
|
549
456
|
|
|
550
|
-
|
|
457
|
+
private disableRestForFork(fork: ForkName, e: EngineRestError): void {
|
|
458
|
+
const executionFork = isForkPostBellatrix(fork) ? executionForkName[fork] : null;
|
|
459
|
+
if (this.restSupport.state === "supported" && executionFork !== null) {
|
|
460
|
+
this.restSupport.capabilities.supportedForks.delete(executionFork);
|
|
461
|
+
}
|
|
462
|
+
if (!this.loggedRestFallbacks.has(fork)) {
|
|
463
|
+
this.loggedRestFallbacks.add(fork);
|
|
464
|
+
this.logger.debug("REST engine API rejected an advertised fork, using JSON-RPC until reconnect", {
|
|
465
|
+
fork,
|
|
466
|
+
executionFork,
|
|
467
|
+
status: e.status,
|
|
468
|
+
type: e.type,
|
|
469
|
+
detail: e.detail,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
551
472
|
}
|
|
552
473
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
474
|
+
/**
|
|
475
|
+
* Pick the transport for a call. Fork-scoped calls only go over REST if the execution client
|
|
476
|
+
* advertises that fork, blob requests only if it serves the needed `/blobs/vN` revision.
|
|
477
|
+
*/
|
|
478
|
+
private async getTransport(fork?: ForkName, blobsRevision?: "v1" | "v2"): Promise<IEngineTransport> {
|
|
479
|
+
if (this.rest === null || this.engineApi === "json-rpc") {
|
|
480
|
+
return this.jsonRpc;
|
|
481
|
+
}
|
|
482
|
+
const restSupport = await this.probeRestSupport(this.rest);
|
|
483
|
+
if (restSupport.state === "pending" && restSupport.error) {
|
|
484
|
+
this.updateEngineState(
|
|
485
|
+
getExecutionEngineState({payloadError: restSupport.error, oldState: this.state}),
|
|
486
|
+
restSupport.error
|
|
487
|
+
);
|
|
488
|
+
throw restSupport.error;
|
|
489
|
+
}
|
|
490
|
+
if (this.engineApi === "ssz") {
|
|
491
|
+
return this.rest;
|
|
492
|
+
}
|
|
493
|
+
if (restSupport.state !== "supported") {
|
|
494
|
+
return this.jsonRpc;
|
|
495
|
+
}
|
|
559
496
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
497
|
+
if (fork !== undefined) {
|
|
498
|
+
const executionFork = isForkPostBellatrix(fork) ? executionForkName[fork] : null;
|
|
499
|
+
if (executionFork === null || !restSupport.capabilities.supportedForks.has(executionFork)) {
|
|
500
|
+
if (!this.loggedRestFallbacks.has(fork)) {
|
|
501
|
+
this.loggedRestFallbacks.add(fork);
|
|
502
|
+
this.logger.debug("Using JSON-RPC for a fork not advertised by the REST engine API", {fork, executionFork});
|
|
563
503
|
}
|
|
504
|
+
return this.jsonRpc;
|
|
564
505
|
}
|
|
565
506
|
}
|
|
507
|
+
if (blobsRevision !== undefined && !restSupport.capabilities.blobsRevisions.has(blobsRevision)) {
|
|
508
|
+
if (!this.loggedRestFallbacks.has(blobsRevision)) {
|
|
509
|
+
this.loggedRestFallbacks.add(blobsRevision);
|
|
510
|
+
this.logger.debug("Using JSON-RPC for a blob revision not advertised by the REST engine API", {blobsRevision});
|
|
511
|
+
}
|
|
512
|
+
return this.jsonRpc;
|
|
513
|
+
}
|
|
566
514
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
EngineApiRpcParamTypes["engine_getBlobsV2"]
|
|
570
|
-
>(
|
|
571
|
-
{
|
|
572
|
-
method: "engine_getBlobsV2",
|
|
573
|
-
params: [versionedHashesHex],
|
|
574
|
-
},
|
|
575
|
-
getBlobsV2Opts
|
|
576
|
-
);
|
|
577
|
-
|
|
578
|
-
// engine_getBlobsV2 does not return partial responses. It returns null if any blob is not found
|
|
579
|
-
const invalidLength = !!response && response.length !== versionedHashesHex.length;
|
|
515
|
+
return this.rest;
|
|
516
|
+
}
|
|
580
517
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
this.
|
|
584
|
-
throw Error(error);
|
|
518
|
+
private probeRestSupport(rest: RestEngineTransport): Promise<RestSupport> {
|
|
519
|
+
if (this.restSupport.state !== "pending") {
|
|
520
|
+
return Promise.resolve(this.restSupport);
|
|
585
521
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
return null;
|
|
522
|
+
if (this.restProbe !== null) {
|
|
523
|
+
return this.restProbe;
|
|
589
524
|
}
|
|
590
|
-
|
|
591
|
-
if (
|
|
592
|
-
|
|
593
|
-
return response.map((data, i) => deserializeBlobAndProofsV2IntoBytes(data, buffers[i]));
|
|
525
|
+
// Transport errors keep the probe pending, do not retry on every call while the EL is down
|
|
526
|
+
if (Date.now() - this.lastRestProbeMs < REST_PROBE_RETRY_MS) {
|
|
527
|
+
return Promise.resolve(this.restSupport);
|
|
594
528
|
}
|
|
595
529
|
|
|
596
|
-
|
|
530
|
+
this.restProbe = rest
|
|
531
|
+
.getCapabilities()
|
|
532
|
+
.then(
|
|
533
|
+
(capabilities): RestSupport => {
|
|
534
|
+
this.restSupport = {state: "supported", capabilities};
|
|
535
|
+
this.loggedRestFallbacks.clear();
|
|
536
|
+
this.logger.debug("Discovered REST engine API capabilities", {
|
|
537
|
+
supportedForks: Array.from(capabilities.supportedForks).join(","),
|
|
538
|
+
blobsRevisions: Array.from(capabilities.blobsRevisions).join(","),
|
|
539
|
+
...capabilities.limits,
|
|
540
|
+
});
|
|
541
|
+
return this.restSupport;
|
|
542
|
+
},
|
|
543
|
+
(e: Error): RestSupport => {
|
|
544
|
+
if (this.engineApi === "auto" && isRestApiAbsent(e)) {
|
|
545
|
+
this.restSupport = {state: "unsupported"};
|
|
546
|
+
this.logger.debug("Execution client does not support engine API over REST, using JSON-RPC", {
|
|
547
|
+
status: e.status,
|
|
548
|
+
type: e.type ?? "unknown",
|
|
549
|
+
});
|
|
550
|
+
} else if (this.engineApi === "auto" && e instanceof EngineRestResponseError) {
|
|
551
|
+
// The REST API answered but cannot be trusted, auto mode must keep the node on a working transport
|
|
552
|
+
this.restSupport = {state: "unsupported"};
|
|
553
|
+
this.logger.warn("Invalid engine API capabilities, using JSON-RPC until reconnect", {}, e);
|
|
554
|
+
} else {
|
|
555
|
+
const transient = isRetryableEngineRestError(e);
|
|
556
|
+
this.restSupport = {state: "pending", error: this.engineApi === "auto" && transient ? undefined : e};
|
|
557
|
+
this.logger.debug(
|
|
558
|
+
"Unable to probe engine API capabilities",
|
|
559
|
+
{
|
|
560
|
+
engineApi: this.engineApi,
|
|
561
|
+
fallback: this.restSupport.error ? "none" : "json-rpc",
|
|
562
|
+
retryAfterMs: REST_PROBE_RETRY_MS,
|
|
563
|
+
},
|
|
564
|
+
e
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
return this.restSupport;
|
|
568
|
+
}
|
|
569
|
+
)
|
|
570
|
+
.finally(() => {
|
|
571
|
+
this.restProbe = null;
|
|
572
|
+
this.lastRestProbeMs = Date.now();
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
return this.restProbe;
|
|
597
576
|
}
|
|
598
577
|
|
|
599
578
|
private async getClientVersion(clientVersion: ClientVersion): Promise<ClientVersion[]> {
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
EngineApiRpcReturnTypes[typeof method],
|
|
604
|
-
EngineApiRpcParamTypes[typeof method]
|
|
605
|
-
>({method, params: [{...clientVersion, commit: `0x${clientVersion.commit}`}]}, getClientVersionOpts);
|
|
606
|
-
|
|
607
|
-
const clientVersions = response.map((cv) => {
|
|
608
|
-
const code = cv.code in ClientCode ? ClientCode[cv.code as keyof typeof ClientCode] : ClientCode.XX;
|
|
609
|
-
return {code, name: cv.name, version: cv.version, commit: strip0xPrefix(cv.commit)};
|
|
610
|
-
});
|
|
579
|
+
const clientVersions = await this.withTransport(undefined, undefined, (transport) =>
|
|
580
|
+
transport.getClientVersion(clientVersion)
|
|
581
|
+
);
|
|
611
582
|
|
|
612
583
|
if (clientVersions.length === 0) {
|
|
613
584
|
throw Error("Received empty client versions array");
|
|
@@ -619,7 +590,7 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
619
590
|
return clientVersions;
|
|
620
591
|
}
|
|
621
592
|
|
|
622
|
-
private updateEngineState(newState: ExecutionEngineState): void {
|
|
593
|
+
private updateEngineState(newState: ExecutionEngineState, error?: Error): void {
|
|
623
594
|
const oldState = this.state;
|
|
624
595
|
|
|
625
596
|
if (oldState === newState) return;
|
|
@@ -627,22 +598,33 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
627
598
|
switch (newState) {
|
|
628
599
|
case ExecutionEngineState.ONLINE:
|
|
629
600
|
this.logger.info("Execution client became online", {oldState, newState});
|
|
601
|
+
if (oldState === ExecutionEngineState.AUTH_FAILED) {
|
|
602
|
+
this.restSupport = {state: "pending"};
|
|
603
|
+
this.lastRestProbeMs = Number.NEGATIVE_INFINITY;
|
|
604
|
+
}
|
|
630
605
|
this.getClientVersion(getLodestarClientVersion(this.opts)).catch((e) => {
|
|
631
606
|
this.logger.debug("Unable to get execution client version", {}, e);
|
|
632
607
|
this.clientVersion = null;
|
|
633
608
|
});
|
|
634
609
|
break;
|
|
635
610
|
case ExecutionEngineState.OFFLINE:
|
|
636
|
-
|
|
611
|
+
// Reprobe before choosing a transport for the next call after a disconnect.
|
|
612
|
+
this.restSupport = {state: "pending"};
|
|
613
|
+
this.lastRestProbeMs = Number.NEGATIVE_INFINITY;
|
|
614
|
+
this.logger.error("Execution client went offline", {oldState, newState}, error);
|
|
637
615
|
break;
|
|
638
616
|
case ExecutionEngineState.SYNCED:
|
|
639
617
|
this.logger.info("Execution client is synced", {oldState, newState});
|
|
640
618
|
break;
|
|
641
619
|
case ExecutionEngineState.SYNCING:
|
|
642
|
-
this.logger.warn(
|
|
620
|
+
this.logger.warn(
|
|
621
|
+
error ? "Execution client request failed" : "Execution client is syncing",
|
|
622
|
+
{oldState, newState},
|
|
623
|
+
error
|
|
624
|
+
);
|
|
643
625
|
break;
|
|
644
626
|
case ExecutionEngineState.AUTH_FAILED:
|
|
645
|
-
this.logger.error("Execution client authentication failed", {oldState, newState});
|
|
627
|
+
this.logger.error("Execution client authentication failed", {oldState, newState}, error);
|
|
646
628
|
break;
|
|
647
629
|
}
|
|
648
630
|
|
|
@@ -650,10 +632,32 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
650
632
|
}
|
|
651
633
|
}
|
|
652
634
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
635
|
+
/** The execution client answered, as opposed to being unreachable */
|
|
636
|
+
function isEngineResponseError(e: Error): boolean {
|
|
637
|
+
return (
|
|
638
|
+
e instanceof HttpRpcError ||
|
|
639
|
+
e instanceof ErrorJsonRpcResponse ||
|
|
640
|
+
e instanceof EngineRestError ||
|
|
641
|
+
e instanceof EngineRestResponseError
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Legacy JSON-RPC servers and proxies reject the capabilities probe in different ways, a 404 or
|
|
647
|
+
* another client error such as 405. Neither serves the REST API. Authentication failures and
|
|
648
|
+
* transient client errors say nothing about REST support.
|
|
649
|
+
*/
|
|
650
|
+
function isRestApiAbsent(e: Error): e is EngineRestError {
|
|
651
|
+
return (
|
|
652
|
+
e instanceof EngineRestError &&
|
|
653
|
+
e.status >= 400 &&
|
|
654
|
+
e.status < 500 &&
|
|
655
|
+
e.status !== 401 &&
|
|
656
|
+
e.status !== 403 &&
|
|
657
|
+
!isRetryableEngineRestError(e)
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function isUnsupportedForkError(e: unknown): e is EngineRestError {
|
|
662
|
+
return e instanceof EngineRestError && e.status === 400 && e.type === "/engine-api/errors/unsupported-fork";
|
|
663
|
+
}
|