@lodestar/beacon-node 1.45.0-dev.51a1c44b27 → 1.45.0-dev.6568180f96
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 +186 -57
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/index.d.ts +1 -1
- package/lib/api/impl/beacon/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/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 +6 -43
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +48 -5
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +13 -3
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +48 -4
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +2 -2
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +2 -2
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/proof/index.d.ts +1 -1
- package/lib/api/impl/proof/index.d.ts.map +1 -1
- package/lib/api/impl/proof/index.js +2 -2
- package/lib/api/impl/proof/index.js.map +1 -1
- package/lib/api/impl/utils.d.ts +23 -0
- package/lib/api/impl/utils.d.ts.map +1 -1
- package/lib/api/impl/utils.js +44 -1
- package/lib/api/impl/utils.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts +0 -14
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +238 -149
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +14 -3
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +4 -22
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -2
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.js +17 -17
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +13 -5
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/builderCircuitBreaker.d.ts +31 -0
- package/lib/chain/builderCircuitBreaker.d.ts.map +1 -0
- package/lib/chain/builderCircuitBreaker.js +59 -0
- package/lib/chain/builderCircuitBreaker.js.map +1 -0
- package/lib/chain/chain.d.ts +3 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +27 -5
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +48 -6
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +25 -4
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +11 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +2 -0
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +14 -1
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/errors/proposerPreferences.js +2 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -1
- package/lib/chain/interface.d.ts +3 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/lightClient/index.d.ts.map +1 -1
- package/lib/chain/lightClient/index.js +11 -6
- package/lib/chain/lightClient/index.js.map +1 -1
- package/lib/chain/lightClient/proofs.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.js +28 -2
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/lightClient/types.d.ts +3 -1
- package/lib/chain/lightClient/types.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +3 -2
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +4 -1
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/options.d.ts +5 -0
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -0
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +12 -7
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +4 -12
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +0 -1
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +0 -1
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +3 -2
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/validation/aggregateAndProof.js +10 -6
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +10 -6
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +53 -4
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +13 -12
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +29 -0
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts +9 -2
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +75 -14
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts.map +1 -1
- package/lib/chain/validation/signatureSets/aggregateAndProof.js +2 -3
- package/lib/chain/validation/signatureSets/aggregateAndProof.js.map +1 -1
- package/lib/db/repositories/lightclientSyncCommitteeWitness.d.ts.map +1 -1
- package/lib/db/repositories/lightclientSyncCommitteeWitness.js +42 -7
- package/lib/db/repositories/lightclientSyncCommitteeWitness.js.map +1 -1
- package/lib/execution/builder/http.d.ts +18 -0
- package/lib/execution/builder/http.d.ts.map +1 -1
- package/lib/execution/builder/http.js +20 -11
- package/lib/execution/builder/http.js.map +1 -1
- package/lib/metrics/metrics/beacon.d.ts +6 -0
- package/lib/metrics/metrics/beacon.d.ts.map +1 -1
- package/lib/metrics/metrics/beacon.js +18 -0
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/network/core/metrics.d.ts +65 -0
- package/lib/network/core/metrics.d.ts.map +1 -1
- package/lib/network/core/metrics.js +94 -0
- package/lib/network/core/metrics.js.map +1 -1
- package/lib/network/discv5/utils.d.ts.map +1 -1
- package/lib/network/discv5/utils.js +12 -3
- package/lib/network/discv5/utils.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts +2 -2
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +22 -9
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +4 -3
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +831 -226
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +47 -4
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/interface.d.ts +4 -1
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +4 -1
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +19 -11
- package/lib/network/network.js.map +1 -1
- package/lib/network/peers/peerManager.d.ts.map +1 -1
- package/lib/network/peers/peerManager.js +10 -0
- package/lib/network/peers/peerManager.js.map +1 -1
- package/lib/network/peers/score/score.d.ts +3 -1
- package/lib/network/peers/score/score.d.ts.map +1 -1
- package/lib/network/peers/score/score.js +6 -1
- package/lib/network/peers/score/score.js.map +1 -1
- package/lib/network/peers/score/store.d.ts.map +1 -1
- package/lib/network/peers/score/store.js +6 -1
- package/lib/network/peers/score/store.js.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.d.ts.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.js +14 -0
- package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +18 -9
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +4 -2
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/index.js +20 -11
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/util.d.ts +1 -0
- package/lib/network/util.d.ts.map +1 -1
- package/lib/network/util.js +4 -0
- package/lib/network/util.js.map +1 -1
- package/lib/sync/range/chain.d.ts +4 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +3 -3
- 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 +10 -0
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/sync.d.ts.map +1 -1
- package/lib/sync/sync.js +7 -0
- package/lib/sync/sync.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +7 -2
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +6 -0
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/util/execution.js +1 -1
- package/lib/util/execution.js.map +1 -1
- package/lib/util/graffiti.d.ts +15 -0
- package/lib/util/graffiti.d.ts.map +1 -1
- package/lib/util/graffiti.js +55 -0
- package/lib/util/graffiti.js.map +1 -1
- package/package.json +23 -21
- package/src/api/impl/beacon/blocks/index.ts +221 -63
- package/src/api/impl/beacon/index.ts +1 -1
- package/src/api/impl/beacon/pool/index.ts +5 -53
- package/src/api/impl/beacon/state/index.ts +55 -3
- package/src/api/impl/beacon/state/utils.ts +68 -4
- package/src/api/impl/debug/index.ts +3 -2
- package/src/api/impl/lodestar/index.ts +2 -2
- package/src/api/impl/proof/index.ts +2 -2
- package/src/api/impl/utils.ts +51 -1
- package/src/api/impl/validator/index.ts +278 -162
- package/src/api/rest/base.ts +17 -3
- package/src/chain/blocks/importBlock.ts +2 -24
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +17 -17
- package/src/chain/blocks/utils/chainSegment.ts +16 -5
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +1 -1
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +1 -1
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
- package/src/chain/builderCircuitBreaker.ts +84 -0
- package/src/chain/chain.ts +38 -5
- package/src/chain/errors/blockError.ts +44 -7
- package/src/chain/errors/executionPayloadEnvelope.ts +9 -0
- package/src/chain/errors/proposerPreferences.ts +16 -1
- package/src/chain/interface.ts +2 -2
- package/src/chain/lightClient/index.ts +12 -7
- package/src/chain/lightClient/proofs.ts +36 -1
- package/src/chain/lightClient/types.ts +3 -1
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/opPools/proposerPreferencesPool.ts +6 -2
- package/src/chain/options.ts +6 -0
- package/src/chain/prepareNextSlot.ts +11 -7
- package/src/chain/produceBlock/produceBlockBody.ts +9 -17
- package/src/chain/seenCache/index.ts +0 -1
- package/src/chain/stateCache/persistentCheckpointsCache.ts +3 -2
- package/src/chain/validation/aggregateAndProof.ts +12 -6
- package/src/chain/validation/attestation.ts +12 -6
- package/src/chain/validation/block.ts +69 -4
- package/src/chain/validation/executionPayloadBid.ts +16 -11
- package/src/chain/validation/executionPayloadEnvelope.ts +37 -0
- package/src/chain/validation/proposerPreferences.ts +86 -14
- package/src/chain/validation/signatureSets/aggregateAndProof.ts +3 -3
- package/src/db/repositories/lightclientSyncCommitteeWitness.ts +57 -9
- package/src/execution/builder/http.ts +30 -17
- package/src/metrics/metrics/beacon.ts +19 -0
- package/src/network/core/metrics.ts +94 -0
- package/src/network/discv5/utils.ts +11 -3
- package/src/network/gossip/encoding.ts +20 -7
- package/src/network/gossip/gossipsub.ts +9 -3
- package/src/network/gossip/topic.ts +58 -4
- package/src/network/interface.ts +3 -1
- package/src/network/network.ts +24 -15
- package/src/network/peers/peerManager.ts +11 -0
- package/src/network/peers/score/score.ts +8 -1
- package/src/network/peers/score/store.ts +8 -1
- package/src/network/peers/utils/prioritizePeers.ts +15 -0
- package/src/network/processor/gossipHandlers.ts +18 -8
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +4 -2
- package/src/network/reqresp/handlers/index.ts +21 -12
- package/src/network/util.ts +5 -0
- package/src/sync/range/chain.ts +4 -5
- package/src/sync/range/range.ts +10 -0
- package/src/sync/sync.ts +10 -0
- package/src/sync/unknownBlock.ts +8 -2
- package/src/sync/utils/downloadByRange.ts +6 -0
- package/src/util/execution.ts +1 -1
- package/src/util/graffiti.ts +78 -0
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +0 -16
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +0 -1
- package/lib/chain/seenCache/seenProposerPreferences.js +0 -26
- package/lib/chain/seenCache/seenProposerPreferences.js.map +0 -1
- package/src/chain/seenCache/seenProposerPreferences.ts +0 -32
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
ssz,
|
|
51
51
|
} from "@lodestar/types";
|
|
52
52
|
import {
|
|
53
|
+
GWEI_TO_WEI,
|
|
53
54
|
TimeoutError,
|
|
54
55
|
defer,
|
|
55
56
|
formatWeiToEth,
|
|
@@ -65,6 +66,8 @@ import {
|
|
|
65
66
|
AttestationError,
|
|
66
67
|
AttestationErrorCode,
|
|
67
68
|
GossipAction,
|
|
69
|
+
ProposerPreferencesError,
|
|
70
|
+
ProposerPreferencesErrorCode,
|
|
68
71
|
SyncCommitteeError,
|
|
69
72
|
SyncCommitteeErrorCode,
|
|
70
73
|
} from "../../../chain/errors/index.js";
|
|
@@ -74,37 +77,23 @@ import {BlockType, ProduceFullDeneb, ProduceFullGloas} from "../../../chain/prod
|
|
|
74
77
|
import {RegenCaller} from "../../../chain/regen/index.js";
|
|
75
78
|
import {CheckpointHex} from "../../../chain/stateCache/types.js";
|
|
76
79
|
import {validateApiAggregateAndProof} from "../../../chain/validation/index.js";
|
|
80
|
+
import {validateGossipProposerPreferences} from "../../../chain/validation/proposerPreferences.js";
|
|
77
81
|
import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js";
|
|
78
82
|
import {ZERO_HASH} from "../../../constants/index.js";
|
|
79
83
|
import {BuilderStatus, NoBidReceived} from "../../../execution/builder/http.js";
|
|
80
84
|
import {validateGossipFnRetryUnknownRoot} from "../../../network/processor/gossipHandlers.js";
|
|
81
85
|
import {CommitteeSubscription} from "../../../network/subnets/index.js";
|
|
82
|
-
import {SyncState} from "../../../sync/index.js";
|
|
83
86
|
import {callInNextEventLoop} from "../../../util/eventLoop.js";
|
|
84
87
|
import {isOptimisticBlock} from "../../../util/forkChoice.js";
|
|
85
|
-
import {
|
|
88
|
+
import {getBlockGraffiti, toGraffitiBytes} from "../../../util/graffiti.js";
|
|
86
89
|
import {getLodestarClientVersion} from "../../../util/metadata.js";
|
|
87
90
|
import {ApiOptions} from "../../options.js";
|
|
88
91
|
import {getStateResponseWithRegen} from "../beacon/state/utils.js";
|
|
89
92
|
import {ApiError, FailureList, IndexedError, NodeIsSyncing, OnlySupportedByDVT} from "../errors.js";
|
|
90
93
|
import {ApiModules} from "../types.js";
|
|
94
|
+
import {notWhileSyncing} from "../utils.js";
|
|
91
95
|
import {computeSubnetForCommitteesAtSlot, getPubkeysForIndices, selectBlockProductionSource} from "./utils.js";
|
|
92
96
|
|
|
93
|
-
/**
|
|
94
|
-
* If the node is within this many epochs from the head, we declare it to be synced regardless of
|
|
95
|
-
* the network sync state.
|
|
96
|
-
*
|
|
97
|
-
* This helps prevent attacks where nodes can convince us that we're syncing some non-existent
|
|
98
|
-
* finalized head.
|
|
99
|
-
*
|
|
100
|
-
* TODO: Lighthouse uses 8 for the attack described above. However, 8 kills Lodestar since validators
|
|
101
|
-
* can trigger regen to fast-forward head state 8 epochs to be immediately invalidated as sync sets
|
|
102
|
-
* a new head. Then the checkpoint state cache grows unbounded with very different states (because
|
|
103
|
-
* they are 8 epochs apart) and causes an OOM. Research a proper solution once regen and the state
|
|
104
|
-
* caches are better.
|
|
105
|
-
*/
|
|
106
|
-
export const SYNC_TOLERANCE_EPOCHS = 1;
|
|
107
|
-
|
|
108
97
|
/**
|
|
109
98
|
* Cutoff time to wait from start of the slot for execution and builder block production apis to resolve.
|
|
110
99
|
* Post this time, race execution and builder to pick whatever resolves first.
|
|
@@ -113,7 +102,7 @@ export const SYNC_TOLERANCE_EPOCHS = 1;
|
|
|
113
102
|
* A cutoff of 2 seconds gives enough time and if there are unexpected delays it ensures we publish
|
|
114
103
|
* in time as proposals post 4 seconds into the slot will likely be orphaned due to proposer boost reorg.
|
|
115
104
|
*
|
|
116
|
-
* TODO GLOAS: re-evaluate cutoff timing
|
|
105
|
+
* TODO GLOAS: re-evaluate cutoff timing due to attestation deadline changes in gloas
|
|
117
106
|
*/
|
|
118
107
|
const BLOCK_PRODUCTION_RACE_CUTOFF_MS = 2_000;
|
|
119
108
|
/** Overall timeout for execution and block production apis */
|
|
@@ -338,36 +327,6 @@ export function getValidatorApi(
|
|
|
338
327
|
return null;
|
|
339
328
|
}
|
|
340
329
|
|
|
341
|
-
/**
|
|
342
|
-
* Reject any request while the node is syncing
|
|
343
|
-
*/
|
|
344
|
-
function notWhileSyncing(): void {
|
|
345
|
-
// Consider node synced before or close to genesis
|
|
346
|
-
if (chain.clock.currentSlot < SLOTS_PER_EPOCH) {
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const syncState = sync.state;
|
|
351
|
-
switch (syncState) {
|
|
352
|
-
case SyncState.SyncingFinalized:
|
|
353
|
-
case SyncState.SyncingHead: {
|
|
354
|
-
const currentSlot = chain.clock.currentSlot;
|
|
355
|
-
const headSlot = chain.forkChoice.getHead().slot;
|
|
356
|
-
if (currentSlot - headSlot > SYNC_TOLERANCE_EPOCHS * SLOTS_PER_EPOCH) {
|
|
357
|
-
throw new NodeIsSyncing(`headSlot ${headSlot} currentSlot ${currentSlot}`);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
case SyncState.Synced:
|
|
364
|
-
return;
|
|
365
|
-
|
|
366
|
-
case SyncState.Stalled:
|
|
367
|
-
throw new NodeIsSyncing("waiting for peers");
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
330
|
/**
|
|
372
331
|
* Post merge, the CL and EL could be out of step in the sync, and could result in
|
|
373
332
|
* Syncing status of the chain head. To be precise:
|
|
@@ -570,7 +529,17 @@ export function getValidatorApi(
|
|
|
570
529
|
builderBoostFactor?: bigint,
|
|
571
530
|
{feeRecipient, builderSelection, strictFeeRecipientCheck}: routes.validator.ExtraProduceBlockOpts = {}
|
|
572
531
|
): Promise<ProduceBlindedBlockOrBlockContentsRes> {
|
|
573
|
-
|
|
532
|
+
builderSelection = builderSelection ?? routes.validator.BuilderSelection.MaxProfit;
|
|
533
|
+
if (builderSelection === routes.validator.BuilderSelection.BuilderOnly) {
|
|
534
|
+
logger.warn("Builder selection builderonly is no longer supported, treating as builderalways");
|
|
535
|
+
builderSelection = routes.validator.BuilderSelection.BuilderAlways;
|
|
536
|
+
}
|
|
537
|
+
builderBoostFactor = builderBoostFactor ?? BigInt(100);
|
|
538
|
+
if (builderBoostFactor > MAX_BUILDER_BOOST_FACTOR) {
|
|
539
|
+
throw new ApiError(400, `Invalid builderBoostFactor=${builderBoostFactor} > MAX_BUILDER_BOOST_FACTOR`);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
notWhileSyncing(chain, sync.state);
|
|
574
543
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
575
544
|
|
|
576
545
|
const parentBlock = chain.getProposerHead(slot);
|
|
@@ -580,38 +549,17 @@ export function getValidatorApi(
|
|
|
580
549
|
metrics?.blockProductionSlotDelta.set(slot - parentSlot);
|
|
581
550
|
|
|
582
551
|
const fork = config.getForkName(slot);
|
|
583
|
-
// set some sensible opts
|
|
584
|
-
// builderSelection will be deprecated and will run in mode MaxProfit if builder is enabled
|
|
585
|
-
// and the actual selection will be determined using builderBoostFactor passed by the validator
|
|
586
|
-
builderSelection = builderSelection ?? routes.validator.BuilderSelection.MaxProfit;
|
|
587
|
-
builderBoostFactor = builderBoostFactor ?? BigInt(100);
|
|
588
|
-
if (builderBoostFactor > MAX_BUILDER_BOOST_FACTOR) {
|
|
589
|
-
throw new ApiError(400, `Invalid builderBoostFactor=${builderBoostFactor} > MAX_BUILDER_BOOST_FACTOR`);
|
|
590
|
-
}
|
|
591
552
|
|
|
592
553
|
const isBuilderEnabled =
|
|
593
554
|
ForkSeq[fork] >= ForkSeq.bellatrix &&
|
|
594
555
|
chain.executionBuilder !== undefined &&
|
|
595
556
|
builderSelection !== routes.validator.BuilderSelection.ExecutionOnly;
|
|
596
557
|
|
|
597
|
-
// At any point either the builder or execution or both flows should be active.
|
|
598
|
-
//
|
|
599
|
-
// Ideally such a scenario should be prevented on startup, but proposerSettingsFile or keymanager
|
|
600
|
-
// configurations could cause a validator pubkey to have builder disabled with builder selection builder only
|
|
601
|
-
// (TODO: independently make sure such an options update is not successful for a validator pubkey)
|
|
602
|
-
//
|
|
603
|
-
// So if builder is disabled ignore builder selection of builder only if caused by user mistake
|
|
604
|
-
// https://github.com/ChainSafe/lodestar/issues/6338
|
|
605
|
-
const isEngineEnabled = !isBuilderEnabled || builderSelection !== routes.validator.BuilderSelection.BuilderOnly;
|
|
606
|
-
|
|
607
|
-
if (!isEngineEnabled && !isBuilderEnabled) {
|
|
608
|
-
throw Error(
|
|
609
|
-
`Internal Error: Neither builder nor execution proposal flow activated isBuilderEnabled=${isBuilderEnabled} builderSelection=${builderSelection}`
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
558
|
const graffitiBytes = toGraffitiBytes(
|
|
614
|
-
graffiti
|
|
559
|
+
getBlockGraffiti(graffiti, getLodestarClientVersion(opts), chain.executionEngine.clientVersion, {
|
|
560
|
+
private: opts.private,
|
|
561
|
+
graffitiAppend: chain.opts.graffitiAppend,
|
|
562
|
+
})
|
|
615
563
|
);
|
|
616
564
|
|
|
617
565
|
const loggerContext = {
|
|
@@ -621,7 +569,6 @@ export function getValidatorApi(
|
|
|
621
569
|
fork,
|
|
622
570
|
builderSelection,
|
|
623
571
|
isBuilderEnabled,
|
|
624
|
-
isEngineEnabled,
|
|
625
572
|
strictFeeRecipientCheck,
|
|
626
573
|
// winston logger doesn't like bigint
|
|
627
574
|
builderBoostFactor: `${builderBoostFactor}`,
|
|
@@ -647,27 +594,25 @@ export function getValidatorApi(
|
|
|
647
594
|
})
|
|
648
595
|
: Promise.reject(new Error("Builder disabled"));
|
|
649
596
|
|
|
650
|
-
const enginePromise =
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
})
|
|
670
|
-
: Promise.reject(new Error("Engine disabled"));
|
|
597
|
+
const enginePromise = produceEngineBlockContents(slot, randaoReveal, graffitiBytes, {
|
|
598
|
+
feeRecipient,
|
|
599
|
+
strictFeeRecipientCheck,
|
|
600
|
+
commonBlockBodyPromise,
|
|
601
|
+
parentBlock,
|
|
602
|
+
}).then((engineBlock) => {
|
|
603
|
+
// Once the engine returns a block, in the event of either:
|
|
604
|
+
// - suspected builder censorship
|
|
605
|
+
// - builder boost factor set to 0 or builder selection `executionalways`
|
|
606
|
+
// we don't need to wait for builder block as engine block will always be selected
|
|
607
|
+
if (
|
|
608
|
+
engineBlock.shouldOverrideBuilder ||
|
|
609
|
+
builderBoostFactor === BigInt(0) ||
|
|
610
|
+
builderSelection === routes.validator.BuilderSelection.ExecutionAlways
|
|
611
|
+
) {
|
|
612
|
+
controller.abort();
|
|
613
|
+
}
|
|
614
|
+
return engineBlock;
|
|
615
|
+
});
|
|
671
616
|
|
|
672
617
|
// Calculate cutoff time based on start of the slot
|
|
673
618
|
const cutoffMs = Math.max(0, BLOCK_PRODUCTION_RACE_CUTOFF_MS - chain.clock.msFromSlot(slot));
|
|
@@ -714,30 +659,24 @@ export function getValidatorApi(
|
|
|
714
659
|
throw Error("Builder and engine both failed to produce the block within timeout");
|
|
715
660
|
}
|
|
716
661
|
|
|
717
|
-
if (builder.status === "pending" && !isEngineEnabled) {
|
|
718
|
-
throw Error("Builder failed to produce the block within timeout");
|
|
719
|
-
}
|
|
720
|
-
|
|
721
662
|
if (engine.status === "pending" && !isBuilderEnabled) {
|
|
722
663
|
throw Error("Engine failed to produce the block within timeout");
|
|
723
664
|
}
|
|
724
665
|
|
|
725
|
-
if (
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
{
|
|
730
|
-
...loggerContext,
|
|
731
|
-
durationMs: engine.durationMs,
|
|
732
|
-
},
|
|
733
|
-
engine.reason
|
|
734
|
-
);
|
|
735
|
-
} else if (engine.status === "pending") {
|
|
736
|
-
logger.warn("Engine failed to produce the block within cutoff time", {
|
|
666
|
+
if (engine.status === "rejected") {
|
|
667
|
+
logger.warn(
|
|
668
|
+
"Engine failed to produce the block",
|
|
669
|
+
{
|
|
737
670
|
...loggerContext,
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
|
|
671
|
+
durationMs: engine.durationMs,
|
|
672
|
+
},
|
|
673
|
+
engine.reason
|
|
674
|
+
);
|
|
675
|
+
} else if (engine.status === "pending") {
|
|
676
|
+
logger.warn("Engine failed to produce the block within cutoff time", {
|
|
677
|
+
...loggerContext,
|
|
678
|
+
cutoffMs,
|
|
679
|
+
});
|
|
741
680
|
}
|
|
742
681
|
|
|
743
682
|
if (isBuilderEnabled) {
|
|
@@ -766,14 +705,12 @@ export function getValidatorApi(
|
|
|
766
705
|
}
|
|
767
706
|
|
|
768
707
|
if (builder.status === "rejected" && engine.status === "rejected") {
|
|
769
|
-
throw Error(
|
|
770
|
-
`${isBuilderEnabled && isEngineEnabled ? "Builder and engine both" : isBuilderEnabled ? "Builder" : "Engine"} failed to produce the block`
|
|
771
|
-
);
|
|
708
|
+
throw Error(`${isBuilderEnabled ? "Builder and engine both" : "Engine"} failed to produce the block`);
|
|
772
709
|
}
|
|
773
710
|
|
|
774
711
|
// handle shouldOverrideBuilder separately
|
|
775
|
-
if (engine.status === "fulfilled" && engine.value.shouldOverrideBuilder) {
|
|
776
|
-
logger.
|
|
712
|
+
if (engine.status === "fulfilled" && engine.value.shouldOverrideBuilder && isBuilderEnabled) {
|
|
713
|
+
logger.warn("Selected engine block: censorship suspected in builder blocks", {
|
|
777
714
|
...loggerContext,
|
|
778
715
|
durationMs: engine.durationMs,
|
|
779
716
|
shouldOverrideBuilder: engine.value.shouldOverrideBuilder,
|
|
@@ -790,11 +727,9 @@ export function getValidatorApi(
|
|
|
790
727
|
|
|
791
728
|
if (builder.status === "fulfilled" && engine.status !== "fulfilled") {
|
|
792
729
|
const reason =
|
|
793
|
-
|
|
794
|
-
? BuilderBlockSelectionReason.
|
|
795
|
-
:
|
|
796
|
-
? BuilderBlockSelectionReason.EnginePending
|
|
797
|
-
: BuilderBlockSelectionReason.EngineError;
|
|
730
|
+
engine.status === "pending"
|
|
731
|
+
? BuilderBlockSelectionReason.EnginePending
|
|
732
|
+
: BuilderBlockSelectionReason.EngineError;
|
|
798
733
|
|
|
799
734
|
logger.info("Selected builder block: no engine block produced", {
|
|
800
735
|
reason,
|
|
@@ -904,14 +839,32 @@ export function getValidatorApi(
|
|
|
904
839
|
return {data, meta};
|
|
905
840
|
},
|
|
906
841
|
|
|
907
|
-
async produceBlockV4({
|
|
842
|
+
async produceBlockV4({
|
|
843
|
+
slot,
|
|
844
|
+
randaoReveal,
|
|
845
|
+
graffiti,
|
|
846
|
+
feeRecipient,
|
|
847
|
+
includePayload,
|
|
848
|
+
builderSelection,
|
|
849
|
+
builderBoostFactor,
|
|
850
|
+
}) {
|
|
908
851
|
const fork = config.getForkName(slot);
|
|
909
852
|
|
|
910
853
|
if (!isForkPostGloas(fork)) {
|
|
911
854
|
throw new ApiError(400, `produceBlockV4 not supported for pre-gloas fork=${fork}`);
|
|
912
855
|
}
|
|
913
856
|
|
|
914
|
-
|
|
857
|
+
builderSelection = builderSelection ?? routes.validator.BuilderSelection.MaxProfit;
|
|
858
|
+
if (builderSelection === routes.validator.BuilderSelection.BuilderOnly) {
|
|
859
|
+
logger.warn("Builder selection builderonly is no longer supported, treating as builderalways");
|
|
860
|
+
builderSelection = routes.validator.BuilderSelection.BuilderAlways;
|
|
861
|
+
}
|
|
862
|
+
builderBoostFactor = builderBoostFactor ?? BigInt(100);
|
|
863
|
+
if (builderBoostFactor > MAX_BUILDER_BOOST_FACTOR) {
|
|
864
|
+
throw new ApiError(400, `Invalid builderBoostFactor=${builderBoostFactor} > MAX_BUILDER_BOOST_FACTOR`);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
notWhileSyncing(chain, sync.state);
|
|
915
868
|
await waitForSlot(slot);
|
|
916
869
|
|
|
917
870
|
const parentBlock = chain.getProposerHead(slot);
|
|
@@ -921,15 +874,23 @@ export function getValidatorApi(
|
|
|
921
874
|
metrics?.blockProductionSlotDelta.set(slot - parentSlot);
|
|
922
875
|
|
|
923
876
|
const graffitiBytes = toGraffitiBytes(
|
|
924
|
-
graffiti
|
|
877
|
+
getBlockGraffiti(graffiti, getLodestarClientVersion(opts), chain.executionEngine.clientVersion, {
|
|
878
|
+
private: opts.private,
|
|
879
|
+
graffitiAppend: chain.opts.graffitiAppend,
|
|
880
|
+
})
|
|
925
881
|
);
|
|
926
882
|
|
|
927
|
-
// TODO GLOAS:
|
|
928
|
-
// the user control bid source preferences and value comparison. Also add external builder api
|
|
929
|
-
// support when it is implemented.
|
|
883
|
+
// TODO GLOAS: add external builder api support when it is implemented
|
|
930
884
|
const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
|
|
931
885
|
const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
|
|
932
|
-
|
|
886
|
+
// Bids are only skipped entirely with executiononly or while the circuit breaker is active,
|
|
887
|
+
// other engine-preferring selections still build a block with the best bid as fallback in
|
|
888
|
+
// case local production fails
|
|
889
|
+
const circuitBreakerActive = chain.builderCircuitBreaker.isActive(slot);
|
|
890
|
+
const builderBid =
|
|
891
|
+
builderSelection === routes.validator.BuilderSelection.ExecutionOnly || circuitBreakerActive
|
|
892
|
+
? null
|
|
893
|
+
: chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
|
|
933
894
|
|
|
934
895
|
const logCtx = {
|
|
935
896
|
slot,
|
|
@@ -937,6 +898,9 @@ export function getValidatorApi(
|
|
|
937
898
|
parentBlockRoot: parentBlockRootHex,
|
|
938
899
|
parentBlockHash: parentBlock.executionPayloadBlockHash,
|
|
939
900
|
fork,
|
|
901
|
+
builderSelection,
|
|
902
|
+
builderBoostFactor,
|
|
903
|
+
circuitBreakerActive,
|
|
940
904
|
...(builderBid !== null
|
|
941
905
|
? {
|
|
942
906
|
bidValue: builderBid.message.value,
|
|
@@ -972,33 +936,101 @@ export function getValidatorApi(
|
|
|
972
936
|
return fn().finally(() => t?.({source}));
|
|
973
937
|
};
|
|
974
938
|
|
|
975
|
-
//
|
|
976
|
-
|
|
977
|
-
|
|
939
|
+
// Calculate cutoff time based on start of the slot, ensures a slow local payload build does
|
|
940
|
+
// not delay the proposal when a builder bid block is available (and vice versa)
|
|
941
|
+
const cutoffMs = Math.max(0, BLOCK_PRODUCTION_RACE_CUTOFF_MS - chain.clock.msFromSlot(slot));
|
|
942
|
+
|
|
943
|
+
// use abort controller to stop waiting for the bid block if the engine block will be selected
|
|
944
|
+
const controller = new AbortController();
|
|
945
|
+
|
|
946
|
+
const enginePromise: ReturnType<typeof chain.produceBlock> = timed(ProducedBlockSource.engine, () =>
|
|
947
|
+
chain.produceBlock(baseAttrs)
|
|
948
|
+
).then((engineBlock) => {
|
|
949
|
+
// No need to wait for the bid block if the engine block will always be selected due to
|
|
950
|
+
// suspected builder censorship, a builder boost factor of 0 or executionalways selection
|
|
951
|
+
if (
|
|
952
|
+
engineBlock.shouldOverrideBuilder ||
|
|
953
|
+
builderBoostFactor === BigInt(0) ||
|
|
954
|
+
builderSelection === routes.validator.BuilderSelection.ExecutionAlways
|
|
955
|
+
) {
|
|
956
|
+
controller.abort();
|
|
957
|
+
}
|
|
958
|
+
return engineBlock;
|
|
959
|
+
});
|
|
960
|
+
const bidPromise: ReturnType<typeof chain.produceBlock> =
|
|
978
961
|
builderBid !== null
|
|
979
962
|
? timed(ProducedBlockSource.builder, () => chain.produceBlock({...baseAttrs, builderBid}))
|
|
980
|
-
: Promise.reject()
|
|
981
|
-
|
|
963
|
+
: Promise.reject(new Error("No builder bid available"));
|
|
964
|
+
|
|
965
|
+
const [engineResult, bidResult] = await resolveOrRacePromises([enginePromise, bidPromise], {
|
|
966
|
+
resolveTimeoutMs: cutoffMs,
|
|
967
|
+
raceTimeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS,
|
|
968
|
+
signal: controller.signal,
|
|
969
|
+
});
|
|
982
970
|
|
|
983
971
|
let bestResult: typeof engineResult | null = null;
|
|
984
972
|
let source: ProducedBlockSource = ProducedBlockSource.engine;
|
|
985
|
-
|
|
973
|
+
|
|
974
|
+
// handle shouldOverrideBuilder separately
|
|
975
|
+
if (engineResult.status === "fulfilled" && engineResult.value.shouldOverrideBuilder && builderBid !== null) {
|
|
976
|
+
source = ProducedBlockSource.engine;
|
|
977
|
+
bestResult = engineResult;
|
|
978
|
+
metrics?.blockProductionSelectionResults.inc({
|
|
979
|
+
source: ProducedBlockSource.engine,
|
|
980
|
+
reason: EngineBlockSelectionReason.BuilderCensorship,
|
|
981
|
+
});
|
|
982
|
+
logger.warn("Selected local block: censorship suspected in builder bid", logCtx);
|
|
983
|
+
} else if (engineResult.status === "fulfilled" && bidResult.status === "fulfilled") {
|
|
984
|
+
const result = selectBlockProductionSource({
|
|
985
|
+
builderSelection,
|
|
986
|
+
builderBoostFactor,
|
|
987
|
+
engineExecutionPayloadValue: engineResult.value.executionPayloadValue,
|
|
988
|
+
// The bid value is the payment to the proposer, in Gwei
|
|
989
|
+
builderExecutionPayloadValue: BigInt(builderBid?.message.value ?? 0) * GWEI_TO_WEI,
|
|
990
|
+
});
|
|
991
|
+
source = result.source;
|
|
992
|
+
metrics?.blockProductionSelectionResults.inc(result);
|
|
993
|
+
logger.info(`Selected ${source} block`, {reason: result.reason, ...logCtx});
|
|
994
|
+
bestResult = source === ProducedBlockSource.builder ? bidResult : engineResult;
|
|
995
|
+
} else if (bidResult.status === "fulfilled") {
|
|
986
996
|
source = ProducedBlockSource.builder;
|
|
987
997
|
bestResult = bidResult;
|
|
988
|
-
|
|
998
|
+
const reason =
|
|
999
|
+
engineResult.status === "pending"
|
|
1000
|
+
? BuilderBlockSelectionReason.EnginePending
|
|
1001
|
+
: BuilderBlockSelectionReason.EngineError;
|
|
1002
|
+
metrics?.blockProductionSelectionResults.inc({source: ProducedBlockSource.builder, reason});
|
|
1003
|
+
logger.info("Selected builder bid block: no local block produced", {
|
|
1004
|
+
reason,
|
|
1005
|
+
...logCtx,
|
|
1006
|
+
error: engineResult.status === "rejected" ? (engineResult.reason as Error).message : undefined,
|
|
1007
|
+
});
|
|
989
1008
|
} else if (engineResult.status === "fulfilled") {
|
|
990
1009
|
source = ProducedBlockSource.engine;
|
|
991
1010
|
bestResult = engineResult;
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1011
|
+
const reason =
|
|
1012
|
+
builderBid === null
|
|
1013
|
+
? EngineBlockSelectionReason.BuilderNoBid
|
|
1014
|
+
: bidResult.status === "pending"
|
|
1015
|
+
? EngineBlockSelectionReason.BuilderPending
|
|
1016
|
+
: EngineBlockSelectionReason.BuilderError;
|
|
1017
|
+
metrics?.blockProductionSelectionResults.inc({source: ProducedBlockSource.engine, reason});
|
|
1018
|
+
logger.info("Selected local block: no builder bid block produced", {
|
|
1019
|
+
reason,
|
|
1020
|
+
...logCtx,
|
|
1021
|
+
error: bidResult.status === "rejected" ? (bidResult.reason as Error).message : undefined,
|
|
1022
|
+
});
|
|
995
1023
|
}
|
|
996
1024
|
|
|
997
1025
|
if (bestResult === null || bestResult.status !== "fulfilled") {
|
|
998
|
-
const engineReason = engineResult.status === "rejected" ? engineResult.reason :
|
|
999
|
-
const bidReason =
|
|
1000
|
-
logger.error("Block production failed", {
|
|
1001
|
-
|
|
1026
|
+
const engineReason = engineResult.status === "rejected" ? engineResult.reason : engineResult.status;
|
|
1027
|
+
const bidReason = bidResult.status === "rejected" ? bidResult.reason : bidResult.status;
|
|
1028
|
+
logger.error("Block production failed", {
|
|
1029
|
+
...logCtx,
|
|
1030
|
+
engineReason: String(engineReason),
|
|
1031
|
+
bidReason: String(bidReason),
|
|
1032
|
+
});
|
|
1033
|
+
throw Error(`Block production failed: engine=${String(engineReason)} builder=${String(bidReason)}`);
|
|
1002
1034
|
}
|
|
1003
1035
|
|
|
1004
1036
|
const {block, executionPayloadValue, consensusBlockValue} = bestResult.value;
|
|
@@ -1019,14 +1051,46 @@ export function getValidatorApi(
|
|
|
1019
1051
|
void chain.persistBlock(block, "produced_engine_block");
|
|
1020
1052
|
}
|
|
1021
1053
|
|
|
1054
|
+
// Include the payload for self-builds unless disabled (stateless flow)
|
|
1055
|
+
const isSelfBuild = source === ProducedBlockSource.engine;
|
|
1056
|
+
if (isSelfBuild && includePayload) {
|
|
1057
|
+
const produceResult = chain.blockProductionCache.get(blockRoot);
|
|
1058
|
+
if (
|
|
1059
|
+
produceResult === undefined ||
|
|
1060
|
+
!isForkPostGloas(produceResult.fork) ||
|
|
1061
|
+
produceResult.type !== BlockType.Full
|
|
1062
|
+
) {
|
|
1063
|
+
throw Error(`Missing cached block production result for produced block slot=${slot} blockRoot=${blockRoot}`);
|
|
1064
|
+
}
|
|
1065
|
+
const {executionPayload, executionRequests, blobsBundle, parentBlockRoot} = produceResult as ProduceFullGloas;
|
|
1066
|
+
|
|
1067
|
+
const blockContents: gloas.BlockContents = {
|
|
1068
|
+
block: block as gloas.BeaconBlock,
|
|
1069
|
+
executionPayloadEnvelope: {
|
|
1070
|
+
payload: executionPayload,
|
|
1071
|
+
executionRequests,
|
|
1072
|
+
builderIndex: BUILDER_INDEX_SELF_BUILD,
|
|
1073
|
+
beaconBlockRoot: fromHex(blockRoot),
|
|
1074
|
+
parentBeaconBlockRoot: parentBlockRoot,
|
|
1075
|
+
},
|
|
1076
|
+
kzgProofs: blobsBundle.proofs,
|
|
1077
|
+
blobs: blobsBundle.blobs,
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
return {
|
|
1081
|
+
data: blockContents,
|
|
1082
|
+
meta: {version: fork, consensusBlockValue, executionPayloadValue, executionPayloadIncluded: true},
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1022
1086
|
return {
|
|
1023
1087
|
data: block as gloas.BeaconBlock,
|
|
1024
|
-
meta: {version: fork, consensusBlockValue},
|
|
1088
|
+
meta: {version: fork, consensusBlockValue, executionPayloadValue, executionPayloadIncluded: false},
|
|
1025
1089
|
};
|
|
1026
1090
|
},
|
|
1027
1091
|
|
|
1028
1092
|
async produceAttestationData({committeeIndex, slot}) {
|
|
1029
|
-
notWhileSyncing();
|
|
1093
|
+
notWhileSyncing(chain, sync.state);
|
|
1030
1094
|
|
|
1031
1095
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
1032
1096
|
|
|
@@ -1107,13 +1171,13 @@ export function getValidatorApi(
|
|
|
1107
1171
|
throw new ApiError(400, `producePayloadAttestationData is not supported before Gloas fork=${fork}`);
|
|
1108
1172
|
}
|
|
1109
1173
|
|
|
1110
|
-
notWhileSyncing();
|
|
1174
|
+
notWhileSyncing(chain, sync.state);
|
|
1111
1175
|
await waitForSlot(slot);
|
|
1112
1176
|
|
|
1113
1177
|
const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
1114
1178
|
if (!block) {
|
|
1115
|
-
// No block is seen at slot. Return
|
|
1116
|
-
|
|
1179
|
+
// No canonical block is seen at slot. Return 204 so vc can skip casting payload attestation.
|
|
1180
|
+
return {data: undefined, meta: {version: fork}, status: 204};
|
|
1117
1181
|
}
|
|
1118
1182
|
|
|
1119
1183
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
|
|
@@ -1192,7 +1256,7 @@ export function getValidatorApi(
|
|
|
1192
1256
|
},
|
|
1193
1257
|
|
|
1194
1258
|
async getProposerDuties({epoch}, _context, opts?: {v2?: boolean}) {
|
|
1195
|
-
notWhileSyncing();
|
|
1259
|
+
notWhileSyncing(chain, sync.state);
|
|
1196
1260
|
|
|
1197
1261
|
const currentEpoch = currentEpochWithDisparity();
|
|
1198
1262
|
const nextEpoch = currentEpoch + 1;
|
|
@@ -1239,7 +1303,7 @@ export function getValidatorApi(
|
|
|
1239
1303
|
// requested epoch is within that range, we can use the head state at current epoch
|
|
1240
1304
|
state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.getDuties);
|
|
1241
1305
|
} else {
|
|
1242
|
-
const res = await getStateResponseWithRegen(chain, startSlot);
|
|
1306
|
+
const res = await getStateResponseWithRegen(chain, sync, startSlot);
|
|
1243
1307
|
|
|
1244
1308
|
state = res.state instanceof Uint8Array ? chain.getHeadState().loadOtherState(res.state) : res.state;
|
|
1245
1309
|
|
|
@@ -1333,7 +1397,7 @@ export function getValidatorApi(
|
|
|
1333
1397
|
},
|
|
1334
1398
|
|
|
1335
1399
|
async getAttesterDuties({epoch, indices}) {
|
|
1336
|
-
notWhileSyncing();
|
|
1400
|
+
notWhileSyncing(chain, sync.state);
|
|
1337
1401
|
|
|
1338
1402
|
if (indices.length === 0) {
|
|
1339
1403
|
throw new ApiError(400, "No validator to get attester duties");
|
|
@@ -1393,7 +1457,7 @@ export function getValidatorApi(
|
|
|
1393
1457
|
},
|
|
1394
1458
|
|
|
1395
1459
|
async getPtcDuties({epoch, indices}) {
|
|
1396
|
-
notWhileSyncing();
|
|
1460
|
+
notWhileSyncing(chain, sync.state);
|
|
1397
1461
|
|
|
1398
1462
|
if (indices.length === 0) {
|
|
1399
1463
|
throw new ApiError(400, "No validator to get PTC duties");
|
|
@@ -1455,7 +1519,7 @@ export function getValidatorApi(
|
|
|
1455
1519
|
* @param validatorIndices an array of the validator indices for which to obtain the duties.
|
|
1456
1520
|
*/
|
|
1457
1521
|
async getSyncCommitteeDuties({epoch, indices}) {
|
|
1458
|
-
notWhileSyncing();
|
|
1522
|
+
notWhileSyncing(chain, sync.state);
|
|
1459
1523
|
|
|
1460
1524
|
if (indices.length === 0) {
|
|
1461
1525
|
throw new ApiError(400, "No validator to get attester duties");
|
|
@@ -1502,7 +1566,7 @@ export function getValidatorApi(
|
|
|
1502
1566
|
},
|
|
1503
1567
|
|
|
1504
1568
|
async getAggregatedAttestationV2({attestationDataRoot, slot, committeeIndex}) {
|
|
1505
|
-
notWhileSyncing();
|
|
1569
|
+
notWhileSyncing(chain, sync.state);
|
|
1506
1570
|
|
|
1507
1571
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
1508
1572
|
|
|
@@ -1525,7 +1589,7 @@ export function getValidatorApi(
|
|
|
1525
1589
|
},
|
|
1526
1590
|
|
|
1527
1591
|
async publishAggregateAndProofsV2({signedAggregateAndProofs}) {
|
|
1528
|
-
notWhileSyncing();
|
|
1592
|
+
notWhileSyncing(chain, sync.state);
|
|
1529
1593
|
|
|
1530
1594
|
const seenTimestampSec = Date.now() / 1000;
|
|
1531
1595
|
const failures: FailureList = [];
|
|
@@ -1586,7 +1650,7 @@ export function getValidatorApi(
|
|
|
1586
1650
|
* https://github.com/ethereum/beacon-APIs/pull/137
|
|
1587
1651
|
*/
|
|
1588
1652
|
async publishContributionAndProofs({contributionAndProofs}) {
|
|
1589
|
-
notWhileSyncing();
|
|
1653
|
+
notWhileSyncing(chain, sync.state);
|
|
1590
1654
|
|
|
1591
1655
|
const failures: FailureList = [];
|
|
1592
1656
|
|
|
@@ -1635,7 +1699,7 @@ export function getValidatorApi(
|
|
|
1635
1699
|
},
|
|
1636
1700
|
|
|
1637
1701
|
async prepareBeaconCommitteeSubnet({subscriptions}) {
|
|
1638
|
-
notWhileSyncing();
|
|
1702
|
+
notWhileSyncing(chain, sync.state);
|
|
1639
1703
|
|
|
1640
1704
|
await network.prepareBeaconCommitteeSubnets(
|
|
1641
1705
|
subscriptions.map(({validatorIndex, slot, isAggregator, committeesAtSlot, committeeIndex}) => ({
|
|
@@ -1668,7 +1732,7 @@ export function getValidatorApi(
|
|
|
1668
1732
|
* https://github.com/ethereum/beacon-APIs/pull/136
|
|
1669
1733
|
*/
|
|
1670
1734
|
async prepareSyncCommitteeSubnets({subscriptions}) {
|
|
1671
|
-
notWhileSyncing();
|
|
1735
|
+
notWhileSyncing(chain, sync.state);
|
|
1672
1736
|
|
|
1673
1737
|
// A `validatorIndex` can be in multiple subnets, so compute the CommitteeSubscription with double for loop
|
|
1674
1738
|
const subs: CommitteeSubscription[] = [];
|
|
@@ -1765,6 +1829,46 @@ export function getValidatorApi(
|
|
|
1765
1829
|
});
|
|
1766
1830
|
},
|
|
1767
1831
|
|
|
1832
|
+
async submitProposerPreferences({signedProposerPreferences}) {
|
|
1833
|
+
const failures: FailureList = [];
|
|
1834
|
+
|
|
1835
|
+
await Promise.all(
|
|
1836
|
+
signedProposerPreferences.map(async (signed, i) => {
|
|
1837
|
+
try {
|
|
1838
|
+
await validateGossipProposerPreferences(chain, signed);
|
|
1839
|
+
|
|
1840
|
+
chain.proposerPreferencesPool.add(signed);
|
|
1841
|
+
await network.publishProposerPreferences(signed);
|
|
1842
|
+
chain.emitter.emit(routes.events.EventType.proposerPreferences, {
|
|
1843
|
+
version: config.getForkName(signed.message.proposalSlot),
|
|
1844
|
+
data: signed,
|
|
1845
|
+
});
|
|
1846
|
+
} catch (e) {
|
|
1847
|
+
const logCtx = {
|
|
1848
|
+
slot: signed.message.proposalSlot,
|
|
1849
|
+
validatorIndex: signed.message.validatorIndex,
|
|
1850
|
+
dependentRoot: toRootHex(signed.message.dependentRoot),
|
|
1851
|
+
};
|
|
1852
|
+
|
|
1853
|
+
if (e instanceof ProposerPreferencesError && e.type.code === ProposerPreferencesErrorCode.ALREADY_KNOWN) {
|
|
1854
|
+
logger.debug("Ignoring known signed proposer preferences", logCtx);
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
failures.push({index: i, message: (e as Error).message});
|
|
1859
|
+
logger.verbose(`Error on submitProposerPreferences [${i}]`, logCtx, e as Error);
|
|
1860
|
+
if (e instanceof ProposerPreferencesError && e.action === GossipAction.REJECT) {
|
|
1861
|
+
chain.persistInvalidSszValue(ssz.gloas.SignedProposerPreferences, signed, "api_reject");
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
})
|
|
1865
|
+
);
|
|
1866
|
+
|
|
1867
|
+
if (failures.length > 0) {
|
|
1868
|
+
throw new IndexedError("Error processing signed proposer preferences", failures);
|
|
1869
|
+
}
|
|
1870
|
+
},
|
|
1871
|
+
|
|
1768
1872
|
async getExecutionPayloadEnvelope({slot, beaconBlockRoot}) {
|
|
1769
1873
|
const fork = config.getForkName(slot);
|
|
1770
1874
|
|
|
@@ -1772,7 +1876,7 @@ export function getValidatorApi(
|
|
|
1772
1876
|
throw new ApiError(400, `getExecutionPayloadEnvelope not supported for pre-gloas fork=${fork}`);
|
|
1773
1877
|
}
|
|
1774
1878
|
|
|
1775
|
-
notWhileSyncing();
|
|
1879
|
+
notWhileSyncing(chain, sync.state);
|
|
1776
1880
|
await waitForSlot(slot);
|
|
1777
1881
|
|
|
1778
1882
|
const blockRootHex = toRootHex(beaconBlockRoot);
|
|
@@ -1790,6 +1894,18 @@ export function getValidatorApi(
|
|
|
1790
1894
|
|
|
1791
1895
|
const {executionPayload, executionRequests, parentBlockRoot} = produceResult as ProduceFullGloas;
|
|
1792
1896
|
|
|
1897
|
+
if (executionPayload === undefined) {
|
|
1898
|
+
// Blocks committing to a builder bid are cached as full but without payload data
|
|
1899
|
+
throw new ApiError(404, `No local execution payload cached for block root ${blockRootHex}`);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
if (executionPayload.slotNumber !== slot) {
|
|
1903
|
+
throw new ApiError(
|
|
1904
|
+
404,
|
|
1905
|
+
`Cached execution payload is for slot=${executionPayload.slotNumber}, requested slot=${slot}`
|
|
1906
|
+
);
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1793
1909
|
const envelope: gloas.ExecutionPayloadEnvelope = {
|
|
1794
1910
|
payload: executionPayload,
|
|
1795
1911
|
executionRequests: executionRequests,
|