@lodestar/beacon-node 1.45.0-dev.fadf0fbb1f → 1.46.0-dev.8fc4e297d2
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 +4 -0
- 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 +2 -2
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -1
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +9 -1
- 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 +192 -143
- 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 +3 -21
- package/lib/chain/blocks/importBlock.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 +2 -0
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +26 -2
- 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 +2 -0
- 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 +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/options.d.ts +4 -0
- package/lib/chain/options.d.ts.map +1 -1
- 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/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 +64 -11
- 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 +80 -232
- 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 +16 -6
- 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/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/util/execution.js +1 -1
- package/lib/util/execution.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 -0
- package/src/api/impl/beacon/state/index.ts +3 -2
- package/src/api/impl/beacon/state/utils.ts +11 -0
- 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 +224 -157
- package/src/api/rest/base.ts +17 -3
- package/src/chain/blocks/importBlock.ts +1 -23
- 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 -2
- 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 -0
- 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 +1 -1
- package/src/chain/options.ts +4 -0
- package/src/chain/prepareNextSlot.ts +11 -7
- 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 +74 -11
- 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 +17 -6
- 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/sync.ts +10 -0
- package/src/sync/unknownBlock.ts +8 -2
- package/src/util/execution.ts +1 -1
|
@@ -3,7 +3,7 @@ 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
9
|
import { AttestationError, AttestationErrorCode, GossipAction, ProposerPreferencesError, ProposerPreferencesErrorCode, SyncCommitteeError, SyncCommitteeErrorCode, } from "../../../chain/errors/index.js";
|
|
@@ -17,28 +17,14 @@ import { validateSyncCommitteeGossipContributionAndProof } from "../../../chain/
|
|
|
17
17
|
import { ZERO_HASH } from "../../../constants/index.js";
|
|
18
18
|
import { BuilderStatus, NoBidReceived } from "../../../execution/builder/http.js";
|
|
19
19
|
import { validateGossipFnRetryUnknownRoot } from "../../../network/processor/gossipHandlers.js";
|
|
20
|
-
import { SyncState } from "../../../sync/index.js";
|
|
21
20
|
import { callInNextEventLoop } from "../../../util/eventLoop.js";
|
|
22
21
|
import { isOptimisticBlock } from "../../../util/forkChoice.js";
|
|
23
22
|
import { getBlockGraffiti, toGraffitiBytes } from "../../../util/graffiti.js";
|
|
24
23
|
import { getLodestarClientVersion } from "../../../util/metadata.js";
|
|
25
24
|
import { getStateResponseWithRegen } from "../beacon/state/utils.js";
|
|
26
25
|
import { ApiError, IndexedError, NodeIsSyncing, OnlySupportedByDVT } from "../errors.js";
|
|
26
|
+
import { notWhileSyncing } from "../utils.js";
|
|
27
27
|
import { computeSubnetForCommitteesAtSlot, getPubkeysForIndices, selectBlockProductionSource } from "./utils.js";
|
|
28
|
-
/**
|
|
29
|
-
* If the node is within this many epochs from the head, we declare it to be synced regardless of
|
|
30
|
-
* the network sync state.
|
|
31
|
-
*
|
|
32
|
-
* This helps prevent attacks where nodes can convince us that we're syncing some non-existent
|
|
33
|
-
* finalized head.
|
|
34
|
-
*
|
|
35
|
-
* TODO: Lighthouse uses 8 for the attack described above. However, 8 kills Lodestar since validators
|
|
36
|
-
* can trigger regen to fast-forward head state 8 epochs to be immediately invalidated as sync sets
|
|
37
|
-
* a new head. Then the checkpoint state cache grows unbounded with very different states (because
|
|
38
|
-
* they are 8 epochs apart) and causes an OOM. Research a proper solution once regen and the state
|
|
39
|
-
* caches are better.
|
|
40
|
-
*/
|
|
41
|
-
export const SYNC_TOLERANCE_EPOCHS = 1;
|
|
42
28
|
/**
|
|
43
29
|
* Cutoff time to wait from start of the slot for execution and builder block production apis to resolve.
|
|
44
30
|
* Post this time, race execution and builder to pick whatever resolves first.
|
|
@@ -47,7 +33,7 @@ export const SYNC_TOLERANCE_EPOCHS = 1;
|
|
|
47
33
|
* A cutoff of 2 seconds gives enough time and if there are unexpected delays it ensures we publish
|
|
48
34
|
* in time as proposals post 4 seconds into the slot will likely be orphaned due to proposer boost reorg.
|
|
49
35
|
*
|
|
50
|
-
* TODO GLOAS: re-evaluate cutoff timing
|
|
36
|
+
* TODO GLOAS: re-evaluate cutoff timing due to attestation deadline changes in gloas
|
|
51
37
|
*/
|
|
52
38
|
const BLOCK_PRODUCTION_RACE_CUTOFF_MS = 2_000;
|
|
53
39
|
/** Overall timeout for execution and block production apis */
|
|
@@ -222,31 +208,6 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
222
208
|
}
|
|
223
209
|
return null;
|
|
224
210
|
}
|
|
225
|
-
/**
|
|
226
|
-
* Reject any request while the node is syncing
|
|
227
|
-
*/
|
|
228
|
-
function notWhileSyncing() {
|
|
229
|
-
// Consider node synced before or close to genesis
|
|
230
|
-
if (chain.clock.currentSlot < SLOTS_PER_EPOCH) {
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
const syncState = sync.state;
|
|
234
|
-
switch (syncState) {
|
|
235
|
-
case SyncState.SyncingFinalized:
|
|
236
|
-
case SyncState.SyncingHead: {
|
|
237
|
-
const currentSlot = chain.clock.currentSlot;
|
|
238
|
-
const headSlot = chain.forkChoice.getHead().slot;
|
|
239
|
-
if (currentSlot - headSlot > SYNC_TOLERANCE_EPOCHS * SLOTS_PER_EPOCH) {
|
|
240
|
-
throw new NodeIsSyncing(`headSlot ${headSlot} currentSlot ${currentSlot}`);
|
|
241
|
-
}
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
case SyncState.Synced:
|
|
245
|
-
return;
|
|
246
|
-
case SyncState.Stalled:
|
|
247
|
-
throw new NodeIsSyncing("waiting for peers");
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
211
|
/**
|
|
251
212
|
* Post merge, the CL and EL could be out of step in the sync, and could result in
|
|
252
213
|
* Syncing status of the chain head. To be precise:
|
|
@@ -403,7 +364,16 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
403
364
|
async function produceEngineOrBuilderBlock(slot, randaoReveal, graffiti,
|
|
404
365
|
// TODO deneb: skip randao verification
|
|
405
366
|
_skipRandaoVerification, builderBoostFactor, { feeRecipient, builderSelection, strictFeeRecipientCheck } = {}) {
|
|
406
|
-
|
|
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);
|
|
407
377
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
408
378
|
const parentBlock = chain.getProposerHead(slot);
|
|
409
379
|
const { blockRoot: parentBlockRootHex, slot: parentSlot } = parentBlock;
|
|
@@ -411,29 +381,9 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
411
381
|
notOnOutOfRangeData(parentBlockRoot);
|
|
412
382
|
metrics?.blockProductionSlotDelta.set(slot - parentSlot);
|
|
413
383
|
const fork = config.getForkName(slot);
|
|
414
|
-
// set some sensible opts
|
|
415
|
-
// builderSelection will be deprecated and will run in mode MaxProfit if builder is enabled
|
|
416
|
-
// and the actual selection will be determined using builderBoostFactor passed by the validator
|
|
417
|
-
builderSelection = builderSelection ?? routes.validator.BuilderSelection.MaxProfit;
|
|
418
|
-
builderBoostFactor = builderBoostFactor ?? BigInt(100);
|
|
419
|
-
if (builderBoostFactor > MAX_BUILDER_BOOST_FACTOR) {
|
|
420
|
-
throw new ApiError(400, `Invalid builderBoostFactor=${builderBoostFactor} > MAX_BUILDER_BOOST_FACTOR`);
|
|
421
|
-
}
|
|
422
384
|
const isBuilderEnabled = ForkSeq[fork] >= ForkSeq.bellatrix &&
|
|
423
385
|
chain.executionBuilder !== undefined &&
|
|
424
386
|
builderSelection !== routes.validator.BuilderSelection.ExecutionOnly;
|
|
425
|
-
// At any point either the builder or execution or both flows should be active.
|
|
426
|
-
//
|
|
427
|
-
// Ideally such a scenario should be prevented on startup, but proposerSettingsFile or keymanager
|
|
428
|
-
// configurations could cause a validator pubkey to have builder disabled with builder selection builder only
|
|
429
|
-
// (TODO: independently make sure such an options update is not successful for a validator pubkey)
|
|
430
|
-
//
|
|
431
|
-
// So if builder is disabled ignore builder selection of builder only if caused by user mistake
|
|
432
|
-
// https://github.com/ChainSafe/lodestar/issues/6338
|
|
433
|
-
const isEngineEnabled = !isBuilderEnabled || builderSelection !== routes.validator.BuilderSelection.BuilderOnly;
|
|
434
|
-
if (!isEngineEnabled && !isBuilderEnabled) {
|
|
435
|
-
throw Error(`Internal Error: Neither builder nor execution proposal flow activated isBuilderEnabled=${isBuilderEnabled} builderSelection=${builderSelection}`);
|
|
436
|
-
}
|
|
437
387
|
const graffitiBytes = toGraffitiBytes(getBlockGraffiti(graffiti, getLodestarClientVersion(opts), chain.executionEngine.clientVersion, {
|
|
438
388
|
private: opts.private,
|
|
439
389
|
graffitiAppend: chain.opts.graffitiAppend,
|
|
@@ -445,7 +395,6 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
445
395
|
fork,
|
|
446
396
|
builderSelection,
|
|
447
397
|
isBuilderEnabled,
|
|
448
|
-
isEngineEnabled,
|
|
449
398
|
strictFeeRecipientCheck,
|
|
450
399
|
// winston logger doesn't like bigint
|
|
451
400
|
builderBoostFactor: `${builderBoostFactor}`,
|
|
@@ -466,25 +415,23 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
466
415
|
parentBlock,
|
|
467
416
|
})
|
|
468
417
|
: Promise.reject(new Error("Builder disabled"));
|
|
469
|
-
const enginePromise =
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
})
|
|
487
|
-
: 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
|
+
});
|
|
488
435
|
// Calculate cutoff time based on start of the slot
|
|
489
436
|
const cutoffMs = Math.max(0, BLOCK_PRODUCTION_RACE_CUTOFF_MS - chain.clock.msFromSlot(slot));
|
|
490
437
|
logger.verbose("Block production race (builder vs execution) starting", {
|
|
@@ -523,25 +470,20 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
523
470
|
if (builder.status === "pending" && engine.status === "pending") {
|
|
524
471
|
throw Error("Builder and engine both failed to produce the block within timeout");
|
|
525
472
|
}
|
|
526
|
-
if (builder.status === "pending" && !isEngineEnabled) {
|
|
527
|
-
throw Error("Builder failed to produce the block within timeout");
|
|
528
|
-
}
|
|
529
473
|
if (engine.status === "pending" && !isBuilderEnabled) {
|
|
530
474
|
throw Error("Engine failed to produce the block within timeout");
|
|
531
475
|
}
|
|
532
|
-
if (
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
});
|
|
544
|
-
}
|
|
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
|
+
});
|
|
545
487
|
}
|
|
546
488
|
if (isBuilderEnabled) {
|
|
547
489
|
if (builder.status === "rejected") {
|
|
@@ -566,11 +508,11 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
566
508
|
}
|
|
567
509
|
}
|
|
568
510
|
if (builder.status === "rejected" && engine.status === "rejected") {
|
|
569
|
-
throw Error(`${isBuilderEnabled
|
|
511
|
+
throw Error(`${isBuilderEnabled ? "Builder and engine both" : "Engine"} failed to produce the block`);
|
|
570
512
|
}
|
|
571
513
|
// handle shouldOverrideBuilder separately
|
|
572
|
-
if (engine.status === "fulfilled" && engine.value.shouldOverrideBuilder) {
|
|
573
|
-
logger.
|
|
514
|
+
if (engine.status === "fulfilled" && engine.value.shouldOverrideBuilder && isBuilderEnabled) {
|
|
515
|
+
logger.warn("Selected engine block: censorship suspected in builder blocks", {
|
|
574
516
|
...loggerContext,
|
|
575
517
|
durationMs: engine.durationMs,
|
|
576
518
|
shouldOverrideBuilder: engine.value.shouldOverrideBuilder,
|
|
@@ -583,11 +525,9 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
583
525
|
return { ...engine.value, executionPayloadBlinded: false, executionPayloadSource: ProducedBlockSource.engine };
|
|
584
526
|
}
|
|
585
527
|
if (builder.status === "fulfilled" && engine.status !== "fulfilled") {
|
|
586
|
-
const reason =
|
|
587
|
-
? BuilderBlockSelectionReason.
|
|
588
|
-
:
|
|
589
|
-
? BuilderBlockSelectionReason.EnginePending
|
|
590
|
-
: BuilderBlockSelectionReason.EngineError;
|
|
528
|
+
const reason = engine.status === "pending"
|
|
529
|
+
? BuilderBlockSelectionReason.EnginePending
|
|
530
|
+
: BuilderBlockSelectionReason.EngineError;
|
|
591
531
|
logger.info("Selected builder block: no engine block produced", {
|
|
592
532
|
reason,
|
|
593
533
|
...loggerContext,
|
|
@@ -671,12 +611,21 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
671
611
|
}
|
|
672
612
|
return { data, meta };
|
|
673
613
|
},
|
|
674
|
-
async produceBlockV4({ slot, randaoReveal, graffiti, feeRecipient }) {
|
|
614
|
+
async produceBlockV4({ slot, randaoReveal, graffiti, feeRecipient, includePayload, builderSelection, builderBoostFactor, }) {
|
|
675
615
|
const fork = config.getForkName(slot);
|
|
676
616
|
if (!isForkPostGloas(fork)) {
|
|
677
617
|
throw new ApiError(400, `produceBlockV4 not supported for pre-gloas fork=${fork}`);
|
|
678
618
|
}
|
|
679
|
-
|
|
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);
|
|
680
629
|
await waitForSlot(slot);
|
|
681
630
|
const parentBlock = chain.getProposerHead(slot);
|
|
682
631
|
const { blockRoot: parentBlockRootHex, slot: parentSlot } = parentBlock;
|
|
@@ -687,18 +636,25 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
687
636
|
private: opts.private,
|
|
688
637
|
graffitiAppend: chain.opts.graffitiAppend,
|
|
689
638
|
}));
|
|
690
|
-
// TODO GLOAS:
|
|
691
|
-
// the user control bid source preferences and value comparison. Also add external builder api
|
|
692
|
-
// support when it is implemented.
|
|
639
|
+
// TODO GLOAS: add external builder api support when it is implemented
|
|
693
640
|
const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
|
|
694
641
|
const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
|
|
695
|
-
|
|
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);
|
|
696
649
|
const logCtx = {
|
|
697
650
|
slot,
|
|
698
651
|
parentSlot,
|
|
699
652
|
parentBlockRoot: parentBlockRootHex,
|
|
700
653
|
parentBlockHash: parentBlock.executionPayloadBlockHash,
|
|
701
654
|
fork,
|
|
655
|
+
builderSelection,
|
|
656
|
+
builderBoostFactor,
|
|
657
|
+
circuitBreakerActive,
|
|
702
658
|
...(builderBid !== null
|
|
703
659
|
? {
|
|
704
660
|
bidValue: builderBid.message.value,
|
|
@@ -729,32 +685,91 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
729
685
|
const t = metrics?.blockProductionTime.startTimer();
|
|
730
686
|
return fn().finally(() => t?.({ source }));
|
|
731
687
|
};
|
|
732
|
-
//
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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
|
+
});
|
|
739
711
|
let bestResult = null;
|
|
740
712
|
let source = ProducedBlockSource.engine;
|
|
741
|
-
|
|
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") {
|
|
742
737
|
source = ProducedBlockSource.builder;
|
|
743
738
|
bestResult = bidResult;
|
|
744
|
-
|
|
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
|
+
});
|
|
745
748
|
}
|
|
746
749
|
else if (engineResult.status === "fulfilled") {
|
|
747
750
|
source = ProducedBlockSource.engine;
|
|
748
751
|
bestResult = engineResult;
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
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
|
+
});
|
|
752
763
|
}
|
|
753
764
|
if (bestResult === null || bestResult.status !== "fulfilled") {
|
|
754
|
-
const engineReason = engineResult.status === "rejected" ? engineResult.reason :
|
|
755
|
-
const bidReason =
|
|
756
|
-
logger.error("Block production failed", {
|
|
757
|
-
|
|
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)}`);
|
|
758
773
|
}
|
|
759
774
|
const { block, executionPayloadValue, consensusBlockValue } = bestResult.value;
|
|
760
775
|
metrics?.blockProductionSuccess.inc({ source });
|
|
@@ -771,13 +786,40 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
771
786
|
if (chain.opts.persistProducedBlocks) {
|
|
772
787
|
void chain.persistBlock(block, "produced_engine_block");
|
|
773
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
|
+
}
|
|
774
816
|
return {
|
|
775
817
|
data: block,
|
|
776
|
-
meta: { version: fork, consensusBlockValue },
|
|
818
|
+
meta: { version: fork, consensusBlockValue, executionPayloadValue, executionPayloadIncluded: false },
|
|
777
819
|
};
|
|
778
820
|
},
|
|
779
821
|
async produceAttestationData({ committeeIndex, slot }) {
|
|
780
|
-
notWhileSyncing();
|
|
822
|
+
notWhileSyncing(chain, sync.state);
|
|
781
823
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
782
824
|
// This needs a state in the same epoch as `slot` such that state.currentJustifiedCheckpoint is correct.
|
|
783
825
|
// Note: This may trigger an epoch transition if there skipped slots at the beginning of the epoch.
|
|
@@ -848,7 +890,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
848
890
|
if (!isForkPostGloas(fork)) {
|
|
849
891
|
throw new ApiError(400, `producePayloadAttestationData is not supported before Gloas fork=${fork}`);
|
|
850
892
|
}
|
|
851
|
-
notWhileSyncing();
|
|
893
|
+
notWhileSyncing(chain, sync.state);
|
|
852
894
|
await waitForSlot(slot);
|
|
853
895
|
const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
854
896
|
if (!block) {
|
|
@@ -917,7 +959,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
917
959
|
return { data: contribution };
|
|
918
960
|
},
|
|
919
961
|
async getProposerDuties({ epoch }, _context, opts) {
|
|
920
|
-
notWhileSyncing();
|
|
962
|
+
notWhileSyncing(chain, sync.state);
|
|
921
963
|
const currentEpoch = currentEpochWithDisparity();
|
|
922
964
|
const nextEpoch = currentEpoch + 1;
|
|
923
965
|
const startSlot = computeStartSlotAtEpoch(epoch);
|
|
@@ -959,7 +1001,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
959
1001
|
state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.getDuties);
|
|
960
1002
|
}
|
|
961
1003
|
else {
|
|
962
|
-
const res = await getStateResponseWithRegen(chain, startSlot);
|
|
1004
|
+
const res = await getStateResponseWithRegen(chain, sync, startSlot);
|
|
963
1005
|
state = res.state instanceof Uint8Array ? chain.getHeadState().loadOtherState(res.state) : res.state;
|
|
964
1006
|
if (state.epoch !== epoch) {
|
|
965
1007
|
throw Error(`Loaded state epoch ${state.epoch} does not match requested epoch ${epoch}`);
|
|
@@ -1035,7 +1077,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1035
1077
|
return this.getProposerDuties(args, context, { v2: true });
|
|
1036
1078
|
},
|
|
1037
1079
|
async getAttesterDuties({ epoch, indices }) {
|
|
1038
|
-
notWhileSyncing();
|
|
1080
|
+
notWhileSyncing(chain, sync.state);
|
|
1039
1081
|
if (indices.length === 0) {
|
|
1040
1082
|
throw new ApiError(400, "No validator to get attester duties");
|
|
1041
1083
|
}
|
|
@@ -1083,7 +1125,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1083
1125
|
};
|
|
1084
1126
|
},
|
|
1085
1127
|
async getPtcDuties({ epoch, indices }) {
|
|
1086
|
-
notWhileSyncing();
|
|
1128
|
+
notWhileSyncing(chain, sync.state);
|
|
1087
1129
|
if (indices.length === 0) {
|
|
1088
1130
|
throw new ApiError(400, "No validator to get PTC duties");
|
|
1089
1131
|
}
|
|
@@ -1136,7 +1178,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1136
1178
|
* @param validatorIndices an array of the validator indices for which to obtain the duties.
|
|
1137
1179
|
*/
|
|
1138
1180
|
async getSyncCommitteeDuties({ epoch, indices }) {
|
|
1139
|
-
notWhileSyncing();
|
|
1181
|
+
notWhileSyncing(chain, sync.state);
|
|
1140
1182
|
if (indices.length === 0) {
|
|
1141
1183
|
throw new ApiError(400, "No validator to get attester duties");
|
|
1142
1184
|
}
|
|
@@ -1176,7 +1218,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1176
1218
|
};
|
|
1177
1219
|
},
|
|
1178
1220
|
async getAggregatedAttestationV2({ attestationDataRoot, slot, committeeIndex }) {
|
|
1179
|
-
notWhileSyncing();
|
|
1221
|
+
notWhileSyncing(chain, sync.state);
|
|
1180
1222
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
1181
1223
|
const dataRootHex = toRootHex(attestationDataRoot);
|
|
1182
1224
|
const aggregate = chain.attestationPool.getAggregate(slot, dataRootHex, committeeIndex);
|
|
@@ -1190,7 +1232,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1190
1232
|
};
|
|
1191
1233
|
},
|
|
1192
1234
|
async publishAggregateAndProofsV2({ signedAggregateAndProofs }) {
|
|
1193
|
-
notWhileSyncing();
|
|
1235
|
+
notWhileSyncing(chain, sync.state);
|
|
1194
1236
|
const seenTimestampSec = Date.now() / 1000;
|
|
1195
1237
|
const failures = [];
|
|
1196
1238
|
const fork = chain.config.getForkName(chain.clock.currentSlot);
|
|
@@ -1236,7 +1278,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1236
1278
|
* https://github.com/ethereum/beacon-APIs/pull/137
|
|
1237
1279
|
*/
|
|
1238
1280
|
async publishContributionAndProofs({ contributionAndProofs }) {
|
|
1239
|
-
notWhileSyncing();
|
|
1281
|
+
notWhileSyncing(chain, sync.state);
|
|
1240
1282
|
const failures = [];
|
|
1241
1283
|
await Promise.all(contributionAndProofs.map(async (contributionAndProof, i) => {
|
|
1242
1284
|
try {
|
|
@@ -1269,7 +1311,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1269
1311
|
}
|
|
1270
1312
|
},
|
|
1271
1313
|
async prepareBeaconCommitteeSubnet({ subscriptions }) {
|
|
1272
|
-
notWhileSyncing();
|
|
1314
|
+
notWhileSyncing(chain, sync.state);
|
|
1273
1315
|
await network.prepareBeaconCommitteeSubnets(subscriptions.map(({ validatorIndex, slot, isAggregator, committeesAtSlot, committeeIndex }) => ({
|
|
1274
1316
|
validatorIndex: validatorIndex,
|
|
1275
1317
|
subnet: computeSubnetForCommitteesAtSlot(slot, committeesAtSlot, committeeIndex),
|
|
@@ -1296,7 +1338,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1296
1338
|
* https://github.com/ethereum/beacon-APIs/pull/136
|
|
1297
1339
|
*/
|
|
1298
1340
|
async prepareSyncCommitteeSubnets({ subscriptions }) {
|
|
1299
|
-
notWhileSyncing();
|
|
1341
|
+
notWhileSyncing(chain, sync.state);
|
|
1300
1342
|
// A `validatorIndex` can be in multiple subnets, so compute the CommitteeSubscription with double for loop
|
|
1301
1343
|
const subs = [];
|
|
1302
1344
|
for (const sub of subscriptions) {
|
|
@@ -1409,7 +1451,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1409
1451
|
if (!isForkPostGloas(fork)) {
|
|
1410
1452
|
throw new ApiError(400, `getExecutionPayloadEnvelope not supported for pre-gloas fork=${fork}`);
|
|
1411
1453
|
}
|
|
1412
|
-
notWhileSyncing();
|
|
1454
|
+
notWhileSyncing(chain, sync.state);
|
|
1413
1455
|
await waitForSlot(slot);
|
|
1414
1456
|
const blockRootHex = toRootHex(beaconBlockRoot);
|
|
1415
1457
|
const produceResult = chain.blockProductionCache.get(blockRootHex);
|
|
@@ -1423,6 +1465,13 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1423
1465
|
throw Error("Cached block production result is not full block");
|
|
1424
1466
|
}
|
|
1425
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
|
+
}
|
|
1426
1475
|
const envelope = {
|
|
1427
1476
|
payload: executionPayload,
|
|
1428
1477
|
executionRequests: executionRequests,
|