@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
|
@@ -3,41 +3,28 @@ import { ExecutionStatus, PayloadStatus } from "@lodestar/fork-choice";
|
|
|
3
3
|
import { BUILDER_INDEX_SELF_BUILD, ForkName, ForkSeq, GENESIS_SLOT, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT, SYNC_COMMITTEE_SUBNET_SIZE, isForkPostBellatrix, isForkPostDeneb, isForkPostElectra, isForkPostFulu, isForkPostGloas, } from "@lodestar/params";
|
|
4
4
|
import { DataAvailabilityStatus, beaconBlockToBlinded, calculateCommitteeAssignments, computeEpochAtSlot, computeStartSlotAtEpoch, computeTimeAtSlot, getCurrentSlot, isStatePostAltair, isStatePostGloas, proposerShufflingDecisionRoot, } from "@lodestar/state-transition";
|
|
5
5
|
import { ProducedBlockSource, getValidatorStatus, ssz, } from "@lodestar/types";
|
|
6
|
-
import { TimeoutError, defer, formatWeiToEth, fromHex, prettyWeiToEth, resolveOrRacePromises, toHex, toRootHex, } from "@lodestar/utils";
|
|
6
|
+
import { GWEI_TO_WEI, TimeoutError, defer, formatWeiToEth, fromHex, prettyWeiToEth, resolveOrRacePromises, toHex, toRootHex, } from "@lodestar/utils";
|
|
7
7
|
import { MAX_BUILDER_BOOST_FACTOR } from "@lodestar/validator";
|
|
8
8
|
import { BlockInputSource } from "../../../chain/blocks/blockInput/types.js";
|
|
9
|
-
import { AttestationError, AttestationErrorCode, GossipAction, SyncCommitteeError, SyncCommitteeErrorCode, } from "../../../chain/errors/index.js";
|
|
9
|
+
import { AttestationError, AttestationErrorCode, GossipAction, ProposerPreferencesError, ProposerPreferencesErrorCode, SyncCommitteeError, SyncCommitteeErrorCode, } from "../../../chain/errors/index.js";
|
|
10
10
|
import { ChainEvent } from "../../../chain/index.js";
|
|
11
11
|
import { PREPARE_NEXT_SLOT_BPS } from "../../../chain/prepareNextSlot.js";
|
|
12
12
|
import { BlockType } from "../../../chain/produceBlock/index.js";
|
|
13
13
|
import { RegenCaller } from "../../../chain/regen/index.js";
|
|
14
14
|
import { validateApiAggregateAndProof } from "../../../chain/validation/index.js";
|
|
15
|
+
import { validateGossipProposerPreferences } from "../../../chain/validation/proposerPreferences.js";
|
|
15
16
|
import { validateSyncCommitteeGossipContributionAndProof } from "../../../chain/validation/syncCommitteeContributionAndProof.js";
|
|
16
17
|
import { ZERO_HASH } from "../../../constants/index.js";
|
|
17
18
|
import { BuilderStatus, NoBidReceived } from "../../../execution/builder/http.js";
|
|
18
19
|
import { validateGossipFnRetryUnknownRoot } from "../../../network/processor/gossipHandlers.js";
|
|
19
|
-
import { SyncState } from "../../../sync/index.js";
|
|
20
20
|
import { callInNextEventLoop } from "../../../util/eventLoop.js";
|
|
21
21
|
import { isOptimisticBlock } from "../../../util/forkChoice.js";
|
|
22
|
-
import {
|
|
22
|
+
import { getBlockGraffiti, toGraffitiBytes } from "../../../util/graffiti.js";
|
|
23
23
|
import { getLodestarClientVersion } from "../../../util/metadata.js";
|
|
24
24
|
import { getStateResponseWithRegen } from "../beacon/state/utils.js";
|
|
25
25
|
import { ApiError, IndexedError, NodeIsSyncing, OnlySupportedByDVT } from "../errors.js";
|
|
26
|
+
import { notWhileSyncing } from "../utils.js";
|
|
26
27
|
import { computeSubnetForCommitteesAtSlot, getPubkeysForIndices, selectBlockProductionSource } from "./utils.js";
|
|
27
|
-
/**
|
|
28
|
-
* If the node is within this many epochs from the head, we declare it to be synced regardless of
|
|
29
|
-
* the network sync state.
|
|
30
|
-
*
|
|
31
|
-
* This helps prevent attacks where nodes can convince us that we're syncing some non-existent
|
|
32
|
-
* finalized head.
|
|
33
|
-
*
|
|
34
|
-
* TODO: Lighthouse uses 8 for the attack described above. However, 8 kills Lodestar since validators
|
|
35
|
-
* can trigger regen to fast-forward head state 8 epochs to be immediately invalidated as sync sets
|
|
36
|
-
* a new head. Then the checkpoint state cache grows unbounded with very different states (because
|
|
37
|
-
* they are 8 epochs apart) and causes an OOM. Research a proper solution once regen and the state
|
|
38
|
-
* caches are better.
|
|
39
|
-
*/
|
|
40
|
-
export const SYNC_TOLERANCE_EPOCHS = 1;
|
|
41
28
|
/**
|
|
42
29
|
* Cutoff time to wait from start of the slot for execution and builder block production apis to resolve.
|
|
43
30
|
* Post this time, race execution and builder to pick whatever resolves first.
|
|
@@ -46,7 +33,7 @@ export const SYNC_TOLERANCE_EPOCHS = 1;
|
|
|
46
33
|
* A cutoff of 2 seconds gives enough time and if there are unexpected delays it ensures we publish
|
|
47
34
|
* in time as proposals post 4 seconds into the slot will likely be orphaned due to proposer boost reorg.
|
|
48
35
|
*
|
|
49
|
-
* TODO GLOAS: re-evaluate cutoff timing
|
|
36
|
+
* TODO GLOAS: re-evaluate cutoff timing due to attestation deadline changes in gloas
|
|
50
37
|
*/
|
|
51
38
|
const BLOCK_PRODUCTION_RACE_CUTOFF_MS = 2_000;
|
|
52
39
|
/** Overall timeout for execution and block production apis */
|
|
@@ -221,31 +208,6 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
221
208
|
}
|
|
222
209
|
return null;
|
|
223
210
|
}
|
|
224
|
-
/**
|
|
225
|
-
* Reject any request while the node is syncing
|
|
226
|
-
*/
|
|
227
|
-
function notWhileSyncing() {
|
|
228
|
-
// Consider node synced before or close to genesis
|
|
229
|
-
if (chain.clock.currentSlot < SLOTS_PER_EPOCH) {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
const syncState = sync.state;
|
|
233
|
-
switch (syncState) {
|
|
234
|
-
case SyncState.SyncingFinalized:
|
|
235
|
-
case SyncState.SyncingHead: {
|
|
236
|
-
const currentSlot = chain.clock.currentSlot;
|
|
237
|
-
const headSlot = chain.forkChoice.getHead().slot;
|
|
238
|
-
if (currentSlot - headSlot > SYNC_TOLERANCE_EPOCHS * SLOTS_PER_EPOCH) {
|
|
239
|
-
throw new NodeIsSyncing(`headSlot ${headSlot} currentSlot ${currentSlot}`);
|
|
240
|
-
}
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
case SyncState.Synced:
|
|
244
|
-
return;
|
|
245
|
-
case SyncState.Stalled:
|
|
246
|
-
throw new NodeIsSyncing("waiting for peers");
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
211
|
/**
|
|
250
212
|
* Post merge, the CL and EL could be out of step in the sync, and could result in
|
|
251
213
|
* Syncing status of the chain head. To be precise:
|
|
@@ -402,7 +364,16 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
402
364
|
async function produceEngineOrBuilderBlock(slot, randaoReveal, graffiti,
|
|
403
365
|
// TODO deneb: skip randao verification
|
|
404
366
|
_skipRandaoVerification, builderBoostFactor, { feeRecipient, builderSelection, strictFeeRecipientCheck } = {}) {
|
|
405
|
-
|
|
367
|
+
builderSelection = builderSelection ?? routes.validator.BuilderSelection.MaxProfit;
|
|
368
|
+
if (builderSelection === routes.validator.BuilderSelection.BuilderOnly) {
|
|
369
|
+
logger.warn("Builder selection builderonly is no longer supported, treating as builderalways");
|
|
370
|
+
builderSelection = routes.validator.BuilderSelection.BuilderAlways;
|
|
371
|
+
}
|
|
372
|
+
builderBoostFactor = builderBoostFactor ?? BigInt(100);
|
|
373
|
+
if (builderBoostFactor > MAX_BUILDER_BOOST_FACTOR) {
|
|
374
|
+
throw new ApiError(400, `Invalid builderBoostFactor=${builderBoostFactor} > MAX_BUILDER_BOOST_FACTOR`);
|
|
375
|
+
}
|
|
376
|
+
notWhileSyncing(chain, sync.state);
|
|
406
377
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
407
378
|
const parentBlock = chain.getProposerHead(slot);
|
|
408
379
|
const { blockRoot: parentBlockRootHex, slot: parentSlot } = parentBlock;
|
|
@@ -410,30 +381,13 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
410
381
|
notOnOutOfRangeData(parentBlockRoot);
|
|
411
382
|
metrics?.blockProductionSlotDelta.set(slot - parentSlot);
|
|
412
383
|
const fork = config.getForkName(slot);
|
|
413
|
-
// set some sensible opts
|
|
414
|
-
// builderSelection will be deprecated and will run in mode MaxProfit if builder is enabled
|
|
415
|
-
// and the actual selection will be determined using builderBoostFactor passed by the validator
|
|
416
|
-
builderSelection = builderSelection ?? routes.validator.BuilderSelection.MaxProfit;
|
|
417
|
-
builderBoostFactor = builderBoostFactor ?? BigInt(100);
|
|
418
|
-
if (builderBoostFactor > MAX_BUILDER_BOOST_FACTOR) {
|
|
419
|
-
throw new ApiError(400, `Invalid builderBoostFactor=${builderBoostFactor} > MAX_BUILDER_BOOST_FACTOR`);
|
|
420
|
-
}
|
|
421
384
|
const isBuilderEnabled = ForkSeq[fork] >= ForkSeq.bellatrix &&
|
|
422
385
|
chain.executionBuilder !== undefined &&
|
|
423
386
|
builderSelection !== routes.validator.BuilderSelection.ExecutionOnly;
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
// (TODO: independently make sure such an options update is not successful for a validator pubkey)
|
|
429
|
-
//
|
|
430
|
-
// So if builder is disabled ignore builder selection of builder only if caused by user mistake
|
|
431
|
-
// https://github.com/ChainSafe/lodestar/issues/6338
|
|
432
|
-
const isEngineEnabled = !isBuilderEnabled || builderSelection !== routes.validator.BuilderSelection.BuilderOnly;
|
|
433
|
-
if (!isEngineEnabled && !isBuilderEnabled) {
|
|
434
|
-
throw Error(`Internal Error: Neither builder nor execution proposal flow activated isBuilderEnabled=${isBuilderEnabled} builderSelection=${builderSelection}`);
|
|
435
|
-
}
|
|
436
|
-
const graffitiBytes = toGraffitiBytes(graffiti ?? getDefaultGraffiti(getLodestarClientVersion(opts), chain.executionEngine.clientVersion, opts));
|
|
387
|
+
const graffitiBytes = toGraffitiBytes(getBlockGraffiti(graffiti, getLodestarClientVersion(opts), chain.executionEngine.clientVersion, {
|
|
388
|
+
private: opts.private,
|
|
389
|
+
graffitiAppend: chain.opts.graffitiAppend,
|
|
390
|
+
}));
|
|
437
391
|
const loggerContext = {
|
|
438
392
|
slot,
|
|
439
393
|
parentSlot,
|
|
@@ -441,7 +395,6 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
441
395
|
fork,
|
|
442
396
|
builderSelection,
|
|
443
397
|
isBuilderEnabled,
|
|
444
|
-
isEngineEnabled,
|
|
445
398
|
strictFeeRecipientCheck,
|
|
446
399
|
// winston logger doesn't like bigint
|
|
447
400
|
builderBoostFactor: `${builderBoostFactor}`,
|
|
@@ -462,25 +415,23 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
462
415
|
parentBlock,
|
|
463
416
|
})
|
|
464
417
|
: Promise.reject(new Error("Builder disabled"));
|
|
465
|
-
const enginePromise =
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
})
|
|
483
|
-
: Promise.reject(new Error("Engine disabled"));
|
|
418
|
+
const enginePromise = produceEngineBlockContents(slot, randaoReveal, graffitiBytes, {
|
|
419
|
+
feeRecipient,
|
|
420
|
+
strictFeeRecipientCheck,
|
|
421
|
+
commonBlockBodyPromise,
|
|
422
|
+
parentBlock,
|
|
423
|
+
}).then((engineBlock) => {
|
|
424
|
+
// Once the engine returns a block, in the event of either:
|
|
425
|
+
// - suspected builder censorship
|
|
426
|
+
// - builder boost factor set to 0 or builder selection `executionalways`
|
|
427
|
+
// we don't need to wait for builder block as engine block will always be selected
|
|
428
|
+
if (engineBlock.shouldOverrideBuilder ||
|
|
429
|
+
builderBoostFactor === BigInt(0) ||
|
|
430
|
+
builderSelection === routes.validator.BuilderSelection.ExecutionAlways) {
|
|
431
|
+
controller.abort();
|
|
432
|
+
}
|
|
433
|
+
return engineBlock;
|
|
434
|
+
});
|
|
484
435
|
// Calculate cutoff time based on start of the slot
|
|
485
436
|
const cutoffMs = Math.max(0, BLOCK_PRODUCTION_RACE_CUTOFF_MS - chain.clock.msFromSlot(slot));
|
|
486
437
|
logger.verbose("Block production race (builder vs execution) starting", {
|
|
@@ -519,25 +470,20 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
519
470
|
if (builder.status === "pending" && engine.status === "pending") {
|
|
520
471
|
throw Error("Builder and engine both failed to produce the block within timeout");
|
|
521
472
|
}
|
|
522
|
-
if (builder.status === "pending" && !isEngineEnabled) {
|
|
523
|
-
throw Error("Builder failed to produce the block within timeout");
|
|
524
|
-
}
|
|
525
473
|
if (engine.status === "pending" && !isBuilderEnabled) {
|
|
526
474
|
throw Error("Engine failed to produce the block within timeout");
|
|
527
475
|
}
|
|
528
|
-
if (
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
});
|
|
540
|
-
}
|
|
476
|
+
if (engine.status === "rejected") {
|
|
477
|
+
logger.warn("Engine failed to produce the block", {
|
|
478
|
+
...loggerContext,
|
|
479
|
+
durationMs: engine.durationMs,
|
|
480
|
+
}, engine.reason);
|
|
481
|
+
}
|
|
482
|
+
else if (engine.status === "pending") {
|
|
483
|
+
logger.warn("Engine failed to produce the block within cutoff time", {
|
|
484
|
+
...loggerContext,
|
|
485
|
+
cutoffMs,
|
|
486
|
+
});
|
|
541
487
|
}
|
|
542
488
|
if (isBuilderEnabled) {
|
|
543
489
|
if (builder.status === "rejected") {
|
|
@@ -562,11 +508,11 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
562
508
|
}
|
|
563
509
|
}
|
|
564
510
|
if (builder.status === "rejected" && engine.status === "rejected") {
|
|
565
|
-
throw Error(`${isBuilderEnabled
|
|
511
|
+
throw Error(`${isBuilderEnabled ? "Builder and engine both" : "Engine"} failed to produce the block`);
|
|
566
512
|
}
|
|
567
513
|
// handle shouldOverrideBuilder separately
|
|
568
|
-
if (engine.status === "fulfilled" && engine.value.shouldOverrideBuilder) {
|
|
569
|
-
logger.
|
|
514
|
+
if (engine.status === "fulfilled" && engine.value.shouldOverrideBuilder && isBuilderEnabled) {
|
|
515
|
+
logger.warn("Selected engine block: censorship suspected in builder blocks", {
|
|
570
516
|
...loggerContext,
|
|
571
517
|
durationMs: engine.durationMs,
|
|
572
518
|
shouldOverrideBuilder: engine.value.shouldOverrideBuilder,
|
|
@@ -579,11 +525,9 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
579
525
|
return { ...engine.value, executionPayloadBlinded: false, executionPayloadSource: ProducedBlockSource.engine };
|
|
580
526
|
}
|
|
581
527
|
if (builder.status === "fulfilled" && engine.status !== "fulfilled") {
|
|
582
|
-
const reason =
|
|
583
|
-
? BuilderBlockSelectionReason.
|
|
584
|
-
:
|
|
585
|
-
? BuilderBlockSelectionReason.EnginePending
|
|
586
|
-
: BuilderBlockSelectionReason.EngineError;
|
|
528
|
+
const reason = engine.status === "pending"
|
|
529
|
+
? BuilderBlockSelectionReason.EnginePending
|
|
530
|
+
: BuilderBlockSelectionReason.EngineError;
|
|
587
531
|
logger.info("Selected builder block: no engine block produced", {
|
|
588
532
|
reason,
|
|
589
533
|
...loggerContext,
|
|
@@ -667,31 +611,50 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
667
611
|
}
|
|
668
612
|
return { data, meta };
|
|
669
613
|
},
|
|
670
|
-
async produceBlockV4({ slot, randaoReveal, graffiti, feeRecipient }) {
|
|
614
|
+
async produceBlockV4({ slot, randaoReveal, graffiti, feeRecipient, includePayload, builderSelection, builderBoostFactor, }) {
|
|
671
615
|
const fork = config.getForkName(slot);
|
|
672
616
|
if (!isForkPostGloas(fork)) {
|
|
673
617
|
throw new ApiError(400, `produceBlockV4 not supported for pre-gloas fork=${fork}`);
|
|
674
618
|
}
|
|
675
|
-
|
|
619
|
+
builderSelection = builderSelection ?? routes.validator.BuilderSelection.MaxProfit;
|
|
620
|
+
if (builderSelection === routes.validator.BuilderSelection.BuilderOnly) {
|
|
621
|
+
logger.warn("Builder selection builderonly is no longer supported, treating as builderalways");
|
|
622
|
+
builderSelection = routes.validator.BuilderSelection.BuilderAlways;
|
|
623
|
+
}
|
|
624
|
+
builderBoostFactor = builderBoostFactor ?? BigInt(100);
|
|
625
|
+
if (builderBoostFactor > MAX_BUILDER_BOOST_FACTOR) {
|
|
626
|
+
throw new ApiError(400, `Invalid builderBoostFactor=${builderBoostFactor} > MAX_BUILDER_BOOST_FACTOR`);
|
|
627
|
+
}
|
|
628
|
+
notWhileSyncing(chain, sync.state);
|
|
676
629
|
await waitForSlot(slot);
|
|
677
630
|
const parentBlock = chain.getProposerHead(slot);
|
|
678
631
|
const { blockRoot: parentBlockRootHex, slot: parentSlot } = parentBlock;
|
|
679
632
|
const parentBlockRoot = fromHex(parentBlockRootHex);
|
|
680
633
|
notOnOutOfRangeData(parentBlockRoot);
|
|
681
634
|
metrics?.blockProductionSlotDelta.set(slot - parentSlot);
|
|
682
|
-
const graffitiBytes = toGraffitiBytes(graffiti
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
635
|
+
const graffitiBytes = toGraffitiBytes(getBlockGraffiti(graffiti, getLodestarClientVersion(opts), chain.executionEngine.clientVersion, {
|
|
636
|
+
private: opts.private,
|
|
637
|
+
graffitiAppend: chain.opts.graffitiAppend,
|
|
638
|
+
}));
|
|
639
|
+
// TODO GLOAS: add external builder api support when it is implemented
|
|
686
640
|
const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
|
|
687
641
|
const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
|
|
688
|
-
|
|
642
|
+
// Bids are only skipped entirely with executiononly or while the circuit breaker is active,
|
|
643
|
+
// other engine-preferring selections still build a block with the best bid as fallback in
|
|
644
|
+
// case local production fails
|
|
645
|
+
const circuitBreakerActive = chain.builderCircuitBreaker.isActive(slot);
|
|
646
|
+
const builderBid = builderSelection === routes.validator.BuilderSelection.ExecutionOnly || circuitBreakerActive
|
|
647
|
+
? null
|
|
648
|
+
: chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
|
|
689
649
|
const logCtx = {
|
|
690
650
|
slot,
|
|
691
651
|
parentSlot,
|
|
692
652
|
parentBlockRoot: parentBlockRootHex,
|
|
693
653
|
parentBlockHash: parentBlock.executionPayloadBlockHash,
|
|
694
654
|
fork,
|
|
655
|
+
builderSelection,
|
|
656
|
+
builderBoostFactor,
|
|
657
|
+
circuitBreakerActive,
|
|
695
658
|
...(builderBid !== null
|
|
696
659
|
? {
|
|
697
660
|
bidValue: builderBid.message.value,
|
|
@@ -722,32 +685,91 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
722
685
|
const t = metrics?.blockProductionTime.startTimer();
|
|
723
686
|
return fn().finally(() => t?.({ source }));
|
|
724
687
|
};
|
|
725
|
-
//
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
688
|
+
// Calculate cutoff time based on start of the slot, ensures a slow local payload build does
|
|
689
|
+
// not delay the proposal when a builder bid block is available (and vice versa)
|
|
690
|
+
const cutoffMs = Math.max(0, BLOCK_PRODUCTION_RACE_CUTOFF_MS - chain.clock.msFromSlot(slot));
|
|
691
|
+
// use abort controller to stop waiting for the bid block if the engine block will be selected
|
|
692
|
+
const controller = new AbortController();
|
|
693
|
+
const enginePromise = timed(ProducedBlockSource.engine, () => chain.produceBlock(baseAttrs)).then((engineBlock) => {
|
|
694
|
+
// No need to wait for the bid block if the engine block will always be selected due to
|
|
695
|
+
// suspected builder censorship, a builder boost factor of 0 or executionalways selection
|
|
696
|
+
if (engineBlock.shouldOverrideBuilder ||
|
|
697
|
+
builderBoostFactor === BigInt(0) ||
|
|
698
|
+
builderSelection === routes.validator.BuilderSelection.ExecutionAlways) {
|
|
699
|
+
controller.abort();
|
|
700
|
+
}
|
|
701
|
+
return engineBlock;
|
|
702
|
+
});
|
|
703
|
+
const bidPromise = builderBid !== null
|
|
704
|
+
? timed(ProducedBlockSource.builder, () => chain.produceBlock({ ...baseAttrs, builderBid }))
|
|
705
|
+
: Promise.reject(new Error("No builder bid available"));
|
|
706
|
+
const [engineResult, bidResult] = await resolveOrRacePromises([enginePromise, bidPromise], {
|
|
707
|
+
resolveTimeoutMs: cutoffMs,
|
|
708
|
+
raceTimeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS,
|
|
709
|
+
signal: controller.signal,
|
|
710
|
+
});
|
|
732
711
|
let bestResult = null;
|
|
733
712
|
let source = ProducedBlockSource.engine;
|
|
734
|
-
|
|
713
|
+
// handle shouldOverrideBuilder separately
|
|
714
|
+
if (engineResult.status === "fulfilled" && engineResult.value.shouldOverrideBuilder && builderBid !== null) {
|
|
715
|
+
source = ProducedBlockSource.engine;
|
|
716
|
+
bestResult = engineResult;
|
|
717
|
+
metrics?.blockProductionSelectionResults.inc({
|
|
718
|
+
source: ProducedBlockSource.engine,
|
|
719
|
+
reason: EngineBlockSelectionReason.BuilderCensorship,
|
|
720
|
+
});
|
|
721
|
+
logger.warn("Selected local block: censorship suspected in builder bid", logCtx);
|
|
722
|
+
}
|
|
723
|
+
else if (engineResult.status === "fulfilled" && bidResult.status === "fulfilled") {
|
|
724
|
+
const result = selectBlockProductionSource({
|
|
725
|
+
builderSelection,
|
|
726
|
+
builderBoostFactor,
|
|
727
|
+
engineExecutionPayloadValue: engineResult.value.executionPayloadValue,
|
|
728
|
+
// The bid value is the payment to the proposer, in Gwei
|
|
729
|
+
builderExecutionPayloadValue: BigInt(builderBid?.message.value ?? 0) * GWEI_TO_WEI,
|
|
730
|
+
});
|
|
731
|
+
source = result.source;
|
|
732
|
+
metrics?.blockProductionSelectionResults.inc(result);
|
|
733
|
+
logger.info(`Selected ${source} block`, { reason: result.reason, ...logCtx });
|
|
734
|
+
bestResult = source === ProducedBlockSource.builder ? bidResult : engineResult;
|
|
735
|
+
}
|
|
736
|
+
else if (bidResult.status === "fulfilled") {
|
|
735
737
|
source = ProducedBlockSource.builder;
|
|
736
738
|
bestResult = bidResult;
|
|
737
|
-
|
|
739
|
+
const reason = engineResult.status === "pending"
|
|
740
|
+
? BuilderBlockSelectionReason.EnginePending
|
|
741
|
+
: BuilderBlockSelectionReason.EngineError;
|
|
742
|
+
metrics?.blockProductionSelectionResults.inc({ source: ProducedBlockSource.builder, reason });
|
|
743
|
+
logger.info("Selected builder bid block: no local block produced", {
|
|
744
|
+
reason,
|
|
745
|
+
...logCtx,
|
|
746
|
+
error: engineResult.status === "rejected" ? engineResult.reason.message : undefined,
|
|
747
|
+
});
|
|
738
748
|
}
|
|
739
749
|
else if (engineResult.status === "fulfilled") {
|
|
740
750
|
source = ProducedBlockSource.engine;
|
|
741
751
|
bestResult = engineResult;
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
752
|
+
const reason = builderBid === null
|
|
753
|
+
? EngineBlockSelectionReason.BuilderNoBid
|
|
754
|
+
: bidResult.status === "pending"
|
|
755
|
+
? EngineBlockSelectionReason.BuilderPending
|
|
756
|
+
: EngineBlockSelectionReason.BuilderError;
|
|
757
|
+
metrics?.blockProductionSelectionResults.inc({ source: ProducedBlockSource.engine, reason });
|
|
758
|
+
logger.info("Selected local block: no builder bid block produced", {
|
|
759
|
+
reason,
|
|
760
|
+
...logCtx,
|
|
761
|
+
error: bidResult.status === "rejected" ? bidResult.reason.message : undefined,
|
|
762
|
+
});
|
|
745
763
|
}
|
|
746
764
|
if (bestResult === null || bestResult.status !== "fulfilled") {
|
|
747
|
-
const engineReason = engineResult.status === "rejected" ? engineResult.reason :
|
|
748
|
-
const bidReason =
|
|
749
|
-
logger.error("Block production failed", {
|
|
750
|
-
|
|
765
|
+
const engineReason = engineResult.status === "rejected" ? engineResult.reason : engineResult.status;
|
|
766
|
+
const bidReason = bidResult.status === "rejected" ? bidResult.reason : bidResult.status;
|
|
767
|
+
logger.error("Block production failed", {
|
|
768
|
+
...logCtx,
|
|
769
|
+
engineReason: String(engineReason),
|
|
770
|
+
bidReason: String(bidReason),
|
|
771
|
+
});
|
|
772
|
+
throw Error(`Block production failed: engine=${String(engineReason)} builder=${String(bidReason)}`);
|
|
751
773
|
}
|
|
752
774
|
const { block, executionPayloadValue, consensusBlockValue } = bestResult.value;
|
|
753
775
|
metrics?.blockProductionSuccess.inc({ source });
|
|
@@ -764,13 +786,40 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
764
786
|
if (chain.opts.persistProducedBlocks) {
|
|
765
787
|
void chain.persistBlock(block, "produced_engine_block");
|
|
766
788
|
}
|
|
789
|
+
// Include the payload for self-builds unless disabled (stateless flow)
|
|
790
|
+
const isSelfBuild = source === ProducedBlockSource.engine;
|
|
791
|
+
if (isSelfBuild && includePayload) {
|
|
792
|
+
const produceResult = chain.blockProductionCache.get(blockRoot);
|
|
793
|
+
if (produceResult === undefined ||
|
|
794
|
+
!isForkPostGloas(produceResult.fork) ||
|
|
795
|
+
produceResult.type !== BlockType.Full) {
|
|
796
|
+
throw Error(`Missing cached block production result for produced block slot=${slot} blockRoot=${blockRoot}`);
|
|
797
|
+
}
|
|
798
|
+
const { executionPayload, executionRequests, blobsBundle, parentBlockRoot } = produceResult;
|
|
799
|
+
const blockContents = {
|
|
800
|
+
block: block,
|
|
801
|
+
executionPayloadEnvelope: {
|
|
802
|
+
payload: executionPayload,
|
|
803
|
+
executionRequests,
|
|
804
|
+
builderIndex: BUILDER_INDEX_SELF_BUILD,
|
|
805
|
+
beaconBlockRoot: fromHex(blockRoot),
|
|
806
|
+
parentBeaconBlockRoot: parentBlockRoot,
|
|
807
|
+
},
|
|
808
|
+
kzgProofs: blobsBundle.proofs,
|
|
809
|
+
blobs: blobsBundle.blobs,
|
|
810
|
+
};
|
|
811
|
+
return {
|
|
812
|
+
data: blockContents,
|
|
813
|
+
meta: { version: fork, consensusBlockValue, executionPayloadValue, executionPayloadIncluded: true },
|
|
814
|
+
};
|
|
815
|
+
}
|
|
767
816
|
return {
|
|
768
817
|
data: block,
|
|
769
|
-
meta: { version: fork, consensusBlockValue },
|
|
818
|
+
meta: { version: fork, consensusBlockValue, executionPayloadValue, executionPayloadIncluded: false },
|
|
770
819
|
};
|
|
771
820
|
},
|
|
772
821
|
async produceAttestationData({ committeeIndex, slot }) {
|
|
773
|
-
notWhileSyncing();
|
|
822
|
+
notWhileSyncing(chain, sync.state);
|
|
774
823
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
775
824
|
// This needs a state in the same epoch as `slot` such that state.currentJustifiedCheckpoint is correct.
|
|
776
825
|
// Note: This may trigger an epoch transition if there skipped slots at the beginning of the epoch.
|
|
@@ -841,12 +890,12 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
841
890
|
if (!isForkPostGloas(fork)) {
|
|
842
891
|
throw new ApiError(400, `producePayloadAttestationData is not supported before Gloas fork=${fork}`);
|
|
843
892
|
}
|
|
844
|
-
notWhileSyncing();
|
|
893
|
+
notWhileSyncing(chain, sync.state);
|
|
845
894
|
await waitForSlot(slot);
|
|
846
895
|
const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
847
896
|
if (!block) {
|
|
848
|
-
// No block is seen at slot. Return
|
|
849
|
-
|
|
897
|
+
// No canonical block is seen at slot. Return 204 so vc can skip casting payload attestation.
|
|
898
|
+
return { data: undefined, meta: { version: fork }, status: 204 };
|
|
850
899
|
}
|
|
851
900
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
|
|
852
901
|
// Spec: set payload_present only if the envelope was seen before get_payload_due_ms()
|
|
@@ -910,7 +959,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
910
959
|
return { data: contribution };
|
|
911
960
|
},
|
|
912
961
|
async getProposerDuties({ epoch }, _context, opts) {
|
|
913
|
-
notWhileSyncing();
|
|
962
|
+
notWhileSyncing(chain, sync.state);
|
|
914
963
|
const currentEpoch = currentEpochWithDisparity();
|
|
915
964
|
const nextEpoch = currentEpoch + 1;
|
|
916
965
|
const startSlot = computeStartSlotAtEpoch(epoch);
|
|
@@ -952,7 +1001,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
952
1001
|
state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.getDuties);
|
|
953
1002
|
}
|
|
954
1003
|
else {
|
|
955
|
-
const res = await getStateResponseWithRegen(chain, startSlot);
|
|
1004
|
+
const res = await getStateResponseWithRegen(chain, sync, startSlot);
|
|
956
1005
|
state = res.state instanceof Uint8Array ? chain.getHeadState().loadOtherState(res.state) : res.state;
|
|
957
1006
|
if (state.epoch !== epoch) {
|
|
958
1007
|
throw Error(`Loaded state epoch ${state.epoch} does not match requested epoch ${epoch}`);
|
|
@@ -1028,7 +1077,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1028
1077
|
return this.getProposerDuties(args, context, { v2: true });
|
|
1029
1078
|
},
|
|
1030
1079
|
async getAttesterDuties({ epoch, indices }) {
|
|
1031
|
-
notWhileSyncing();
|
|
1080
|
+
notWhileSyncing(chain, sync.state);
|
|
1032
1081
|
if (indices.length === 0) {
|
|
1033
1082
|
throw new ApiError(400, "No validator to get attester duties");
|
|
1034
1083
|
}
|
|
@@ -1076,7 +1125,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1076
1125
|
};
|
|
1077
1126
|
},
|
|
1078
1127
|
async getPtcDuties({ epoch, indices }) {
|
|
1079
|
-
notWhileSyncing();
|
|
1128
|
+
notWhileSyncing(chain, sync.state);
|
|
1080
1129
|
if (indices.length === 0) {
|
|
1081
1130
|
throw new ApiError(400, "No validator to get PTC duties");
|
|
1082
1131
|
}
|
|
@@ -1129,7 +1178,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1129
1178
|
* @param validatorIndices an array of the validator indices for which to obtain the duties.
|
|
1130
1179
|
*/
|
|
1131
1180
|
async getSyncCommitteeDuties({ epoch, indices }) {
|
|
1132
|
-
notWhileSyncing();
|
|
1181
|
+
notWhileSyncing(chain, sync.state);
|
|
1133
1182
|
if (indices.length === 0) {
|
|
1134
1183
|
throw new ApiError(400, "No validator to get attester duties");
|
|
1135
1184
|
}
|
|
@@ -1169,7 +1218,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1169
1218
|
};
|
|
1170
1219
|
},
|
|
1171
1220
|
async getAggregatedAttestationV2({ attestationDataRoot, slot, committeeIndex }) {
|
|
1172
|
-
notWhileSyncing();
|
|
1221
|
+
notWhileSyncing(chain, sync.state);
|
|
1173
1222
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
1174
1223
|
const dataRootHex = toRootHex(attestationDataRoot);
|
|
1175
1224
|
const aggregate = chain.attestationPool.getAggregate(slot, dataRootHex, committeeIndex);
|
|
@@ -1183,7 +1232,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1183
1232
|
};
|
|
1184
1233
|
},
|
|
1185
1234
|
async publishAggregateAndProofsV2({ signedAggregateAndProofs }) {
|
|
1186
|
-
notWhileSyncing();
|
|
1235
|
+
notWhileSyncing(chain, sync.state);
|
|
1187
1236
|
const seenTimestampSec = Date.now() / 1000;
|
|
1188
1237
|
const failures = [];
|
|
1189
1238
|
const fork = chain.config.getForkName(chain.clock.currentSlot);
|
|
@@ -1229,7 +1278,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1229
1278
|
* https://github.com/ethereum/beacon-APIs/pull/137
|
|
1230
1279
|
*/
|
|
1231
1280
|
async publishContributionAndProofs({ contributionAndProofs }) {
|
|
1232
|
-
notWhileSyncing();
|
|
1281
|
+
notWhileSyncing(chain, sync.state);
|
|
1233
1282
|
const failures = [];
|
|
1234
1283
|
await Promise.all(contributionAndProofs.map(async (contributionAndProof, i) => {
|
|
1235
1284
|
try {
|
|
@@ -1262,7 +1311,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1262
1311
|
}
|
|
1263
1312
|
},
|
|
1264
1313
|
async prepareBeaconCommitteeSubnet({ subscriptions }) {
|
|
1265
|
-
notWhileSyncing();
|
|
1314
|
+
notWhileSyncing(chain, sync.state);
|
|
1266
1315
|
await network.prepareBeaconCommitteeSubnets(subscriptions.map(({ validatorIndex, slot, isAggregator, committeesAtSlot, committeeIndex }) => ({
|
|
1267
1316
|
validatorIndex: validatorIndex,
|
|
1268
1317
|
subnet: computeSubnetForCommitteesAtSlot(slot, committeesAtSlot, committeeIndex),
|
|
@@ -1289,7 +1338,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1289
1338
|
* https://github.com/ethereum/beacon-APIs/pull/136
|
|
1290
1339
|
*/
|
|
1291
1340
|
async prepareSyncCommitteeSubnets({ subscriptions }) {
|
|
1292
|
-
notWhileSyncing();
|
|
1341
|
+
notWhileSyncing(chain, sync.state);
|
|
1293
1342
|
// A `validatorIndex` can be in multiple subnets, so compute the CommitteeSubscription with double for loop
|
|
1294
1343
|
const subs = [];
|
|
1295
1344
|
for (const sub of subscriptions) {
|
|
@@ -1364,12 +1413,45 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1364
1413
|
count: filteredRegistrations.length,
|
|
1365
1414
|
});
|
|
1366
1415
|
},
|
|
1416
|
+
async submitProposerPreferences({ signedProposerPreferences }) {
|
|
1417
|
+
const failures = [];
|
|
1418
|
+
await Promise.all(signedProposerPreferences.map(async (signed, i) => {
|
|
1419
|
+
try {
|
|
1420
|
+
await validateGossipProposerPreferences(chain, signed);
|
|
1421
|
+
chain.proposerPreferencesPool.add(signed);
|
|
1422
|
+
await network.publishProposerPreferences(signed);
|
|
1423
|
+
chain.emitter.emit(routes.events.EventType.proposerPreferences, {
|
|
1424
|
+
version: config.getForkName(signed.message.proposalSlot),
|
|
1425
|
+
data: signed,
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
catch (e) {
|
|
1429
|
+
const logCtx = {
|
|
1430
|
+
slot: signed.message.proposalSlot,
|
|
1431
|
+
validatorIndex: signed.message.validatorIndex,
|
|
1432
|
+
dependentRoot: toRootHex(signed.message.dependentRoot),
|
|
1433
|
+
};
|
|
1434
|
+
if (e instanceof ProposerPreferencesError && e.type.code === ProposerPreferencesErrorCode.ALREADY_KNOWN) {
|
|
1435
|
+
logger.debug("Ignoring known signed proposer preferences", logCtx);
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
failures.push({ index: i, message: e.message });
|
|
1439
|
+
logger.verbose(`Error on submitProposerPreferences [${i}]`, logCtx, e);
|
|
1440
|
+
if (e instanceof ProposerPreferencesError && e.action === GossipAction.REJECT) {
|
|
1441
|
+
chain.persistInvalidSszValue(ssz.gloas.SignedProposerPreferences, signed, "api_reject");
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}));
|
|
1445
|
+
if (failures.length > 0) {
|
|
1446
|
+
throw new IndexedError("Error processing signed proposer preferences", failures);
|
|
1447
|
+
}
|
|
1448
|
+
},
|
|
1367
1449
|
async getExecutionPayloadEnvelope({ slot, beaconBlockRoot }) {
|
|
1368
1450
|
const fork = config.getForkName(slot);
|
|
1369
1451
|
if (!isForkPostGloas(fork)) {
|
|
1370
1452
|
throw new ApiError(400, `getExecutionPayloadEnvelope not supported for pre-gloas fork=${fork}`);
|
|
1371
1453
|
}
|
|
1372
|
-
notWhileSyncing();
|
|
1454
|
+
notWhileSyncing(chain, sync.state);
|
|
1373
1455
|
await waitForSlot(slot);
|
|
1374
1456
|
const blockRootHex = toRootHex(beaconBlockRoot);
|
|
1375
1457
|
const produceResult = chain.blockProductionCache.get(blockRootHex);
|
|
@@ -1383,6 +1465,13 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1383
1465
|
throw Error("Cached block production result is not full block");
|
|
1384
1466
|
}
|
|
1385
1467
|
const { executionPayload, executionRequests, parentBlockRoot } = produceResult;
|
|
1468
|
+
if (executionPayload === undefined) {
|
|
1469
|
+
// Blocks committing to a builder bid are cached as full but without payload data
|
|
1470
|
+
throw new ApiError(404, `No local execution payload cached for block root ${blockRootHex}`);
|
|
1471
|
+
}
|
|
1472
|
+
if (executionPayload.slotNumber !== slot) {
|
|
1473
|
+
throw new ApiError(404, `Cached execution payload is for slot=${executionPayload.slotNumber}, requested slot=${slot}`);
|
|
1474
|
+
}
|
|
1386
1475
|
const envelope = {
|
|
1387
1476
|
payload: executionPayload,
|
|
1388
1477
|
executionRequests: executionRequests,
|