@lodestar/beacon-node 1.47.0-dev.09607ce6d4 → 1.47.0-dev.101e2c290d
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 +3 -2
- 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 +21 -2
- package/lib/api/impl/beacon/pool/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.map +1 -1
- package/lib/api/impl/events/index.d.ts +1 -1
- package/lib/api/impl/events/index.d.ts.map +1 -1
- package/lib/api/impl/events/index.js +12 -3
- package/lib/api/impl/events/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +7 -0
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +1 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +11 -9
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +14 -6
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/bls/index.d.ts +1 -1
- package/lib/chain/bls/index.d.ts.map +1 -1
- package/lib/chain/bls/interface.d.ts +5 -5
- package/lib/chain/bls/interface.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts +5 -11
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +116 -101
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
- package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
- package/lib/chain/bls/multithread/jobItem.js +10 -65
- package/lib/chain/bls/multithread/jobItem.js.map +1 -1
- package/lib/chain/bls/multithread/types.d.ts +29 -8
- package/lib/chain/bls/multithread/types.d.ts.map +1 -1
- package/lib/chain/bls/multithread/types.js +12 -0
- package/lib/chain/bls/multithread/types.js.map +1 -1
- package/lib/chain/bls/multithread/utils.d.ts +4 -0
- package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
- package/lib/chain/bls/multithread/utils.js +10 -0
- package/lib/chain/bls/multithread/utils.js.map +1 -1
- package/lib/chain/bls/multithread/worker.js +49 -23
- package/lib/chain/bls/multithread/worker.js.map +1 -1
- package/lib/chain/bls/singleThread.d.ts +4 -10
- package/lib/chain/bls/singleThread.d.ts.map +1 -1
- package/lib/chain/bls/singleThread.js +8 -45
- package/lib/chain/bls/singleThread.js.map +1 -1
- package/lib/chain/bls/utils.d.ts +4 -4
- package/lib/chain/bls/utils.d.ts.map +1 -1
- package/lib/chain/bls/utils.js +18 -19
- package/lib/chain/bls/utils.js.map +1 -1
- package/lib/chain/chain.d.ts +4 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +16 -6
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +1 -0
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +6 -1
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +6 -0
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/errors/proposerPreferences.js +1 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +4 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +4 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/attestationPool.d.ts +1 -1
- package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/attestationPool.js +1 -1
- package/lib/chain/opPools/attestationPool.js.map +1 -1
- package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts +16 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts.map +1 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.js +57 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.js.map +1 -0
- package/lib/chain/opPools/index.d.ts +1 -0
- package/lib/chain/opPools/index.d.ts.map +1 -1
- package/lib/chain/opPools/index.js +1 -0
- package/lib/chain/opPools/index.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +3 -3
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +1 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +1 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +23 -5
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +4 -2
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +2 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts +2 -2
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +5 -5
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
- package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
- package/lib/chain/seenCache/seenAttesters.js +25 -4
- package/lib/chain/seenCache/seenAttesters.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +5 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +6 -0
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +47 -5
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +117 -7
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +3 -3
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +1 -4
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +1 -1
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +1 -2
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
- 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 +5 -14
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +13 -4
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +1 -1
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/chain/validation/voluntaryExit.d.ts +9 -1
- package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
- package/lib/chain/validation/voluntaryExit.js +46 -1
- package/lib/chain/validation/voluntaryExit.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +62 -22
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +114 -52
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +13 -0
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +37 -0
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/libp2p/index.d.ts.map +1 -1
- package/lib/network/libp2p/index.js +10 -2
- package/lib/network/libp2p/index.js.map +1 -1
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +22 -43
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/constants.d.ts +7 -0
- package/lib/network/processor/constants.d.ts.map +1 -0
- package/lib/network/processor/constants.js +7 -0
- package/lib/network/processor/constants.js.map +1 -0
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +5 -1
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +3 -0
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +36 -15
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/score.d.ts.map +1 -1
- package/lib/network/reqresp/score.js +5 -11
- package/lib/network/reqresp/score.js.map +1 -1
- package/lib/network/util.d.ts +0 -1
- package/lib/network/util.d.ts.map +1 -1
- package/lib/network/util.js +0 -4
- package/lib/network/util.js.map +1 -1
- package/lib/node/deferredVoluntaryExitPublisher.d.ts +15 -0
- package/lib/node/deferredVoluntaryExitPublisher.d.ts.map +1 -0
- package/lib/node/deferredVoluntaryExitPublisher.js +38 -0
- package/lib/node/deferredVoluntaryExitPublisher.js.map +1 -0
- package/lib/node/nodejs.d.ts +2 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +2 -0
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/range/batch.d.ts +23 -5
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +61 -22
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +2 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +23 -10
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/types.d.ts +32 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +33 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +13 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +204 -61
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +16 -11
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +3 -1
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/package.json +35 -35
- package/src/api/impl/beacon/blocks/index.ts +3 -2
- package/src/api/impl/beacon/pool/index.ts +22 -2
- package/src/api/impl/beacon/state/utils.ts +2 -1
- package/src/api/impl/events/index.ts +12 -4
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/rest/base.ts +7 -0
- package/src/chain/archiveStore/archiveStore.ts +1 -1
- package/src/chain/blocks/importExecutionPayload.ts +12 -11
- package/src/chain/blocks/index.ts +18 -6
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
- package/src/chain/bls/index.ts +1 -1
- package/src/chain/bls/interface.ts +7 -3
- package/src/chain/bls/multithread/index.ts +148 -120
- package/src/chain/bls/multithread/jobItem.ts +12 -82
- package/src/chain/bls/multithread/types.ts +33 -8
- package/src/chain/bls/multithread/utils.ts +11 -0
- package/src/chain/bls/multithread/worker.ts +85 -27
- package/src/chain/bls/singleThread.ts +11 -55
- package/src/chain/bls/utils.ts +23 -24
- package/src/chain/chain.ts +17 -6
- package/src/chain/errors/blockError.ts +8 -2
- package/src/chain/errors/proposerPreferences.ts +7 -0
- package/src/chain/forkChoice/index.ts +4 -0
- package/src/chain/interface.ts +4 -1
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/opPools/attestationPool.ts +1 -1
- package/src/chain/opPools/deferredVoluntaryExitPool.ts +65 -0
- package/src/chain/opPools/index.ts +1 -0
- package/src/chain/opPools/payloadAttestationPool.ts +3 -3
- package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
- package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
- package/src/chain/opPools/utils.ts +1 -1
- package/src/chain/prepareNextSlot.ts +23 -5
- package/src/chain/regen/interface.ts +4 -2
- package/src/chain/regen/queued.ts +5 -5
- package/src/chain/seenCache/seenAttesters.ts +31 -5
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
- package/src/chain/stateCache/fifoBlockStateCache.ts +8 -1
- package/src/chain/stateCache/persistentCheckpointsCache.ts +133 -8
- package/src/chain/stateCache/types.ts +3 -3
- package/src/chain/validation/attestation.ts +1 -7
- package/src/chain/validation/attesterSlashing.ts +1 -8
- package/src/chain/validation/executionPayloadBid.ts +1 -2
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
- package/src/chain/validation/payloadAttestationMessage.ts +6 -17
- package/src/chain/validation/proposerPreferences.ts +15 -5
- package/src/chain/validation/proposerSlashing.ts +1 -1
- package/src/chain/validation/voluntaryExit.ts +59 -2
- package/src/metrics/metrics/lodestar.ts +126 -57
- package/src/network/gossip/topic.ts +38 -0
- package/src/network/libp2p/index.ts +11 -2
- package/src/network/network.ts +27 -55
- package/src/network/processor/constants.ts +6 -0
- package/src/network/processor/gossipHandlers.ts +5 -1
- package/src/network/processor/index.ts +42 -19
- package/src/network/reqresp/score.ts +5 -11
- package/src/network/util.ts +0 -5
- package/src/node/deferredVoluntaryExitPublisher.ts +54 -0
- package/src/node/nodejs.ts +5 -1
- package/src/sync/range/batch.ts +83 -28
- package/src/sync/range/chain.ts +26 -11
- package/src/sync/types.ts +36 -1
- package/src/sync/unknownBlock.ts +240 -68
- package/src/sync/utils/downloadByRange.ts +21 -12
- package/src/sync/utils/downloadByRoot.ts +3 -1
- package/lib/chain/bls/maybeBatch.d.ts +0 -12
- package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
- package/lib/chain/bls/maybeBatch.js +0 -35
- package/lib/chain/bls/maybeBatch.js.map +0 -1
- package/src/chain/bls/maybeBatch.ts +0 -45
package/lib/sync/unknownBlock.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { routes } from "@lodestar/api";
|
|
2
|
-
import { ForkSeq, isForkPostGloas } from "@lodestar/params";
|
|
2
|
+
import { ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas } from "@lodestar/params";
|
|
3
3
|
import { RequestError, RequestErrorCode } from "@lodestar/reqresp";
|
|
4
4
|
import { computeTimeAtSlot } from "@lodestar/state-transition";
|
|
5
5
|
import { fromHex, prettyPrintIndices, pruneSetToMax, sleep, toRootHex } from "@lodestar/utils";
|
|
@@ -12,17 +12,22 @@ import { ChainEvent } from "../chain/index.js";
|
|
|
12
12
|
import { validateGloasBlockDataColumnSidecars } from "../chain/validation/dataColumnSidecar.js";
|
|
13
13
|
import { validateGossipExecutionPayloadEnvelope } from "../chain/validation/executionPayloadEnvelope.js";
|
|
14
14
|
import { NetworkEvent, prettyPrintPeerIdStr } from "../network/index.js";
|
|
15
|
+
import { MAX_PEERS_PER_ROOT } from "../network/processor/constants.js";
|
|
15
16
|
import { shuffle } from "../util/shuffle.js";
|
|
16
17
|
import { sortBy } from "../util/sortBy.js";
|
|
17
18
|
import { wrapError } from "../util/wrapError.js";
|
|
18
19
|
import { MAX_CONCURRENT_REQUESTS } from "./constants.js";
|
|
19
|
-
import { PendingBlockInputStatus, PendingBlockType, PendingPayloadInputStatus, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, getPayloadSyncCacheItemRootHex, getPayloadSyncCacheItemSlot, isPendingBlockInput, isPendingPayloadEnvelope, isPendingPayloadInput, } from "./types.js";
|
|
20
|
+
import { DroppedItemReason, PendingBlockInputStatus, PendingBlockType, PendingPayloadInputStatus, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, getPayloadSyncCacheItemRootHex, getPayloadSyncCacheItemSlot, isPendingBlockInput, isPendingPayloadEnvelope, isPendingPayloadInput, } from "./types.js";
|
|
20
21
|
import { DownloadByRootError, downloadByRoot } from "./utils/downloadByRoot.js";
|
|
21
22
|
import { getAllDescendantBlocks, getUnknownAndAncestorBlocks } from "./utils/pendingBlocksTree.js";
|
|
22
23
|
import { getRateLimitedUntilMs } from "./utils/rateLimit.js";
|
|
23
24
|
const MAX_ATTEMPTS_PER_BLOCK = 5;
|
|
24
25
|
const MAX_KNOWN_BAD_BLOCKS = 500;
|
|
25
26
|
const MAX_PENDING_BLOCKS = 100;
|
|
27
|
+
/**
|
|
28
|
+
* A pending payload without slot can only stay at most 2 epochs.
|
|
29
|
+
*/
|
|
30
|
+
const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
|
|
26
31
|
var FetchResult;
|
|
27
32
|
(function (FetchResult) {
|
|
28
33
|
FetchResult["SuccessResolved"] = "success_resolved";
|
|
@@ -115,6 +120,7 @@ export class BlockInputSync {
|
|
|
115
120
|
this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
116
121
|
this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
117
122
|
this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
123
|
+
this.chain.emitter.on(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
118
124
|
this.chain.emitter.on(routes.events.EventType.block, this.onBlockImported);
|
|
119
125
|
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
120
126
|
this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
@@ -130,6 +136,7 @@ export class BlockInputSync {
|
|
|
130
136
|
this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
131
137
|
this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
132
138
|
this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
139
|
+
this.chain.emitter.off(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
133
140
|
this.chain.emitter.off(routes.events.EventType.block, this.onBlockImported);
|
|
134
141
|
this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
135
142
|
this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
@@ -147,10 +154,12 @@ export class BlockInputSync {
|
|
|
147
154
|
*/
|
|
148
155
|
onUnknownBlockRoot = (data) => {
|
|
149
156
|
try {
|
|
150
|
-
this.addByRootHex(data.rootHex, data.peer);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
157
|
+
const isNewRoot = this.addByRootHex(data.rootHex, data.peer);
|
|
158
|
+
if (isNewRoot) {
|
|
159
|
+
this.triggerUnknownBlockSearch();
|
|
160
|
+
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_BLOCK_ROOT });
|
|
161
|
+
this.metrics?.blockInputSync.source.inc({ source: data.source });
|
|
162
|
+
}
|
|
154
163
|
}
|
|
155
164
|
catch (e) {
|
|
156
165
|
this.logger.debug("Error handling unknownBlockRoot event", {}, e);
|
|
@@ -172,10 +181,12 @@ export class BlockInputSync {
|
|
|
172
181
|
};
|
|
173
182
|
onUnknownEnvelopeBlockRoot = (data) => {
|
|
174
183
|
try {
|
|
175
|
-
this.addByPayloadRootHex(data.rootHex, data.peer, data.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
const isNewRoot = this.addByPayloadRootHex(data.rootHex, data.peer, this.resolvePayloadSlot(data.rootHex));
|
|
185
|
+
if (isNewRoot) {
|
|
186
|
+
this.triggerUnknownBlockSearch();
|
|
187
|
+
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT });
|
|
188
|
+
this.metrics?.blockInputSync.payloadSource.inc({ source: data.source });
|
|
189
|
+
}
|
|
179
190
|
}
|
|
180
191
|
catch (e) {
|
|
181
192
|
this.logger.debug("Error handling unknownEnvelopeBlockRoot event", {}, e);
|
|
@@ -208,12 +219,12 @@ export class BlockInputSync {
|
|
|
208
219
|
parentRoot: missingDependency.parentRootHex,
|
|
209
220
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
210
221
|
});
|
|
211
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
222
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
212
223
|
}
|
|
213
224
|
return;
|
|
214
225
|
}
|
|
215
226
|
if (missingDependency.kind === "parentPayload") {
|
|
216
|
-
this.addByPayloadRootHex(missingDependency.rootHex, data.peer);
|
|
227
|
+
this.addByPayloadRootHex(missingDependency.rootHex, data.peer, missingDependency.slot);
|
|
217
228
|
}
|
|
218
229
|
else if (missingDependency.kind === "parentBlock") {
|
|
219
230
|
this.addByRootHex(missingDependency.rootHex, data.peer);
|
|
@@ -236,6 +247,71 @@ export class BlockInputSync {
|
|
|
236
247
|
this.pendingPayloads.delete(blockRoot);
|
|
237
248
|
this.triggerUnknownBlockSearch();
|
|
238
249
|
};
|
|
250
|
+
/**
|
|
251
|
+
* Drop pending payloads and blocks that fell below the finalized slot.
|
|
252
|
+
* See https://github.com/ChainSafe/lodestar/issues/9800
|
|
253
|
+
*/
|
|
254
|
+
pruneFinalized = (checkpoint) => {
|
|
255
|
+
// Use the actual finalized block slot, not computeStartSlotAtEpoch(checkpoint.epoch)
|
|
256
|
+
// because the payload at the finalized block is not finalized
|
|
257
|
+
const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
|
|
258
|
+
const nowSec = Date.now() / 1000;
|
|
259
|
+
const maxSecInCache = (PRUNE_UNRESOLVED_SLOT_EPOCHS * SLOTS_PER_EPOCH * this.config.SLOT_DURATION_MS) / 1000;
|
|
260
|
+
let belowFinalizedPayloads = 0;
|
|
261
|
+
let agedOutPayloads = 0;
|
|
262
|
+
let unresolvedSlotPayloads = 0;
|
|
263
|
+
for (const [rootHex, payload] of this.pendingPayloads) {
|
|
264
|
+
// Last-effort resolve for a slot-less PendingPayloadRootHex, and store it back for later passes.
|
|
265
|
+
if (!isPendingPayloadInput(payload) && !isPendingPayloadEnvelope(payload) && payload.slot === undefined) {
|
|
266
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
267
|
+
if (payload.slot === undefined) {
|
|
268
|
+
// Still unresolvable: age it out so the MAX_PENDING_BLOCKS cap cannot stay saturated.
|
|
269
|
+
if (nowSec - payload.timeAddedSec > maxSecInCache) {
|
|
270
|
+
this.pendingPayloads.delete(rootHex);
|
|
271
|
+
agedOutPayloads++;
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
unresolvedSlotPayloads++;
|
|
275
|
+
this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
|
|
276
|
+
root: rootHex,
|
|
277
|
+
finalizedSlot,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const slot = getPayloadSyncCacheItemSlot(payload);
|
|
284
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
285
|
+
this.pendingPayloads.delete(rootHex);
|
|
286
|
+
belowFinalizedPayloads++;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
let deletedBlocks = 0;
|
|
290
|
+
for (const [rootHex, block] of this.pendingBlocks) {
|
|
291
|
+
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
292
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
293
|
+
this.pendingBlocks.delete(rootHex);
|
|
294
|
+
deletedBlocks++;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (belowFinalizedPayloads > 0) {
|
|
298
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.belowFinalized }, belowFinalizedPayloads);
|
|
299
|
+
}
|
|
300
|
+
if (agedOutPayloads > 0) {
|
|
301
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.agedOut }, agedOutPayloads);
|
|
302
|
+
}
|
|
303
|
+
if (deletedBlocks > 0) {
|
|
304
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.belowFinalized }, deletedBlocks);
|
|
305
|
+
}
|
|
306
|
+
this.logger.debug("BlockInputSync.pruneFinalized dropped pre-finalized pending items", {
|
|
307
|
+
finalizedSlot,
|
|
308
|
+
finalizedRoot: checkpoint.rootHex,
|
|
309
|
+
belowFinalizedPayloads,
|
|
310
|
+
agedOutPayloads,
|
|
311
|
+
deletedBlocks,
|
|
312
|
+
unresolvedSlotPayloads,
|
|
313
|
+
});
|
|
314
|
+
};
|
|
239
315
|
addByRootHex = (rootHex, peerIdStr) => {
|
|
240
316
|
let pendingBlock = this.pendingBlocks.get(rootHex);
|
|
241
317
|
let added = false;
|
|
@@ -253,13 +329,16 @@ export class BlockInputSync {
|
|
|
253
329
|
peerIdStr: peerIdStr ?? "unknown peer",
|
|
254
330
|
});
|
|
255
331
|
}
|
|
256
|
-
|
|
332
|
+
// bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
|
|
333
|
+
// Once at the cap, ignore further peers rather than evicting already-tracked ones.
|
|
334
|
+
if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
257
335
|
pendingBlock.peerIdStrings.add(peerIdStr);
|
|
258
336
|
}
|
|
259
337
|
// TODO: check this prune methodology
|
|
260
338
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
261
339
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
262
340
|
if (prunedItemCount > 0) {
|
|
341
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
263
342
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
264
343
|
}
|
|
265
344
|
return added;
|
|
@@ -283,13 +362,16 @@ export class BlockInputSync {
|
|
|
283
362
|
delaySec: this.chain.clock.secFromSlot(blockInput.slot),
|
|
284
363
|
});
|
|
285
364
|
}
|
|
286
|
-
|
|
365
|
+
// bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
|
|
366
|
+
// Once at the cap, ignore further peers rather than evicting already-tracked ones.
|
|
367
|
+
if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
287
368
|
pendingBlock.peerIdStrings.add(peerIdStr);
|
|
288
369
|
}
|
|
289
370
|
// TODO: check this prune methodology
|
|
290
371
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
291
372
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
292
373
|
if (prunedItemCount > 0) {
|
|
374
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
293
375
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
294
376
|
}
|
|
295
377
|
};
|
|
@@ -313,18 +395,47 @@ export class BlockInputSync {
|
|
|
313
395
|
peerIdStr: peerIdStr ?? "unknown peer",
|
|
314
396
|
});
|
|
315
397
|
}
|
|
316
|
-
if (
|
|
398
|
+
else if (
|
|
399
|
+
// update a previously slot-less entry once a slot becomes available
|
|
400
|
+
slot !== undefined &&
|
|
401
|
+
!isPendingPayloadInput(pendingPayload) &&
|
|
402
|
+
!isPendingPayloadEnvelope(pendingPayload) &&
|
|
403
|
+
pendingPayload.slot === undefined) {
|
|
404
|
+
pendingPayload.slot = slot;
|
|
405
|
+
}
|
|
406
|
+
if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
317
407
|
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
318
408
|
}
|
|
319
409
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
320
410
|
if (prunedItemCount > 0) {
|
|
411
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
321
412
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
322
413
|
}
|
|
323
414
|
return added;
|
|
324
415
|
};
|
|
416
|
+
/**
|
|
417
|
+
* Resolve the block slot for a payload root.
|
|
418
|
+
*/
|
|
419
|
+
resolvePayloadSlot(rootHex) {
|
|
420
|
+
// a pending block of the same root
|
|
421
|
+
const pendingBlock = this.pendingBlocks.get(rootHex);
|
|
422
|
+
if (pendingBlock) {
|
|
423
|
+
const pendingBlockSlot = getBlockInputSyncCacheItemSlot(pendingBlock);
|
|
424
|
+
if (typeof pendingBlockSlot === "number") {
|
|
425
|
+
return pendingBlockSlot;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
// the block is in fork choice
|
|
429
|
+
const forkChoiceSlot = this.chain.forkChoice.getBlockHexDefaultStatus(rootHex)?.slot;
|
|
430
|
+
if (forkChoiceSlot !== undefined) {
|
|
431
|
+
return forkChoiceSlot;
|
|
432
|
+
}
|
|
433
|
+
// a seen payload envelope input
|
|
434
|
+
return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
|
|
435
|
+
}
|
|
325
436
|
addByPayloadInput = (payloadInput, peerIdStr, envelope) => {
|
|
326
437
|
const pendingPayload = this.toPendingPayloadInput(payloadInput, this.pendingPayloads.get(payloadInput.blockRootHex), envelope);
|
|
327
|
-
if (peerIdStr) {
|
|
438
|
+
if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
328
439
|
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
329
440
|
}
|
|
330
441
|
this.pendingPayloads.set(payloadInput.blockRootHex, pendingPayload);
|
|
@@ -335,6 +446,7 @@ export class BlockInputSync {
|
|
|
335
446
|
});
|
|
336
447
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
337
448
|
if (prunedItemCount > 0) {
|
|
449
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
338
450
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
339
451
|
}
|
|
340
452
|
};
|
|
@@ -360,7 +472,8 @@ export class BlockInputSync {
|
|
|
360
472
|
*/
|
|
361
473
|
getMissingBlockDependency(blockInput) {
|
|
362
474
|
const parentRootHex = blockInput.parentRootHex;
|
|
363
|
-
|
|
475
|
+
const parentBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRootHex);
|
|
476
|
+
if (!parentBlock) {
|
|
364
477
|
return { kind: "parentBlock", rootHex: parentRootHex };
|
|
365
478
|
}
|
|
366
479
|
if (!blockInput.hasBlock()) {
|
|
@@ -377,14 +490,8 @@ export class BlockInputSync {
|
|
|
377
490
|
if (this.chain.forkChoice.hasPayloadHexUnsafe(parentRootHex)) {
|
|
378
491
|
return { kind: "invalidParentPayload", parentRootHex, parentBlockHashHex };
|
|
379
492
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
|
|
383
|
-
return { kind: "parentPayload", rootHex: parentRootHex };
|
|
384
|
-
}
|
|
385
|
-
return { kind: "invalidParentPayload", parentRootHex, parentBlockHashHex };
|
|
386
|
-
}
|
|
387
|
-
return { kind: "parentPayload", rootHex: parentRootHex };
|
|
493
|
+
// Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
|
|
494
|
+
return { kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot };
|
|
388
495
|
}
|
|
389
496
|
advancePendingBlock(pendingBlock) {
|
|
390
497
|
const missingDependency = this.getMissingBlockDependency(pendingBlock.blockInput);
|
|
@@ -402,9 +509,9 @@ export class BlockInputSync {
|
|
|
402
509
|
return added ? "queued_parent_block" : "blocked";
|
|
403
510
|
}
|
|
404
511
|
case "parentPayload": {
|
|
405
|
-
let added = this.addByPayloadRootHex(missingDependency.rootHex);
|
|
512
|
+
let added = this.addByPayloadRootHex(missingDependency.rootHex, undefined, missingDependency.slot);
|
|
406
513
|
for (const peerIdStr of pendingBlock.peerIdStrings) {
|
|
407
|
-
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr) || added;
|
|
514
|
+
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr, missingDependency.slot) || added;
|
|
408
515
|
}
|
|
409
516
|
return added ? "queued_parent_payload" : "blocked";
|
|
410
517
|
}
|
|
@@ -415,7 +522,7 @@ export class BlockInputSync {
|
|
|
415
522
|
parentRoot: missingDependency.parentRootHex,
|
|
416
523
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
417
524
|
});
|
|
418
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
525
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
419
526
|
return "removed";
|
|
420
527
|
}
|
|
421
528
|
}
|
|
@@ -604,7 +711,7 @@ export class BlockInputSync {
|
|
|
604
711
|
...logCtx2,
|
|
605
712
|
finalizedSlot,
|
|
606
713
|
});
|
|
607
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
714
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
|
|
608
715
|
}
|
|
609
716
|
else {
|
|
610
717
|
this.onUnknownBlockRoot({ rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot });
|
|
@@ -622,7 +729,7 @@ export class BlockInputSync {
|
|
|
622
729
|
}
|
|
623
730
|
this.metrics?.blockInputSync.downloadedBlocksError.inc();
|
|
624
731
|
this.logger.debug("Ignoring unknown block root after many failed downloads", logCtx, res.err);
|
|
625
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
732
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.unavailable);
|
|
626
733
|
}
|
|
627
734
|
}
|
|
628
735
|
/**
|
|
@@ -679,9 +786,9 @@ export class BlockInputSync {
|
|
|
679
786
|
// no need to update status to "processed", delete anyway
|
|
680
787
|
this.pendingBlocks.delete(blockRootHex);
|
|
681
788
|
if (isForkPostGloas(fork)) {
|
|
682
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.
|
|
789
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
|
|
683
790
|
if (!payloadInput) {
|
|
684
|
-
this.logger.
|
|
791
|
+
this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
|
|
685
792
|
}
|
|
686
793
|
else {
|
|
687
794
|
// Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
|
|
@@ -717,17 +824,17 @@ export class BlockInputSync {
|
|
|
717
824
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
718
825
|
// Removing the block(s) without penalizing the peers, hoping for EL to
|
|
719
826
|
// recover on a latter download + verify attempt.
|
|
720
|
-
this.removeAllDescendants(pendingBlock);
|
|
827
|
+
this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
|
|
721
828
|
break;
|
|
722
829
|
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
723
830
|
// the peer served a bad block
|
|
724
831
|
this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
|
|
725
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
832
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
|
|
726
833
|
break;
|
|
727
834
|
default:
|
|
728
835
|
// Block is not correct with respect to our chain. Log error loudly
|
|
729
836
|
this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
|
|
730
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
837
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
|
|
731
838
|
}
|
|
732
839
|
}
|
|
733
840
|
// Probably a queue error or something unwanted happened, mark as pending to try again latter
|
|
@@ -748,7 +855,6 @@ export class BlockInputSync {
|
|
|
748
855
|
this.pendingPayloads.delete(rootHex);
|
|
749
856
|
return;
|
|
750
857
|
}
|
|
751
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
752
858
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
753
859
|
// Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
|
|
754
860
|
// non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
|
|
@@ -762,8 +868,9 @@ export class BlockInputSync {
|
|
|
762
868
|
}
|
|
763
869
|
return;
|
|
764
870
|
}
|
|
871
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
765
872
|
if (!payloadInput) {
|
|
766
|
-
this.logger.debug("
|
|
873
|
+
this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
|
|
767
874
|
root: rootHex,
|
|
768
875
|
});
|
|
769
876
|
return;
|
|
@@ -775,6 +882,7 @@ export class BlockInputSync {
|
|
|
775
882
|
const pendingPayloadByRoot = {
|
|
776
883
|
status: PendingPayloadInputStatus.pending,
|
|
777
884
|
rootHex,
|
|
885
|
+
slot: pendingPayload.envelope.message.payload.slotNumber,
|
|
778
886
|
timeAddedSec: pendingPayload.timeAddedSec,
|
|
779
887
|
peerIdStrings: new Set(pendingPayload.peerIdStrings),
|
|
780
888
|
};
|
|
@@ -806,6 +914,9 @@ export class BlockInputSync {
|
|
|
806
914
|
if (payload.status !== PendingPayloadInputStatus.pending) {
|
|
807
915
|
return;
|
|
808
916
|
}
|
|
917
|
+
if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
|
|
918
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
919
|
+
}
|
|
809
920
|
const payloadSlot = getPayloadSyncCacheItemSlot(payload);
|
|
810
921
|
const logCtx = {
|
|
811
922
|
slot: payloadSlot,
|
|
@@ -818,7 +929,13 @@ export class BlockInputSync {
|
|
|
818
929
|
const res = await wrapError(this.fetchPayloadInput(payload));
|
|
819
930
|
if (!res.err) {
|
|
820
931
|
const pendingPayload = res.result;
|
|
821
|
-
|
|
932
|
+
const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
933
|
+
// finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
|
|
934
|
+
if (!this.pendingPayloads.has(resultRootHex)) {
|
|
935
|
+
this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
this.pendingPayloads.set(resultRootHex, pendingPayload);
|
|
822
939
|
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
823
940
|
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
824
941
|
}
|
|
@@ -885,16 +1002,24 @@ export class BlockInputSync {
|
|
|
885
1002
|
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
886
1003
|
break;
|
|
887
1004
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1005
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1006
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1007
|
+
break;
|
|
888
1008
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1009
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1010
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1011
|
+
break;
|
|
889
1012
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
890
1013
|
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
891
1014
|
// reintroducing envelope replacement / recreation flows.
|
|
892
1015
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
893
|
-
this.removePendingPayloadAndDescendants(rootHex);
|
|
1016
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
894
1017
|
break;
|
|
895
1018
|
default:
|
|
896
1019
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
897
|
-
this.pendingPayloads.delete(rootHex)
|
|
1020
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1021
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.unknown }, 1);
|
|
1022
|
+
}
|
|
898
1023
|
}
|
|
899
1024
|
return;
|
|
900
1025
|
}
|
|
@@ -913,7 +1038,7 @@ export class BlockInputSync {
|
|
|
913
1038
|
let slot = getPayloadSyncCacheItemSlot(cacheItem);
|
|
914
1039
|
let payloadInput = isPendingPayloadInput(cacheItem)
|
|
915
1040
|
? cacheItem.payloadInput
|
|
916
|
-
: this.chain.seenPayloadEnvelopeInputCache.
|
|
1041
|
+
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
917
1042
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
918
1043
|
let i = 0;
|
|
919
1044
|
let deferredByRateLimit = false;
|
|
@@ -921,12 +1046,16 @@ export class BlockInputSync {
|
|
|
921
1046
|
const pendingColumns = payloadInput?.hasAllData()
|
|
922
1047
|
? new Set()
|
|
923
1048
|
: new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
|
|
924
|
-
|
|
1049
|
+
// prefer peers that gossiped this payload root to us (#9923)
|
|
1050
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers, cacheItem.peerIdStrings);
|
|
925
1051
|
if (peerMeta === null) {
|
|
926
1052
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
927
1053
|
throw new UnknownBlockRateLimitedError(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`);
|
|
928
1054
|
}
|
|
929
|
-
|
|
1055
|
+
const reason = pendingColumns.size > 0
|
|
1056
|
+
? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
|
|
1057
|
+
: "no peer available to download pending payload";
|
|
1058
|
+
throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
|
|
930
1059
|
}
|
|
931
1060
|
const { peerId, client: peerClient } = peerMeta;
|
|
932
1061
|
cacheItem.peerIdStrings.add(peerId);
|
|
@@ -935,7 +1064,7 @@ export class BlockInputSync {
|
|
|
935
1064
|
envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
|
|
936
1065
|
slot = envelope.message.payload.slotNumber;
|
|
937
1066
|
}
|
|
938
|
-
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.
|
|
1067
|
+
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
939
1068
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
940
1069
|
// Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
|
|
941
1070
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
@@ -1080,7 +1209,8 @@ export class BlockInputSync {
|
|
|
1080
1209
|
const pendingColumns = isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
|
|
1081
1210
|
? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
|
|
1082
1211
|
: defaultPendingColumns;
|
|
1083
|
-
|
|
1212
|
+
// prefer peers that gossiped this block root to us (#9923)
|
|
1213
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers, cacheItem.peerIdStrings);
|
|
1084
1214
|
if (peerMeta === null) {
|
|
1085
1215
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
1086
1216
|
throw new UnknownBlockRateLimitedError(`Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`);
|
|
@@ -1206,9 +1336,9 @@ export class BlockInputSync {
|
|
|
1206
1336
|
* Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
|
|
1207
1337
|
* referenced more than one bad block.
|
|
1208
1338
|
*/
|
|
1209
|
-
removeAndDownScoreAllDescendants(block) {
|
|
1339
|
+
removeAndDownScoreAllDescendants(block, headReason) {
|
|
1210
1340
|
// Get all blocks that are a descendant of this one
|
|
1211
|
-
const badPendingBlocks = this.removeAllDescendants(block);
|
|
1341
|
+
const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
|
|
1212
1342
|
// just console log and do not penalize on pending/bad blocks for debugging
|
|
1213
1343
|
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
1214
1344
|
for (const block of badPendingBlocks) {
|
|
@@ -1232,16 +1362,21 @@ export class BlockInputSync {
|
|
|
1232
1362
|
pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
|
|
1233
1363
|
}
|
|
1234
1364
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1235
|
-
removePendingPayloadAndDescendants(rootHex) {
|
|
1365
|
+
removePendingPayloadAndDescendants(rootHex, headReason) {
|
|
1236
1366
|
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1237
1367
|
// later validation/finalization logic decides when it can leave memory.
|
|
1238
|
-
this.pendingPayloads.delete(rootHex)
|
|
1368
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1369
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: headReason }, 1);
|
|
1370
|
+
}
|
|
1239
1371
|
const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
|
|
1240
|
-
this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
|
|
1241
1372
|
for (const block of badPendingBlocks) {
|
|
1242
1373
|
const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1243
|
-
this.pendingBlocks.delete(descendantRootHex)
|
|
1244
|
-
|
|
1374
|
+
if (this.pendingBlocks.delete(descendantRootHex)) {
|
|
1375
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.invalidParent }, 1);
|
|
1376
|
+
}
|
|
1377
|
+
if (this.pendingPayloads.delete(descendantRootHex)) {
|
|
1378
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.invalidParent }, 1);
|
|
1379
|
+
}
|
|
1245
1380
|
this.chain.seenBlockInputCache.prune(descendantRootHex);
|
|
1246
1381
|
this.logger.debug("Removing pending descendant after invalid parent payload", {
|
|
1247
1382
|
slot: getBlockInputSyncCacheItemSlot(block),
|
|
@@ -1250,16 +1385,20 @@ export class BlockInputSync {
|
|
|
1250
1385
|
});
|
|
1251
1386
|
}
|
|
1252
1387
|
}
|
|
1253
|
-
removeAllDescendants(block) {
|
|
1388
|
+
removeAllDescendants(block, headReason, descendantReason = DroppedItemReason.invalidParent) {
|
|
1254
1389
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1255
1390
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
1256
|
-
// Get all blocks that are a descendant of this one
|
|
1391
|
+
// Get all blocks that are a descendant of this one (index 0 is the head item itself)
|
|
1257
1392
|
const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
|
|
1258
|
-
|
|
1259
|
-
for (const block of badPendingBlocks) {
|
|
1393
|
+
for (const [i, block] of badPendingBlocks.entries()) {
|
|
1260
1394
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1261
|
-
|
|
1262
|
-
this.
|
|
1395
|
+
const reason = i === 0 ? headReason : descendantReason;
|
|
1396
|
+
if (this.pendingBlocks.delete(rootHex)) {
|
|
1397
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason }, 1);
|
|
1398
|
+
}
|
|
1399
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1400
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason }, 1);
|
|
1401
|
+
}
|
|
1263
1402
|
this.chain.seenBlockInputCache.prune(rootHex);
|
|
1264
1403
|
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1265
1404
|
// importBlock()-owned lifecycle.
|
|
@@ -1330,17 +1469,21 @@ export class UnknownBlockPeerBalancer {
|
|
|
1330
1469
|
return retryAt;
|
|
1331
1470
|
}
|
|
1332
1471
|
/**
|
|
1333
|
-
* called from fetchBlockInput()
|
|
1472
|
+
* called from fetchBlockInput()/fetchPayloadInput() with a block/payload root.
|
|
1334
1473
|
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
1335
1474
|
* pendingColumns is empty for prefulu, or the 1st time we we download a block by root
|
|
1475
|
+
* preferredPeers are the peers NetworkProcessor told us gossiped this root (from
|
|
1476
|
+
* pendingBlock/cacheItem.peerIdStrings). They are preferred but not required.
|
|
1336
1477
|
*/
|
|
1337
|
-
bestPeerForPendingColumns(pendingColumns, excludedPeers) {
|
|
1478
|
+
bestPeerForPendingColumns(pendingColumns, excludedPeers, preferredPeers) {
|
|
1338
1479
|
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
1339
1480
|
if (eligiblePeers.length === 0) {
|
|
1340
1481
|
return null;
|
|
1341
1482
|
}
|
|
1342
1483
|
const sortedEligiblePeers = sortBy(shuffle(eligiblePeers),
|
|
1343
|
-
// prefer peers
|
|
1484
|
+
// 1st key: prefer peers we heard about this root from
|
|
1485
|
+
(peerId) => (preferredPeers?.has(peerId) ? 0 : 1),
|
|
1486
|
+
// 2nd key (tiebreak): prefer peers with least active req
|
|
1344
1487
|
(peerId) => this.activeRequests.get(peerId) ?? 0);
|
|
1345
1488
|
const bestPeerId = sortedEligiblePeers[0];
|
|
1346
1489
|
this.onRequest(bestPeerId);
|