@lodestar/beacon-node 1.47.0-dev.f3dcc639a0 → 1.47.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 +40 -12
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +21 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -1
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/events/index.d.ts +1 -1
- package/lib/api/impl/events/index.d.ts.map +1 -1
- package/lib/api/impl/events/index.js +12 -3
- package/lib/api/impl/events/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +232 -49
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/impl/validator/utils.js +1 -1
- package/lib/api/impl/validator/utils.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +7 -0
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +1 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +11 -9
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +14 -6
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/bls/index.d.ts +1 -1
- package/lib/chain/bls/index.d.ts.map +1 -1
- package/lib/chain/bls/interface.d.ts +5 -5
- package/lib/chain/bls/interface.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts +5 -11
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +116 -101
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
- package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
- package/lib/chain/bls/multithread/jobItem.js +10 -65
- package/lib/chain/bls/multithread/jobItem.js.map +1 -1
- package/lib/chain/bls/multithread/types.d.ts +29 -8
- package/lib/chain/bls/multithread/types.d.ts.map +1 -1
- package/lib/chain/bls/multithread/types.js +12 -0
- package/lib/chain/bls/multithread/types.js.map +1 -1
- package/lib/chain/bls/multithread/utils.d.ts +4 -0
- package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
- package/lib/chain/bls/multithread/utils.js +10 -0
- package/lib/chain/bls/multithread/utils.js.map +1 -1
- package/lib/chain/bls/multithread/worker.js +49 -23
- package/lib/chain/bls/multithread/worker.js.map +1 -1
- package/lib/chain/bls/singleThread.d.ts +4 -10
- package/lib/chain/bls/singleThread.d.ts.map +1 -1
- package/lib/chain/bls/singleThread.js +8 -45
- package/lib/chain/bls/singleThread.js.map +1 -1
- package/lib/chain/bls/utils.d.ts +4 -4
- package/lib/chain/bls/utils.d.ts.map +1 -1
- package/lib/chain/bls/utils.js +18 -19
- package/lib/chain/bls/utils.js.map +1 -1
- package/lib/chain/chain.d.ts +8 -3
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +20 -7
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +1 -0
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +6 -1
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +6 -0
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/errors/proposerPreferences.js +1 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +4 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +6 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/attestationPool.d.ts +1 -1
- package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/attestationPool.js +1 -1
- package/lib/chain/opPools/attestationPool.js.map +1 -1
- package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts +16 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts.map +1 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.js +57 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.js.map +1 -0
- package/lib/chain/opPools/executionPayloadBidPool.d.ts +7 -2
- package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.js +4 -4
- package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
- package/lib/chain/opPools/index.d.ts +2 -1
- package/lib/chain/opPools/index.d.ts.map +1 -1
- package/lib/chain/opPools/index.js +1 -0
- package/lib/chain/opPools/index.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +3 -3
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +1 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +1 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +23 -5
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +4 -2
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +2 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts +2 -2
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +5 -5
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
- package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
- package/lib/chain/seenCache/seenAttesters.js +25 -4
- package/lib/chain/seenCache/seenAttesters.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +5 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +6 -0
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +47 -5
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +117 -7
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +3 -3
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +1 -4
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +1 -1
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +3 -4
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +5 -14
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +12 -3
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +1 -1
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/chain/validation/voluntaryExit.d.ts +9 -1
- package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
- package/lib/chain/validation/voluntaryExit.js +46 -1
- package/lib/chain/validation/voluntaryExit.js.map +1 -1
- package/lib/execution/builder/apiClient.d.ts +64 -0
- package/lib/execution/builder/apiClient.d.ts.map +1 -0
- package/lib/execution/builder/apiClient.js +184 -0
- package/lib/execution/builder/apiClient.js.map +1 -0
- package/lib/execution/builder/validateBid.d.ts +22 -0
- package/lib/execution/builder/validateBid.d.ts.map +1 -0
- package/lib/execution/builder/validateBid.js +111 -0
- package/lib/execution/builder/validateBid.js.map +1 -0
- package/lib/metrics/metrics/lodestar.d.ts +74 -22
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +142 -52
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +13 -0
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +37 -0
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/libp2p/index.d.ts.map +1 -1
- package/lib/network/libp2p/index.js +10 -2
- package/lib/network/libp2p/index.js.map +1 -1
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +22 -43
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/constants.d.ts +7 -0
- package/lib/network/processor/constants.d.ts.map +1 -0
- package/lib/network/processor/constants.js +7 -0
- package/lib/network/processor/constants.js.map +1 -0
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +6 -2
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +3 -0
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +36 -15
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/score.d.ts.map +1 -1
- package/lib/network/reqresp/score.js +5 -11
- package/lib/network/reqresp/score.js.map +1 -1
- package/lib/network/util.d.ts +0 -1
- package/lib/network/util.d.ts.map +1 -1
- package/lib/network/util.js +0 -4
- package/lib/network/util.js.map +1 -1
- package/lib/node/deferredVoluntaryExitPublisher.d.ts +15 -0
- package/lib/node/deferredVoluntaryExitPublisher.d.ts.map +1 -0
- package/lib/node/deferredVoluntaryExitPublisher.js +38 -0
- package/lib/node/deferredVoluntaryExitPublisher.js.map +1 -0
- package/lib/node/nodejs.d.ts +2 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +7 -0
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/range/batch.d.ts +23 -5
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +61 -22
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +2 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +23 -10
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/types.d.ts +32 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +33 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +15 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +220 -61
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +16 -11
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +3 -1
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/package.json +35 -35
- package/src/api/impl/beacon/blocks/index.ts +47 -15
- package/src/api/impl/beacon/pool/index.ts +22 -2
- package/src/api/impl/beacon/state/utils.ts +2 -1
- package/src/api/impl/events/index.ts +12 -4
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/validator/index.ts +285 -49
- package/src/api/impl/validator/utils.ts +1 -1
- package/src/api/rest/base.ts +7 -0
- package/src/chain/archiveStore/archiveStore.ts +1 -1
- package/src/chain/blocks/importExecutionPayload.ts +12 -11
- package/src/chain/blocks/index.ts +18 -6
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
- package/src/chain/bls/index.ts +1 -1
- package/src/chain/bls/interface.ts +7 -3
- package/src/chain/bls/multithread/index.ts +148 -120
- package/src/chain/bls/multithread/jobItem.ts +12 -82
- package/src/chain/bls/multithread/types.ts +33 -8
- package/src/chain/bls/multithread/utils.ts +11 -0
- package/src/chain/bls/multithread/worker.ts +85 -27
- package/src/chain/bls/singleThread.ts +11 -55
- package/src/chain/bls/utils.ts +23 -24
- package/src/chain/chain.ts +23 -6
- package/src/chain/errors/blockError.ts +8 -2
- package/src/chain/errors/proposerPreferences.ts +7 -0
- package/src/chain/forkChoice/index.ts +4 -0
- package/src/chain/interface.ts +6 -1
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/opPools/attestationPool.ts +1 -1
- package/src/chain/opPools/deferredVoluntaryExitPool.ts +65 -0
- package/src/chain/opPools/executionPayloadBidPool.ts +13 -7
- package/src/chain/opPools/index.ts +2 -1
- package/src/chain/opPools/payloadAttestationPool.ts +3 -3
- package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
- package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
- package/src/chain/opPools/utils.ts +1 -1
- package/src/chain/prepareNextSlot.ts +23 -5
- package/src/chain/produceBlock/produceBlockBody.ts +1 -1
- package/src/chain/regen/interface.ts +4 -2
- package/src/chain/regen/queued.ts +5 -5
- package/src/chain/seenCache/seenAttesters.ts +31 -5
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
- package/src/chain/stateCache/fifoBlockStateCache.ts +8 -1
- package/src/chain/stateCache/persistentCheckpointsCache.ts +133 -8
- package/src/chain/stateCache/types.ts +3 -3
- package/src/chain/validation/attestation.ts +1 -7
- package/src/chain/validation/attesterSlashing.ts +1 -8
- package/src/chain/validation/executionPayloadBid.ts +3 -4
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
- package/src/chain/validation/payloadAttestationMessage.ts +6 -17
- package/src/chain/validation/proposerPreferences.ts +14 -4
- package/src/chain/validation/proposerSlashing.ts +1 -1
- package/src/chain/validation/voluntaryExit.ts +59 -2
- package/src/execution/builder/apiClient.ts +244 -0
- package/src/execution/builder/validateBid.ts +166 -0
- package/src/metrics/metrics/lodestar.ts +155 -57
- package/src/network/gossip/topic.ts +38 -0
- package/src/network/libp2p/index.ts +11 -2
- package/src/network/network.ts +27 -55
- package/src/network/processor/constants.ts +6 -0
- package/src/network/processor/gossipHandlers.ts +6 -2
- package/src/network/processor/index.ts +42 -19
- package/src/network/reqresp/score.ts +5 -11
- package/src/network/util.ts +0 -5
- package/src/node/deferredVoluntaryExitPublisher.ts +54 -0
- package/src/node/nodejs.ts +10 -1
- package/src/sync/range/batch.ts +83 -28
- package/src/sync/range/chain.ts +26 -11
- package/src/sync/types.ts +36 -1
- package/src/sync/unknownBlock.ts +258 -68
- package/src/sync/utils/downloadByRange.ts +21 -12
- package/src/sync/utils/downloadByRoot.ts +3 -1
- package/lib/chain/bls/maybeBatch.d.ts +0 -12
- package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
- package/lib/chain/bls/maybeBatch.js +0 -35
- package/lib/chain/bls/maybeBatch.js.map +0 -1
- package/src/chain/bls/maybeBatch.ts +0 -45
|
@@ -52,12 +52,16 @@ import {
|
|
|
52
52
|
import {
|
|
53
53
|
GWEI_TO_WEI,
|
|
54
54
|
TimeoutError,
|
|
55
|
+
byteArrayEquals,
|
|
55
56
|
defer,
|
|
56
57
|
formatWeiToEth,
|
|
57
58
|
fromHex,
|
|
59
|
+
prettyGweiToEth,
|
|
58
60
|
prettyWeiToEth,
|
|
59
61
|
resolveOrRacePromises,
|
|
62
|
+
sleep,
|
|
60
63
|
toHex,
|
|
64
|
+
toPrintableUrl,
|
|
61
65
|
toRootHex,
|
|
62
66
|
} from "@lodestar/utils";
|
|
63
67
|
import {MAX_BUILDER_BOOST_FACTOR} from "@lodestar/validator";
|
|
@@ -72,6 +76,7 @@ import {
|
|
|
72
76
|
SyncCommitteeErrorCode,
|
|
73
77
|
} from "../../../chain/errors/index.js";
|
|
74
78
|
import {ChainEvent, CommonBlockBody} from "../../../chain/index.js";
|
|
79
|
+
import {PooledExecutionPayloadBid} from "../../../chain/opPools/index.js";
|
|
75
80
|
import {PREPARE_NEXT_SLOT_BPS} from "../../../chain/prepareNextSlot.js";
|
|
76
81
|
import {BlockType, ProduceFullDeneb, ProduceFullGloas} from "../../../chain/produceBlock/index.js";
|
|
77
82
|
import {RegenCaller} from "../../../chain/regen/index.js";
|
|
@@ -80,7 +85,9 @@ import {validateApiAggregateAndProof} from "../../../chain/validation/index.js";
|
|
|
80
85
|
import {validateGossipProposerPreferences} from "../../../chain/validation/proposerPreferences.js";
|
|
81
86
|
import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js";
|
|
82
87
|
import {ZERO_HASH} from "../../../constants/index.js";
|
|
88
|
+
import {BUILDER_BID_DEADLINE_MS, BuilderApiBid, decodeBuilderUrl} from "../../../execution/builder/apiClient.js";
|
|
83
89
|
import {BuilderStatus, NoBidReceived} from "../../../execution/builder/http.js";
|
|
90
|
+
import {getBuilderBidTotalGwei, validateBuilderApiExecutionPayloadBid} from "../../../execution/builder/validateBid.js";
|
|
84
91
|
import {validateGossipFnRetryUnknownRoot} from "../../../network/processor/gossipHandlers.js";
|
|
85
92
|
import {CommitteeSubscription} from "../../../network/subnets/index.js";
|
|
86
93
|
import {callInNextEventLoop} from "../../../util/eventLoop.js";
|
|
@@ -112,6 +119,55 @@ import {
|
|
|
112
119
|
const BLOCK_PRODUCTION_RACE_CUTOFF_MS = 2_000;
|
|
113
120
|
/** Overall timeout for execution and block production apis */
|
|
114
121
|
const BLOCK_PRODUCTION_RACE_TIMEOUT_MS = 12_000;
|
|
122
|
+
/** Rejection message of the bid block branch when there is no viable bid to commit to */
|
|
123
|
+
const NO_BID_AVAILABLE = "No builder bid available";
|
|
124
|
+
|
|
125
|
+
type BidCandidate = {
|
|
126
|
+
signedBid: gloas.SignedExecutionPayloadBid;
|
|
127
|
+
/** Total payment in Gwei, counting the execution payment at most at the entry's cap */
|
|
128
|
+
totalGwei: bigint;
|
|
129
|
+
boostFactor: bigint;
|
|
130
|
+
url?: string;
|
|
131
|
+
/** Time in milliseconds from the slot start when the bid was received */
|
|
132
|
+
receivedMs: number;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Return the best bid by boosted total payment. A candidate with the max boost factor is
|
|
137
|
+
* preferred over any other regardless of value, ties prefer a builder api bid over the
|
|
138
|
+
* p2p bid, and the earlier received bid between builder api bids.
|
|
139
|
+
*/
|
|
140
|
+
function selectBestBid(candidates: BidCandidate[]): BidCandidate | null {
|
|
141
|
+
const boostedValue = ({totalGwei, boostFactor}: BidCandidate): bigint => boostFactor * totalGwei;
|
|
142
|
+
let best: BidCandidate | null = null;
|
|
143
|
+
for (const candidate of candidates) {
|
|
144
|
+
if (best === null) {
|
|
145
|
+
best = candidate;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
// Preserve max boost preference before comparing bid values
|
|
149
|
+
const candidateIsMaxBoost = candidate.boostFactor === MAX_BUILDER_BOOST_FACTOR;
|
|
150
|
+
const bestIsMaxBoost = best.boostFactor === MAX_BUILDER_BOOST_FACTOR;
|
|
151
|
+
if (candidateIsMaxBoost !== bestIsMaxBoost) {
|
|
152
|
+
if (candidateIsMaxBoost) {
|
|
153
|
+
best = candidate;
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const candidateValue = boostedValue(candidate);
|
|
158
|
+
const bestValue = boostedValue(best);
|
|
159
|
+
if (candidateValue > bestValue) {
|
|
160
|
+
best = candidate;
|
|
161
|
+
} else if (
|
|
162
|
+
candidateValue === bestValue &&
|
|
163
|
+
// A tie prefers a builder api bid over the p2p bid, and the earlier received bid otherwise
|
|
164
|
+
(best.url === undefined || (candidate.url !== undefined && candidate.receivedMs < best.receivedMs))
|
|
165
|
+
) {
|
|
166
|
+
best = candidate;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return best;
|
|
170
|
+
}
|
|
115
171
|
|
|
116
172
|
type ProduceBlockContentsRes = {executionPayloadValue: Wei; consensusBlockValue: Wei} & {
|
|
117
173
|
data: BlockContents;
|
|
@@ -857,7 +913,7 @@ export function getValidatorApi(
|
|
|
857
913
|
feeRecipient,
|
|
858
914
|
strictFeeRecipientCheck,
|
|
859
915
|
includePayload,
|
|
860
|
-
|
|
916
|
+
builderConfig,
|
|
861
917
|
}) {
|
|
862
918
|
const fork = config.getForkName(slot);
|
|
863
919
|
|
|
@@ -865,7 +921,7 @@ export function getValidatorApi(
|
|
|
865
921
|
throw new ApiError(400, `produceBlockV4 not supported for pre-gloas fork=${fork}`);
|
|
866
922
|
}
|
|
867
923
|
|
|
868
|
-
builderBoostFactor = builderBoostFactor
|
|
924
|
+
const builderBoostFactor = builderConfig.builderBoostFactor;
|
|
869
925
|
if (builderBoostFactor > MAX_BUILDER_BOOST_FACTOR) {
|
|
870
926
|
throw new ApiError(400, `Invalid builderBoostFactor=${builderBoostFactor} > MAX_BUILDER_BOOST_FACTOR`);
|
|
871
927
|
}
|
|
@@ -892,32 +948,117 @@ export function getValidatorApi(
|
|
|
892
948
|
})
|
|
893
949
|
);
|
|
894
950
|
|
|
895
|
-
// TODO GLOAS: add external builder api support when it is implemented
|
|
896
951
|
const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
|
|
897
952
|
const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
|
|
953
|
+
// Post-gloas every proposal parent has an execution payload hash
|
|
954
|
+
if (bidParentBlockHash === null) {
|
|
955
|
+
throw new ApiError(500, `Unknown parent block hash for proposal parent ${parentBlockRootHex}`);
|
|
956
|
+
}
|
|
898
957
|
const circuitBreakerActive = chain.builderCircuitBreaker.isActive(slot, parentBlock);
|
|
899
|
-
// Keep a builder bid as fallback unless the circuit breaker is active
|
|
900
|
-
const builderBid = circuitBreakerActive
|
|
901
|
-
? null
|
|
902
|
-
: chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
|
|
903
958
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
959
|
+
// Fire builder API bid requests while the local payload is built, one request per entry.
|
|
960
|
+
// Any entry failure yields no bid and never fails block production.
|
|
961
|
+
let builderApiBidsPromise: Promise<BuilderApiBid[]> = Promise.resolve([]);
|
|
962
|
+
if (builderConfig.builders.length > 0 && !circuitBreakerActive) {
|
|
963
|
+
try {
|
|
964
|
+
const proposerIndex = chain.getHeadState().getBeaconProposer(slot);
|
|
965
|
+
const proposerPubkey = chain.pubkeyCache.getOrThrow(proposerIndex).toBytes();
|
|
966
|
+
builderApiBidsPromise = chain.builderApiClient.getExecutionPayloadBids(
|
|
967
|
+
builderConfig.builders,
|
|
968
|
+
slot,
|
|
969
|
+
fromHex(bidParentBlockHash),
|
|
970
|
+
parentBlockRoot,
|
|
971
|
+
proposerPubkey
|
|
972
|
+
);
|
|
973
|
+
} catch (e) {
|
|
974
|
+
logger.warn("Unable to request builder API bids", {slot}, e as Error);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// A builder bid is expected if builders are configured or a p2p bid was already received.
|
|
979
|
+
// Used by the censorship override which may run before the bid deadline
|
|
980
|
+
const builderBidExpected =
|
|
981
|
+
builderConfig.builders.length > 0 ||
|
|
982
|
+
(!circuitBreakerActive &&
|
|
983
|
+
chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex) !== null);
|
|
984
|
+
|
|
985
|
+
// Select the p2p bid once builders had time to bid up, matching the deadline advertised
|
|
986
|
+
// on builder API bid requests, unless the circuit breaker is active
|
|
987
|
+
const p2pBidPromise: Promise<PooledExecutionPayloadBid | null> = circuitBreakerActive
|
|
988
|
+
? Promise.resolve(null)
|
|
989
|
+
: sleep(Math.max(0, BUILDER_BID_DEADLINE_MS - chain.clock.msFromSlot(slot))).then(() => {
|
|
990
|
+
const p2pBid = chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
|
|
991
|
+
// Discard p2p bids below the proposer's configured floor on the total payment.
|
|
992
|
+
// A p2p bid's total is just its value since gossip validation enforces executionPayment=0.
|
|
993
|
+
if (p2pBid !== null && BigInt(p2pBid.signedBid.message.value) < builderConfig.minBid) {
|
|
994
|
+
logger.info("Best p2p bid below configured minimum", {
|
|
995
|
+
slot,
|
|
996
|
+
bidValue: prettyGweiToEth(p2pBid.signedBid.message.value),
|
|
997
|
+
minBid: prettyGweiToEth(builderConfig.minBid),
|
|
998
|
+
});
|
|
999
|
+
return null;
|
|
918
1000
|
}
|
|
919
|
-
|
|
920
|
-
|
|
1001
|
+
return p2pBid;
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
// Candidates are ranked by their boosted counted total payment, the p2p bid is governed
|
|
1005
|
+
// by the top-level factors and each builder API bid by its own entry. Ties prefer the
|
|
1006
|
+
// earliest received builder API bid, so the signed block can be routed back to its
|
|
1007
|
+
// builder directly.
|
|
1008
|
+
const bestBidPromise: Promise<BidCandidate | null> = (async () => {
|
|
1009
|
+
const [builderApiBids, p2pBid] = await Promise.all([builderApiBidsPromise, p2pBidPromise]);
|
|
1010
|
+
|
|
1011
|
+
const candidates = (
|
|
1012
|
+
await Promise.all(
|
|
1013
|
+
builderApiBids.map(async ({url, entry, signedBid, receivedMs}): Promise<BidCandidate | null> => {
|
|
1014
|
+
try {
|
|
1015
|
+
await validateBuilderApiExecutionPayloadBid(chain, signedBid, {
|
|
1016
|
+
slot,
|
|
1017
|
+
parentBlock,
|
|
1018
|
+
parentBlockHash: bidParentBlockHash,
|
|
1019
|
+
parentBlockRoot: parentBlockRootHex,
|
|
1020
|
+
entry,
|
|
1021
|
+
});
|
|
1022
|
+
return {
|
|
1023
|
+
signedBid,
|
|
1024
|
+
totalGwei: getBuilderBidTotalGwei(signedBid.message, entry.maxExecutionPayment),
|
|
1025
|
+
boostFactor: entry.builderBoostFactor,
|
|
1026
|
+
url,
|
|
1027
|
+
receivedMs,
|
|
1028
|
+
};
|
|
1029
|
+
} catch (e) {
|
|
1030
|
+
metrics?.builderApi.bidsDiscarded.inc();
|
|
1031
|
+
logger.warn("Ignoring invalid builder API bid", {slot, builder: toPrintableUrl(url)}, e as Error);
|
|
1032
|
+
return null;
|
|
1033
|
+
}
|
|
1034
|
+
})
|
|
1035
|
+
)
|
|
1036
|
+
).filter((candidate): candidate is BidCandidate => candidate !== null);
|
|
1037
|
+
|
|
1038
|
+
if (p2pBid !== null) {
|
|
1039
|
+
candidates.push({
|
|
1040
|
+
signedBid: p2pBid.signedBid,
|
|
1041
|
+
totalGwei: BigInt(p2pBid.signedBid.message.value),
|
|
1042
|
+
boostFactor: builderConfig.builderBoostFactor,
|
|
1043
|
+
receivedMs: p2pBid.receivedMs,
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const best = selectBestBid(candidates);
|
|
1048
|
+
if (candidates.length > 0) {
|
|
1049
|
+
logger.debug("Ranked builder bid candidates", {
|
|
1050
|
+
slot,
|
|
1051
|
+
candidates: candidates
|
|
1052
|
+
.map(
|
|
1053
|
+
(candidate) =>
|
|
1054
|
+
`${candidate.url ?? "p2p"}:total=${prettyGweiToEth(candidate.totalGwei)}:boost=${candidate.boostFactor}:received=${candidate.receivedMs}ms`
|
|
1055
|
+
)
|
|
1056
|
+
.join(","),
|
|
1057
|
+
bidSource: best?.url ?? "p2p",
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
return best;
|
|
1061
|
+
})();
|
|
921
1062
|
|
|
922
1063
|
const commonBlockBodyPromise = chain.produceCommonBlockBody({
|
|
923
1064
|
slot,
|
|
@@ -937,9 +1078,6 @@ export function getValidatorApi(
|
|
|
937
1078
|
};
|
|
938
1079
|
|
|
939
1080
|
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.engine});
|
|
940
|
-
if (builderBid !== null) {
|
|
941
|
-
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
|
|
942
|
-
}
|
|
943
1081
|
|
|
944
1082
|
const timed = <T>(source: ProducedBlockSource, fn: () => Promise<T>): Promise<T> => {
|
|
945
1083
|
const t = metrics?.blockProductionTime.startTimer();
|
|
@@ -956,19 +1094,25 @@ export function getValidatorApi(
|
|
|
956
1094
|
const enginePromise: ReturnType<typeof chain.produceBlock> = timed(ProducedBlockSource.engine, () =>
|
|
957
1095
|
chain.produceBlock(baseAttrs)
|
|
958
1096
|
).then((engineBlock) => {
|
|
959
|
-
// No need to wait for the bid block if the engine block will always be selected
|
|
960
|
-
// suspected builder censorship or
|
|
961
|
-
|
|
1097
|
+
// No need to wait for the bid block if the engine block will always be selected, either
|
|
1098
|
+
// due to suspected builder censorship, or because no builders are configured and the
|
|
1099
|
+
// boost factor of 0 always prefers the local block over p2p bids
|
|
1100
|
+
if (engineBlock.shouldOverrideBuilder || (builderConfig.builders.length === 0 && builderBoostFactor === 0n)) {
|
|
962
1101
|
controller.abort();
|
|
963
1102
|
}
|
|
964
1103
|
return engineBlock;
|
|
965
1104
|
});
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1105
|
+
const bidBlockPromise: ReturnType<typeof chain.produceBlock> = bestBidPromise.then((candidate) => {
|
|
1106
|
+
if (candidate === null) {
|
|
1107
|
+
throw new Error(NO_BID_AVAILABLE);
|
|
1108
|
+
}
|
|
1109
|
+
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
|
|
1110
|
+
return timed(ProducedBlockSource.builder, () =>
|
|
1111
|
+
chain.produceBlock({...baseAttrs, builderBid: candidate.signedBid})
|
|
1112
|
+
);
|
|
1113
|
+
});
|
|
970
1114
|
|
|
971
|
-
const [engineResult,
|
|
1115
|
+
const [engineResult, bidBlockResult] = await resolveOrRacePromises([enginePromise, bidBlockPromise], {
|
|
972
1116
|
resolveTimeoutMs: cutoffMs,
|
|
973
1117
|
raceTimeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS,
|
|
974
1118
|
signal: controller.signal,
|
|
@@ -977,29 +1121,74 @@ export function getValidatorApi(
|
|
|
977
1121
|
let bestResult: typeof engineResult | null = null;
|
|
978
1122
|
let source: ProducedBlockSource = ProducedBlockSource.engine;
|
|
979
1123
|
|
|
1124
|
+
// Resolved instantly whenever the bid branch produced a block
|
|
1125
|
+
const bestBid = bidBlockResult.status === "fulfilled" ? await bestBidPromise : null;
|
|
1126
|
+
|
|
1127
|
+
const logCtx = {
|
|
1128
|
+
slot,
|
|
1129
|
+
parentSlot,
|
|
1130
|
+
parentBlockRoot: parentBlockRootHex,
|
|
1131
|
+
parentBlockHash: parentBlock.executionPayloadBlockHash,
|
|
1132
|
+
fork,
|
|
1133
|
+
builderBoostFactor,
|
|
1134
|
+
strictFeeRecipientCheck,
|
|
1135
|
+
circuitBreakerActive,
|
|
1136
|
+
builderEntries: builderConfig.builders.length,
|
|
1137
|
+
...(bestBid !== null
|
|
1138
|
+
? {
|
|
1139
|
+
bidSource: bestBid.url !== undefined ? toPrintableUrl(bestBid.url) : "p2p",
|
|
1140
|
+
bidValue: prettyGweiToEth(bestBid.signedBid.message.value),
|
|
1141
|
+
bidExecutionPayment: prettyGweiToEth(bestBid.signedBid.message.executionPayment),
|
|
1142
|
+
// The full bid total and the counted total used during bid selection
|
|
1143
|
+
bidTotal: prettyGweiToEth(
|
|
1144
|
+
BigInt(bestBid.signedBid.message.value) + bestBid.signedBid.message.executionPayment
|
|
1145
|
+
),
|
|
1146
|
+
bidCountedTotal: prettyGweiToEth(bestBid.totalGwei),
|
|
1147
|
+
bidBoostFactor: bestBid.boostFactor,
|
|
1148
|
+
builderIndex: bestBid.signedBid.message.builderIndex,
|
|
1149
|
+
bidBlockHash: toRootHex(bestBid.signedBid.message.blockHash),
|
|
1150
|
+
bidReceivedMs: bestBid.receivedMs,
|
|
1151
|
+
}
|
|
1152
|
+
: {}),
|
|
1153
|
+
};
|
|
1154
|
+
|
|
980
1155
|
// handle shouldOverrideBuilder separately
|
|
981
|
-
if (
|
|
1156
|
+
if (
|
|
1157
|
+
engineResult.status === "fulfilled" &&
|
|
1158
|
+
engineResult.value.shouldOverrideBuilder &&
|
|
1159
|
+
(builderBidExpected || bidBlockResult.status === "fulfilled")
|
|
1160
|
+
) {
|
|
982
1161
|
source = ProducedBlockSource.engine;
|
|
983
1162
|
bestResult = engineResult;
|
|
984
1163
|
metrics?.blockProductionSelectionResults.inc({
|
|
985
1164
|
source: ProducedBlockSource.engine,
|
|
986
1165
|
reason: EngineBlockSelectionReason.BuilderCensorship,
|
|
987
1166
|
});
|
|
988
|
-
logger.warn("Selected local block: censorship suspected in builder bid",
|
|
989
|
-
|
|
1167
|
+
logger.warn("Selected local block: censorship suspected in builder bid", {
|
|
1168
|
+
...logCtx,
|
|
1169
|
+
durationMs: engineResult.durationMs,
|
|
1170
|
+
...getBlockValueLogInfo(engineResult.value),
|
|
1171
|
+
});
|
|
1172
|
+
} else if (engineResult.status === "fulfilled" && bidBlockResult.status === "fulfilled") {
|
|
990
1173
|
const result = selectBlockProductionSourceByBoostFactor({
|
|
991
|
-
builderBoostFactor,
|
|
1174
|
+
builderBoostFactor: bestBid?.boostFactor ?? builderBoostFactor,
|
|
992
1175
|
engineExecutionPayloadValue: engineResult.value.executionPayloadValue,
|
|
993
|
-
// The bid
|
|
994
|
-
builderExecutionPayloadValue:
|
|
1176
|
+
// The bid total payment is its value plus its counted executionPayment, in Gwei
|
|
1177
|
+
builderExecutionPayloadValue: (bestBid?.totalGwei ?? 0n) * GWEI_TO_WEI,
|
|
995
1178
|
});
|
|
996
1179
|
source = result.source;
|
|
997
1180
|
metrics?.blockProductionSelectionResults.inc(result);
|
|
998
|
-
logger.info(`Selected ${source} block`, {
|
|
999
|
-
|
|
1000
|
-
|
|
1181
|
+
logger.info(`Selected ${source} block`, {
|
|
1182
|
+
reason: result.reason,
|
|
1183
|
+
...logCtx,
|
|
1184
|
+
engineDurationMs: engineResult.durationMs,
|
|
1185
|
+
...getBlockValueLogInfo(engineResult.value, ProducedBlockSource.engine),
|
|
1186
|
+
builderDurationMs: bidBlockResult.durationMs,
|
|
1187
|
+
});
|
|
1188
|
+
bestResult = source === ProducedBlockSource.builder ? bidBlockResult : engineResult;
|
|
1189
|
+
} else if (bidBlockResult.status === "fulfilled") {
|
|
1001
1190
|
source = ProducedBlockSource.builder;
|
|
1002
|
-
bestResult =
|
|
1191
|
+
bestResult = bidBlockResult;
|
|
1003
1192
|
const reason =
|
|
1004
1193
|
engineResult.status === "pending"
|
|
1005
1194
|
? BuilderBlockSelectionReason.EnginePending
|
|
@@ -1008,28 +1197,32 @@ export function getValidatorApi(
|
|
|
1008
1197
|
logger.info("Selected builder bid block: no local block produced", {
|
|
1009
1198
|
reason,
|
|
1010
1199
|
...logCtx,
|
|
1200
|
+
durationMs: bidBlockResult.durationMs,
|
|
1201
|
+
...getBlockValueLogInfo(bidBlockResult.value),
|
|
1011
1202
|
error: engineResult.status === "rejected" ? (engineResult.reason as Error).message : undefined,
|
|
1012
1203
|
});
|
|
1013
1204
|
} else if (engineResult.status === "fulfilled") {
|
|
1014
1205
|
source = ProducedBlockSource.engine;
|
|
1015
1206
|
bestResult = engineResult;
|
|
1016
1207
|
const reason =
|
|
1017
|
-
|
|
1208
|
+
bidBlockResult.status === "rejected" && (bidBlockResult.reason as Error).message === NO_BID_AVAILABLE
|
|
1018
1209
|
? EngineBlockSelectionReason.BuilderNoBid
|
|
1019
|
-
:
|
|
1210
|
+
: bidBlockResult.status === "pending"
|
|
1020
1211
|
? EngineBlockSelectionReason.BuilderPending
|
|
1021
1212
|
: EngineBlockSelectionReason.BuilderError;
|
|
1022
1213
|
metrics?.blockProductionSelectionResults.inc({source: ProducedBlockSource.engine, reason});
|
|
1023
1214
|
logger.info("Selected local block: no builder bid block produced", {
|
|
1024
1215
|
reason,
|
|
1025
1216
|
...logCtx,
|
|
1026
|
-
|
|
1217
|
+
durationMs: engineResult.durationMs,
|
|
1218
|
+
...getBlockValueLogInfo(engineResult.value),
|
|
1219
|
+
error: bidBlockResult.status === "rejected" ? (bidBlockResult.reason as Error).message : undefined,
|
|
1027
1220
|
});
|
|
1028
1221
|
}
|
|
1029
1222
|
|
|
1030
1223
|
if (bestResult === null || bestResult.status !== "fulfilled") {
|
|
1031
1224
|
const engineReason = engineResult.status === "rejected" ? engineResult.reason : engineResult.status;
|
|
1032
|
-
const bidReason =
|
|
1225
|
+
const bidReason = bidBlockResult.status === "rejected" ? bidBlockResult.reason : bidBlockResult.status;
|
|
1033
1226
|
logger.error("Block production failed", {
|
|
1034
1227
|
...logCtx,
|
|
1035
1228
|
engineReason: String(engineReason),
|
|
@@ -1093,7 +1286,13 @@ export function getValidatorApi(
|
|
|
1093
1286
|
|
|
1094
1287
|
return {
|
|
1095
1288
|
data: block as gloas.BeaconBlock,
|
|
1096
|
-
meta: {
|
|
1289
|
+
meta: {
|
|
1290
|
+
version: fork,
|
|
1291
|
+
consensusBlockValue,
|
|
1292
|
+
executionPayloadValue,
|
|
1293
|
+
executionPayloadIncluded: false,
|
|
1294
|
+
builderUrl: source === ProducedBlockSource.builder ? bestBid?.url : undefined,
|
|
1295
|
+
},
|
|
1097
1296
|
};
|
|
1098
1297
|
},
|
|
1099
1298
|
|
|
@@ -1877,6 +2076,43 @@ export function getValidatorApi(
|
|
|
1877
2076
|
}
|
|
1878
2077
|
},
|
|
1879
2078
|
|
|
2079
|
+
async submitBuilderPreferences({builderPreferences}) {
|
|
2080
|
+
const failures: FailureList = [];
|
|
2081
|
+
|
|
2082
|
+
await Promise.all(
|
|
2083
|
+
builderPreferences.map(async (entry, i) => {
|
|
2084
|
+
let builder = Buffer.from(entry.url).toString("utf8");
|
|
2085
|
+
try {
|
|
2086
|
+
const url = decodeBuilderUrl(entry.url);
|
|
2087
|
+
builder = toPrintableUrl(url);
|
|
2088
|
+
const proposerIndex = chain.getHeadState().getBeaconProposer(entry.auth.message.slot);
|
|
2089
|
+
const expectedProposerPubkey = chain.pubkeyCache.getOrThrow(proposerIndex).toBytes();
|
|
2090
|
+
if (!byteArrayEquals(entry.proposerPubkey, expectedProposerPubkey)) {
|
|
2091
|
+
throw new ApiError(
|
|
2092
|
+
400,
|
|
2093
|
+
`Invalid proposer pubkey for builder preferences slot=${entry.auth.message.slot}`
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
await chain.builderApiClient.submitBuilderPreferences(url, entry.proposerPubkey, {
|
|
2097
|
+
preferences: {maxExecutionPayment: entry.maxExecutionPayment},
|
|
2098
|
+
auth: entry.auth,
|
|
2099
|
+
});
|
|
2100
|
+
} catch (e) {
|
|
2101
|
+
failures.push({index: i, message: (e as Error).message});
|
|
2102
|
+
logger.verbose(
|
|
2103
|
+
`Error on submitBuilderPreferences [${i}]`,
|
|
2104
|
+
{slot: entry.auth.message.slot, builder},
|
|
2105
|
+
e as Error
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
})
|
|
2109
|
+
);
|
|
2110
|
+
|
|
2111
|
+
if (failures.length > 0) {
|
|
2112
|
+
throw new IndexedError("Error submitting builder preferences", failures);
|
|
2113
|
+
}
|
|
2114
|
+
},
|
|
2115
|
+
|
|
1880
2116
|
async getExecutionPayloadEnvelope({slot, beaconBlockRoot}) {
|
|
1881
2117
|
const fork = config.getForkName(slot);
|
|
1882
2118
|
|
|
@@ -89,7 +89,7 @@ export function selectBlockProductionSourceByBoostFactor({
|
|
|
89
89
|
return {source: ProducedBlockSource.builder, reason: BuilderBlockSelectionReason.BuilderPreferred};
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
if (engineExecutionPayloadValue >=
|
|
92
|
+
if (engineExecutionPayloadValue * 100n >= builderExecutionPayloadValue * builderBoostFactor) {
|
|
93
93
|
return {source: ProducedBlockSource.engine, reason: EngineBlockSelectionReason.BlockValue};
|
|
94
94
|
}
|
|
95
95
|
|
package/src/api/rest/base.ts
CHANGED
|
@@ -104,6 +104,13 @@ export class RestApiServer {
|
|
|
104
104
|
|
|
105
105
|
// To parse our ApiError -> statusCode
|
|
106
106
|
server.setErrorHandler<FastifyError | Error>((err, _req, res) => {
|
|
107
|
+
if (res.raw.headersSent) {
|
|
108
|
+
// A status code can no longer be written, e.g. if a long-lived SSE stream failed mid-flight.
|
|
109
|
+
// End the response instead, else the socket is left open and the client never notices.
|
|
110
|
+
if (!res.raw.writableEnded && !res.raw.destroyed) res.raw.end();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
107
114
|
const stacktraces = opts.stacktraces ? err.stack?.split("\n") : undefined;
|
|
108
115
|
if ("validation" in err && err.validation) {
|
|
109
116
|
const {instancePath = "unknown", message} = err.validation?.[0] ?? {};
|
|
@@ -192,7 +192,7 @@ export class ArchiveStore {
|
|
|
192
192
|
|
|
193
193
|
private onCheckpoint = (): void => {
|
|
194
194
|
const headStateRoot = this.chain.forkChoice.getHead().stateRoot;
|
|
195
|
-
this.chain.regen.
|
|
195
|
+
this.chain.regen.onCheckpoint(
|
|
196
196
|
this.chain.forkChoice.getFinalizedCheckpoint().epoch,
|
|
197
197
|
this.chain.forkChoice.getJustifiedCheckpoint().epoch,
|
|
198
198
|
headStateRoot
|
|
@@ -58,11 +58,12 @@ export type PayloadErrorType =
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
export class PayloadError extends Error {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
constructor(
|
|
62
|
+
readonly payloadInput: PayloadEnvelopeInput,
|
|
63
|
+
readonly type: PayloadErrorType,
|
|
64
|
+
message?: string
|
|
65
|
+
) {
|
|
64
66
|
super(message ?? type.code);
|
|
65
|
-
this.type = type;
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -125,7 +126,7 @@ export async function importExecutionPayload(
|
|
|
125
126
|
// 2. Get ProtoBlock for parent root lookup
|
|
126
127
|
const protoBlock = this.forkChoice.getBlockHexDefaultStatus(blockRootHex);
|
|
127
128
|
if (!protoBlock) {
|
|
128
|
-
throw new PayloadError({
|
|
129
|
+
throw new PayloadError(payloadInput, {
|
|
129
130
|
code: PayloadErrorCode.BLOCK_NOT_IN_FORK_CHOICE,
|
|
130
131
|
blockRootHex,
|
|
131
132
|
});
|
|
@@ -140,13 +141,13 @@ export async function importExecutionPayload(
|
|
|
140
141
|
);
|
|
141
142
|
|
|
142
143
|
if (blockState == null) {
|
|
143
|
-
throw new PayloadError({
|
|
144
|
+
throw new PayloadError(payloadInput, {
|
|
144
145
|
code: PayloadErrorCode.MISS_BLOCK_STATE,
|
|
145
146
|
blockRootHex: protoBlock.blockRoot,
|
|
146
147
|
});
|
|
147
148
|
}
|
|
148
149
|
if (!isStatePostGloas(blockState)) {
|
|
149
|
-
throw new PayloadError({
|
|
150
|
+
throw new PayloadError(payloadInput, {
|
|
150
151
|
code: PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR,
|
|
151
152
|
message: `Expected gloas+ state for payload import, got fork=${blockState.forkName}`,
|
|
152
153
|
});
|
|
@@ -161,6 +162,7 @@ export async function importExecutionPayload(
|
|
|
161
162
|
});
|
|
162
163
|
} catch (e) {
|
|
163
164
|
throw new PayloadError(
|
|
165
|
+
payloadInput,
|
|
164
166
|
{
|
|
165
167
|
code: PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR,
|
|
166
168
|
message: (e as Error).message,
|
|
@@ -184,7 +186,6 @@ export async function importExecutionPayload(
|
|
|
184
186
|
: verifyExecutionPayloadEnvelopeSignature(
|
|
185
187
|
this.config,
|
|
186
188
|
blockState,
|
|
187
|
-
this.pubkeyCache,
|
|
188
189
|
signedEnvelope,
|
|
189
190
|
payloadInput.proposerIndex,
|
|
190
191
|
this.bls
|
|
@@ -193,7 +194,7 @@ export async function importExecutionPayload(
|
|
|
193
194
|
|
|
194
195
|
// 4b. Check signature verification result
|
|
195
196
|
if (!signatureValid) {
|
|
196
|
-
throw new PayloadError({code: PayloadErrorCode.INVALID_SIGNATURE});
|
|
197
|
+
throw new PayloadError(payloadInput, {code: PayloadErrorCode.INVALID_SIGNATURE});
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
// 4c. Handle EL response
|
|
@@ -202,7 +203,7 @@ export async function importExecutionPayload(
|
|
|
202
203
|
break;
|
|
203
204
|
|
|
204
205
|
case ExecutionPayloadStatus.INVALID:
|
|
205
|
-
throw new PayloadError({
|
|
206
|
+
throw new PayloadError(payloadInput, {
|
|
206
207
|
code: PayloadErrorCode.EXECUTION_ENGINE_INVALID,
|
|
207
208
|
execStatus: execResult.status,
|
|
208
209
|
errorMessage: execResult.validationError ?? "",
|
|
@@ -215,7 +216,7 @@ export async function importExecutionPayload(
|
|
|
215
216
|
case ExecutionPayloadStatus.INVALID_BLOCK_HASH:
|
|
216
217
|
case ExecutionPayloadStatus.ELERROR:
|
|
217
218
|
case ExecutionPayloadStatus.UNAVAILABLE:
|
|
218
|
-
throw new PayloadError({
|
|
219
|
+
throw new PayloadError(payloadInput, {
|
|
219
220
|
code: PayloadErrorCode.EXECUTION_ENGINE_ERROR,
|
|
220
221
|
execStatus: execResult.status,
|
|
221
222
|
errorMessage: execResult.validationError ?? "",
|
|
@@ -8,7 +8,7 @@ import {BlockError, BlockErrorCode, isBlockErrorAborted} from "../errors/index.j
|
|
|
8
8
|
import {BlockProcessOpts} from "../options.js";
|
|
9
9
|
import {IBlockInput} from "./blockInput/types.js";
|
|
10
10
|
import {importBlock} from "./importBlock.js";
|
|
11
|
-
import {importExecutionPayload} from "./importExecutionPayload.js";
|
|
11
|
+
import {PayloadError, importExecutionPayload} from "./importExecutionPayload.js";
|
|
12
12
|
import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
13
13
|
import {FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
|
|
14
14
|
import {assertLinearChainSegment} from "./utils/chainSegment.js";
|
|
@@ -164,13 +164,21 @@ export async function processBlocks(
|
|
|
164
164
|
return; // Ignore
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
// above functions should only throw BlockError
|
|
168
|
-
const err =
|
|
167
|
+
// above functions should only throw BlockError, or PayloadError from the gloas payload import
|
|
168
|
+
const err = getBlockOrPayloadError(e, blocks[0].getBlock());
|
|
169
169
|
|
|
170
170
|
// TODO: De-duplicate with logic above
|
|
171
171
|
// ChainEvent.errorBlock
|
|
172
|
-
if (!(err instanceof BlockError)) {
|
|
173
|
-
this.logger.debug("
|
|
172
|
+
if (!(err instanceof BlockError) && !(err instanceof PayloadError)) {
|
|
173
|
+
this.logger.debug("Neither BlockError nor PayloadError received", {}, err);
|
|
174
|
+
} else if (err instanceof PayloadError) {
|
|
175
|
+
if (!opts.disableOnBlockError) {
|
|
176
|
+
this.logger.debug(
|
|
177
|
+
"Payload error",
|
|
178
|
+
{slot: err.payloadInput.slot, blockRoot: err.payloadInput.blockRootHex},
|
|
179
|
+
err
|
|
180
|
+
);
|
|
181
|
+
}
|
|
174
182
|
} else if (!opts.disableOnBlockError) {
|
|
175
183
|
this.logger.debug("Block error", {slot: err.signedBlock.message.slot}, err);
|
|
176
184
|
|
|
@@ -201,11 +209,15 @@ export async function processBlocks(
|
|
|
201
209
|
}
|
|
202
210
|
}
|
|
203
211
|
|
|
204
|
-
function
|
|
212
|
+
function getBlockOrPayloadError(e: unknown, block: SignedBeaconBlock): BlockError | PayloadError {
|
|
205
213
|
if (e instanceof BlockError) {
|
|
206
214
|
return e;
|
|
207
215
|
}
|
|
208
216
|
|
|
217
|
+
if (e instanceof PayloadError) {
|
|
218
|
+
return e;
|
|
219
|
+
}
|
|
220
|
+
|
|
209
221
|
if (e instanceof Error) {
|
|
210
222
|
const blockError = new BlockError(block, {code: BlockErrorCode.BEACON_CHAIN_ERROR, error: e});
|
|
211
223
|
blockError.stack = e.stack;
|