@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/src/sync/unknownBlock.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
|
-
import {
|
|
3
|
+
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
4
|
+
import {ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas} from "@lodestar/params";
|
|
4
5
|
import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
|
|
5
6
|
import {computeTimeAtSlot} from "@lodestar/state-transition";
|
|
6
7
|
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
@@ -16,6 +17,7 @@ import {validateGossipExecutionPayloadEnvelope} from "../chain/validation/execut
|
|
|
16
17
|
import {Metrics} from "../metrics/index.js";
|
|
17
18
|
import {INetwork, NetworkEvent, NetworkEventData, prettyPrintPeerIdStr} from "../network/index.js";
|
|
18
19
|
import {PeerSyncMeta} from "../network/peers/peersData.js";
|
|
20
|
+
import {MAX_PEERS_PER_ROOT} from "../network/processor/constants.js";
|
|
19
21
|
import {PeerIdStr} from "../util/peerId.js";
|
|
20
22
|
import {shuffle} from "../util/shuffle.js";
|
|
21
23
|
import {sortBy} from "../util/sortBy.js";
|
|
@@ -24,6 +26,7 @@ import {MAX_CONCURRENT_REQUESTS} from "./constants.js";
|
|
|
24
26
|
import {SyncOptions} from "./options.js";
|
|
25
27
|
import {
|
|
26
28
|
BlockInputSyncCacheItem,
|
|
29
|
+
DroppedItemReason,
|
|
27
30
|
PayloadSyncCacheItem,
|
|
28
31
|
PendingBlockInput,
|
|
29
32
|
PendingBlockInputStatus,
|
|
@@ -47,6 +50,10 @@ import {getRateLimitedUntilMs} from "./utils/rateLimit.js";
|
|
|
47
50
|
const MAX_ATTEMPTS_PER_BLOCK = 5;
|
|
48
51
|
const MAX_KNOWN_BAD_BLOCKS = 500;
|
|
49
52
|
const MAX_PENDING_BLOCKS = 100;
|
|
53
|
+
/**
|
|
54
|
+
* A pending payload without slot can only stay at most 2 epochs.
|
|
55
|
+
*/
|
|
56
|
+
const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
|
|
50
57
|
|
|
51
58
|
type AdvancePendingBlockResult =
|
|
52
59
|
| "ready"
|
|
@@ -154,6 +161,7 @@ export class BlockInputSync {
|
|
|
154
161
|
this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
155
162
|
this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
156
163
|
this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
164
|
+
this.chain.emitter.on(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
157
165
|
this.chain.emitter.on(routes.events.EventType.block, this.onBlockImported);
|
|
158
166
|
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
159
167
|
this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
@@ -170,6 +178,7 @@ export class BlockInputSync {
|
|
|
170
178
|
this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
171
179
|
this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
172
180
|
this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
181
|
+
this.chain.emitter.off(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
173
182
|
this.chain.emitter.off(routes.events.EventType.block, this.onBlockImported);
|
|
174
183
|
this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
175
184
|
this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
@@ -190,10 +199,12 @@ export class BlockInputSync {
|
|
|
190
199
|
*/
|
|
191
200
|
private onUnknownBlockRoot = (data: ChainEventData[ChainEvent.unknownBlockRoot]): void => {
|
|
192
201
|
try {
|
|
193
|
-
this.addByRootHex(data.rootHex, data.peer);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
202
|
+
const isNewRoot = this.addByRootHex(data.rootHex, data.peer);
|
|
203
|
+
if (isNewRoot) {
|
|
204
|
+
this.triggerUnknownBlockSearch();
|
|
205
|
+
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_BLOCK_ROOT});
|
|
206
|
+
this.metrics?.blockInputSync.source.inc({source: data.source});
|
|
207
|
+
}
|
|
197
208
|
} catch (e) {
|
|
198
209
|
this.logger.debug("Error handling unknownBlockRoot event", {}, e as Error);
|
|
199
210
|
}
|
|
@@ -215,10 +226,12 @@ export class BlockInputSync {
|
|
|
215
226
|
|
|
216
227
|
private onUnknownEnvelopeBlockRoot = (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRoot]): void => {
|
|
217
228
|
try {
|
|
218
|
-
this.addByPayloadRootHex(data.rootHex, data.peer, data.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
229
|
+
const isNewRoot = this.addByPayloadRootHex(data.rootHex, data.peer, this.resolvePayloadSlot(data.rootHex));
|
|
230
|
+
if (isNewRoot) {
|
|
231
|
+
this.triggerUnknownBlockSearch();
|
|
232
|
+
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT});
|
|
233
|
+
this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
|
|
234
|
+
}
|
|
222
235
|
} catch (e) {
|
|
223
236
|
this.logger.debug("Error handling unknownEnvelopeBlockRoot event", {}, e as Error);
|
|
224
237
|
}
|
|
@@ -252,13 +265,13 @@ export class BlockInputSync {
|
|
|
252
265
|
parentRoot: missingDependency.parentRootHex,
|
|
253
266
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
254
267
|
});
|
|
255
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
268
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
256
269
|
}
|
|
257
270
|
return;
|
|
258
271
|
}
|
|
259
272
|
|
|
260
273
|
if (missingDependency.kind === "parentPayload") {
|
|
261
|
-
this.addByPayloadRootHex(missingDependency.rootHex, data.peer);
|
|
274
|
+
this.addByPayloadRootHex(missingDependency.rootHex, data.peer, missingDependency.slot);
|
|
262
275
|
} else if (missingDependency.kind === "parentBlock") {
|
|
263
276
|
this.addByRootHex(missingDependency.rootHex, data.peer);
|
|
264
277
|
}
|
|
@@ -284,6 +297,80 @@ export class BlockInputSync {
|
|
|
284
297
|
this.triggerUnknownBlockSearch();
|
|
285
298
|
};
|
|
286
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Drop pending payloads and blocks that fell below the finalized slot.
|
|
302
|
+
* See https://github.com/ChainSafe/lodestar/issues/9800
|
|
303
|
+
*/
|
|
304
|
+
private pruneFinalized = (checkpoint: CheckpointWithHex): void => {
|
|
305
|
+
// Use the actual finalized block slot, not computeStartSlotAtEpoch(checkpoint.epoch)
|
|
306
|
+
// because the payload at the finalized block is not finalized
|
|
307
|
+
const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
|
|
308
|
+
const nowSec = Date.now() / 1000;
|
|
309
|
+
const maxSecInCache = (PRUNE_UNRESOLVED_SLOT_EPOCHS * SLOTS_PER_EPOCH * this.config.SLOT_DURATION_MS) / 1000;
|
|
310
|
+
|
|
311
|
+
let belowFinalizedPayloads = 0;
|
|
312
|
+
let agedOutPayloads = 0;
|
|
313
|
+
let unresolvedSlotPayloads = 0;
|
|
314
|
+
|
|
315
|
+
for (const [rootHex, payload] of this.pendingPayloads) {
|
|
316
|
+
// Last-effort resolve for a slot-less PendingPayloadRootHex, and store it back for later passes.
|
|
317
|
+
if (!isPendingPayloadInput(payload) && !isPendingPayloadEnvelope(payload) && payload.slot === undefined) {
|
|
318
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
319
|
+
if (payload.slot === undefined) {
|
|
320
|
+
// Still unresolvable: age it out so the MAX_PENDING_BLOCKS cap cannot stay saturated.
|
|
321
|
+
if (nowSec - payload.timeAddedSec > maxSecInCache) {
|
|
322
|
+
this.pendingPayloads.delete(rootHex);
|
|
323
|
+
agedOutPayloads++;
|
|
324
|
+
} else {
|
|
325
|
+
unresolvedSlotPayloads++;
|
|
326
|
+
this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
|
|
327
|
+
root: rootHex,
|
|
328
|
+
finalizedSlot,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const slot = getPayloadSyncCacheItemSlot(payload);
|
|
336
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
337
|
+
this.pendingPayloads.delete(rootHex);
|
|
338
|
+
belowFinalizedPayloads++;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
let deletedBlocks = 0;
|
|
343
|
+
for (const [rootHex, block] of this.pendingBlocks) {
|
|
344
|
+
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
345
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
346
|
+
this.pendingBlocks.delete(rootHex);
|
|
347
|
+
deletedBlocks++;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (belowFinalizedPayloads > 0) {
|
|
352
|
+
this.metrics?.blockInputSync.removedPayloads.inc(
|
|
353
|
+
{reason: DroppedItemReason.belowFinalized},
|
|
354
|
+
belowFinalizedPayloads
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
if (agedOutPayloads > 0) {
|
|
358
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.agedOut}, agedOutPayloads);
|
|
359
|
+
}
|
|
360
|
+
if (deletedBlocks > 0) {
|
|
361
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.belowFinalized}, deletedBlocks);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
this.logger.debug("BlockInputSync.pruneFinalized dropped pre-finalized pending items", {
|
|
365
|
+
finalizedSlot,
|
|
366
|
+
finalizedRoot: checkpoint.rootHex,
|
|
367
|
+
belowFinalizedPayloads,
|
|
368
|
+
agedOutPayloads,
|
|
369
|
+
deletedBlocks,
|
|
370
|
+
unresolvedSlotPayloads,
|
|
371
|
+
});
|
|
372
|
+
};
|
|
373
|
+
|
|
287
374
|
private addByRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr): boolean => {
|
|
288
375
|
let pendingBlock = this.pendingBlocks.get(rootHex);
|
|
289
376
|
let added = false;
|
|
@@ -303,7 +390,9 @@ export class BlockInputSync {
|
|
|
303
390
|
});
|
|
304
391
|
}
|
|
305
392
|
|
|
306
|
-
|
|
393
|
+
// bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
|
|
394
|
+
// Once at the cap, ignore further peers rather than evicting already-tracked ones.
|
|
395
|
+
if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
307
396
|
pendingBlock.peerIdStrings.add(peerIdStr);
|
|
308
397
|
}
|
|
309
398
|
|
|
@@ -311,6 +400,7 @@ export class BlockInputSync {
|
|
|
311
400
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
312
401
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
313
402
|
if (prunedItemCount > 0) {
|
|
403
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
314
404
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
315
405
|
}
|
|
316
406
|
return added;
|
|
@@ -337,7 +427,9 @@ export class BlockInputSync {
|
|
|
337
427
|
});
|
|
338
428
|
}
|
|
339
429
|
|
|
340
|
-
|
|
430
|
+
// bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
|
|
431
|
+
// Once at the cap, ignore further peers rather than evicting already-tracked ones.
|
|
432
|
+
if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
341
433
|
pendingBlock.peerIdStrings.add(peerIdStr);
|
|
342
434
|
}
|
|
343
435
|
|
|
@@ -345,6 +437,7 @@ export class BlockInputSync {
|
|
|
345
437
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
346
438
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
347
439
|
if (prunedItemCount > 0) {
|
|
440
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
348
441
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
349
442
|
}
|
|
350
443
|
};
|
|
@@ -369,19 +462,50 @@ export class BlockInputSync {
|
|
|
369
462
|
root: rootHex,
|
|
370
463
|
peerIdStr: peerIdStr ?? "unknown peer",
|
|
371
464
|
});
|
|
465
|
+
} else if (
|
|
466
|
+
// update a previously slot-less entry once a slot becomes available
|
|
467
|
+
slot !== undefined &&
|
|
468
|
+
!isPendingPayloadInput(pendingPayload) &&
|
|
469
|
+
!isPendingPayloadEnvelope(pendingPayload) &&
|
|
470
|
+
pendingPayload.slot === undefined
|
|
471
|
+
) {
|
|
472
|
+
pendingPayload.slot = slot;
|
|
372
473
|
}
|
|
373
|
-
|
|
374
|
-
if (peerIdStr) {
|
|
474
|
+
if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
375
475
|
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
376
476
|
}
|
|
377
477
|
|
|
378
478
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
379
479
|
if (prunedItemCount > 0) {
|
|
480
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
380
481
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
381
482
|
}
|
|
382
483
|
return added;
|
|
383
484
|
};
|
|
384
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Resolve the block slot for a payload root.
|
|
488
|
+
*/
|
|
489
|
+
private resolvePayloadSlot(rootHex: RootHex): Slot | undefined {
|
|
490
|
+
// a pending block of the same root
|
|
491
|
+
const pendingBlock = this.pendingBlocks.get(rootHex);
|
|
492
|
+
if (pendingBlock) {
|
|
493
|
+
const pendingBlockSlot = getBlockInputSyncCacheItemSlot(pendingBlock);
|
|
494
|
+
if (typeof pendingBlockSlot === "number") {
|
|
495
|
+
return pendingBlockSlot;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// the block is in fork choice
|
|
500
|
+
const forkChoiceSlot = this.chain.forkChoice.getBlockHexDefaultStatus(rootHex)?.slot;
|
|
501
|
+
if (forkChoiceSlot !== undefined) {
|
|
502
|
+
return forkChoiceSlot;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// a seen payload envelope input
|
|
506
|
+
return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
|
|
507
|
+
}
|
|
508
|
+
|
|
385
509
|
private addByPayloadInput = (
|
|
386
510
|
payloadInput: PayloadEnvelopeInput,
|
|
387
511
|
peerIdStr?: PeerIdStr,
|
|
@@ -393,7 +517,7 @@ export class BlockInputSync {
|
|
|
393
517
|
envelope
|
|
394
518
|
);
|
|
395
519
|
|
|
396
|
-
if (peerIdStr) {
|
|
520
|
+
if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
397
521
|
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
398
522
|
}
|
|
399
523
|
|
|
@@ -407,6 +531,7 @@ export class BlockInputSync {
|
|
|
407
531
|
|
|
408
532
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
409
533
|
if (prunedItemCount > 0) {
|
|
534
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
410
535
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
411
536
|
}
|
|
412
537
|
};
|
|
@@ -436,10 +561,12 @@ export class BlockInputSync {
|
|
|
436
561
|
blockInput: IBlockInput
|
|
437
562
|
):
|
|
438
563
|
| {kind: "ready"}
|
|
439
|
-
| {kind: "block" | "parentBlock"
|
|
564
|
+
| {kind: "block" | "parentBlock"; rootHex: RootHex}
|
|
565
|
+
| {kind: "parentPayload"; rootHex: RootHex; slot: Slot}
|
|
440
566
|
| {kind: "invalidParentPayload"; parentRootHex: RootHex; parentBlockHashHex: RootHex} {
|
|
441
567
|
const parentRootHex = blockInput.parentRootHex;
|
|
442
|
-
|
|
568
|
+
const parentBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRootHex);
|
|
569
|
+
if (!parentBlock) {
|
|
443
570
|
return {kind: "parentBlock", rootHex: parentRootHex};
|
|
444
571
|
}
|
|
445
572
|
|
|
@@ -461,16 +588,8 @@ export class BlockInputSync {
|
|
|
461
588
|
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
462
589
|
}
|
|
463
590
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
|
|
467
|
-
return {kind: "parentPayload", rootHex: parentRootHex};
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return {kind: "parentPayload", rootHex: parentRootHex};
|
|
591
|
+
// Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
|
|
592
|
+
return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
|
|
474
593
|
}
|
|
475
594
|
|
|
476
595
|
private advancePendingBlock(pendingBlock: PendingBlockInput): AdvancePendingBlockResult {
|
|
@@ -493,9 +612,9 @@ export class BlockInputSync {
|
|
|
493
612
|
}
|
|
494
613
|
|
|
495
614
|
case "parentPayload": {
|
|
496
|
-
let added = this.addByPayloadRootHex(missingDependency.rootHex);
|
|
615
|
+
let added = this.addByPayloadRootHex(missingDependency.rootHex, undefined, missingDependency.slot);
|
|
497
616
|
for (const peerIdStr of pendingBlock.peerIdStrings) {
|
|
498
|
-
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr) || added;
|
|
617
|
+
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr, missingDependency.slot) || added;
|
|
499
618
|
}
|
|
500
619
|
return added ? "queued_parent_payload" : "blocked";
|
|
501
620
|
}
|
|
@@ -507,7 +626,7 @@ export class BlockInputSync {
|
|
|
507
626
|
parentRoot: missingDependency.parentRootHex,
|
|
508
627
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
509
628
|
});
|
|
510
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
629
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
511
630
|
return "removed";
|
|
512
631
|
}
|
|
513
632
|
}
|
|
@@ -730,7 +849,7 @@ export class BlockInputSync {
|
|
|
730
849
|
...logCtx2,
|
|
731
850
|
finalizedSlot,
|
|
732
851
|
});
|
|
733
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
852
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
|
|
734
853
|
} else {
|
|
735
854
|
this.onUnknownBlockRoot({rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot});
|
|
736
855
|
}
|
|
@@ -747,7 +866,7 @@ export class BlockInputSync {
|
|
|
747
866
|
|
|
748
867
|
this.metrics?.blockInputSync.downloadedBlocksError.inc();
|
|
749
868
|
this.logger.debug("Ignoring unknown block root after many failed downloads", logCtx, res.err);
|
|
750
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
869
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.unavailable);
|
|
751
870
|
}
|
|
752
871
|
}
|
|
753
872
|
|
|
@@ -811,9 +930,9 @@ export class BlockInputSync {
|
|
|
811
930
|
this.pendingBlocks.delete(blockRootHex);
|
|
812
931
|
|
|
813
932
|
if (isForkPostGloas(fork)) {
|
|
814
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.
|
|
933
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
|
|
815
934
|
if (!payloadInput) {
|
|
816
|
-
this.logger.
|
|
935
|
+
this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
|
|
817
936
|
} else {
|
|
818
937
|
// Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
|
|
819
938
|
// The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
|
|
@@ -855,19 +974,19 @@ export class BlockInputSync {
|
|
|
855
974
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
856
975
|
// Removing the block(s) without penalizing the peers, hoping for EL to
|
|
857
976
|
// recover on a latter download + verify attempt.
|
|
858
|
-
this.removeAllDescendants(pendingBlock);
|
|
977
|
+
this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
|
|
859
978
|
break;
|
|
860
979
|
|
|
861
980
|
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
862
981
|
// the peer served a bad block
|
|
863
982
|
this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
|
|
864
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
983
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
|
|
865
984
|
break;
|
|
866
985
|
|
|
867
986
|
default:
|
|
868
987
|
// Block is not correct with respect to our chain. Log error loudly
|
|
869
988
|
this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
|
|
870
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
989
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
|
|
871
990
|
}
|
|
872
991
|
}
|
|
873
992
|
|
|
@@ -886,12 +1005,12 @@ export class BlockInputSync {
|
|
|
886
1005
|
*/
|
|
887
1006
|
private async reconcilePayloadEnvelope(pendingPayload: PendingPayloadEnvelope): Promise<void> {
|
|
888
1007
|
const rootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
1008
|
+
|
|
889
1009
|
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
890
1010
|
this.pendingPayloads.delete(rootHex);
|
|
891
1011
|
return;
|
|
892
1012
|
}
|
|
893
1013
|
|
|
894
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
895
1014
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
896
1015
|
// Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
|
|
897
1016
|
// non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
|
|
@@ -907,8 +1026,9 @@ export class BlockInputSync {
|
|
|
907
1026
|
return;
|
|
908
1027
|
}
|
|
909
1028
|
|
|
1029
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
910
1030
|
if (!payloadInput) {
|
|
911
|
-
this.logger.debug("
|
|
1031
|
+
this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
|
|
912
1032
|
root: rootHex,
|
|
913
1033
|
});
|
|
914
1034
|
return;
|
|
@@ -928,6 +1048,7 @@ export class BlockInputSync {
|
|
|
928
1048
|
const pendingPayloadByRoot: PendingPayloadRootHex = {
|
|
929
1049
|
status: PendingPayloadInputStatus.pending,
|
|
930
1050
|
rootHex,
|
|
1051
|
+
slot: pendingPayload.envelope.message.payload.slotNumber,
|
|
931
1052
|
timeAddedSec: pendingPayload.timeAddedSec,
|
|
932
1053
|
peerIdStrings: new Set(pendingPayload.peerIdStrings),
|
|
933
1054
|
};
|
|
@@ -967,6 +1088,10 @@ export class BlockInputSync {
|
|
|
967
1088
|
return;
|
|
968
1089
|
}
|
|
969
1090
|
|
|
1091
|
+
if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
|
|
1092
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
970
1095
|
const payloadSlot = getPayloadSyncCacheItemSlot(payload);
|
|
971
1096
|
const logCtx = {
|
|
972
1097
|
slot: payloadSlot,
|
|
@@ -982,7 +1107,13 @@ export class BlockInputSync {
|
|
|
982
1107
|
const res = await wrapError(this.fetchPayloadInput(payload));
|
|
983
1108
|
if (!res.err) {
|
|
984
1109
|
const pendingPayload = res.result;
|
|
985
|
-
|
|
1110
|
+
const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
1111
|
+
// finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
|
|
1112
|
+
if (!this.pendingPayloads.has(resultRootHex)) {
|
|
1113
|
+
this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
this.pendingPayloads.set(resultRootHex, pendingPayload);
|
|
986
1117
|
|
|
987
1118
|
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
988
1119
|
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
@@ -1059,17 +1190,27 @@ export class BlockInputSync {
|
|
|
1059
1190
|
break;
|
|
1060
1191
|
|
|
1061
1192
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1193
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1194
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1195
|
+
break;
|
|
1196
|
+
|
|
1062
1197
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1198
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1199
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1200
|
+
break;
|
|
1201
|
+
|
|
1063
1202
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1064
1203
|
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
1065
1204
|
// reintroducing envelope replacement / recreation flows.
|
|
1066
1205
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1067
|
-
this.removePendingPayloadAndDescendants(rootHex);
|
|
1206
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
1068
1207
|
break;
|
|
1069
1208
|
|
|
1070
1209
|
default:
|
|
1071
1210
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1072
|
-
this.pendingPayloads.delete(rootHex)
|
|
1211
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1212
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.unknown}, 1);
|
|
1213
|
+
}
|
|
1073
1214
|
}
|
|
1074
1215
|
return;
|
|
1075
1216
|
}
|
|
@@ -1093,7 +1234,7 @@ export class BlockInputSync {
|
|
|
1093
1234
|
let slot = getPayloadSyncCacheItemSlot(cacheItem);
|
|
1094
1235
|
let payloadInput = isPendingPayloadInput(cacheItem)
|
|
1095
1236
|
? cacheItem.payloadInput
|
|
1096
|
-
: this.chain.seenPayloadEnvelopeInputCache.
|
|
1237
|
+
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1097
1238
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
1098
1239
|
|
|
1099
1240
|
let i = 0;
|
|
@@ -1102,7 +1243,12 @@ export class BlockInputSync {
|
|
|
1102
1243
|
const pendingColumns = payloadInput?.hasAllData()
|
|
1103
1244
|
? new Set<number>()
|
|
1104
1245
|
: new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
|
|
1105
|
-
|
|
1246
|
+
// prefer peers that gossiped this payload root to us (#9923)
|
|
1247
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
|
|
1248
|
+
pendingColumns,
|
|
1249
|
+
excludedPeers,
|
|
1250
|
+
cacheItem.peerIdStrings
|
|
1251
|
+
);
|
|
1106
1252
|
if (peerMeta === null) {
|
|
1107
1253
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
1108
1254
|
throw new UnknownBlockRateLimitedError(
|
|
@@ -1110,9 +1256,11 @@ export class BlockInputSync {
|
|
|
1110
1256
|
);
|
|
1111
1257
|
}
|
|
1112
1258
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1259
|
+
const reason =
|
|
1260
|
+
pendingColumns.size > 0
|
|
1261
|
+
? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
|
|
1262
|
+
: "no peer available to download pending payload";
|
|
1263
|
+
throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
|
|
1116
1264
|
}
|
|
1117
1265
|
|
|
1118
1266
|
const {peerId, client: peerClient} = peerMeta;
|
|
@@ -1124,7 +1272,7 @@ export class BlockInputSync {
|
|
|
1124
1272
|
slot = envelope.message.payload.slotNumber;
|
|
1125
1273
|
}
|
|
1126
1274
|
|
|
1127
|
-
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.
|
|
1275
|
+
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1128
1276
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
1129
1277
|
// Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
|
|
1130
1278
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
@@ -1309,7 +1457,12 @@ export class BlockInputSync {
|
|
|
1309
1457
|
isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
|
|
1310
1458
|
? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
|
|
1311
1459
|
: defaultPendingColumns;
|
|
1312
|
-
|
|
1460
|
+
// prefer peers that gossiped this block root to us (#9923)
|
|
1461
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
|
|
1462
|
+
pendingColumns,
|
|
1463
|
+
excludedPeers,
|
|
1464
|
+
cacheItem.peerIdStrings
|
|
1465
|
+
);
|
|
1313
1466
|
if (peerMeta === null) {
|
|
1314
1467
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
1315
1468
|
throw new UnknownBlockRateLimitedError(
|
|
@@ -1457,9 +1610,9 @@ export class BlockInputSync {
|
|
|
1457
1610
|
* Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
|
|
1458
1611
|
* referenced more than one bad block.
|
|
1459
1612
|
*/
|
|
1460
|
-
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem): void {
|
|
1613
|
+
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
|
|
1461
1614
|
// Get all blocks that are a descendant of this one
|
|
1462
|
-
const badPendingBlocks = this.removeAllDescendants(block);
|
|
1615
|
+
const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
|
|
1463
1616
|
// just console log and do not penalize on pending/bad blocks for debugging
|
|
1464
1617
|
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
1465
1618
|
|
|
@@ -1486,18 +1639,22 @@ export class BlockInputSync {
|
|
|
1486
1639
|
}
|
|
1487
1640
|
|
|
1488
1641
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1489
|
-
private removePendingPayloadAndDescendants(rootHex: RootHex): void {
|
|
1642
|
+
private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
|
|
1490
1643
|
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1491
1644
|
// later validation/finalization logic decides when it can leave memory.
|
|
1492
|
-
this.pendingPayloads.delete(rootHex)
|
|
1645
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1646
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
|
|
1647
|
+
}
|
|
1493
1648
|
|
|
1494
1649
|
const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
|
|
1495
|
-
this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
|
|
1496
|
-
|
|
1497
1650
|
for (const block of badPendingBlocks) {
|
|
1498
1651
|
const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1499
|
-
this.pendingBlocks.delete(descendantRootHex)
|
|
1500
|
-
|
|
1652
|
+
if (this.pendingBlocks.delete(descendantRootHex)) {
|
|
1653
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.invalidParent}, 1);
|
|
1654
|
+
}
|
|
1655
|
+
if (this.pendingPayloads.delete(descendantRootHex)) {
|
|
1656
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.invalidParent}, 1);
|
|
1657
|
+
}
|
|
1501
1658
|
this.chain.seenBlockInputCache.prune(descendantRootHex);
|
|
1502
1659
|
this.logger.debug("Removing pending descendant after invalid parent payload", {
|
|
1503
1660
|
slot: getBlockInputSyncCacheItemSlot(block),
|
|
@@ -1507,18 +1664,25 @@ export class BlockInputSync {
|
|
|
1507
1664
|
}
|
|
1508
1665
|
}
|
|
1509
1666
|
|
|
1510
|
-
private removeAllDescendants(
|
|
1667
|
+
private removeAllDescendants(
|
|
1668
|
+
block: BlockInputSyncCacheItem,
|
|
1669
|
+
headReason: DroppedItemReason,
|
|
1670
|
+
descendantReason: DroppedItemReason = DroppedItemReason.invalidParent
|
|
1671
|
+
): BlockInputSyncCacheItem[] {
|
|
1511
1672
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1512
1673
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
1513
|
-
// Get all blocks that are a descendant of this one
|
|
1674
|
+
// Get all blocks that are a descendant of this one (index 0 is the head item itself)
|
|
1514
1675
|
const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
|
|
1515
1676
|
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
for (const block of badPendingBlocks) {
|
|
1677
|
+
for (const [i, block] of badPendingBlocks.entries()) {
|
|
1519
1678
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1520
|
-
|
|
1521
|
-
this.
|
|
1679
|
+
const reason = i === 0 ? headReason : descendantReason;
|
|
1680
|
+
if (this.pendingBlocks.delete(rootHex)) {
|
|
1681
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason}, 1);
|
|
1682
|
+
}
|
|
1683
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1684
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason}, 1);
|
|
1685
|
+
}
|
|
1522
1686
|
this.chain.seenBlockInputCache.prune(rootHex);
|
|
1523
1687
|
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1524
1688
|
// importBlock()-owned lifecycle.
|
|
@@ -1609,11 +1773,17 @@ export class UnknownBlockPeerBalancer {
|
|
|
1609
1773
|
}
|
|
1610
1774
|
|
|
1611
1775
|
/**
|
|
1612
|
-
* called from fetchBlockInput()
|
|
1776
|
+
* called from fetchBlockInput()/fetchPayloadInput() with a block/payload root.
|
|
1613
1777
|
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
1614
1778
|
* pendingColumns is empty for prefulu, or the 1st time we we download a block by root
|
|
1779
|
+
* preferredPeers are the peers NetworkProcessor told us gossiped this root (from
|
|
1780
|
+
* pendingBlock/cacheItem.peerIdStrings). They are preferred but not required.
|
|
1615
1781
|
*/
|
|
1616
|
-
bestPeerForPendingColumns(
|
|
1782
|
+
bestPeerForPendingColumns(
|
|
1783
|
+
pendingColumns: Set<number>,
|
|
1784
|
+
excludedPeers: Set<PeerIdStr>,
|
|
1785
|
+
preferredPeers?: Set<PeerIdStr>
|
|
1786
|
+
): PeerSyncMeta | null {
|
|
1617
1787
|
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
1618
1788
|
if (eligiblePeers.length === 0) {
|
|
1619
1789
|
return null;
|
|
@@ -1621,7 +1791,9 @@ export class UnknownBlockPeerBalancer {
|
|
|
1621
1791
|
|
|
1622
1792
|
const sortedEligiblePeers = sortBy(
|
|
1623
1793
|
shuffle(eligiblePeers),
|
|
1624
|
-
// prefer peers
|
|
1794
|
+
// 1st key: prefer peers we heard about this root from
|
|
1795
|
+
(peerId) => (preferredPeers?.has(peerId) ? 0 : 1),
|
|
1796
|
+
// 2nd key (tiebreak): prefer peers with least active req
|
|
1625
1797
|
(peerId) => this.activeRequests.get(peerId) ?? 0
|
|
1626
1798
|
);
|
|
1627
1799
|
|