@lodestar/beacon-node 1.47.0-dev.0bcaaaebb5 → 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 +6 -8
- 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 +25 -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/impl/validator/index.js +1 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +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/archiveStore/historicalState/metrics.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.js +21 -0
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +2 -7
- package/lib/chain/blocks/importBlock.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/builderCircuitBreaker.d.ts +6 -5
- package/lib/chain/builderCircuitBreaker.d.ts.map +1 -1
- package/lib/chain/builderCircuitBreaker.js +28 -15
- package/lib/chain/builderCircuitBreaker.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/options.d.ts +1 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +24 -6
- 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/execution/builder/http.d.ts +10 -7
- package/lib/execution/builder/http.d.ts.map +1 -1
- package/lib/execution/builder/http.js +11 -8
- package/lib/execution/builder/http.js.map +1 -1
- package/lib/metrics/metrics/beacon.d.ts +3 -3
- package/lib/metrics/metrics/beacon.d.ts.map +1 -1
- package/lib/metrics/metrics/beacon.js +9 -9
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +72 -22
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +130 -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 +12 -3
- 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 +218 -63
- 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 +7 -8
- package/src/api/impl/beacon/pool/index.ts +27 -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/impl/validator/index.ts +1 -1
- package/src/api/rest/base.ts +7 -0
- package/src/chain/archiveStore/archiveStore.ts +1 -1
- package/src/chain/archiveStore/historicalState/metrics.ts +22 -0
- package/src/chain/blocks/importBlock.ts +1 -6
- 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/builderCircuitBreaker.ts +31 -17
- 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/options.ts +1 -1
- package/src/chain/prepareNextSlot.ts +24 -6
- 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/execution/builder/http.ts +11 -11
- package/src/metrics/metrics/beacon.ts +9 -9
- package/src/metrics/metrics/lodestar.ts +142 -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 +14 -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 +255 -70
- 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
|
|
|
@@ -764,9 +883,10 @@ export class BlockInputSync {
|
|
|
764
883
|
// this prevents unbundling attack
|
|
765
884
|
// see https://lighthouse-blog.sigmaprime.io/mev-unbundling-rpc.html
|
|
766
885
|
const {slot: blockSlot, proposerIndex} = pendingBlock.blockInput.getBlock().message;
|
|
886
|
+
const blockRootHex = pendingBlock.blockInput.blockRootHex;
|
|
767
887
|
const logCtx = {
|
|
768
888
|
slot: blockSlot,
|
|
769
|
-
root:
|
|
889
|
+
root: blockRootHex,
|
|
770
890
|
delaySec: this.chain.clock.secFromSlot(blockSlot),
|
|
771
891
|
};
|
|
772
892
|
this.logger.verbose("Processing downloaded block", logCtx);
|
|
@@ -780,7 +900,7 @@ export class BlockInputSync {
|
|
|
780
900
|
// eligible for proposer boost to prevent unbundling attack
|
|
781
901
|
this.logger.verbose("Avoid proposer boost for this block of known proposer", {
|
|
782
902
|
slot: blockSlot,
|
|
783
|
-
root:
|
|
903
|
+
root: blockRootHex,
|
|
784
904
|
proposerIndex,
|
|
785
905
|
});
|
|
786
906
|
await sleep(proposerBoostWindowMs);
|
|
@@ -807,12 +927,24 @@ export class BlockInputSync {
|
|
|
807
927
|
if (!res.err) {
|
|
808
928
|
this.logger.verbose("Processed block from unknown sync", logCtx);
|
|
809
929
|
// no need to update status to "processed", delete anyway
|
|
810
|
-
this.pendingBlocks.delete(
|
|
930
|
+
this.pendingBlocks.delete(blockRootHex);
|
|
931
|
+
|
|
932
|
+
if (isForkPostGloas(fork)) {
|
|
933
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
|
|
934
|
+
if (!payloadInput) {
|
|
935
|
+
this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
|
|
936
|
+
} else {
|
|
937
|
+
// Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
|
|
938
|
+
// The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
|
|
939
|
+
this.chain.getBlobsTracker.triggerGetBlobs(payloadInput);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
811
943
|
// Re-enter the scheduler so descendants blocked on either parent blocks or parent payloads
|
|
812
944
|
// are advanced through the same dependency checks as every other pending item.
|
|
813
945
|
this.triggerUnknownBlockSearch();
|
|
814
946
|
} else {
|
|
815
|
-
const errorData = {slot: pendingBlock.blockInput.slot, root:
|
|
947
|
+
const errorData = {slot: pendingBlock.blockInput.slot, root: blockRootHex};
|
|
816
948
|
if (res.err instanceof BlockError) {
|
|
817
949
|
switch (res.err.type.code) {
|
|
818
950
|
// This cases are already handled with `{ignoreIfKnown: true}`
|
|
@@ -842,19 +974,19 @@ export class BlockInputSync {
|
|
|
842
974
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
843
975
|
// Removing the block(s) without penalizing the peers, hoping for EL to
|
|
844
976
|
// recover on a latter download + verify attempt.
|
|
845
|
-
this.removeAllDescendants(pendingBlock);
|
|
977
|
+
this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
|
|
846
978
|
break;
|
|
847
979
|
|
|
848
980
|
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
849
981
|
// the peer served a bad block
|
|
850
982
|
this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
|
|
851
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
983
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
|
|
852
984
|
break;
|
|
853
985
|
|
|
854
986
|
default:
|
|
855
987
|
// Block is not correct with respect to our chain. Log error loudly
|
|
856
988
|
this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
|
|
857
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
989
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
|
|
858
990
|
}
|
|
859
991
|
}
|
|
860
992
|
|
|
@@ -873,12 +1005,12 @@ export class BlockInputSync {
|
|
|
873
1005
|
*/
|
|
874
1006
|
private async reconcilePayloadEnvelope(pendingPayload: PendingPayloadEnvelope): Promise<void> {
|
|
875
1007
|
const rootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
1008
|
+
|
|
876
1009
|
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
877
1010
|
this.pendingPayloads.delete(rootHex);
|
|
878
1011
|
return;
|
|
879
1012
|
}
|
|
880
1013
|
|
|
881
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
882
1014
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
883
1015
|
// Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
|
|
884
1016
|
// non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
|
|
@@ -894,8 +1026,9 @@ export class BlockInputSync {
|
|
|
894
1026
|
return;
|
|
895
1027
|
}
|
|
896
1028
|
|
|
1029
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
897
1030
|
if (!payloadInput) {
|
|
898
|
-
this.logger.debug("
|
|
1031
|
+
this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
|
|
899
1032
|
root: rootHex,
|
|
900
1033
|
});
|
|
901
1034
|
return;
|
|
@@ -915,6 +1048,7 @@ export class BlockInputSync {
|
|
|
915
1048
|
const pendingPayloadByRoot: PendingPayloadRootHex = {
|
|
916
1049
|
status: PendingPayloadInputStatus.pending,
|
|
917
1050
|
rootHex,
|
|
1051
|
+
slot: pendingPayload.envelope.message.payload.slotNumber,
|
|
918
1052
|
timeAddedSec: pendingPayload.timeAddedSec,
|
|
919
1053
|
peerIdStrings: new Set(pendingPayload.peerIdStrings),
|
|
920
1054
|
};
|
|
@@ -954,6 +1088,10 @@ export class BlockInputSync {
|
|
|
954
1088
|
return;
|
|
955
1089
|
}
|
|
956
1090
|
|
|
1091
|
+
if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
|
|
1092
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
957
1095
|
const payloadSlot = getPayloadSyncCacheItemSlot(payload);
|
|
958
1096
|
const logCtx = {
|
|
959
1097
|
slot: payloadSlot,
|
|
@@ -969,7 +1107,13 @@ export class BlockInputSync {
|
|
|
969
1107
|
const res = await wrapError(this.fetchPayloadInput(payload));
|
|
970
1108
|
if (!res.err) {
|
|
971
1109
|
const pendingPayload = res.result;
|
|
972
|
-
|
|
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);
|
|
973
1117
|
|
|
974
1118
|
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
975
1119
|
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
@@ -1046,17 +1190,27 @@ export class BlockInputSync {
|
|
|
1046
1190
|
break;
|
|
1047
1191
|
|
|
1048
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
|
+
|
|
1049
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
|
+
|
|
1050
1202
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1051
1203
|
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
1052
1204
|
// reintroducing envelope replacement / recreation flows.
|
|
1053
1205
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1054
|
-
this.removePendingPayloadAndDescendants(rootHex);
|
|
1206
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
1055
1207
|
break;
|
|
1056
1208
|
|
|
1057
1209
|
default:
|
|
1058
1210
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1059
|
-
this.pendingPayloads.delete(rootHex)
|
|
1211
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1212
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.unknown}, 1);
|
|
1213
|
+
}
|
|
1060
1214
|
}
|
|
1061
1215
|
return;
|
|
1062
1216
|
}
|
|
@@ -1080,7 +1234,7 @@ export class BlockInputSync {
|
|
|
1080
1234
|
let slot = getPayloadSyncCacheItemSlot(cacheItem);
|
|
1081
1235
|
let payloadInput = isPendingPayloadInput(cacheItem)
|
|
1082
1236
|
? cacheItem.payloadInput
|
|
1083
|
-
: this.chain.seenPayloadEnvelopeInputCache.
|
|
1237
|
+
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1084
1238
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
1085
1239
|
|
|
1086
1240
|
let i = 0;
|
|
@@ -1089,7 +1243,12 @@ export class BlockInputSync {
|
|
|
1089
1243
|
const pendingColumns = payloadInput?.hasAllData()
|
|
1090
1244
|
? new Set<number>()
|
|
1091
1245
|
: new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
|
|
1092
|
-
|
|
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
|
+
);
|
|
1093
1252
|
if (peerMeta === null) {
|
|
1094
1253
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
1095
1254
|
throw new UnknownBlockRateLimitedError(
|
|
@@ -1097,9 +1256,11 @@ export class BlockInputSync {
|
|
|
1097
1256
|
);
|
|
1098
1257
|
}
|
|
1099
1258
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
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}`);
|
|
1103
1264
|
}
|
|
1104
1265
|
|
|
1105
1266
|
const {peerId, client: peerClient} = peerMeta;
|
|
@@ -1111,7 +1272,7 @@ export class BlockInputSync {
|
|
|
1111
1272
|
slot = envelope.message.payload.slotNumber;
|
|
1112
1273
|
}
|
|
1113
1274
|
|
|
1114
|
-
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.
|
|
1275
|
+
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1115
1276
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
1116
1277
|
// Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
|
|
1117
1278
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
@@ -1296,7 +1457,12 @@ export class BlockInputSync {
|
|
|
1296
1457
|
isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
|
|
1297
1458
|
? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
|
|
1298
1459
|
: defaultPendingColumns;
|
|
1299
|
-
|
|
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
|
+
);
|
|
1300
1466
|
if (peerMeta === null) {
|
|
1301
1467
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
1302
1468
|
throw new UnknownBlockRateLimitedError(
|
|
@@ -1444,9 +1610,9 @@ export class BlockInputSync {
|
|
|
1444
1610
|
* Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
|
|
1445
1611
|
* referenced more than one bad block.
|
|
1446
1612
|
*/
|
|
1447
|
-
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem): void {
|
|
1613
|
+
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
|
|
1448
1614
|
// Get all blocks that are a descendant of this one
|
|
1449
|
-
const badPendingBlocks = this.removeAllDescendants(block);
|
|
1615
|
+
const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
|
|
1450
1616
|
// just console log and do not penalize on pending/bad blocks for debugging
|
|
1451
1617
|
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
1452
1618
|
|
|
@@ -1473,18 +1639,22 @@ export class BlockInputSync {
|
|
|
1473
1639
|
}
|
|
1474
1640
|
|
|
1475
1641
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1476
|
-
private removePendingPayloadAndDescendants(rootHex: RootHex): void {
|
|
1642
|
+
private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
|
|
1477
1643
|
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1478
1644
|
// later validation/finalization logic decides when it can leave memory.
|
|
1479
|
-
this.pendingPayloads.delete(rootHex)
|
|
1645
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1646
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
|
|
1647
|
+
}
|
|
1480
1648
|
|
|
1481
1649
|
const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
|
|
1482
|
-
this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
|
|
1483
|
-
|
|
1484
1650
|
for (const block of badPendingBlocks) {
|
|
1485
1651
|
const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1486
|
-
this.pendingBlocks.delete(descendantRootHex)
|
|
1487
|
-
|
|
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
|
+
}
|
|
1488
1658
|
this.chain.seenBlockInputCache.prune(descendantRootHex);
|
|
1489
1659
|
this.logger.debug("Removing pending descendant after invalid parent payload", {
|
|
1490
1660
|
slot: getBlockInputSyncCacheItemSlot(block),
|
|
@@ -1494,18 +1664,25 @@ export class BlockInputSync {
|
|
|
1494
1664
|
}
|
|
1495
1665
|
}
|
|
1496
1666
|
|
|
1497
|
-
private removeAllDescendants(
|
|
1667
|
+
private removeAllDescendants(
|
|
1668
|
+
block: BlockInputSyncCacheItem,
|
|
1669
|
+
headReason: DroppedItemReason,
|
|
1670
|
+
descendantReason: DroppedItemReason = DroppedItemReason.invalidParent
|
|
1671
|
+
): BlockInputSyncCacheItem[] {
|
|
1498
1672
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1499
1673
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
1500
|
-
// 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)
|
|
1501
1675
|
const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
|
|
1502
1676
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
for (const block of badPendingBlocks) {
|
|
1677
|
+
for (const [i, block] of badPendingBlocks.entries()) {
|
|
1506
1678
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1507
|
-
|
|
1508
|
-
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
|
+
}
|
|
1509
1686
|
this.chain.seenBlockInputCache.prune(rootHex);
|
|
1510
1687
|
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1511
1688
|
// importBlock()-owned lifecycle.
|
|
@@ -1596,11 +1773,17 @@ export class UnknownBlockPeerBalancer {
|
|
|
1596
1773
|
}
|
|
1597
1774
|
|
|
1598
1775
|
/**
|
|
1599
|
-
* called from fetchBlockInput()
|
|
1776
|
+
* called from fetchBlockInput()/fetchPayloadInput() with a block/payload root.
|
|
1600
1777
|
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
1601
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.
|
|
1602
1781
|
*/
|
|
1603
|
-
bestPeerForPendingColumns(
|
|
1782
|
+
bestPeerForPendingColumns(
|
|
1783
|
+
pendingColumns: Set<number>,
|
|
1784
|
+
excludedPeers: Set<PeerIdStr>,
|
|
1785
|
+
preferredPeers?: Set<PeerIdStr>
|
|
1786
|
+
): PeerSyncMeta | null {
|
|
1604
1787
|
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
1605
1788
|
if (eligiblePeers.length === 0) {
|
|
1606
1789
|
return null;
|
|
@@ -1608,7 +1791,9 @@ export class UnknownBlockPeerBalancer {
|
|
|
1608
1791
|
|
|
1609
1792
|
const sortedEligiblePeers = sortBy(
|
|
1610
1793
|
shuffle(eligiblePeers),
|
|
1611
|
-
// 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
|
|
1612
1797
|
(peerId) => this.activeRequests.get(peerId) ?? 0
|
|
1613
1798
|
);
|
|
1614
1799
|
|