@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
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
computeEpochAtSlot,
|
|
19
19
|
computeTimeAtSlot,
|
|
20
|
+
isStatePostGloas,
|
|
20
21
|
reconstructSignedBlockContents,
|
|
21
22
|
signedBeaconBlockToBlinded,
|
|
22
23
|
signedBlockToSignedHeader,
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
fulu,
|
|
32
33
|
gloas,
|
|
33
34
|
isDenebBlockContents,
|
|
35
|
+
isSignedExecutionPayloadEnvelopeContents,
|
|
34
36
|
sszTypesFor,
|
|
35
37
|
} from "@lodestar/types";
|
|
36
38
|
import {fromHex, sleep, toHex, toRootHex} from "@lodestar/utils";
|
|
@@ -38,9 +40,16 @@ import {BlockInputSource, isBlockInputBlobs, isBlockInputColumns} from "../../..
|
|
|
38
40
|
import {PayloadEnvelopeInputSource} from "../../../../chain/blocks/payloadEnvelopeInput/index.js";
|
|
39
41
|
import {ImportBlockOpts} from "../../../../chain/blocks/types.js";
|
|
40
42
|
import {verifyBlocksInEpoch} from "../../../../chain/blocks/verifyBlock.js";
|
|
43
|
+
import {verifyExecutionPayloadEnvelope} from "../../../../chain/blocks/verifyExecutionPayloadEnvelope.js";
|
|
41
44
|
import {BeaconChain} from "../../../../chain/chain.js";
|
|
42
45
|
import {ChainEvent} from "../../../../chain/emitter.js";
|
|
43
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
BlockError,
|
|
48
|
+
BlockErrorCode,
|
|
49
|
+
BlockGossipError,
|
|
50
|
+
ExecutionPayloadEnvelopeError,
|
|
51
|
+
ExecutionPayloadEnvelopeErrorCode,
|
|
52
|
+
} from "../../../../chain/errors/index.js";
|
|
44
53
|
import {
|
|
45
54
|
BlockType,
|
|
46
55
|
ProduceFullBellatrix,
|
|
@@ -48,6 +57,7 @@ import {
|
|
|
48
57
|
ProduceFullFulu,
|
|
49
58
|
ProduceFullGloas,
|
|
50
59
|
} from "../../../../chain/produceBlock/index.js";
|
|
60
|
+
import {RegenCaller} from "../../../../chain/regen/index.js";
|
|
51
61
|
import {validateGossipBlock} from "../../../../chain/validation/block.js";
|
|
52
62
|
import {validateApiExecutionPayloadBid} from "../../../../chain/validation/executionPayloadBid.js";
|
|
53
63
|
import {validateApiExecutionPayloadEnvelope} from "../../../../chain/validation/executionPayloadEnvelope.js";
|
|
@@ -249,7 +259,7 @@ export function getBeaconBlockApi({
|
|
|
249
259
|
"api_reject_parent_unknown"
|
|
250
260
|
);
|
|
251
261
|
throw new BlockError(signedBlock, {
|
|
252
|
-
code: BlockErrorCode.
|
|
262
|
+
code: BlockErrorCode.PARENT_BLOCK_UNKNOWN,
|
|
253
263
|
parentRoot: toRootHex(signedBlock.message.parentRoot),
|
|
254
264
|
});
|
|
255
265
|
}
|
|
@@ -334,7 +344,8 @@ export function getBeaconBlockApi({
|
|
|
334
344
|
.catch((e) => {
|
|
335
345
|
if (
|
|
336
346
|
e instanceof BlockError &&
|
|
337
|
-
(e.type.code === BlockErrorCode.
|
|
347
|
+
(e.type.code === BlockErrorCode.PARENT_BLOCK_UNKNOWN ||
|
|
348
|
+
e.type.code === BlockErrorCode.PARENT_PAYLOAD_UNKNOWN)
|
|
338
349
|
) {
|
|
339
350
|
chain.emitter.emit(ChainEvent.blockUnknownParent, {
|
|
340
351
|
blockInput: blockForImport,
|
|
@@ -345,7 +356,9 @@ export function getBeaconBlockApi({
|
|
|
345
356
|
throw e;
|
|
346
357
|
}),
|
|
347
358
|
];
|
|
348
|
-
const sentPeersArr = await promiseAllMaybeAsync<number | void>(
|
|
359
|
+
const sentPeersArr = await promiseAllMaybeAsync<{sentPeers: number; alreadyPublished: boolean} | number | void>(
|
|
360
|
+
publishPromises
|
|
361
|
+
);
|
|
349
362
|
|
|
350
363
|
if (isForkPostGloas(fork)) {
|
|
351
364
|
// After gloas, data columns are not published with the block but when publishing the execution payload envelope
|
|
@@ -356,10 +369,13 @@ export function getBeaconBlockApi({
|
|
|
356
369
|
// + 1 because we publish to beacon_block first
|
|
357
370
|
for (let i = 0; i < dataColumnSidecars.length; i++) {
|
|
358
371
|
// + 1 because we publish to beacon_block first
|
|
359
|
-
const sentPeers = sentPeersArr[i + 1] as number;
|
|
360
|
-
// sent peers could be 0 as we set `allowPublishToZeroTopicPeers=true` in network.publishDataColumnSidecar()
|
|
372
|
+
const {sentPeers, alreadyPublished} = sentPeersArr[i + 1] as {sentPeers: number; alreadyPublished: boolean};
|
|
373
|
+
// sent peers could be 0 as we set `allowPublishToZeroTopicPeers=true` in network.publishDataColumnSidecar()
|
|
361
374
|
metrics?.dataColumns.sentPeersPerSubnet.observe(sentPeers);
|
|
362
|
-
|
|
375
|
+
// A duplicate publish (alreadyPublished=true) means the column is already propagating on the network —
|
|
376
|
+
// expected in self-build flows where peers gossip columns back to us before we publish the envelope.
|
|
377
|
+
// Only warn when we genuinely failed to reach any peer. See https://github.com/ChainSafe/lodestar/issues/9527.
|
|
378
|
+
if (sentPeers === 0 && !alreadyPublished) {
|
|
363
379
|
columnsPublishedWithZeroPeers++;
|
|
364
380
|
}
|
|
365
381
|
}
|
|
@@ -651,8 +667,22 @@ export function getBeaconBlockApi({
|
|
|
651
667
|
publishBlockV2,
|
|
652
668
|
publishBlindedBlockV2,
|
|
653
669
|
|
|
654
|
-
async publishExecutionPayloadEnvelope({
|
|
670
|
+
async publishExecutionPayloadEnvelope({signedEnvelopeOrContents, broadcastValidation}) {
|
|
655
671
|
const seenTimestampSec = Date.now() / 1000;
|
|
672
|
+
|
|
673
|
+
const blobDataIncluded = isSignedExecutionPayloadEnvelopeContents(signedEnvelopeOrContents);
|
|
674
|
+
let signedExecutionPayloadEnvelope: gloas.SignedExecutionPayloadEnvelope;
|
|
675
|
+
// Blobs and KZG proofs submitted alongside the envelope in the stateless flow
|
|
676
|
+
let submittedContents: {kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs} | null = null;
|
|
677
|
+
|
|
678
|
+
if (blobDataIncluded) {
|
|
679
|
+
signedExecutionPayloadEnvelope = signedEnvelopeOrContents.signedExecutionPayloadEnvelope;
|
|
680
|
+
submittedContents = {kzgProofs: signedEnvelopeOrContents.kzgProofs, blobs: signedEnvelopeOrContents.blobs};
|
|
681
|
+
} else {
|
|
682
|
+
// Stateful flow, blobs and KZG proofs are attached from the block production cache below
|
|
683
|
+
signedExecutionPayloadEnvelope = signedEnvelopeOrContents;
|
|
684
|
+
}
|
|
685
|
+
|
|
656
686
|
const envelope = signedExecutionPayloadEnvelope.message;
|
|
657
687
|
const slot = envelope.payload.slotNumber;
|
|
658
688
|
const fork = config.getForkName(slot);
|
|
@@ -663,7 +693,6 @@ export function getBeaconBlockApi({
|
|
|
663
693
|
throw new ApiError(400, `publishExecutionPayloadEnvelope not supported for pre-gloas fork=${fork}`);
|
|
664
694
|
}
|
|
665
695
|
|
|
666
|
-
// TODO GLOAS: review checks, do we want to implement `broadcast_validation`?
|
|
667
696
|
let block = chain.forkChoice.getBlockHex(blockRootHex, PayloadStatus.EMPTY);
|
|
668
697
|
if (block === null) {
|
|
669
698
|
// Only wait if the envelope is for the current slot
|
|
@@ -683,39 +712,172 @@ export function getBeaconBlockApi({
|
|
|
683
712
|
throw new ApiError(400, `Envelope slot ${slot} does not match block slot ${block.slot}`);
|
|
684
713
|
}
|
|
685
714
|
|
|
686
|
-
await validateApiExecutionPayloadEnvelope(chain, signedExecutionPayloadEnvelope);
|
|
687
|
-
|
|
688
715
|
const isSelfBuild = envelope.builderIndex === BUILDER_INDEX_SELF_BUILD;
|
|
689
|
-
|
|
716
|
+
const cachedResult = chain.blockProductionCache.get(blockRootHex);
|
|
717
|
+
// Only use the cached result if it contains payload data, blocks committing to a bid are cached without it
|
|
718
|
+
const cachedGloasResult =
|
|
719
|
+
cachedResult !== undefined &&
|
|
720
|
+
isForkPostGloas(cachedResult.fork) &&
|
|
721
|
+
cachedResult.type === BlockType.Full &&
|
|
722
|
+
(cachedResult as ProduceFullGloas).executionPayload !== undefined
|
|
723
|
+
? (cachedResult as ProduceFullGloas)
|
|
724
|
+
: undefined;
|
|
725
|
+
|
|
726
|
+
broadcastValidation = broadcastValidation ?? routes.beacon.BroadcastValidation.gossip;
|
|
727
|
+
const valLogMeta = {
|
|
728
|
+
slot,
|
|
729
|
+
blockRoot: blockRootHex,
|
|
730
|
+
blockHash: blockHashHex,
|
|
731
|
+
builderIndex: envelope.builderIndex,
|
|
732
|
+
isSelfBuild,
|
|
733
|
+
blobDataIncluded,
|
|
734
|
+
broadcastValidation,
|
|
735
|
+
...(submittedContents !== null ? {submittedBlobs: submittedContents.blobs.length} : {}),
|
|
736
|
+
};
|
|
737
|
+
try {
|
|
738
|
+
switch (broadcastValidation) {
|
|
739
|
+
case routes.beacon.BroadcastValidation.none: {
|
|
740
|
+
chain.logger.debug("Skipping broadcast validation of execution payload envelope", valLogMeta);
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
690
743
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
744
|
+
case routes.beacon.BroadcastValidation.gossip: {
|
|
745
|
+
await validateApiExecutionPayloadEnvelope(chain, signedExecutionPayloadEnvelope);
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
case routes.beacon.BroadcastValidation.consensusAndEquivocation:
|
|
750
|
+
case routes.beacon.BroadcastValidation.consensus: {
|
|
751
|
+
await validateApiExecutionPayloadEnvelope(chain, signedExecutionPayloadEnvelope);
|
|
752
|
+
|
|
753
|
+
// Verify the envelope against the post-block state
|
|
754
|
+
const blockState = await chain.regen
|
|
755
|
+
.getBlockSlotState(block, block.slot, {dontTransferCache: true}, RegenCaller.restApi)
|
|
756
|
+
.catch((e) => {
|
|
757
|
+
chain.logger.debug("Failed to regenerate block state for consensus checks", valLogMeta, e as Error);
|
|
758
|
+
return null;
|
|
759
|
+
});
|
|
760
|
+
if (blockState === null || !isStatePostGloas(blockState)) {
|
|
761
|
+
throw new ApiError(
|
|
762
|
+
500,
|
|
763
|
+
`Unable to regenerate block state for consensus checks slot=${slot} blockRoot=${blockRootHex}`
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
try {
|
|
767
|
+
// Signature and executionRequestsRoot are already verified by gossip validation above
|
|
768
|
+
verifyExecutionPayloadEnvelope(chain.config, blockState, envelope, {verifyExecutionRequestsRoot: false});
|
|
769
|
+
} catch (error) {
|
|
770
|
+
chain.logger.error(
|
|
771
|
+
"Consensus checks failed while publishing execution payload envelope",
|
|
772
|
+
valLogMeta,
|
|
773
|
+
error as Error
|
|
774
|
+
);
|
|
775
|
+
throw new ApiError(400, (error as Error).message);
|
|
776
|
+
}
|
|
777
|
+
chain.logger.debug("Consensus validated while publishing execution payload envelope", valLogMeta);
|
|
778
|
+
|
|
779
|
+
// TODO GLOAS: check the block is not a proposer equivocation before publishing the envelope
|
|
780
|
+
if (broadcastValidation === routes.beacon.BroadcastValidation.consensusAndEquivocation) {
|
|
781
|
+
const message = `Equivocation checks not yet implemented for broadcastValidation=${broadcastValidation}`;
|
|
782
|
+
if (chain.opts.broadcastValidationStrictness === "error") {
|
|
783
|
+
throw Error(message);
|
|
784
|
+
}
|
|
785
|
+
chain.logger.warn(message, valLogMeta);
|
|
786
|
+
}
|
|
787
|
+
break;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
default: {
|
|
791
|
+
const message = `Broadcast validation of ${broadcastValidation} type not implemented yet`;
|
|
792
|
+
if (chain.opts.broadcastValidationStrictness === "error") {
|
|
793
|
+
throw Error(message);
|
|
794
|
+
}
|
|
795
|
+
chain.logger.warn(message, valLogMeta);
|
|
796
|
+
}
|
|
699
797
|
}
|
|
700
|
-
|
|
701
|
-
|
|
798
|
+
} catch (error) {
|
|
799
|
+
if (
|
|
800
|
+
error instanceof ExecutionPayloadEnvelopeError &&
|
|
801
|
+
error.type.code === ExecutionPayloadEnvelopeErrorCode.ENVELOPE_ALREADY_KNOWN
|
|
802
|
+
) {
|
|
803
|
+
// The envelope may already be known, e.g. received via gossip from another node in a
|
|
804
|
+
// multi node setup, this is benign and treated as a successful publish (same as blocks)
|
|
805
|
+
if (submittedContents === null) {
|
|
806
|
+
chain.logger.debug("Ignoring already-known execution payload envelope during publishing", valLogMeta);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
// The envelope may have been gossiped without its data columns being published, e.g. if
|
|
810
|
+
// another beacon node failed mid-publish, still publish columns from the submitted blobs
|
|
811
|
+
chain.logger.debug("Publishing data columns of already-known execution payload envelope", valLogMeta);
|
|
812
|
+
} else {
|
|
813
|
+
throw error;
|
|
702
814
|
}
|
|
815
|
+
}
|
|
703
816
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
817
|
+
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
818
|
+
if (!payloadInput) {
|
|
819
|
+
// The block is awaited above (queuing if the envelope arrived first), and both the API and
|
|
820
|
+
// gossip import paths seed the PayloadEnvelopeInput before importing the block, so the input
|
|
821
|
+
// should exist here.
|
|
822
|
+
throw new ApiError(404, `PayloadEnvelopeInput not found for slot=${slot} blockRoot=${blockRootHex}`);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
let dataColumnSidecars: gloas.DataColumnSidecar[] = [];
|
|
826
|
+
let cells: fulu.Cell[][] | undefined;
|
|
827
|
+
let kzgProofs: deneb.KZGProofs | undefined;
|
|
828
|
+
let dataColumnTimer: (() => number) | undefined;
|
|
829
|
+
|
|
830
|
+
if (submittedContents !== null) {
|
|
831
|
+
// Validate submitted blob data against bid commitments before computing data column sidecars
|
|
832
|
+
const expectedBlobCount = payloadInput.getVersionedHashes().length;
|
|
833
|
+
if (submittedContents.blobs.length !== expectedBlobCount) {
|
|
834
|
+
throw new ApiError(
|
|
835
|
+
400,
|
|
836
|
+
`Submitted blob count does not match bid commitments submitted=${submittedContents.blobs.length} expected=${expectedBlobCount}`
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
const expectedProofCount = expectedBlobCount * NUMBER_OF_COLUMNS;
|
|
840
|
+
if (submittedContents.kzgProofs.length !== expectedProofCount) {
|
|
841
|
+
throw new ApiError(
|
|
842
|
+
400,
|
|
843
|
+
`Submitted KZG proof count does not match bid commitments submitted=${submittedContents.kzgProofs.length} expected=${expectedProofCount}`
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
if (submittedContents.blobs.length > 0) {
|
|
847
|
+
// If the block was produced by this node, reuse the cached cells and only time the
|
|
848
|
+
// metric when cells are actually computed from the submitted blobs
|
|
849
|
+
if (cachedGloasResult?.cells) {
|
|
850
|
+
cells = cachedGloasResult.cells;
|
|
851
|
+
} else {
|
|
852
|
+
dataColumnTimer = metrics?.peerDas.dataColumnSidecarComputationTime.startTimer();
|
|
853
|
+
cells = submittedContents.blobs.map((blob) => kzg.computeCells(blob));
|
|
854
|
+
}
|
|
855
|
+
kzgProofs = submittedContents.kzgProofs;
|
|
856
|
+
}
|
|
857
|
+
} else if (cachedGloasResult !== undefined) {
|
|
858
|
+
if (cachedGloasResult.cells && cachedGloasResult.blobsBundle.commitments.length > 0) {
|
|
859
|
+
cells = cachedGloasResult.cells;
|
|
860
|
+
kzgProofs = cachedGloasResult.blobsBundle.proofs;
|
|
716
861
|
}
|
|
717
862
|
} else {
|
|
718
|
-
//
|
|
863
|
+
// An envelope without blob data can only be published via the beacon node that cached them at block production
|
|
864
|
+
if (payloadInput.getVersionedHashes().length > 0) {
|
|
865
|
+
throw new ApiError(
|
|
866
|
+
400,
|
|
867
|
+
`No cached blob data to attach to execution payload envelope for block root ${blockRootHex}`
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (cells !== undefined && kzgProofs !== undefined && cells.length > 0) {
|
|
873
|
+
const proofs = kzgProofs;
|
|
874
|
+
const cellsAndProofs = cells.map((rowCells, rowIndex) => ({
|
|
875
|
+
cells: rowCells,
|
|
876
|
+
proofs: proofs.slice(rowIndex * NUMBER_OF_COLUMNS, (rowIndex + 1) * NUMBER_OF_COLUMNS),
|
|
877
|
+
}));
|
|
878
|
+
|
|
879
|
+
dataColumnSidecars = getGloasDataColumnSidecars(slot, envelope.beaconBlockRoot, cellsAndProofs);
|
|
880
|
+
dataColumnTimer?.();
|
|
719
881
|
}
|
|
720
882
|
|
|
721
883
|
// If called near a slot boundary (e.g. late in slot N-1), hold briefly so gossip aligns with slot N.
|
|
@@ -724,21 +886,18 @@ export function getBeaconBlockApi({
|
|
|
724
886
|
await sleep(msToBlockSlot);
|
|
725
887
|
}
|
|
726
888
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
889
|
+
if (payloadInput.hasPayloadEnvelope()) {
|
|
890
|
+
// The envelope may have been added while this request was being validated, e.g. via gossip
|
|
891
|
+
chain.logger.debug("Execution payload envelope already added during publishing", valLogMeta);
|
|
892
|
+
} else {
|
|
893
|
+
payloadInput.addPayloadEnvelope({
|
|
894
|
+
envelope: signedExecutionPayloadEnvelope,
|
|
895
|
+
source: PayloadEnvelopeInputSource.api,
|
|
896
|
+
seenTimestampSec,
|
|
897
|
+
peerIdStr: undefined,
|
|
898
|
+
});
|
|
733
899
|
}
|
|
734
900
|
|
|
735
|
-
payloadInput.addPayloadEnvelope({
|
|
736
|
-
envelope: signedExecutionPayloadEnvelope,
|
|
737
|
-
source: PayloadEnvelopeInputSource.api,
|
|
738
|
-
seenTimestampSec,
|
|
739
|
-
peerIdStr: undefined,
|
|
740
|
-
});
|
|
741
|
-
|
|
742
901
|
if (dataColumnSidecars.length > 0) {
|
|
743
902
|
for (const columnSidecar of dataColumnSidecars) {
|
|
744
903
|
payloadInput.addColumn({
|
|
@@ -750,31 +909,28 @@ export function getBeaconBlockApi({
|
|
|
750
909
|
}
|
|
751
910
|
}
|
|
752
911
|
|
|
753
|
-
const valLogMeta = {
|
|
754
|
-
slot,
|
|
755
|
-
blockRoot: blockRootHex,
|
|
756
|
-
blockHash: blockHashHex,
|
|
757
|
-
builderIndex: envelope.builderIndex,
|
|
758
|
-
isSelfBuild,
|
|
759
|
-
dataColumns: dataColumnSidecars.length,
|
|
760
|
-
};
|
|
761
|
-
|
|
762
912
|
const delaySec = seenTimestampSec - computeTimeAtSlot(config, slot, chain.genesisTime);
|
|
763
913
|
metrics?.gossipExecutionPayloadEnvelope.elapsedTimeTillReceived.observe({source: OpSource.api}, delaySec);
|
|
764
914
|
chain.validatorMonitor?.registerExecutionPayloadEnvelope(OpSource.api, delaySec, signedExecutionPayloadEnvelope);
|
|
765
915
|
|
|
766
|
-
chain.logger.info("Publishing execution payload envelope",
|
|
916
|
+
chain.logger.info("Publishing execution payload envelope", {
|
|
917
|
+
...valLogMeta,
|
|
918
|
+
dataColumns: dataColumnSidecars.length,
|
|
919
|
+
});
|
|
767
920
|
|
|
768
921
|
const publishPromises = [
|
|
769
922
|
// Gossip the signed execution payload envelope first
|
|
770
923
|
() => network.publishSignedExecutionPayloadEnvelope(signedExecutionPayloadEnvelope),
|
|
771
|
-
//
|
|
924
|
+
// Publish all data column sidecars
|
|
772
925
|
...dataColumnSidecars.map((dataColumnSidecar) => () => network.publishDataColumnSidecar(dataColumnSidecar)),
|
|
773
|
-
// Import execution payload. Signature
|
|
926
|
+
// Import execution payload. Signature is verified during broadcast validation, an already
|
|
927
|
+
// known envelope was verified by its original source and `none` deliberately skips validation
|
|
774
928
|
() => chain.processExecutionPayload(payloadInput, {validSignature: true}),
|
|
775
929
|
];
|
|
776
930
|
|
|
777
|
-
const publishPromise = promiseAllMaybeAsync<number | void>(
|
|
931
|
+
const publishPromise = promiseAllMaybeAsync<{sentPeers: number; alreadyPublished: boolean} | number | void>(
|
|
932
|
+
publishPromises
|
|
933
|
+
);
|
|
778
934
|
|
|
779
935
|
chain.emitter.emit(routes.events.EventType.executionPayloadGossip, {
|
|
780
936
|
slot,
|
|
@@ -790,9 +946,11 @@ export function getBeaconBlockApi({
|
|
|
790
946
|
let columnsPublishedWithZeroPeers = 0;
|
|
791
947
|
// Skip first entry (envelope); the final entry is processExecutionPayload(), which returns void.
|
|
792
948
|
for (let i = 0; i < dataColumnSidecars.length; i++) {
|
|
793
|
-
const sentPeers = sentPeersArr[i + 1] as number;
|
|
949
|
+
const {sentPeers, alreadyPublished} = sentPeersArr[i + 1] as {sentPeers: number; alreadyPublished: boolean};
|
|
794
950
|
metrics?.dataColumns.sentPeersPerSubnet.observe(sentPeers);
|
|
795
|
-
|
|
951
|
+
// A duplicate publish means the column is already propagating — expected in self-build flows.
|
|
952
|
+
// Only warn when we genuinely failed to reach any peer. See https://github.com/ChainSafe/lodestar/issues/9527.
|
|
953
|
+
if (sentPeers === 0 && !alreadyPublished) {
|
|
796
954
|
columnsPublishedWithZeroPeers++;
|
|
797
955
|
}
|
|
798
956
|
}
|
|
@@ -7,7 +7,7 @@ import {getBeaconRewardsApi} from "./rewards/index.js";
|
|
|
7
7
|
import {getBeaconStateApi} from "./state/index.js";
|
|
8
8
|
|
|
9
9
|
export function getBeaconApi(
|
|
10
|
-
modules: Pick<ApiModules, "chain" | "config" | "logger" | "metrics" | "network" | "db">
|
|
10
|
+
modules: Pick<ApiModules, "chain" | "config" | "logger" | "metrics" | "network" | "db" | "sync">
|
|
11
11
|
): ApplicationMethods<routes.beacon.Endpoints> {
|
|
12
12
|
const block = getBeaconBlockApi(modules);
|
|
13
13
|
const pool = getBeaconPoolApi(modules);
|
|
@@ -241,6 +241,11 @@ export function getBeaconPoolApi({
|
|
|
241
241
|
payloadAttestationMessage.data.blobDataAvailable
|
|
242
242
|
);
|
|
243
243
|
|
|
244
|
+
chain.emitter.emit(routes.events.EventType.payloadAttestationMessage, {
|
|
245
|
+
version: chain.config.getForkName(slot),
|
|
246
|
+
data: payloadAttestationMessage,
|
|
247
|
+
});
|
|
248
|
+
|
|
244
249
|
await network.publishPayloadAttestationMessage(payloadAttestationMessage);
|
|
245
250
|
} catch (e) {
|
|
246
251
|
const logCtx = {
|
|
@@ -26,11 +26,12 @@ import {
|
|
|
26
26
|
export function getBeaconStateApi({
|
|
27
27
|
chain,
|
|
28
28
|
config,
|
|
29
|
-
|
|
29
|
+
sync,
|
|
30
|
+
}: Pick<ApiModules, "chain" | "config" | "sync">): ApplicationMethods<routes.beacon.state.Endpoints> {
|
|
30
31
|
async function getState(
|
|
31
32
|
stateId: routes.beacon.StateId
|
|
32
33
|
): Promise<{state: IBeaconStateView; executionOptimistic: boolean; finalized: boolean}> {
|
|
33
|
-
const {state, executionOptimistic, finalized} = await getStateResponseWithRegen(chain, stateId);
|
|
34
|
+
const {state, executionOptimistic, finalized} = await getStateResponseWithRegen(chain, sync, stateId);
|
|
34
35
|
|
|
35
36
|
return {
|
|
36
37
|
state: state instanceof Uint8Array ? chain.getHeadState().loadOtherState(state) : state,
|
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
} from "@lodestar/types";
|
|
15
15
|
import {byteArrayEquals, fromHex} from "@lodestar/utils";
|
|
16
16
|
import {IBeaconChain} from "../../../../chain/index.js";
|
|
17
|
+
import {IBeaconSync} from "../../../../sync/index.js";
|
|
17
18
|
import {ApiError, ValidationError} from "../../errors.js";
|
|
19
|
+
import {notWhileSyncing} from "../../utils.js";
|
|
18
20
|
|
|
19
21
|
export function resolveStateId(
|
|
20
22
|
forkChoice: IForkChoice,
|
|
@@ -51,8 +53,17 @@ export function resolveStateId(
|
|
|
51
53
|
|
|
52
54
|
export async function getStateResponseWithRegen(
|
|
53
55
|
chain: IBeaconChain,
|
|
56
|
+
sync: IBeaconSync,
|
|
54
57
|
inStateId: routes.beacon.StateId
|
|
55
58
|
): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean}> {
|
|
59
|
+
// "head", "finalized" and "justified" resolve to already-available cached states, and "genesis" to a
|
|
60
|
+
// historical DB read - none trigger the forward regen that can walk back past the block-root window
|
|
61
|
+
// (SLOTS_PER_HISTORICAL_ROOT) and wedge a far-behind node. Keep serving those (node observability,
|
|
62
|
+
// dashboards, validator client checks) even while syncing; guard only the regen-capable lookups.
|
|
63
|
+
if (inStateId !== "head" && inStateId !== "finalized" && inStateId !== "justified" && inStateId !== "genesis") {
|
|
64
|
+
notWhileSyncing(chain, sync.state);
|
|
65
|
+
}
|
|
66
|
+
|
|
56
67
|
const stateId = resolveStateId(chain.forkChoice, inStateId);
|
|
57
68
|
|
|
58
69
|
const res =
|
|
@@ -39,7 +39,8 @@ function toPayloadStatusName(status: PayloadStatus): "pending" | "empty" | "full
|
|
|
39
39
|
export function getDebugApi({
|
|
40
40
|
chain,
|
|
41
41
|
config,
|
|
42
|
-
|
|
42
|
+
sync,
|
|
43
|
+
}: Pick<ApiModules, "chain" | "config" | "db" | "sync">): ApplicationMethods<routes.debug.Endpoints> {
|
|
43
44
|
return {
|
|
44
45
|
async getDebugChainHeadsV2() {
|
|
45
46
|
const heads = chain.forkChoice.getHeads();
|
|
@@ -132,7 +133,7 @@ export function getDebugApi({
|
|
|
132
133
|
},
|
|
133
134
|
|
|
134
135
|
async getStateV2({stateId}, context) {
|
|
135
|
-
const {state, executionOptimistic, finalized} = await getStateResponseWithRegen(chain, stateId);
|
|
136
|
+
const {state, executionOptimistic, finalized} = await getStateResponseWithRegen(chain, sync, stateId);
|
|
136
137
|
let slot: number, data: Uint8Array | BeaconState;
|
|
137
138
|
if (state instanceof Uint8Array) {
|
|
138
139
|
slot = getStateSlotFromBytes(state);
|
|
@@ -217,7 +217,7 @@ export function getLodestarApi({
|
|
|
217
217
|
},
|
|
218
218
|
|
|
219
219
|
async getHistoricalSummaries({stateId}) {
|
|
220
|
-
const {state, executionOptimistic, finalized} = await getStateResponseWithRegen(chain, stateId);
|
|
220
|
+
const {state, executionOptimistic, finalized} = await getStateResponseWithRegen(chain, sync, stateId);
|
|
221
221
|
|
|
222
222
|
const stateView = state instanceof Uint8Array ? chain.getHeadState().loadOtherState(state) : state;
|
|
223
223
|
|
|
@@ -332,7 +332,7 @@ export function getLodestarApi({
|
|
|
332
332
|
|
|
333
333
|
for (const [epoch, attestationsPerEpoch] of attestations) {
|
|
334
334
|
const slot = computeStartSlotAtEpoch(epoch);
|
|
335
|
-
const {state} = await getStateResponseWithRegen(chain, slot);
|
|
335
|
+
const {state} = await getStateResponseWithRegen(chain, sync, slot);
|
|
336
336
|
const stateView = state instanceof Uint8Array ? chain.getHeadState().loadOtherState(state) : state;
|
|
337
337
|
const shuffling = stateView.getShufflingAtEpoch(epoch);
|
|
338
338
|
for (const attestation of attestationsPerEpoch) {
|
|
@@ -8,7 +8,7 @@ import {ApiModules} from "../types.js";
|
|
|
8
8
|
|
|
9
9
|
export function getProofApi(
|
|
10
10
|
opts: ApiOptions,
|
|
11
|
-
{chain, config}: Pick<ApiModules, "chain" | "config" | "db">
|
|
11
|
+
{chain, config, sync}: Pick<ApiModules, "chain" | "config" | "db" | "sync">
|
|
12
12
|
): ApplicationMethods<routes.proof.Endpoints> {
|
|
13
13
|
// It's currently possible to request gigantic proofs (eg: a proof of the entire beacon state)
|
|
14
14
|
// We want some some sort of resistance against this DoS vector.
|
|
@@ -21,7 +21,7 @@ export function getProofApi(
|
|
|
21
21
|
throw new Error("Requested proof is too large.");
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const res = await getStateResponseWithRegen(chain, stateId);
|
|
24
|
+
const res = await getStateResponseWithRegen(chain, sync, stateId);
|
|
25
25
|
|
|
26
26
|
const state = res.state instanceof Uint8Array ? chain.getHeadState().loadOtherState(res.state) : res.state;
|
|
27
27
|
|
package/src/api/impl/utils.ts
CHANGED
|
@@ -1,4 +1,54 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
|
+
import type {IBeaconChain} from "../../chain/index.js";
|
|
3
|
+
import {SyncState} from "../../sync/index.js";
|
|
4
|
+
import {ApiError, NodeIsSyncing} from "./errors.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* If the node is within this many epochs from the head, we declare it to be synced regardless of
|
|
8
|
+
* the network sync state.
|
|
9
|
+
*
|
|
10
|
+
* This helps prevent attacks where nodes can convince us that we're syncing some non-existent
|
|
11
|
+
* finalized head.
|
|
12
|
+
*
|
|
13
|
+
* TODO: Lighthouse uses 8 for the attack described above. However, 8 kills Lodestar since validators
|
|
14
|
+
* can trigger regen to fast-forward head state 8 epochs to be immediately invalidated as sync sets
|
|
15
|
+
* a new head. Then the checkpoint state cache grows unbounded with very different states (because
|
|
16
|
+
* they are 8 epochs apart) and causes an OOM. Research a proper solution once regen and the state
|
|
17
|
+
* caches are better.
|
|
18
|
+
*/
|
|
19
|
+
export const SYNC_TOLERANCE_EPOCHS = 1;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Reject any request while the node is syncing. Used by endpoints that must not serve while the
|
|
23
|
+
* node is behind — validator duties, and beacon state lookups whose regen could otherwise walk
|
|
24
|
+
* back past the block-root window (`SLOTS_PER_HISTORICAL_ROOT`) and wedge the node. Throws
|
|
25
|
+
* {@link NodeIsSyncing} (503).
|
|
26
|
+
*/
|
|
27
|
+
export function notWhileSyncing(chain: IBeaconChain, syncState: SyncState): void {
|
|
28
|
+
// Consider node synced before or close to genesis
|
|
29
|
+
if (chain.clock.currentSlot < SLOTS_PER_EPOCH) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
switch (syncState) {
|
|
34
|
+
case SyncState.SyncingFinalized:
|
|
35
|
+
case SyncState.SyncingHead: {
|
|
36
|
+
const currentSlot = chain.clock.currentSlot;
|
|
37
|
+
const headSlot = chain.forkChoice.getHead().slot;
|
|
38
|
+
if (currentSlot - headSlot > SYNC_TOLERANCE_EPOCHS * SLOTS_PER_EPOCH) {
|
|
39
|
+
throw new NodeIsSyncing(`headSlot ${headSlot} currentSlot ${currentSlot}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
case SyncState.Synced:
|
|
46
|
+
return;
|
|
47
|
+
|
|
48
|
+
case SyncState.Stalled:
|
|
49
|
+
throw new NodeIsSyncing("waiting for peers");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
2
52
|
|
|
3
53
|
/**
|
|
4
54
|
* Ensures that the array contains unique values, and throws an ApiError
|