@lodestar/beacon-node 1.44.0-dev.7418ddd9f7 → 1.44.0-dev.762dd5f186
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 +36 -33
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +1 -26
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/config/constants.d.ts +5 -0
- package/lib/api/impl/config/constants.d.ts.map +1 -1
- package/lib/api/impl/config/constants.js +6 -1
- package/lib/api/impl/config/constants.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +69 -12
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +28 -0
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +41 -36
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +0 -4
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +15 -3
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +3 -3
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/chain.d.ts +2 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +9 -2
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +2 -1
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +0 -7
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +0 -3
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +12 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +2 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/payloadAttestation.d.ts +6 -0
- package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
- package/lib/chain/errors/payloadAttestation.js +1 -0
- package/lib/chain/errors/payloadAttestation.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts +4 -4
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +10 -7
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- 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 +1 -10
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +2 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +7 -7
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +1 -1
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +1 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts +0 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +0 -4
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +8 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +0 -5
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +0 -5
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -4
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +5 -2
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +0 -2
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/stateCache/types.js.map +1 -1
- package/lib/chain/validation/block.d.ts +5 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +4 -14
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +45 -6
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +1 -3
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +24 -4
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/execution/engine/http.js +1 -1
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +2 -2
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/interface.js +6 -2
- package/lib/execution/engine/interface.js.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +3 -5
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +15 -7
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +47 -9
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +4 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +10 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +770 -3
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +10 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +2 -2
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +25 -23
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +9 -5
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +13 -3
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts +2 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +16 -6
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts +2 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +15 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/index.js +4 -4
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +23 -5
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/sync/types.d.ts +9 -1
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +9 -2
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +64 -30
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/dataColumns.d.ts.map +1 -1
- package/lib/util/dataColumns.js +16 -11
- package/lib/util/dataColumns.js.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/package.json +15 -17
- package/src/api/impl/beacon/blocks/index.ts +40 -42
- package/src/api/impl/beacon/pool/index.ts +2 -41
- package/src/api/impl/config/constants.ts +10 -0
- package/src/api/impl/debug/index.ts +73 -12
- package/src/api/impl/lodestar/index.ts +30 -0
- package/src/api/impl/validator/index.ts +52 -53
- package/src/chain/archiveStore/archiveStore.ts +0 -5
- package/src/chain/blocks/importBlock.ts +20 -5
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +3 -5
- package/src/chain/chain.ts +12 -4
- package/src/chain/emitter.ts +3 -2
- package/src/chain/errors/blockError.ts +0 -4
- package/src/chain/errors/executionPayloadBid.ts +14 -0
- package/src/chain/errors/payloadAttestation.ts +2 -0
- package/src/chain/forkChoice/index.ts +13 -0
- package/src/chain/interface.ts +1 -2
- package/src/chain/options.ts +1 -0
- package/src/chain/prepareNextSlot.ts +1 -11
- package/src/chain/produceBlock/produceBlockBody.ts +10 -10
- package/src/chain/regen/interface.ts +1 -1
- package/src/chain/regen/queued.ts +0 -5
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +8 -3
- package/src/chain/stateCache/fifoBlockStateCache.ts +0 -6
- package/src/chain/stateCache/persistentCheckpointsCache.ts +6 -2
- package/src/chain/stateCache/types.ts +0 -2
- package/src/chain/validation/block.ts +12 -16
- package/src/chain/validation/executionPayloadBid.ts +48 -6
- package/src/chain/validation/executionPayloadEnvelope.ts +1 -3
- package/src/chain/validation/payloadAttestationMessage.ts +26 -4
- package/src/execution/engine/http.ts +1 -1
- package/src/execution/engine/interface.ts +12 -2
- package/src/execution/engine/mock.ts +6 -5
- package/src/execution/engine/types.ts +75 -14
- package/src/metrics/metrics/lodestar.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +10 -2
- package/src/network/processor/index.ts +27 -27
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +12 -5
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -3
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +22 -6
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +20 -1
- package/src/network/reqresp/handlers/index.ts +4 -4
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +22 -5
- package/src/sync/types.ts +11 -2
- package/src/sync/unknownBlock.ts +70 -31
- package/src/util/dataColumns.ts +17 -12
- package/src/util/sszBytes.ts +1 -1
|
@@ -30,7 +30,6 @@ export enum ArchiveStoreTask {
|
|
|
30
30
|
PruneHistory = "prune_history",
|
|
31
31
|
OnFinalizedCheckpoint = "on_finalized_checkpoint",
|
|
32
32
|
MaybeArchiveState = "maybe_archive_state",
|
|
33
|
-
RegenPruneOnFinalized = "regen_prune_on_finalized",
|
|
34
33
|
ForkchoicePrune = "forkchoice_prune",
|
|
35
34
|
UpdateBackfillRange = "update_backfill_range",
|
|
36
35
|
}
|
|
@@ -229,10 +228,6 @@ export class ArchiveStore {
|
|
|
229
228
|
await this.statesArchiverStrategy.maybeArchiveState(finalized, this.metrics);
|
|
230
229
|
timer?.({source: ArchiveStoreTask.MaybeArchiveState});
|
|
231
230
|
|
|
232
|
-
timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
|
|
233
|
-
this.chain.regen.pruneOnFinalized(finalizedEpoch);
|
|
234
|
-
timer?.({source: ArchiveStoreTask.RegenPruneOnFinalized});
|
|
235
|
-
|
|
236
231
|
// tasks rely on extended fork choice
|
|
237
232
|
timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
|
|
238
233
|
const prunedBlocks = this.chain.forkChoice.prune(finalized.rootHex);
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
} from "@lodestar/state-transition";
|
|
23
23
|
import {Attestation, BeaconBlock, altair, capella, electra, isGloasBeaconBlock, phase0, ssz} from "@lodestar/types";
|
|
24
24
|
import {isErrorAborted, toRootHex} from "@lodestar/utils";
|
|
25
|
-
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
25
|
+
import {GENESIS_SLOT, ZERO_HASH_HEX} from "../../constants/index.js";
|
|
26
26
|
import {callInNextEventLoop} from "../../util/eventLoop.js";
|
|
27
27
|
import {isOptimisticBlock} from "../../util/forkChoice.js";
|
|
28
28
|
import {isQueueErrorAborted} from "../../util/queue/index.js";
|
|
@@ -189,10 +189,12 @@ export async function importBlock(
|
|
|
189
189
|
this.seenBlockAttesters.addIndices(blockEpoch, indexedAttestation.attestingIndices);
|
|
190
190
|
|
|
191
191
|
const correctHead = ssz.Root.equals(rootCache.getBlockRootAtSlot(attestation.data.slot), beaconBlockRoot);
|
|
192
|
-
const missedSlotVote =
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
const missedSlotVote =
|
|
193
|
+
attestation.data.slot > GENESIS_SLOT &&
|
|
194
|
+
ssz.Root.equals(
|
|
195
|
+
rootCache.getBlockRootAtSlot(attestation.data.slot - 1),
|
|
196
|
+
rootCache.getBlockRootAtSlot(attestation.data.slot)
|
|
197
|
+
);
|
|
196
198
|
this.validatorMonitor?.registerAttestationInBlock(
|
|
197
199
|
indexedAttestation,
|
|
198
200
|
parentBlockSlot,
|
|
@@ -263,6 +265,7 @@ export async function importBlock(
|
|
|
263
265
|
if (ptcIndices.length > 0) {
|
|
264
266
|
this.forkChoice.notifyPtcMessages(
|
|
265
267
|
toRootHex(payloadAttestation.data.beaconBlockRoot),
|
|
268
|
+
payloadAttestation.data.slot,
|
|
266
269
|
ptcIndices,
|
|
267
270
|
payloadAttestation.data.payloadPresent,
|
|
268
271
|
payloadAttestation.data.blobDataAvailable
|
|
@@ -280,6 +283,18 @@ export async function importBlock(
|
|
|
280
283
|
const newHead = this.recomputeForkChoiceHead(ForkchoiceCaller.importBlock);
|
|
281
284
|
const currFinalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
|
|
282
285
|
|
|
286
|
+
// Prune the gloas payload-envelope cache below the new head's parent so it stays bounded during
|
|
287
|
+
// syncing. On a synced node, cache holds just 2 entries — head (parent for
|
|
288
|
+
// next-slot production) and head.parent (proposer-boost-reorg fallback)
|
|
289
|
+
if (fork >= ForkSeq.gloas) {
|
|
290
|
+
callInNextEventLoop(() => {
|
|
291
|
+
const newHeadParent = this.forkChoice.getBlockHexDefaultStatus(newHead.parentRoot);
|
|
292
|
+
if (newHeadParent) {
|
|
293
|
+
this.seenPayloadEnvelopeInputCache.pruneBelowParent(newHeadParent);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
283
298
|
if (newHead.blockRoot !== oldHead.blockRoot) {
|
|
284
299
|
// Set head state as strong reference
|
|
285
300
|
this.regen.updateHeadState(newHead, postState);
|
|
@@ -61,10 +61,8 @@ export function verifyExecutionPayloadEnvelope(
|
|
|
61
61
|
`Prev randao mismatch between bid and payload bid=${toHex(bid.prevRandao)} payload=${toHex(payload.prevRandao)}`
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
|
-
if (
|
|
65
|
-
throw new Error(
|
|
66
|
-
`Gas limit mismatch between payload and bid payload=${payload.gasLimit} bid=${Number(bid.gasLimit)}`
|
|
67
|
-
);
|
|
64
|
+
if (bid.gasLimit !== payload.gasLimit) {
|
|
65
|
+
throw new Error(`Gas limit mismatch between payload and bid payload=${payload.gasLimit} bid=${bid.gasLimit}`);
|
|
68
66
|
}
|
|
69
67
|
if (!byteArrayEquals(bid.blockHash, payload.blockHash)) {
|
|
70
68
|
throw new Error(
|
|
@@ -74,7 +72,7 @@ export function verifyExecutionPayloadEnvelope(
|
|
|
74
72
|
// Verify execution_requests_root matches bid commitment.
|
|
75
73
|
// Can be skipped if already verified during gossip validation.
|
|
76
74
|
if (verifyExecutionRequestsRoot) {
|
|
77
|
-
const requestsRoot = ssz.
|
|
75
|
+
const requestsRoot = ssz.gloas.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
|
|
78
76
|
if (!byteArrayEquals(requestsRoot, bid.executionRequestsRoot)) {
|
|
79
77
|
throw new Error(
|
|
80
78
|
`Execution requests root mismatch envelope=${toRootHex(requestsRoot)} bid=${toRootHex(bid.executionRequestsRoot)}`
|
package/src/chain/chain.ts
CHANGED
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import {PrivateKey} from "@libp2p/interface";
|
|
3
3
|
import {Type} from "@chainsafe/ssz";
|
|
4
4
|
import {BeaconConfig} from "@lodestar/config";
|
|
5
|
-
import {CheckpointWithHex, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
|
|
5
|
+
import {CheckpointWithHex, ForkChoiceStateGetter, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
|
|
6
6
|
import {LoggerNode} from "@lodestar/logger/node";
|
|
7
7
|
import {
|
|
8
8
|
EFFECTIVE_BALANCE_INCREMENT,
|
|
@@ -40,7 +40,6 @@ import {
|
|
|
40
40
|
ValidatorIndex,
|
|
41
41
|
Wei,
|
|
42
42
|
deneb,
|
|
43
|
-
electra,
|
|
44
43
|
gloas,
|
|
45
44
|
isBlindedBeaconBlock,
|
|
46
45
|
phase0,
|
|
@@ -382,6 +381,14 @@ export class BeaconChain implements IBeaconChain {
|
|
|
382
381
|
blockStateCache.setHeadState(anchorState);
|
|
383
382
|
checkpointStateCache.add(checkpoint, anchorState);
|
|
384
383
|
|
|
384
|
+
const forkChoiceStateGetter: ForkChoiceStateGetter = ({stateRoot, checkpoint}) => {
|
|
385
|
+
if (stateRoot) return blockStateCache.get(stateRoot);
|
|
386
|
+
|
|
387
|
+
if (checkpoint) return checkpointStateCache.get({epoch: checkpoint.epoch, rootHex: checkpoint.rootHex});
|
|
388
|
+
|
|
389
|
+
return null;
|
|
390
|
+
};
|
|
391
|
+
|
|
385
392
|
const forkChoice = initializeForkChoice(
|
|
386
393
|
config,
|
|
387
394
|
emitter,
|
|
@@ -390,6 +397,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
390
397
|
isAnchorStateFinalized,
|
|
391
398
|
opts,
|
|
392
399
|
this.justifiedBalancesGetter.bind(this),
|
|
400
|
+
forkChoiceStateGetter,
|
|
393
401
|
metrics,
|
|
394
402
|
logger
|
|
395
403
|
);
|
|
@@ -916,10 +924,10 @@ export class BeaconChain implements IBeaconChain {
|
|
|
916
924
|
async getParentExecutionRequests(
|
|
917
925
|
parentBlockSlot: Slot,
|
|
918
926
|
parentBlockRootHex: RootHex
|
|
919
|
-
): Promise<
|
|
927
|
+
): Promise<gloas.ExecutionRequests> {
|
|
920
928
|
// at the fork boundary, parent is pre-gloas
|
|
921
929
|
if (!isForkPostGloas(this.config.getForkName(parentBlockSlot))) {
|
|
922
|
-
return ssz.
|
|
930
|
+
return ssz.gloas.ExecutionRequests.defaultValue();
|
|
923
931
|
}
|
|
924
932
|
const envelope = await this.getExecutionPayloadEnvelope(parentBlockSlot, parentBlockRootHex);
|
|
925
933
|
if (envelope === null) {
|
package/src/chain/emitter.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {StrictEventEmitter} from "strict-event-emitter-types";
|
|
|
3
3
|
import {routes} from "@lodestar/api";
|
|
4
4
|
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
5
5
|
import {IBeaconStateView} from "@lodestar/state-transition";
|
|
6
|
-
import {DataColumnSidecar, RootHex, deneb, phase0} from "@lodestar/types";
|
|
6
|
+
import {DataColumnSidecar, RootHex, Slot, deneb, phase0} from "@lodestar/types";
|
|
7
7
|
import {PeerIdStr} from "../util/peerId.js";
|
|
8
8
|
import {BlockInputSource, IBlockInput} from "./blocks/blockInput/types.js";
|
|
9
9
|
import {PayloadEnvelopeInput} from "./blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
@@ -94,7 +94,8 @@ export type ChainEventData = {
|
|
|
94
94
|
peer: PeerIdStr;
|
|
95
95
|
source: BlockInputSource;
|
|
96
96
|
};
|
|
97
|
-
|
|
97
|
+
// slot is the message slot, not necessarily the envelope's slot, but useful as a logging/prune hint
|
|
98
|
+
[ChainEvent.unknownEnvelopeBlockRoot]: {rootHex: RootHex; slot: Slot; peer?: PeerIdStr; source: BlockInputSource};
|
|
98
99
|
};
|
|
99
100
|
|
|
100
101
|
export type IChainEvents = ApiEvents & {
|
|
@@ -61,9 +61,6 @@ export enum BlockErrorCode {
|
|
|
61
61
|
TRANSACTIONS_TOO_BIG = "BLOCK_ERROR_TRANSACTIONS_TOO_BIG",
|
|
62
62
|
/** Execution engine is unavailable, syncing, or api call errored. Peers must not be downscored on this code */
|
|
63
63
|
EXECUTION_ENGINE_ERROR = "BLOCK_ERROR_EXECUTION_ERROR",
|
|
64
|
-
/** The attestation head block is too far behind the attestation slot, causing many skip slots.
|
|
65
|
-
This is deemed a DoS risk */
|
|
66
|
-
TOO_MANY_SKIPPED_SLOTS = "TOO_MANY_SKIPPED_SLOTS",
|
|
67
64
|
/** The blobs are unavailable */
|
|
68
65
|
DATA_UNAVAILABLE = "BLOCK_ERROR_DATA_UNAVAILABLE",
|
|
69
66
|
/** Block contains too many kzg commitments */
|
|
@@ -89,7 +86,6 @@ export type BlockErrorType =
|
|
|
89
86
|
| {code: BlockErrorCode.FUTURE_SLOT; blockSlot: Slot; currentSlot: Slot}
|
|
90
87
|
| {code: BlockErrorCode.STATE_ROOT_MISMATCH}
|
|
91
88
|
| {code: BlockErrorCode.GENESIS_BLOCK}
|
|
92
|
-
| {code: BlockErrorCode.TOO_MANY_SKIPPED_SLOTS; parentSlot: Slot; blockSlot: Slot}
|
|
93
89
|
| {code: BlockErrorCode.WOULD_REVERT_FINALIZED_SLOT; blockSlot: Slot; finalizedSlot: Slot}
|
|
94
90
|
| {code: BlockErrorCode.ALREADY_KNOWN; root: RootHex}
|
|
95
91
|
| {code: BlockErrorCode.REPEAT_PROPOSAL; proposerIndex: ValidatorIndex}
|
|
@@ -3,6 +3,7 @@ import {GossipActionError} from "./gossipValidation.js";
|
|
|
3
3
|
|
|
4
4
|
export enum ExecutionPayloadBidErrorCode {
|
|
5
5
|
BUILDER_NOT_ELIGIBLE = "EXECUTION_PAYLOAD_BID_ERROR_BUILDER_NOT_ELIGIBLE",
|
|
6
|
+
INVALID_BUILDER_VERSION = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_BUILDER_VERSION",
|
|
6
7
|
NON_ZERO_EXECUTION_PAYMENT = "EXECUTION_PAYLOAD_BID_ERROR_NON_ZERO_EXECUTION_PAYMENT",
|
|
7
8
|
BID_ALREADY_KNOWN = "EXECUTION_PAYLOAD_BID_ERROR_BID_ALREADY_KNOWN",
|
|
8
9
|
BID_TOO_LOW = "EXECUTION_PAYLOAD_BID_ERROR_BID_TOO_LOW",
|
|
@@ -12,6 +13,7 @@ export enum ExecutionPayloadBidErrorCode {
|
|
|
12
13
|
UNKNOWN_PARENT_BLOCK_HASH = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_PARENT_BLOCK_HASH",
|
|
13
14
|
INVALID_SLOT = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SLOT",
|
|
14
15
|
NOT_LATER_THAN_PARENT = "EXECUTION_PAYLOAD_BID_ERROR_NOT_LATER_THAN_PARENT",
|
|
16
|
+
INVALID_PREV_RANDAO = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_PREV_RANDAO",
|
|
15
17
|
INVALID_SIGNATURE = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SIGNATURE",
|
|
16
18
|
NO_MATCHING_PROPOSER_PREFERENCES = "EXECUTION_PAYLOAD_BID_ERROR_NO_MATCHING_PROPOSER_PREFERENCES",
|
|
17
19
|
PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH = "EXECUTION_PAYLOAD_BID_ERROR_PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH",
|
|
@@ -20,6 +22,12 @@ export enum ExecutionPayloadBidErrorCode {
|
|
|
20
22
|
|
|
21
23
|
export type ExecutionPayloadBidErrorType =
|
|
22
24
|
| {code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE; builderIndex: BuilderIndex}
|
|
25
|
+
| {
|
|
26
|
+
code: ExecutionPayloadBidErrorCode.INVALID_BUILDER_VERSION;
|
|
27
|
+
builderIndex: BuilderIndex;
|
|
28
|
+
version: number;
|
|
29
|
+
expectedVersion: number;
|
|
30
|
+
}
|
|
23
31
|
| {
|
|
24
32
|
code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT;
|
|
25
33
|
builderIndex: BuilderIndex;
|
|
@@ -43,6 +51,12 @@ export type ExecutionPayloadBidErrorType =
|
|
|
43
51
|
| {code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH; parentBlockHash: RootHex}
|
|
44
52
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SLOT; builderIndex: BuilderIndex; slot: Slot}
|
|
45
53
|
| {code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
|
|
54
|
+
| {
|
|
55
|
+
code: ExecutionPayloadBidErrorCode.INVALID_PREV_RANDAO;
|
|
56
|
+
builderIndex: BuilderIndex;
|
|
57
|
+
bidPrevRandao: string;
|
|
58
|
+
expectedPrevRandao: string;
|
|
59
|
+
}
|
|
46
60
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE; builderIndex: BuilderIndex; slot: Slot}
|
|
47
61
|
| {
|
|
48
62
|
code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES;
|
|
@@ -5,6 +5,7 @@ export enum PayloadAttestationErrorCode {
|
|
|
5
5
|
NOT_CURRENT_SLOT = "PAYLOAD_ATTESTATION_ERROR_NOT_CURRENT_SLOT",
|
|
6
6
|
PAYLOAD_ATTESTATION_ALREADY_KNOWN = "PAYLOAD_ATTESTATION_ERROR_PAYLOAD_ATTESTATION_ALREADY_KNOWN",
|
|
7
7
|
UNKNOWN_BLOCK_ROOT = "PAYLOAD_ATTESTATION_ERROR_UNKNOWN_BLOCK_ROOT",
|
|
8
|
+
INVALID_BLOCK_SLOT = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK_SLOT",
|
|
8
9
|
INVALID_BLOCK = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK",
|
|
9
10
|
INVALID_ATTESTER = "PAYLOAD_ATTESTATION_ERROR_INVALID_ATTESTER",
|
|
10
11
|
INVALID_SIGNATURE = "PAYLOAD_ATTESTATION_ERROR_INVALID_SIGNATURE",
|
|
@@ -18,6 +19,7 @@ export type PayloadAttestationErrorType =
|
|
|
18
19
|
blockRoot: RootHex;
|
|
19
20
|
}
|
|
20
21
|
| {code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT; blockRoot: RootHex}
|
|
22
|
+
| {code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT; blockRoot: RootHex; blockSlot: Slot; slot: Slot}
|
|
21
23
|
| {code: PayloadAttestationErrorCode.INVALID_BLOCK; blockRoot: RootHex}
|
|
22
24
|
| {code: PayloadAttestationErrorCode.INVALID_ATTESTER; attesterIndex: ValidatorIndex}
|
|
23
25
|
| {code: PayloadAttestationErrorCode.INVALID_SIGNATURE};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import {routes} from "@lodestar/api";
|
|
1
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
3
|
import {
|
|
3
4
|
ExecutionStatus,
|
|
4
5
|
ForkChoice,
|
|
6
|
+
ForkChoiceStateGetter,
|
|
5
7
|
ForkChoiceStore,
|
|
6
8
|
JustifiedBalancesGetter,
|
|
7
9
|
PayloadStatus,
|
|
@@ -45,6 +47,7 @@ export function initializeForkChoice(
|
|
|
45
47
|
isFinalizedState: boolean,
|
|
46
48
|
opts: ForkChoiceOpts,
|
|
47
49
|
justifiedBalancesGetter: JustifiedBalancesGetter,
|
|
50
|
+
stateGetter: ForkChoiceStateGetter,
|
|
48
51
|
metrics: Metrics | null,
|
|
49
52
|
logger?: Logger
|
|
50
53
|
): ForkChoice {
|
|
@@ -56,6 +59,7 @@ export function initializeForkChoice(
|
|
|
56
59
|
state,
|
|
57
60
|
opts,
|
|
58
61
|
justifiedBalancesGetter,
|
|
62
|
+
stateGetter,
|
|
59
63
|
metrics,
|
|
60
64
|
logger
|
|
61
65
|
)
|
|
@@ -66,6 +70,7 @@ export function initializeForkChoice(
|
|
|
66
70
|
state,
|
|
67
71
|
opts,
|
|
68
72
|
justifiedBalancesGetter,
|
|
73
|
+
stateGetter,
|
|
69
74
|
metrics,
|
|
70
75
|
logger
|
|
71
76
|
);
|
|
@@ -81,6 +86,7 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
81
86
|
state: IBeaconStateView,
|
|
82
87
|
opts: ForkChoiceOpts,
|
|
83
88
|
justifiedBalancesGetter: JustifiedBalancesGetter,
|
|
89
|
+
stateGetter: ForkChoiceStateGetter,
|
|
84
90
|
metrics: Metrics | null,
|
|
85
91
|
logger?: Logger
|
|
86
92
|
): ForkChoice {
|
|
@@ -112,9 +118,12 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
112
118
|
finalizedCheckpoint,
|
|
113
119
|
justifiedBalances,
|
|
114
120
|
justifiedBalancesGetter,
|
|
121
|
+
stateGetter,
|
|
115
122
|
{
|
|
116
123
|
onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
|
|
117
124
|
onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
|
|
125
|
+
onFastConfirmation: ({block, slot, currentSlot}) =>
|
|
126
|
+
emitter.emit(routes.events.EventType.fastConfirmation, {block, slot, currentSlot}),
|
|
118
127
|
}
|
|
119
128
|
),
|
|
120
129
|
|
|
@@ -172,6 +181,7 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
172
181
|
unfinalizedState: IBeaconStateView,
|
|
173
182
|
opts: ForkChoiceOpts,
|
|
174
183
|
justifiedBalancesGetter: JustifiedBalancesGetter,
|
|
184
|
+
stateGetter: ForkChoiceStateGetter,
|
|
175
185
|
metrics: Metrics | null,
|
|
176
186
|
logger?: Logger
|
|
177
187
|
): ForkChoice {
|
|
@@ -203,9 +213,12 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
203
213
|
finalizedCheckpoint,
|
|
204
214
|
justifiedBalances,
|
|
205
215
|
justifiedBalancesGetter,
|
|
216
|
+
stateGetter,
|
|
206
217
|
{
|
|
207
218
|
onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
|
|
208
219
|
onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
|
|
220
|
+
onFastConfirmation: ({block, slot, currentSlot}) =>
|
|
221
|
+
emitter.emit(routes.events.EventType.fastConfirmation, {block, slot, currentSlot}),
|
|
209
222
|
}
|
|
210
223
|
);
|
|
211
224
|
|
package/src/chain/interface.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
altair,
|
|
19
19
|
capella,
|
|
20
20
|
deneb,
|
|
21
|
-
electra,
|
|
22
21
|
gloas,
|
|
23
22
|
phase0,
|
|
24
23
|
rewards,
|
|
@@ -236,7 +235,7 @@ export interface IBeaconChain {
|
|
|
236
235
|
blockSlot: Slot,
|
|
237
236
|
blockRootHex: string
|
|
238
237
|
): Promise<gloas.SignedExecutionPayloadEnvelope | null>;
|
|
239
|
-
getParentExecutionRequests(parentBlockSlot: Slot, parentBlockRootHex: RootHex): Promise<
|
|
238
|
+
getParentExecutionRequests(parentBlockSlot: Slot, parentBlockRootHex: RootHex): Promise<gloas.ExecutionRequests>;
|
|
240
239
|
|
|
241
240
|
produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody>;
|
|
242
241
|
produceBlock(blockAttributes: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}): Promise<{
|
package/src/chain/options.ts
CHANGED
|
@@ -105,6 +105,7 @@ export const defaultChainOptions: IChainOptions = {
|
|
|
105
105
|
proposerBoost: true,
|
|
106
106
|
proposerBoostReorg: true,
|
|
107
107
|
computeUnrealized: true,
|
|
108
|
+
fastConfirmation: false,
|
|
108
109
|
suggestedFeeRecipient: defaultValidatorOptions.suggestedFeeRecipient,
|
|
109
110
|
serveHistoricalState: false,
|
|
110
111
|
assertCorrectProgressiveBalances: false,
|
|
@@ -170,7 +170,7 @@ export class PrepareNextSlotScheduler {
|
|
|
170
170
|
let stateAfterParentPayload: IBeaconStateViewBellatrix = updatedPrepareState;
|
|
171
171
|
if (isStatePostGloas(updatedPrepareState)) {
|
|
172
172
|
// Spec: should_build_on_full(store, head) — see produceBlockBody.ts for context.
|
|
173
|
-
if (this.chain.forkChoice.shouldBuildOnFull(updatedHead)) {
|
|
173
|
+
if (this.chain.forkChoice.shouldBuildOnFull(updatedHead, prepareSlot)) {
|
|
174
174
|
parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.blockHash;
|
|
175
175
|
// Skip applying parent payload unless we're proposing the next slot or have to emit payload_attributes events
|
|
176
176
|
if (feeRecipient !== undefined || this.chain.opts.emitPayloadAttributes === true) {
|
|
@@ -217,16 +217,6 @@ export class PrepareNextSlotScheduler {
|
|
|
217
217
|
});
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
221
|
-
// Cutoff = slot of the parent of the block we'll actually build on (post-reorg).
|
|
222
|
-
// Steady state: cache holds just 2 entries — head (parent for next-slot production)
|
|
223
|
-
// and head.parent (proposer-boost-reorg fallback). Anything older is evicted.
|
|
224
|
-
const updatedHeadParent = this.chain.forkChoice.getBlockHexDefaultStatus(updatedHead.parentRoot);
|
|
225
|
-
if (updatedHeadParent) {
|
|
226
|
-
this.chain.seenPayloadEnvelopeInputCache.pruneBelowParent(updatedHeadParent);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
220
|
this.computeStateHashTreeRoot(updatedPrepareState, isEpochTransition);
|
|
231
221
|
|
|
232
222
|
// If emitPayloadAttributes is true emit a SSE payloadAttributes event for
|
|
@@ -108,7 +108,7 @@ export type ProduceFullGloas = {
|
|
|
108
108
|
type: BlockType.Full;
|
|
109
109
|
fork: ForkPostGloas;
|
|
110
110
|
executionPayload: ExecutionPayload<ForkPostGloas>;
|
|
111
|
-
executionRequests:
|
|
111
|
+
executionRequests: gloas.ExecutionRequests;
|
|
112
112
|
blobsBundle: BlobsBundle<ForkPostGloas>;
|
|
113
113
|
cells: fulu.Cell[][];
|
|
114
114
|
parentBlockRoot: Root;
|
|
@@ -228,7 +228,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
228
228
|
);
|
|
229
229
|
const parentExecutionRequests = isExtendingPayload
|
|
230
230
|
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
231
|
-
: ssz.
|
|
231
|
+
: ssz.gloas.ExecutionRequests.defaultValue();
|
|
232
232
|
executionPayloadValue = BigInt(builderBid.message.value) * GWEI_TO_WEI;
|
|
233
233
|
|
|
234
234
|
const commonBlockBody = await commonBlockBodyPromise;
|
|
@@ -272,20 +272,20 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
272
272
|
|
|
273
273
|
// Get execution payload from EL
|
|
274
274
|
let parentBlockHash: Bytes32;
|
|
275
|
-
let parentExecutionRequests:
|
|
275
|
+
let parentExecutionRequests: gloas.ExecutionRequests;
|
|
276
276
|
// Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
|
|
277
277
|
let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
|
|
278
278
|
// Spec: should_build_on_full(store, head). `parentBlock` is the proposer's head
|
|
279
|
-
// (set by chain.getProposerHead(slot)). Returns false when the PTC majority
|
|
280
|
-
//
|
|
281
|
-
const isBuildingOnFull = this.forkChoice.shouldBuildOnFull(parentBlock);
|
|
279
|
+
// (set by chain.getProposerHead(slot)). Returns false when the PTC majority signalled
|
|
280
|
+
// the blob data is not available or the payload was not timely, forcing a build on EMPTY (reorg).
|
|
281
|
+
const isBuildingOnFull = this.forkChoice.shouldBuildOnFull(parentBlock, blockSlot);
|
|
282
282
|
if (isBuildingOnFull) {
|
|
283
283
|
parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
|
|
284
284
|
parentExecutionRequests = await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot);
|
|
285
285
|
stateAfterParentPayload = currentState.withParentPayloadApplied(parentExecutionRequests);
|
|
286
286
|
} else {
|
|
287
287
|
parentBlockHash = currentState.latestExecutionPayloadBid.parentBlockHash;
|
|
288
|
-
parentExecutionRequests = ssz.
|
|
288
|
+
parentExecutionRequests = ssz.gloas.ExecutionRequests.defaultValue();
|
|
289
289
|
}
|
|
290
290
|
const prepareRes = await prepareExecutionPayload(
|
|
291
291
|
this,
|
|
@@ -344,13 +344,13 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
344
344
|
blockHash: executionPayload.blockHash,
|
|
345
345
|
prevRandao: currentState.getRandaoMix(currentState.epoch),
|
|
346
346
|
feeRecipient: executionPayload.feeRecipient,
|
|
347
|
-
gasLimit:
|
|
347
|
+
gasLimit: executionPayload.gasLimit,
|
|
348
348
|
builderIndex: BUILDER_INDEX_SELF_BUILD,
|
|
349
349
|
slot: blockSlot,
|
|
350
350
|
value: 0,
|
|
351
351
|
executionPayment: 0,
|
|
352
352
|
blobKzgCommitments: blobsBundle.commitments,
|
|
353
|
-
executionRequestsRoot: ssz.
|
|
353
|
+
executionRequestsRoot: ssz.gloas.ExecutionRequests.hashTreeRoot(executionRequests as gloas.ExecutionRequests),
|
|
354
354
|
};
|
|
355
355
|
const signedBid: gloas.SignedExecutionPayloadBid = {
|
|
356
356
|
message: bid,
|
|
@@ -375,7 +375,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
375
375
|
// Store execution payload data required to construct execution payload envelope later
|
|
376
376
|
const gloasResult = produceResult as ProduceFullGloas;
|
|
377
377
|
gloasResult.executionPayload = executionPayload as ExecutionPayload<ForkPostGloas>;
|
|
378
|
-
gloasResult.executionRequests = executionRequests;
|
|
378
|
+
gloasResult.executionRequests = executionRequests as gloas.ExecutionRequests;
|
|
379
379
|
gloasResult.blobsBundle = blobsBundle;
|
|
380
380
|
gloasResult.cells = cells;
|
|
381
381
|
gloasResult.parentBlockRoot = fromHex(parentBlock.blockRoot);
|
|
@@ -22,6 +22,7 @@ export enum RegenCaller {
|
|
|
22
22
|
validateGossipAttestation = "validateGossipAttestation",
|
|
23
23
|
validateGossipVoluntaryExit = "validateGossipVoluntaryExit",
|
|
24
24
|
validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid",
|
|
25
|
+
validateGossipPayloadAttestationMessage = "validateGossipPayloadAttestationMessage",
|
|
25
26
|
validateGossipProposerPreferences = "validateGossipProposerPreferences",
|
|
26
27
|
onForkChoiceFinalized = "onForkChoiceFinalized",
|
|
27
28
|
restApi = "restApi",
|
|
@@ -46,7 +47,6 @@ export interface IStateRegenerator extends IStateRegeneratorInternal {
|
|
|
46
47
|
getCheckpointStateSync(cp: CheckpointHex): IBeaconStateView | null;
|
|
47
48
|
getClosestHeadState(head: ProtoBlock): IBeaconStateView | null;
|
|
48
49
|
pruneOnCheckpoint(finalizedEpoch: Epoch, justifiedEpoch: Epoch, headStateRoot: RootHex): void;
|
|
49
|
-
pruneOnFinalized(finalizedEpoch: Epoch): void;
|
|
50
50
|
processState(blockRootHex: RootHex, postState: IBeaconStateView): void;
|
|
51
51
|
addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView): void;
|
|
52
52
|
updateHeadState(newHead: ProtoBlock, maybeHeadState: IBeaconStateView): void;
|
|
@@ -143,11 +143,6 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
143
143
|
this.blockStateCache.prune(headStateRoot);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
pruneOnFinalized(finalizedEpoch: number): void {
|
|
147
|
-
this.checkpointStateCache.pruneFinalized(finalizedEpoch);
|
|
148
|
-
this.blockStateCache.deleteAllBeforeEpoch(finalizedEpoch);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
146
|
processState(blockRootHex: RootHex, postState: IBeaconStateView): void {
|
|
152
147
|
this.blockStateCache.add(postState);
|
|
153
148
|
this.checkpointStateCache.processState(blockRootHex, postState).catch((e) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {CheckpointWithHex, IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
|
|
2
|
+
import {CheckpointWithHex, IForkChoice, PayloadStatus, ProtoBlock} from "@lodestar/fork-choice";
|
|
3
3
|
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
4
|
import {RootHex} from "@lodestar/types";
|
|
5
5
|
import {Logger} from "@lodestar/utils";
|
|
@@ -154,9 +154,14 @@ export class SeenPayloadEnvelopeInput {
|
|
|
154
154
|
|
|
155
155
|
pruneBelowParent(parentBlock: ProtoBlock): void {
|
|
156
156
|
for (const block of this.forkChoice.getAllAncestorBlocks(parentBlock.blockRoot, parentBlock.payloadStatus)) {
|
|
157
|
-
|
|
157
|
+
// Only evict once the payload is FULL (revealed/imported) — on an EMPTY/PENDING branch we may
|
|
158
|
+
// still need to download the FULL envelope (see #9475), and evicting would make payload-by-root
|
|
159
|
+
// sync throw "Missing PayloadEnvelopeInput for known block".
|
|
160
|
+
if (block.slot < parentBlock.slot && block.payloadStatus === PayloadStatus.FULL) {
|
|
158
161
|
const input = this.payloadInputs.get(block.blockRoot);
|
|
159
|
-
|
|
162
|
+
// ...and don't evict while columns are still being gathered: writeDataColumnsToDb awaits the
|
|
163
|
+
// same hasComputedAllData() before persisting. Such entries are pruned by a later call.
|
|
164
|
+
if (input?.hasComputedAllData()) {
|
|
160
165
|
this.evictPayloadInput(input);
|
|
161
166
|
this.logger?.verbose("SeenPayloadEnvelopeInput.pruneBelowParent deleted", {
|
|
162
167
|
slot: block.slot,
|
|
@@ -167,12 +167,6 @@ export class FIFOBlockStateCache implements BlockStateCache {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
/**
|
|
171
|
-
* No need for this implementation
|
|
172
|
-
* This is only to conform to the old api
|
|
173
|
-
*/
|
|
174
|
-
deleteAllBeforeEpoch(): void {}
|
|
175
|
-
|
|
176
170
|
/**
|
|
177
171
|
* ONLY FOR DEBUGGING PURPOSES. For lodestar debug API.
|
|
178
172
|
*/
|
|
@@ -414,11 +414,12 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
|
|
|
414
414
|
|
|
415
415
|
/**
|
|
416
416
|
* Prune all checkpoint states before the provided finalized epoch.
|
|
417
|
+
* Driven sequentially from processState() so it never interleaves with persist.
|
|
417
418
|
*/
|
|
418
|
-
pruneFinalized(finalizedEpoch: Epoch): void {
|
|
419
|
+
private async pruneFinalized(finalizedEpoch: Epoch): Promise<void> {
|
|
419
420
|
for (const epoch of this.epochIndex.keys()) {
|
|
420
421
|
if (epoch < finalizedEpoch) {
|
|
421
|
-
this.deleteAllEpochItems(epoch).catch((e) =>
|
|
422
|
+
await this.deleteAllEpochItems(epoch).catch((e) =>
|
|
422
423
|
this.logger.debug("Error delete all epoch items", {epoch, finalizedEpoch}, e as Error)
|
|
423
424
|
);
|
|
424
425
|
}
|
|
@@ -476,6 +477,9 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
|
|
|
476
477
|
* As of Mar 2024, it takes <=350ms to persist a holesky state on fast server
|
|
477
478
|
*/
|
|
478
479
|
async processState(blockRootHex: RootHex, state: IBeaconStateView): Promise<number> {
|
|
480
|
+
// prune finalized in the same flow so a finalized cp state is pruned, never persisted
|
|
481
|
+
await this.pruneFinalized(state.finalizedCheckpoint.epoch);
|
|
482
|
+
|
|
479
483
|
let persistCount = 0;
|
|
480
484
|
// it's important to sort the epochs in ascending order, in case of big reorg we always want to keep the most recent checkpoint states
|
|
481
485
|
const sortedEpochs = Array.from(this.epochIndex.keys()).sort((a, b) => a - b);
|
|
@@ -30,7 +30,6 @@ export interface BlockStateCache {
|
|
|
30
30
|
clear(): void;
|
|
31
31
|
size: number;
|
|
32
32
|
prune(headStateRootHex: RootHex): void;
|
|
33
|
-
deleteAllBeforeEpoch(finalizedEpoch: Epoch): void;
|
|
34
33
|
dumpSummary(): routes.lodestar.StateCacheItem[];
|
|
35
34
|
/** Expose beacon states stored in cache. Use with caution */
|
|
36
35
|
getStates(): IterableIterator<IBeaconStateView>;
|
|
@@ -67,7 +66,6 @@ export interface CheckpointStateCache {
|
|
|
67
66
|
getOrReloadLatest(rootHex: RootHex, maxEpoch: Epoch): Promise<IBeaconStateView | null>;
|
|
68
67
|
updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch): number | null;
|
|
69
68
|
prune(finalizedEpoch: Epoch, justifiedEpoch: Epoch): void;
|
|
70
|
-
pruneFinalized(finalizedEpoch: Epoch): void;
|
|
71
69
|
processState(blockRootHex: RootHex, state: IBeaconStateView): Promise<number>;
|
|
72
70
|
clear(): void;
|
|
73
71
|
dumpSummary(): routes.lodestar.StateCacheItem[];
|
|
@@ -15,12 +15,17 @@ import {BlockErrorCode, BlockGossipError, GossipAction} from "../errors/index.js
|
|
|
15
15
|
import {IBeaconChain} from "../interface.js";
|
|
16
16
|
import {RegenCaller} from "../regen/index.js";
|
|
17
17
|
|
|
18
|
+
export type GossipBlockValidationResult = {
|
|
19
|
+
/** Number of skipped slots between the block and its parent (blockSlot - parentSlot - 1) */
|
|
20
|
+
skippedSlots: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
18
23
|
export async function validateGossipBlock(
|
|
19
24
|
config: ChainForkConfig,
|
|
20
25
|
chain: IBeaconChain,
|
|
21
26
|
signedBlock: SignedBeaconBlock,
|
|
22
27
|
fork: ForkName
|
|
23
|
-
): Promise<
|
|
28
|
+
): Promise<GossipBlockValidationResult> {
|
|
24
29
|
const block = signedBlock.message;
|
|
25
30
|
const blockSlot = block.slot;
|
|
26
31
|
const blockEpoch = computeEpochAtSlot(blockSlot);
|
|
@@ -109,21 +114,6 @@ export async function validateGossipBlock(
|
|
|
109
114
|
}
|
|
110
115
|
}
|
|
111
116
|
|
|
112
|
-
// [IGNORE] The attestation head block is too far behind the attestation slot, causing many skip slots.
|
|
113
|
-
// This is deemed a DoS risk because we need to get the proposerShuffling. To get the shuffling we have
|
|
114
|
-
// to do a bunch of epoch transitions, the longer the distance between the parent and block,
|
|
115
|
-
// the more we have to do. epochTransitions are expensive ~750ms, so we must limit how many a
|
|
116
|
-
// single bad block can trigger
|
|
117
|
-
// Note: Ensure this check is done before calling chain.regen.getBlockSlotStat as this is the function that does various epoch transitions.
|
|
118
|
-
// Note: This validation check is not part of the spec.
|
|
119
|
-
if (chain.opts.maxSkipSlots != null && parentBlock.slot + chain.opts.maxSkipSlots < blockSlot) {
|
|
120
|
-
throw new BlockGossipError(GossipAction.IGNORE, {
|
|
121
|
-
code: BlockErrorCode.TOO_MANY_SKIPPED_SLOTS,
|
|
122
|
-
parentSlot: parentBlock.slot,
|
|
123
|
-
blockSlot,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
117
|
// [REJECT] The block is from a higher slot than its parent.
|
|
128
118
|
if (parentBlock.slot >= blockSlot) {
|
|
129
119
|
throw new BlockGossipError(GossipAction.REJECT, {
|
|
@@ -133,6 +123,10 @@ export async function validateGossipBlock(
|
|
|
133
123
|
});
|
|
134
124
|
}
|
|
135
125
|
|
|
126
|
+
// Number of skipped slots between block and parent (non-spec). Previously this gated blocks via
|
|
127
|
+
// maxSkipSlots; now the caller only observes it so legitimate post-skip blocks are no longer ignored.
|
|
128
|
+
const skippedSlots = blockSlot - parentBlock.slot - 1;
|
|
129
|
+
|
|
136
130
|
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in Consensus Layer -- i.e. validate that len(body.signed_beacon_block.message.blob_kzg_commitments) <= MAX_BLOBS_PER_BLOCK
|
|
137
131
|
if (isForkPostDeneb(fork) && !isForkPostGloas(fork)) {
|
|
138
132
|
const blobKzgCommitmentsLen = (block as deneb.BeaconBlock).body.blobKzgCommitments.length;
|
|
@@ -247,4 +241,6 @@ export async function validateGossipBlock(
|
|
|
247
241
|
}
|
|
248
242
|
|
|
249
243
|
chain.seenBlockProposers.add(blockSlot, proposerIndex);
|
|
244
|
+
|
|
245
|
+
return {skippedSlots};
|
|
250
246
|
}
|