@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/lib/sync/unknownBlock.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { routes } from "@lodestar/api";
|
|
2
|
-
import { ForkSeq } from "@lodestar/params";
|
|
2
|
+
import { ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas } from "@lodestar/params";
|
|
3
3
|
import { RequestError, RequestErrorCode } from "@lodestar/reqresp";
|
|
4
4
|
import { computeTimeAtSlot } from "@lodestar/state-transition";
|
|
5
5
|
import { fromHex, prettyPrintIndices, pruneSetToMax, sleep, toRootHex } from "@lodestar/utils";
|
|
@@ -12,17 +12,22 @@ import { ChainEvent } from "../chain/index.js";
|
|
|
12
12
|
import { validateGloasBlockDataColumnSidecars } from "../chain/validation/dataColumnSidecar.js";
|
|
13
13
|
import { validateGossipExecutionPayloadEnvelope } from "../chain/validation/executionPayloadEnvelope.js";
|
|
14
14
|
import { NetworkEvent, prettyPrintPeerIdStr } from "../network/index.js";
|
|
15
|
+
import { MAX_PEERS_PER_ROOT } from "../network/processor/constants.js";
|
|
15
16
|
import { shuffle } from "../util/shuffle.js";
|
|
16
17
|
import { sortBy } from "../util/sortBy.js";
|
|
17
18
|
import { wrapError } from "../util/wrapError.js";
|
|
18
19
|
import { MAX_CONCURRENT_REQUESTS } from "./constants.js";
|
|
19
|
-
import { PendingBlockInputStatus, PendingBlockType, PendingPayloadInputStatus, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, getPayloadSyncCacheItemRootHex, getPayloadSyncCacheItemSlot, isPendingBlockInput, isPendingPayloadEnvelope, isPendingPayloadInput, } from "./types.js";
|
|
20
|
+
import { DroppedItemReason, PendingBlockInputStatus, PendingBlockType, PendingPayloadInputStatus, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, getPayloadSyncCacheItemRootHex, getPayloadSyncCacheItemSlot, isPendingBlockInput, isPendingPayloadEnvelope, isPendingPayloadInput, } from "./types.js";
|
|
20
21
|
import { DownloadByRootError, downloadByRoot } from "./utils/downloadByRoot.js";
|
|
21
22
|
import { getAllDescendantBlocks, getUnknownAndAncestorBlocks } from "./utils/pendingBlocksTree.js";
|
|
22
23
|
import { getRateLimitedUntilMs } from "./utils/rateLimit.js";
|
|
23
24
|
const MAX_ATTEMPTS_PER_BLOCK = 5;
|
|
24
25
|
const MAX_KNOWN_BAD_BLOCKS = 500;
|
|
25
26
|
const MAX_PENDING_BLOCKS = 100;
|
|
27
|
+
/**
|
|
28
|
+
* A pending payload without slot can only stay at most 2 epochs.
|
|
29
|
+
*/
|
|
30
|
+
const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
|
|
26
31
|
var FetchResult;
|
|
27
32
|
(function (FetchResult) {
|
|
28
33
|
FetchResult["SuccessResolved"] = "success_resolved";
|
|
@@ -115,6 +120,7 @@ export class BlockInputSync {
|
|
|
115
120
|
this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
116
121
|
this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
117
122
|
this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
123
|
+
this.chain.emitter.on(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
118
124
|
this.chain.emitter.on(routes.events.EventType.block, this.onBlockImported);
|
|
119
125
|
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
120
126
|
this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
@@ -130,6 +136,7 @@ export class BlockInputSync {
|
|
|
130
136
|
this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
131
137
|
this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
132
138
|
this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
139
|
+
this.chain.emitter.off(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
133
140
|
this.chain.emitter.off(routes.events.EventType.block, this.onBlockImported);
|
|
134
141
|
this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
135
142
|
this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
@@ -147,10 +154,12 @@ export class BlockInputSync {
|
|
|
147
154
|
*/
|
|
148
155
|
onUnknownBlockRoot = (data) => {
|
|
149
156
|
try {
|
|
150
|
-
this.addByRootHex(data.rootHex, data.peer);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
157
|
+
const isNewRoot = this.addByRootHex(data.rootHex, data.peer);
|
|
158
|
+
if (isNewRoot) {
|
|
159
|
+
this.triggerUnknownBlockSearch();
|
|
160
|
+
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_BLOCK_ROOT });
|
|
161
|
+
this.metrics?.blockInputSync.source.inc({ source: data.source });
|
|
162
|
+
}
|
|
154
163
|
}
|
|
155
164
|
catch (e) {
|
|
156
165
|
this.logger.debug("Error handling unknownBlockRoot event", {}, e);
|
|
@@ -172,10 +181,12 @@ export class BlockInputSync {
|
|
|
172
181
|
};
|
|
173
182
|
onUnknownEnvelopeBlockRoot = (data) => {
|
|
174
183
|
try {
|
|
175
|
-
this.addByPayloadRootHex(data.rootHex, data.peer, data.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
const isNewRoot = this.addByPayloadRootHex(data.rootHex, data.peer, this.resolvePayloadSlot(data.rootHex));
|
|
185
|
+
if (isNewRoot) {
|
|
186
|
+
this.triggerUnknownBlockSearch();
|
|
187
|
+
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT });
|
|
188
|
+
this.metrics?.blockInputSync.payloadSource.inc({ source: data.source });
|
|
189
|
+
}
|
|
179
190
|
}
|
|
180
191
|
catch (e) {
|
|
181
192
|
this.logger.debug("Error handling unknownEnvelopeBlockRoot event", {}, e);
|
|
@@ -208,12 +219,12 @@ export class BlockInputSync {
|
|
|
208
219
|
parentRoot: missingDependency.parentRootHex,
|
|
209
220
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
210
221
|
});
|
|
211
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
222
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
212
223
|
}
|
|
213
224
|
return;
|
|
214
225
|
}
|
|
215
226
|
if (missingDependency.kind === "parentPayload") {
|
|
216
|
-
this.addByPayloadRootHex(missingDependency.rootHex, data.peer);
|
|
227
|
+
this.addByPayloadRootHex(missingDependency.rootHex, data.peer, missingDependency.slot);
|
|
217
228
|
}
|
|
218
229
|
else if (missingDependency.kind === "parentBlock") {
|
|
219
230
|
this.addByRootHex(missingDependency.rootHex, data.peer);
|
|
@@ -236,6 +247,71 @@ export class BlockInputSync {
|
|
|
236
247
|
this.pendingPayloads.delete(blockRoot);
|
|
237
248
|
this.triggerUnknownBlockSearch();
|
|
238
249
|
};
|
|
250
|
+
/**
|
|
251
|
+
* Drop pending payloads and blocks that fell below the finalized slot.
|
|
252
|
+
* See https://github.com/ChainSafe/lodestar/issues/9800
|
|
253
|
+
*/
|
|
254
|
+
pruneFinalized = (checkpoint) => {
|
|
255
|
+
// Use the actual finalized block slot, not computeStartSlotAtEpoch(checkpoint.epoch)
|
|
256
|
+
// because the payload at the finalized block is not finalized
|
|
257
|
+
const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
|
|
258
|
+
const nowSec = Date.now() / 1000;
|
|
259
|
+
const maxSecInCache = (PRUNE_UNRESOLVED_SLOT_EPOCHS * SLOTS_PER_EPOCH * this.config.SLOT_DURATION_MS) / 1000;
|
|
260
|
+
let belowFinalizedPayloads = 0;
|
|
261
|
+
let agedOutPayloads = 0;
|
|
262
|
+
let unresolvedSlotPayloads = 0;
|
|
263
|
+
for (const [rootHex, payload] of this.pendingPayloads) {
|
|
264
|
+
// Last-effort resolve for a slot-less PendingPayloadRootHex, and store it back for later passes.
|
|
265
|
+
if (!isPendingPayloadInput(payload) && !isPendingPayloadEnvelope(payload) && payload.slot === undefined) {
|
|
266
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
267
|
+
if (payload.slot === undefined) {
|
|
268
|
+
// Still unresolvable: age it out so the MAX_PENDING_BLOCKS cap cannot stay saturated.
|
|
269
|
+
if (nowSec - payload.timeAddedSec > maxSecInCache) {
|
|
270
|
+
this.pendingPayloads.delete(rootHex);
|
|
271
|
+
agedOutPayloads++;
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
unresolvedSlotPayloads++;
|
|
275
|
+
this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
|
|
276
|
+
root: rootHex,
|
|
277
|
+
finalizedSlot,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const slot = getPayloadSyncCacheItemSlot(payload);
|
|
284
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
285
|
+
this.pendingPayloads.delete(rootHex);
|
|
286
|
+
belowFinalizedPayloads++;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
let deletedBlocks = 0;
|
|
290
|
+
for (const [rootHex, block] of this.pendingBlocks) {
|
|
291
|
+
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
292
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
293
|
+
this.pendingBlocks.delete(rootHex);
|
|
294
|
+
deletedBlocks++;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (belowFinalizedPayloads > 0) {
|
|
298
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.belowFinalized }, belowFinalizedPayloads);
|
|
299
|
+
}
|
|
300
|
+
if (agedOutPayloads > 0) {
|
|
301
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.agedOut }, agedOutPayloads);
|
|
302
|
+
}
|
|
303
|
+
if (deletedBlocks > 0) {
|
|
304
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.belowFinalized }, deletedBlocks);
|
|
305
|
+
}
|
|
306
|
+
this.logger.debug("BlockInputSync.pruneFinalized dropped pre-finalized pending items", {
|
|
307
|
+
finalizedSlot,
|
|
308
|
+
finalizedRoot: checkpoint.rootHex,
|
|
309
|
+
belowFinalizedPayloads,
|
|
310
|
+
agedOutPayloads,
|
|
311
|
+
deletedBlocks,
|
|
312
|
+
unresolvedSlotPayloads,
|
|
313
|
+
});
|
|
314
|
+
};
|
|
239
315
|
addByRootHex = (rootHex, peerIdStr) => {
|
|
240
316
|
let pendingBlock = this.pendingBlocks.get(rootHex);
|
|
241
317
|
let added = false;
|
|
@@ -253,13 +329,16 @@ export class BlockInputSync {
|
|
|
253
329
|
peerIdStr: peerIdStr ?? "unknown peer",
|
|
254
330
|
});
|
|
255
331
|
}
|
|
256
|
-
|
|
332
|
+
// bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
|
|
333
|
+
// Once at the cap, ignore further peers rather than evicting already-tracked ones.
|
|
334
|
+
if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
257
335
|
pendingBlock.peerIdStrings.add(peerIdStr);
|
|
258
336
|
}
|
|
259
337
|
// TODO: check this prune methodology
|
|
260
338
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
261
339
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
262
340
|
if (prunedItemCount > 0) {
|
|
341
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
263
342
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
264
343
|
}
|
|
265
344
|
return added;
|
|
@@ -283,13 +362,16 @@ export class BlockInputSync {
|
|
|
283
362
|
delaySec: this.chain.clock.secFromSlot(blockInput.slot),
|
|
284
363
|
});
|
|
285
364
|
}
|
|
286
|
-
|
|
365
|
+
// bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
|
|
366
|
+
// Once at the cap, ignore further peers rather than evicting already-tracked ones.
|
|
367
|
+
if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
287
368
|
pendingBlock.peerIdStrings.add(peerIdStr);
|
|
288
369
|
}
|
|
289
370
|
// TODO: check this prune methodology
|
|
290
371
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
291
372
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
292
373
|
if (prunedItemCount > 0) {
|
|
374
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
293
375
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
294
376
|
}
|
|
295
377
|
};
|
|
@@ -313,18 +395,47 @@ export class BlockInputSync {
|
|
|
313
395
|
peerIdStr: peerIdStr ?? "unknown peer",
|
|
314
396
|
});
|
|
315
397
|
}
|
|
316
|
-
if (
|
|
398
|
+
else if (
|
|
399
|
+
// update a previously slot-less entry once a slot becomes available
|
|
400
|
+
slot !== undefined &&
|
|
401
|
+
!isPendingPayloadInput(pendingPayload) &&
|
|
402
|
+
!isPendingPayloadEnvelope(pendingPayload) &&
|
|
403
|
+
pendingPayload.slot === undefined) {
|
|
404
|
+
pendingPayload.slot = slot;
|
|
405
|
+
}
|
|
406
|
+
if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
317
407
|
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
318
408
|
}
|
|
319
409
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
320
410
|
if (prunedItemCount > 0) {
|
|
411
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
321
412
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
322
413
|
}
|
|
323
414
|
return added;
|
|
324
415
|
};
|
|
416
|
+
/**
|
|
417
|
+
* Resolve the block slot for a payload root.
|
|
418
|
+
*/
|
|
419
|
+
resolvePayloadSlot(rootHex) {
|
|
420
|
+
// a pending block of the same root
|
|
421
|
+
const pendingBlock = this.pendingBlocks.get(rootHex);
|
|
422
|
+
if (pendingBlock) {
|
|
423
|
+
const pendingBlockSlot = getBlockInputSyncCacheItemSlot(pendingBlock);
|
|
424
|
+
if (typeof pendingBlockSlot === "number") {
|
|
425
|
+
return pendingBlockSlot;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
// the block is in fork choice
|
|
429
|
+
const forkChoiceSlot = this.chain.forkChoice.getBlockHexDefaultStatus(rootHex)?.slot;
|
|
430
|
+
if (forkChoiceSlot !== undefined) {
|
|
431
|
+
return forkChoiceSlot;
|
|
432
|
+
}
|
|
433
|
+
// a seen payload envelope input
|
|
434
|
+
return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
|
|
435
|
+
}
|
|
325
436
|
addByPayloadInput = (payloadInput, peerIdStr, envelope) => {
|
|
326
437
|
const pendingPayload = this.toPendingPayloadInput(payloadInput, this.pendingPayloads.get(payloadInput.blockRootHex), envelope);
|
|
327
|
-
if (peerIdStr) {
|
|
438
|
+
if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
328
439
|
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
329
440
|
}
|
|
330
441
|
this.pendingPayloads.set(payloadInput.blockRootHex, pendingPayload);
|
|
@@ -335,6 +446,7 @@ export class BlockInputSync {
|
|
|
335
446
|
});
|
|
336
447
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
337
448
|
if (prunedItemCount > 0) {
|
|
449
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
338
450
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
339
451
|
}
|
|
340
452
|
};
|
|
@@ -360,7 +472,8 @@ export class BlockInputSync {
|
|
|
360
472
|
*/
|
|
361
473
|
getMissingBlockDependency(blockInput) {
|
|
362
474
|
const parentRootHex = blockInput.parentRootHex;
|
|
363
|
-
|
|
475
|
+
const parentBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRootHex);
|
|
476
|
+
if (!parentBlock) {
|
|
364
477
|
return { kind: "parentBlock", rootHex: parentRootHex };
|
|
365
478
|
}
|
|
366
479
|
if (!blockInput.hasBlock()) {
|
|
@@ -377,14 +490,8 @@ export class BlockInputSync {
|
|
|
377
490
|
if (this.chain.forkChoice.hasPayloadHexUnsafe(parentRootHex)) {
|
|
378
491
|
return { kind: "invalidParentPayload", parentRootHex, parentBlockHashHex };
|
|
379
492
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
|
|
383
|
-
return { kind: "parentPayload", rootHex: parentRootHex };
|
|
384
|
-
}
|
|
385
|
-
return { kind: "invalidParentPayload", parentRootHex, parentBlockHashHex };
|
|
386
|
-
}
|
|
387
|
-
return { kind: "parentPayload", rootHex: parentRootHex };
|
|
493
|
+
// Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
|
|
494
|
+
return { kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot };
|
|
388
495
|
}
|
|
389
496
|
advancePendingBlock(pendingBlock) {
|
|
390
497
|
const missingDependency = this.getMissingBlockDependency(pendingBlock.blockInput);
|
|
@@ -402,9 +509,9 @@ export class BlockInputSync {
|
|
|
402
509
|
return added ? "queued_parent_block" : "blocked";
|
|
403
510
|
}
|
|
404
511
|
case "parentPayload": {
|
|
405
|
-
let added = this.addByPayloadRootHex(missingDependency.rootHex);
|
|
512
|
+
let added = this.addByPayloadRootHex(missingDependency.rootHex, undefined, missingDependency.slot);
|
|
406
513
|
for (const peerIdStr of pendingBlock.peerIdStrings) {
|
|
407
|
-
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr) || added;
|
|
514
|
+
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr, missingDependency.slot) || added;
|
|
408
515
|
}
|
|
409
516
|
return added ? "queued_parent_payload" : "blocked";
|
|
410
517
|
}
|
|
@@ -415,7 +522,7 @@ export class BlockInputSync {
|
|
|
415
522
|
parentRoot: missingDependency.parentRootHex,
|
|
416
523
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
417
524
|
});
|
|
418
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
525
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
419
526
|
return "removed";
|
|
420
527
|
}
|
|
421
528
|
}
|
|
@@ -604,7 +711,7 @@ export class BlockInputSync {
|
|
|
604
711
|
...logCtx2,
|
|
605
712
|
finalizedSlot,
|
|
606
713
|
});
|
|
607
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
714
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
|
|
608
715
|
}
|
|
609
716
|
else {
|
|
610
717
|
this.onUnknownBlockRoot({ rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot });
|
|
@@ -622,7 +729,7 @@ export class BlockInputSync {
|
|
|
622
729
|
}
|
|
623
730
|
this.metrics?.blockInputSync.downloadedBlocksError.inc();
|
|
624
731
|
this.logger.debug("Ignoring unknown block root after many failed downloads", logCtx, res.err);
|
|
625
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
732
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.unavailable);
|
|
626
733
|
}
|
|
627
734
|
}
|
|
628
735
|
/**
|
|
@@ -637,9 +744,10 @@ export class BlockInputSync {
|
|
|
637
744
|
// this prevents unbundling attack
|
|
638
745
|
// see https://lighthouse-blog.sigmaprime.io/mev-unbundling-rpc.html
|
|
639
746
|
const { slot: blockSlot, proposerIndex } = pendingBlock.blockInput.getBlock().message;
|
|
747
|
+
const blockRootHex = pendingBlock.blockInput.blockRootHex;
|
|
640
748
|
const logCtx = {
|
|
641
749
|
slot: blockSlot,
|
|
642
|
-
root:
|
|
750
|
+
root: blockRootHex,
|
|
643
751
|
delaySec: this.chain.clock.secFromSlot(blockSlot),
|
|
644
752
|
};
|
|
645
753
|
this.logger.verbose("Processing downloaded block", logCtx);
|
|
@@ -651,7 +759,7 @@ export class BlockInputSync {
|
|
|
651
759
|
// eligible for proposer boost to prevent unbundling attack
|
|
652
760
|
this.logger.verbose("Avoid proposer boost for this block of known proposer", {
|
|
653
761
|
slot: blockSlot,
|
|
654
|
-
root:
|
|
762
|
+
root: blockRootHex,
|
|
655
763
|
proposerIndex,
|
|
656
764
|
});
|
|
657
765
|
await sleep(proposerBoostWindowMs);
|
|
@@ -676,13 +784,24 @@ export class BlockInputSync {
|
|
|
676
784
|
if (!res.err) {
|
|
677
785
|
this.logger.verbose("Processed block from unknown sync", logCtx);
|
|
678
786
|
// no need to update status to "processed", delete anyway
|
|
679
|
-
this.pendingBlocks.delete(
|
|
787
|
+
this.pendingBlocks.delete(blockRootHex);
|
|
788
|
+
if (isForkPostGloas(fork)) {
|
|
789
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
|
|
790
|
+
if (!payloadInput) {
|
|
791
|
+
this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
|
|
792
|
+
}
|
|
793
|
+
else {
|
|
794
|
+
// Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
|
|
795
|
+
// The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
|
|
796
|
+
this.chain.getBlobsTracker.triggerGetBlobs(payloadInput);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
680
799
|
// Re-enter the scheduler so descendants blocked on either parent blocks or parent payloads
|
|
681
800
|
// are advanced through the same dependency checks as every other pending item.
|
|
682
801
|
this.triggerUnknownBlockSearch();
|
|
683
802
|
}
|
|
684
803
|
else {
|
|
685
|
-
const errorData = { slot: pendingBlock.blockInput.slot, root:
|
|
804
|
+
const errorData = { slot: pendingBlock.blockInput.slot, root: blockRootHex };
|
|
686
805
|
if (res.err instanceof BlockError) {
|
|
687
806
|
switch (res.err.type.code) {
|
|
688
807
|
// This cases are already handled with `{ignoreIfKnown: true}`
|
|
@@ -705,17 +824,17 @@ export class BlockInputSync {
|
|
|
705
824
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
706
825
|
// Removing the block(s) without penalizing the peers, hoping for EL to
|
|
707
826
|
// recover on a latter download + verify attempt.
|
|
708
|
-
this.removeAllDescendants(pendingBlock);
|
|
827
|
+
this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
|
|
709
828
|
break;
|
|
710
829
|
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
711
830
|
// the peer served a bad block
|
|
712
831
|
this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
|
|
713
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
832
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
|
|
714
833
|
break;
|
|
715
834
|
default:
|
|
716
835
|
// Block is not correct with respect to our chain. Log error loudly
|
|
717
836
|
this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
|
|
718
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
837
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
|
|
719
838
|
}
|
|
720
839
|
}
|
|
721
840
|
// Probably a queue error or something unwanted happened, mark as pending to try again latter
|
|
@@ -736,7 +855,6 @@ export class BlockInputSync {
|
|
|
736
855
|
this.pendingPayloads.delete(rootHex);
|
|
737
856
|
return;
|
|
738
857
|
}
|
|
739
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
740
858
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
741
859
|
// Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
|
|
742
860
|
// non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
|
|
@@ -750,8 +868,9 @@ export class BlockInputSync {
|
|
|
750
868
|
}
|
|
751
869
|
return;
|
|
752
870
|
}
|
|
871
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
753
872
|
if (!payloadInput) {
|
|
754
|
-
this.logger.debug("
|
|
873
|
+
this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
|
|
755
874
|
root: rootHex,
|
|
756
875
|
});
|
|
757
876
|
return;
|
|
@@ -763,6 +882,7 @@ export class BlockInputSync {
|
|
|
763
882
|
const pendingPayloadByRoot = {
|
|
764
883
|
status: PendingPayloadInputStatus.pending,
|
|
765
884
|
rootHex,
|
|
885
|
+
slot: pendingPayload.envelope.message.payload.slotNumber,
|
|
766
886
|
timeAddedSec: pendingPayload.timeAddedSec,
|
|
767
887
|
peerIdStrings: new Set(pendingPayload.peerIdStrings),
|
|
768
888
|
};
|
|
@@ -794,6 +914,9 @@ export class BlockInputSync {
|
|
|
794
914
|
if (payload.status !== PendingPayloadInputStatus.pending) {
|
|
795
915
|
return;
|
|
796
916
|
}
|
|
917
|
+
if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
|
|
918
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
919
|
+
}
|
|
797
920
|
const payloadSlot = getPayloadSyncCacheItemSlot(payload);
|
|
798
921
|
const logCtx = {
|
|
799
922
|
slot: payloadSlot,
|
|
@@ -806,7 +929,13 @@ export class BlockInputSync {
|
|
|
806
929
|
const res = await wrapError(this.fetchPayloadInput(payload));
|
|
807
930
|
if (!res.err) {
|
|
808
931
|
const pendingPayload = res.result;
|
|
809
|
-
|
|
932
|
+
const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
933
|
+
// finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
|
|
934
|
+
if (!this.pendingPayloads.has(resultRootHex)) {
|
|
935
|
+
this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
this.pendingPayloads.set(resultRootHex, pendingPayload);
|
|
810
939
|
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
811
940
|
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
812
941
|
}
|
|
@@ -873,16 +1002,24 @@ export class BlockInputSync {
|
|
|
873
1002
|
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
874
1003
|
break;
|
|
875
1004
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1005
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1006
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1007
|
+
break;
|
|
876
1008
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1009
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1010
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1011
|
+
break;
|
|
877
1012
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
878
1013
|
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
879
1014
|
// reintroducing envelope replacement / recreation flows.
|
|
880
1015
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
881
|
-
this.removePendingPayloadAndDescendants(rootHex);
|
|
1016
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
882
1017
|
break;
|
|
883
1018
|
default:
|
|
884
1019
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
885
|
-
this.pendingPayloads.delete(rootHex)
|
|
1020
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1021
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.unknown }, 1);
|
|
1022
|
+
}
|
|
886
1023
|
}
|
|
887
1024
|
return;
|
|
888
1025
|
}
|
|
@@ -901,7 +1038,7 @@ export class BlockInputSync {
|
|
|
901
1038
|
let slot = getPayloadSyncCacheItemSlot(cacheItem);
|
|
902
1039
|
let payloadInput = isPendingPayloadInput(cacheItem)
|
|
903
1040
|
? cacheItem.payloadInput
|
|
904
|
-
: this.chain.seenPayloadEnvelopeInputCache.
|
|
1041
|
+
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
905
1042
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
906
1043
|
let i = 0;
|
|
907
1044
|
let deferredByRateLimit = false;
|
|
@@ -909,12 +1046,16 @@ export class BlockInputSync {
|
|
|
909
1046
|
const pendingColumns = payloadInput?.hasAllData()
|
|
910
1047
|
? new Set()
|
|
911
1048
|
: new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
|
|
912
|
-
|
|
1049
|
+
// prefer peers that gossiped this payload root to us (#9923)
|
|
1050
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers, cacheItem.peerIdStrings);
|
|
913
1051
|
if (peerMeta === null) {
|
|
914
1052
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
915
1053
|
throw new UnknownBlockRateLimitedError(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`);
|
|
916
1054
|
}
|
|
917
|
-
|
|
1055
|
+
const reason = pendingColumns.size > 0
|
|
1056
|
+
? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
|
|
1057
|
+
: "no peer available to download pending payload";
|
|
1058
|
+
throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
|
|
918
1059
|
}
|
|
919
1060
|
const { peerId, client: peerClient } = peerMeta;
|
|
920
1061
|
cacheItem.peerIdStrings.add(peerId);
|
|
@@ -923,7 +1064,7 @@ export class BlockInputSync {
|
|
|
923
1064
|
envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
|
|
924
1065
|
slot = envelope.message.payload.slotNumber;
|
|
925
1066
|
}
|
|
926
|
-
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.
|
|
1067
|
+
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
927
1068
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
928
1069
|
// Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
|
|
929
1070
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
@@ -1068,7 +1209,8 @@ export class BlockInputSync {
|
|
|
1068
1209
|
const pendingColumns = isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
|
|
1069
1210
|
? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
|
|
1070
1211
|
: defaultPendingColumns;
|
|
1071
|
-
|
|
1212
|
+
// prefer peers that gossiped this block root to us (#9923)
|
|
1213
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers, cacheItem.peerIdStrings);
|
|
1072
1214
|
if (peerMeta === null) {
|
|
1073
1215
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
1074
1216
|
throw new UnknownBlockRateLimitedError(`Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`);
|
|
@@ -1194,9 +1336,9 @@ export class BlockInputSync {
|
|
|
1194
1336
|
* Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
|
|
1195
1337
|
* referenced more than one bad block.
|
|
1196
1338
|
*/
|
|
1197
|
-
removeAndDownScoreAllDescendants(block) {
|
|
1339
|
+
removeAndDownScoreAllDescendants(block, headReason) {
|
|
1198
1340
|
// Get all blocks that are a descendant of this one
|
|
1199
|
-
const badPendingBlocks = this.removeAllDescendants(block);
|
|
1341
|
+
const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
|
|
1200
1342
|
// just console log and do not penalize on pending/bad blocks for debugging
|
|
1201
1343
|
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
1202
1344
|
for (const block of badPendingBlocks) {
|
|
@@ -1220,16 +1362,21 @@ export class BlockInputSync {
|
|
|
1220
1362
|
pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
|
|
1221
1363
|
}
|
|
1222
1364
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1223
|
-
removePendingPayloadAndDescendants(rootHex) {
|
|
1365
|
+
removePendingPayloadAndDescendants(rootHex, headReason) {
|
|
1224
1366
|
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1225
1367
|
// later validation/finalization logic decides when it can leave memory.
|
|
1226
|
-
this.pendingPayloads.delete(rootHex)
|
|
1368
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1369
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: headReason }, 1);
|
|
1370
|
+
}
|
|
1227
1371
|
const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
|
|
1228
|
-
this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
|
|
1229
1372
|
for (const block of badPendingBlocks) {
|
|
1230
1373
|
const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1231
|
-
this.pendingBlocks.delete(descendantRootHex)
|
|
1232
|
-
|
|
1374
|
+
if (this.pendingBlocks.delete(descendantRootHex)) {
|
|
1375
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.invalidParent }, 1);
|
|
1376
|
+
}
|
|
1377
|
+
if (this.pendingPayloads.delete(descendantRootHex)) {
|
|
1378
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.invalidParent }, 1);
|
|
1379
|
+
}
|
|
1233
1380
|
this.chain.seenBlockInputCache.prune(descendantRootHex);
|
|
1234
1381
|
this.logger.debug("Removing pending descendant after invalid parent payload", {
|
|
1235
1382
|
slot: getBlockInputSyncCacheItemSlot(block),
|
|
@@ -1238,16 +1385,20 @@ export class BlockInputSync {
|
|
|
1238
1385
|
});
|
|
1239
1386
|
}
|
|
1240
1387
|
}
|
|
1241
|
-
removeAllDescendants(block) {
|
|
1388
|
+
removeAllDescendants(block, headReason, descendantReason = DroppedItemReason.invalidParent) {
|
|
1242
1389
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1243
1390
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
1244
|
-
// Get all blocks that are a descendant of this one
|
|
1391
|
+
// Get all blocks that are a descendant of this one (index 0 is the head item itself)
|
|
1245
1392
|
const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
|
|
1246
|
-
|
|
1247
|
-
for (const block of badPendingBlocks) {
|
|
1393
|
+
for (const [i, block] of badPendingBlocks.entries()) {
|
|
1248
1394
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1249
|
-
|
|
1250
|
-
this.
|
|
1395
|
+
const reason = i === 0 ? headReason : descendantReason;
|
|
1396
|
+
if (this.pendingBlocks.delete(rootHex)) {
|
|
1397
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason }, 1);
|
|
1398
|
+
}
|
|
1399
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1400
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason }, 1);
|
|
1401
|
+
}
|
|
1251
1402
|
this.chain.seenBlockInputCache.prune(rootHex);
|
|
1252
1403
|
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1253
1404
|
// importBlock()-owned lifecycle.
|
|
@@ -1318,17 +1469,21 @@ export class UnknownBlockPeerBalancer {
|
|
|
1318
1469
|
return retryAt;
|
|
1319
1470
|
}
|
|
1320
1471
|
/**
|
|
1321
|
-
* called from fetchBlockInput()
|
|
1472
|
+
* called from fetchBlockInput()/fetchPayloadInput() with a block/payload root.
|
|
1322
1473
|
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
1323
1474
|
* pendingColumns is empty for prefulu, or the 1st time we we download a block by root
|
|
1475
|
+
* preferredPeers are the peers NetworkProcessor told us gossiped this root (from
|
|
1476
|
+
* pendingBlock/cacheItem.peerIdStrings). They are preferred but not required.
|
|
1324
1477
|
*/
|
|
1325
|
-
bestPeerForPendingColumns(pendingColumns, excludedPeers) {
|
|
1478
|
+
bestPeerForPendingColumns(pendingColumns, excludedPeers, preferredPeers) {
|
|
1326
1479
|
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
1327
1480
|
if (eligiblePeers.length === 0) {
|
|
1328
1481
|
return null;
|
|
1329
1482
|
}
|
|
1330
1483
|
const sortedEligiblePeers = sortBy(shuffle(eligiblePeers),
|
|
1331
|
-
// prefer peers
|
|
1484
|
+
// 1st key: prefer peers we heard about this root from
|
|
1485
|
+
(peerId) => (preferredPeers?.has(peerId) ? 0 : 1),
|
|
1486
|
+
// 2nd key (tiebreak): prefer peers with least active req
|
|
1332
1487
|
(peerId) => this.activeRequests.get(peerId) ?? 0);
|
|
1333
1488
|
const bestPeerId = sortedEligiblePeers[0];
|
|
1334
1489
|
this.onRequest(bestPeerId);
|