@lodestar/beacon-node 1.47.0-dev.ee197a8d7f → 1.47.0-dev.f0f26cd2b6
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 +44 -12
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +21 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -1
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/events/index.d.ts +1 -1
- package/lib/api/impl/events/index.d.ts.map +1 -1
- package/lib/api/impl/events/index.js +12 -3
- package/lib/api/impl/events/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +232 -49
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/impl/validator/utils.js +1 -1
- package/lib/api/impl/validator/utils.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +7 -0
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +1 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +11 -9
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +21 -13
- 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/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +5 -2
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/bls/index.d.ts +1 -1
- package/lib/chain/bls/index.d.ts.map +1 -1
- package/lib/chain/bls/interface.d.ts +5 -5
- package/lib/chain/bls/interface.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts +5 -11
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +116 -101
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
- package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
- package/lib/chain/bls/multithread/jobItem.js +10 -65
- package/lib/chain/bls/multithread/jobItem.js.map +1 -1
- package/lib/chain/bls/multithread/types.d.ts +29 -8
- package/lib/chain/bls/multithread/types.d.ts.map +1 -1
- package/lib/chain/bls/multithread/types.js +12 -0
- package/lib/chain/bls/multithread/types.js.map +1 -1
- package/lib/chain/bls/multithread/utils.d.ts +4 -0
- package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
- package/lib/chain/bls/multithread/utils.js +10 -0
- package/lib/chain/bls/multithread/utils.js.map +1 -1
- package/lib/chain/bls/multithread/worker.js +49 -23
- package/lib/chain/bls/multithread/worker.js.map +1 -1
- package/lib/chain/bls/singleThread.d.ts +4 -10
- package/lib/chain/bls/singleThread.d.ts.map +1 -1
- package/lib/chain/bls/singleThread.js +8 -45
- package/lib/chain/bls/singleThread.js.map +1 -1
- package/lib/chain/bls/utils.d.ts +4 -4
- package/lib/chain/bls/utils.d.ts.map +1 -1
- package/lib/chain/bls/utils.js +18 -19
- package/lib/chain/bls/utils.js.map +1 -1
- package/lib/chain/chain.d.ts +9 -4
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +22 -8
- 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 +8 -4
- 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/executionPayloadBidPool.d.ts +7 -2
- package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.js +4 -4
- package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
- package/lib/chain/opPools/index.d.ts +2 -1
- package/lib/chain/opPools/index.d.ts.map +1 -1
- package/lib/chain/opPools/index.js +1 -0
- package/lib/chain/opPools/index.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +3 -3
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +1 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +1 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts +5 -0
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +47 -6
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +3 -25
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +5 -2
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +3 -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/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +40 -10
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts +13 -3
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +147 -12
- 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.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +12 -3
- 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/apiClient.d.ts +70 -0
- package/lib/execution/builder/apiClient.d.ts.map +1 -0
- package/lib/execution/builder/apiClient.js +202 -0
- package/lib/execution/builder/apiClient.js.map +1 -0
- package/lib/execution/builder/validateBid.d.ts +22 -0
- package/lib/execution/builder/validateBid.d.ts.map +1 -0
- package/lib/execution/builder/validateBid.js +111 -0
- package/lib/execution/builder/validateBid.js.map +1 -0
- package/lib/metrics/metrics/lodestar.d.ts +84 -23
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +161 -55
- 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/network.d.ts.map +1 -1
- package/lib/network/network.js +23 -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 +6 -2
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipQueues/indexed.d.ts.map +1 -1
- package/lib/network/processor/gossipQueues/indexed.js +0 -1
- package/lib/network/processor/gossipQueues/indexed.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 +52 -23
- 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 +7 -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 +32 -12
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.js +1 -1
- package/lib/sync/range/range.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 +15 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +220 -61
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +16 -11
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +3 -1
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/package.json +35 -35
- package/src/api/impl/beacon/blocks/index.ts +51 -15
- package/src/api/impl/beacon/pool/index.ts +22 -2
- package/src/api/impl/beacon/state/utils.ts +2 -1
- package/src/api/impl/events/index.ts +12 -4
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/validator/index.ts +285 -49
- package/src/api/impl/validator/utils.ts +1 -1
- package/src/api/rest/base.ts +7 -0
- package/src/chain/archiveStore/archiveStore.ts +1 -1
- package/src/chain/blocks/importExecutionPayload.ts +12 -11
- package/src/chain/blocks/index.ts +25 -13
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
- package/src/chain/blocks/verifyBlock.ts +5 -2
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
- package/src/chain/bls/index.ts +1 -1
- package/src/chain/bls/interface.ts +7 -3
- package/src/chain/bls/multithread/index.ts +148 -120
- package/src/chain/bls/multithread/jobItem.ts +12 -82
- package/src/chain/bls/multithread/types.ts +33 -8
- package/src/chain/bls/multithread/utils.ts +11 -0
- package/src/chain/bls/multithread/worker.ts +85 -27
- package/src/chain/bls/singleThread.ts +11 -55
- package/src/chain/bls/utils.ts +23 -24
- package/src/chain/chain.ts +25 -7
- 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 +8 -3
- 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/executionPayloadBidPool.ts +13 -7
- package/src/chain/opPools/index.ts +2 -1
- package/src/chain/opPools/payloadAttestationPool.ts +3 -3
- package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
- package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
- package/src/chain/opPools/utils.ts +1 -1
- package/src/chain/prepareNextSlot.ts +49 -6
- package/src/chain/produceBlock/computeNewStateRoot.ts +3 -38
- package/src/chain/produceBlock/produceBlockBody.ts +1 -1
- package/src/chain/regen/interface.ts +5 -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/block.ts +43 -10
- package/src/chain/validation/executionPayloadBid.ts +168 -14
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
- package/src/chain/validation/payloadAttestationMessage.ts +6 -17
- package/src/chain/validation/proposerPreferences.ts +14 -4
- package/src/chain/validation/proposerSlashing.ts +1 -1
- package/src/chain/validation/voluntaryExit.ts +59 -2
- package/src/execution/builder/apiClient.ts +264 -0
- package/src/execution/builder/validateBid.ts +166 -0
- package/src/metrics/metrics/lodestar.ts +176 -61
- package/src/network/gossip/topic.ts +38 -0
- package/src/network/network.ts +29 -54
- package/src/network/processor/constants.ts +6 -0
- package/src/network/processor/gossipHandlers.ts +6 -2
- package/src/network/processor/gossipQueues/indexed.ts +0 -1
- package/src/network/processor/index.ts +67 -30
- 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 +10 -1
- package/src/sync/range/batch.ts +83 -28
- package/src/sync/range/chain.ts +35 -13
- package/src/sync/range/range.ts +1 -1
- package/src/sync/types.ts +36 -1
- package/src/sync/unknownBlock.ts +258 -68
- package/src/sync/utils/downloadByRange.ts +21 -12
- package/src/sync/utils/downloadByRoot.ts +3 -1
- package/lib/chain/bls/maybeBatch.d.ts +0 -12
- package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
- package/lib/chain/bls/maybeBatch.js +0 -35
- package/lib/chain/bls/maybeBatch.js.map +0 -1
- package/src/chain/bls/maybeBatch.ts +0 -45
package/lib/sync/unknownBlock.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { routes } from "@lodestar/api";
|
|
2
|
-
import { ForkSeq, isForkPostGloas } from "@lodestar/params";
|
|
2
|
+
import { ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas } from "@lodestar/params";
|
|
3
3
|
import { RequestError, RequestErrorCode } from "@lodestar/reqresp";
|
|
4
4
|
import { computeTimeAtSlot } from "@lodestar/state-transition";
|
|
5
5
|
import { fromHex, prettyPrintIndices, pruneSetToMax, sleep, toRootHex } from "@lodestar/utils";
|
|
@@ -12,17 +12,22 @@ import { ChainEvent } from "../chain/index.js";
|
|
|
12
12
|
import { validateGloasBlockDataColumnSidecars } from "../chain/validation/dataColumnSidecar.js";
|
|
13
13
|
import { validateGossipExecutionPayloadEnvelope } from "../chain/validation/executionPayloadEnvelope.js";
|
|
14
14
|
import { NetworkEvent, prettyPrintPeerIdStr } from "../network/index.js";
|
|
15
|
+
import { MAX_PEERS_PER_ROOT } from "../network/processor/constants.js";
|
|
15
16
|
import { shuffle } from "../util/shuffle.js";
|
|
16
17
|
import { sortBy } from "../util/sortBy.js";
|
|
17
18
|
import { wrapError } from "../util/wrapError.js";
|
|
18
19
|
import { MAX_CONCURRENT_REQUESTS } from "./constants.js";
|
|
19
|
-
import { PendingBlockInputStatus, PendingBlockType, PendingPayloadInputStatus, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, getPayloadSyncCacheItemRootHex, getPayloadSyncCacheItemSlot, isPendingBlockInput, isPendingPayloadEnvelope, isPendingPayloadInput, } from "./types.js";
|
|
20
|
+
import { DroppedItemReason, PendingBlockInputStatus, PendingBlockType, PendingPayloadInputStatus, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, getPayloadSyncCacheItemRootHex, getPayloadSyncCacheItemSlot, isPendingBlockInput, isPendingPayloadEnvelope, isPendingPayloadInput, } from "./types.js";
|
|
20
21
|
import { DownloadByRootError, downloadByRoot } from "./utils/downloadByRoot.js";
|
|
21
22
|
import { getAllDescendantBlocks, getUnknownAndAncestorBlocks } from "./utils/pendingBlocksTree.js";
|
|
22
23
|
import { getRateLimitedUntilMs } from "./utils/rateLimit.js";
|
|
23
24
|
const MAX_ATTEMPTS_PER_BLOCK = 5;
|
|
24
25
|
const MAX_KNOWN_BAD_BLOCKS = 500;
|
|
25
26
|
const MAX_PENDING_BLOCKS = 100;
|
|
27
|
+
/**
|
|
28
|
+
* A pending payload without slot can only stay at most 2 epochs.
|
|
29
|
+
*/
|
|
30
|
+
const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
|
|
26
31
|
var FetchResult;
|
|
27
32
|
(function (FetchResult) {
|
|
28
33
|
FetchResult["SuccessResolved"] = "success_resolved";
|
|
@@ -115,10 +120,20 @@ 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);
|
|
121
127
|
this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
128
|
+
// Seed the balancer with peers that connected before we subscribed
|
|
129
|
+
for (const peerId of this.network.getConnectedPeers()) {
|
|
130
|
+
try {
|
|
131
|
+
this.peerBalancer.onPeerConnected(peerId, this.network.getConnectedPeerSyncMeta(peerId));
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
this.logger.debug("Failed to seed connected peer into BlockInputSync balancer", { peerId }, e);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
122
137
|
this.subscribedToNetworkEvents = true;
|
|
123
138
|
}
|
|
124
139
|
}
|
|
@@ -130,10 +145,12 @@ export class BlockInputSync {
|
|
|
130
145
|
this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
131
146
|
this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
132
147
|
this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
148
|
+
this.chain.emitter.off(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
133
149
|
this.chain.emitter.off(routes.events.EventType.block, this.onBlockImported);
|
|
134
150
|
this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
135
151
|
this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
136
152
|
this.network.events.off(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
153
|
+
this.peerBalancer.clear();
|
|
137
154
|
this.subscribedToNetworkEvents = false;
|
|
138
155
|
}
|
|
139
156
|
close() {
|
|
@@ -147,10 +164,12 @@ export class BlockInputSync {
|
|
|
147
164
|
*/
|
|
148
165
|
onUnknownBlockRoot = (data) => {
|
|
149
166
|
try {
|
|
150
|
-
this.addByRootHex(data.rootHex, data.peer);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
167
|
+
const isNewRoot = this.addByRootHex(data.rootHex, data.peer);
|
|
168
|
+
if (isNewRoot) {
|
|
169
|
+
this.triggerUnknownBlockSearch();
|
|
170
|
+
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_BLOCK_ROOT });
|
|
171
|
+
this.metrics?.blockInputSync.source.inc({ source: data.source });
|
|
172
|
+
}
|
|
154
173
|
}
|
|
155
174
|
catch (e) {
|
|
156
175
|
this.logger.debug("Error handling unknownBlockRoot event", {}, e);
|
|
@@ -172,10 +191,12 @@ export class BlockInputSync {
|
|
|
172
191
|
};
|
|
173
192
|
onUnknownEnvelopeBlockRoot = (data) => {
|
|
174
193
|
try {
|
|
175
|
-
this.addByPayloadRootHex(data.rootHex, data.peer, data.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
194
|
+
const isNewRoot = this.addByPayloadRootHex(data.rootHex, data.peer, this.resolvePayloadSlot(data.rootHex));
|
|
195
|
+
if (isNewRoot) {
|
|
196
|
+
this.triggerUnknownBlockSearch();
|
|
197
|
+
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT });
|
|
198
|
+
this.metrics?.blockInputSync.payloadSource.inc({ source: data.source });
|
|
199
|
+
}
|
|
179
200
|
}
|
|
180
201
|
catch (e) {
|
|
181
202
|
this.logger.debug("Error handling unknownEnvelopeBlockRoot event", {}, e);
|
|
@@ -208,12 +229,12 @@ export class BlockInputSync {
|
|
|
208
229
|
parentRoot: missingDependency.parentRootHex,
|
|
209
230
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
210
231
|
});
|
|
211
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
232
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
212
233
|
}
|
|
213
234
|
return;
|
|
214
235
|
}
|
|
215
236
|
if (missingDependency.kind === "parentPayload") {
|
|
216
|
-
this.addByPayloadRootHex(missingDependency.rootHex, data.peer);
|
|
237
|
+
this.addByPayloadRootHex(missingDependency.rootHex, data.peer, missingDependency.slot);
|
|
217
238
|
}
|
|
218
239
|
else if (missingDependency.kind === "parentBlock") {
|
|
219
240
|
this.addByRootHex(missingDependency.rootHex, data.peer);
|
|
@@ -236,6 +257,71 @@ export class BlockInputSync {
|
|
|
236
257
|
this.pendingPayloads.delete(blockRoot);
|
|
237
258
|
this.triggerUnknownBlockSearch();
|
|
238
259
|
};
|
|
260
|
+
/**
|
|
261
|
+
* Drop pending payloads and blocks that fell below the finalized slot.
|
|
262
|
+
* See https://github.com/ChainSafe/lodestar/issues/9800
|
|
263
|
+
*/
|
|
264
|
+
pruneFinalized = (checkpoint) => {
|
|
265
|
+
// Use the actual finalized block slot, not computeStartSlotAtEpoch(checkpoint.epoch)
|
|
266
|
+
// because the payload at the finalized block is not finalized
|
|
267
|
+
const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
|
|
268
|
+
const nowSec = Date.now() / 1000;
|
|
269
|
+
const maxSecInCache = (PRUNE_UNRESOLVED_SLOT_EPOCHS * SLOTS_PER_EPOCH * this.config.SLOT_DURATION_MS) / 1000;
|
|
270
|
+
let belowFinalizedPayloads = 0;
|
|
271
|
+
let agedOutPayloads = 0;
|
|
272
|
+
let unresolvedSlotPayloads = 0;
|
|
273
|
+
for (const [rootHex, payload] of this.pendingPayloads) {
|
|
274
|
+
// Last-effort resolve for a slot-less PendingPayloadRootHex, and store it back for later passes.
|
|
275
|
+
if (!isPendingPayloadInput(payload) && !isPendingPayloadEnvelope(payload) && payload.slot === undefined) {
|
|
276
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
277
|
+
if (payload.slot === undefined) {
|
|
278
|
+
// Still unresolvable: age it out so the MAX_PENDING_BLOCKS cap cannot stay saturated.
|
|
279
|
+
if (nowSec - payload.timeAddedSec > maxSecInCache) {
|
|
280
|
+
this.pendingPayloads.delete(rootHex);
|
|
281
|
+
agedOutPayloads++;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
unresolvedSlotPayloads++;
|
|
285
|
+
this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
|
|
286
|
+
root: rootHex,
|
|
287
|
+
finalizedSlot,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const slot = getPayloadSyncCacheItemSlot(payload);
|
|
294
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
295
|
+
this.pendingPayloads.delete(rootHex);
|
|
296
|
+
belowFinalizedPayloads++;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
let deletedBlocks = 0;
|
|
300
|
+
for (const [rootHex, block] of this.pendingBlocks) {
|
|
301
|
+
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
302
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
303
|
+
this.pendingBlocks.delete(rootHex);
|
|
304
|
+
deletedBlocks++;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (belowFinalizedPayloads > 0) {
|
|
308
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.belowFinalized }, belowFinalizedPayloads);
|
|
309
|
+
}
|
|
310
|
+
if (agedOutPayloads > 0) {
|
|
311
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.agedOut }, agedOutPayloads);
|
|
312
|
+
}
|
|
313
|
+
if (deletedBlocks > 0) {
|
|
314
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.belowFinalized }, deletedBlocks);
|
|
315
|
+
}
|
|
316
|
+
this.logger.debug("BlockInputSync.pruneFinalized dropped pre-finalized pending items", {
|
|
317
|
+
finalizedSlot,
|
|
318
|
+
finalizedRoot: checkpoint.rootHex,
|
|
319
|
+
belowFinalizedPayloads,
|
|
320
|
+
agedOutPayloads,
|
|
321
|
+
deletedBlocks,
|
|
322
|
+
unresolvedSlotPayloads,
|
|
323
|
+
});
|
|
324
|
+
};
|
|
239
325
|
addByRootHex = (rootHex, peerIdStr) => {
|
|
240
326
|
let pendingBlock = this.pendingBlocks.get(rootHex);
|
|
241
327
|
let added = false;
|
|
@@ -253,13 +339,16 @@ export class BlockInputSync {
|
|
|
253
339
|
peerIdStr: peerIdStr ?? "unknown peer",
|
|
254
340
|
});
|
|
255
341
|
}
|
|
256
|
-
|
|
342
|
+
// bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
|
|
343
|
+
// Once at the cap, ignore further peers rather than evicting already-tracked ones.
|
|
344
|
+
if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
257
345
|
pendingBlock.peerIdStrings.add(peerIdStr);
|
|
258
346
|
}
|
|
259
347
|
// TODO: check this prune methodology
|
|
260
348
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
261
349
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
262
350
|
if (prunedItemCount > 0) {
|
|
351
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
263
352
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
264
353
|
}
|
|
265
354
|
return added;
|
|
@@ -283,13 +372,16 @@ export class BlockInputSync {
|
|
|
283
372
|
delaySec: this.chain.clock.secFromSlot(blockInput.slot),
|
|
284
373
|
});
|
|
285
374
|
}
|
|
286
|
-
|
|
375
|
+
// bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
|
|
376
|
+
// Once at the cap, ignore further peers rather than evicting already-tracked ones.
|
|
377
|
+
if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
287
378
|
pendingBlock.peerIdStrings.add(peerIdStr);
|
|
288
379
|
}
|
|
289
380
|
// TODO: check this prune methodology
|
|
290
381
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
291
382
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
292
383
|
if (prunedItemCount > 0) {
|
|
384
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
293
385
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
294
386
|
}
|
|
295
387
|
};
|
|
@@ -313,18 +405,47 @@ export class BlockInputSync {
|
|
|
313
405
|
peerIdStr: peerIdStr ?? "unknown peer",
|
|
314
406
|
});
|
|
315
407
|
}
|
|
316
|
-
if (
|
|
408
|
+
else if (
|
|
409
|
+
// update a previously slot-less entry once a slot becomes available
|
|
410
|
+
slot !== undefined &&
|
|
411
|
+
!isPendingPayloadInput(pendingPayload) &&
|
|
412
|
+
!isPendingPayloadEnvelope(pendingPayload) &&
|
|
413
|
+
pendingPayload.slot === undefined) {
|
|
414
|
+
pendingPayload.slot = slot;
|
|
415
|
+
}
|
|
416
|
+
if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
317
417
|
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
318
418
|
}
|
|
319
419
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
320
420
|
if (prunedItemCount > 0) {
|
|
421
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
321
422
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
322
423
|
}
|
|
323
424
|
return added;
|
|
324
425
|
};
|
|
426
|
+
/**
|
|
427
|
+
* Resolve the block slot for a payload root.
|
|
428
|
+
*/
|
|
429
|
+
resolvePayloadSlot(rootHex) {
|
|
430
|
+
// a pending block of the same root
|
|
431
|
+
const pendingBlock = this.pendingBlocks.get(rootHex);
|
|
432
|
+
if (pendingBlock) {
|
|
433
|
+
const pendingBlockSlot = getBlockInputSyncCacheItemSlot(pendingBlock);
|
|
434
|
+
if (typeof pendingBlockSlot === "number") {
|
|
435
|
+
return pendingBlockSlot;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// the block is in fork choice
|
|
439
|
+
const forkChoiceSlot = this.chain.forkChoice.getBlockHexDefaultStatus(rootHex)?.slot;
|
|
440
|
+
if (forkChoiceSlot !== undefined) {
|
|
441
|
+
return forkChoiceSlot;
|
|
442
|
+
}
|
|
443
|
+
// a seen payload envelope input
|
|
444
|
+
return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
|
|
445
|
+
}
|
|
325
446
|
addByPayloadInput = (payloadInput, peerIdStr, envelope) => {
|
|
326
447
|
const pendingPayload = this.toPendingPayloadInput(payloadInput, this.pendingPayloads.get(payloadInput.blockRootHex), envelope);
|
|
327
|
-
if (peerIdStr) {
|
|
448
|
+
if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
328
449
|
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
329
450
|
}
|
|
330
451
|
this.pendingPayloads.set(payloadInput.blockRootHex, pendingPayload);
|
|
@@ -335,6 +456,7 @@ export class BlockInputSync {
|
|
|
335
456
|
});
|
|
336
457
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
337
458
|
if (prunedItemCount > 0) {
|
|
459
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.capacity }, prunedItemCount);
|
|
338
460
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
339
461
|
}
|
|
340
462
|
};
|
|
@@ -360,7 +482,8 @@ export class BlockInputSync {
|
|
|
360
482
|
*/
|
|
361
483
|
getMissingBlockDependency(blockInput) {
|
|
362
484
|
const parentRootHex = blockInput.parentRootHex;
|
|
363
|
-
|
|
485
|
+
const parentBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRootHex);
|
|
486
|
+
if (!parentBlock) {
|
|
364
487
|
return { kind: "parentBlock", rootHex: parentRootHex };
|
|
365
488
|
}
|
|
366
489
|
if (!blockInput.hasBlock()) {
|
|
@@ -377,14 +500,8 @@ export class BlockInputSync {
|
|
|
377
500
|
if (this.chain.forkChoice.hasPayloadHexUnsafe(parentRootHex)) {
|
|
378
501
|
return { kind: "invalidParentPayload", parentRootHex, parentBlockHashHex };
|
|
379
502
|
}
|
|
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 };
|
|
503
|
+
// Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
|
|
504
|
+
return { kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot };
|
|
388
505
|
}
|
|
389
506
|
advancePendingBlock(pendingBlock) {
|
|
390
507
|
const missingDependency = this.getMissingBlockDependency(pendingBlock.blockInput);
|
|
@@ -402,9 +519,9 @@ export class BlockInputSync {
|
|
|
402
519
|
return added ? "queued_parent_block" : "blocked";
|
|
403
520
|
}
|
|
404
521
|
case "parentPayload": {
|
|
405
|
-
let added = this.addByPayloadRootHex(missingDependency.rootHex);
|
|
522
|
+
let added = this.addByPayloadRootHex(missingDependency.rootHex, undefined, missingDependency.slot);
|
|
406
523
|
for (const peerIdStr of pendingBlock.peerIdStrings) {
|
|
407
|
-
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr) || added;
|
|
524
|
+
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr, missingDependency.slot) || added;
|
|
408
525
|
}
|
|
409
526
|
return added ? "queued_parent_payload" : "blocked";
|
|
410
527
|
}
|
|
@@ -415,7 +532,7 @@ export class BlockInputSync {
|
|
|
415
532
|
parentRoot: missingDependency.parentRootHex,
|
|
416
533
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
417
534
|
});
|
|
418
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
535
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
419
536
|
return "removed";
|
|
420
537
|
}
|
|
421
538
|
}
|
|
@@ -604,7 +721,7 @@ export class BlockInputSync {
|
|
|
604
721
|
...logCtx2,
|
|
605
722
|
finalizedSlot,
|
|
606
723
|
});
|
|
607
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
724
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
|
|
608
725
|
}
|
|
609
726
|
else {
|
|
610
727
|
this.onUnknownBlockRoot({ rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot });
|
|
@@ -622,7 +739,7 @@ export class BlockInputSync {
|
|
|
622
739
|
}
|
|
623
740
|
this.metrics?.blockInputSync.downloadedBlocksError.inc();
|
|
624
741
|
this.logger.debug("Ignoring unknown block root after many failed downloads", logCtx, res.err);
|
|
625
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
742
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.unavailable);
|
|
626
743
|
}
|
|
627
744
|
}
|
|
628
745
|
/**
|
|
@@ -679,9 +796,9 @@ export class BlockInputSync {
|
|
|
679
796
|
// no need to update status to "processed", delete anyway
|
|
680
797
|
this.pendingBlocks.delete(blockRootHex);
|
|
681
798
|
if (isForkPostGloas(fork)) {
|
|
682
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.
|
|
799
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
|
|
683
800
|
if (!payloadInput) {
|
|
684
|
-
this.logger.
|
|
801
|
+
this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
|
|
685
802
|
}
|
|
686
803
|
else {
|
|
687
804
|
// Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
|
|
@@ -717,17 +834,17 @@ export class BlockInputSync {
|
|
|
717
834
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
718
835
|
// Removing the block(s) without penalizing the peers, hoping for EL to
|
|
719
836
|
// recover on a latter download + verify attempt.
|
|
720
|
-
this.removeAllDescendants(pendingBlock);
|
|
837
|
+
this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
|
|
721
838
|
break;
|
|
722
839
|
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
723
840
|
// the peer served a bad block
|
|
724
841
|
this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
|
|
725
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
842
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
|
|
726
843
|
break;
|
|
727
844
|
default:
|
|
728
845
|
// Block is not correct with respect to our chain. Log error loudly
|
|
729
846
|
this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
|
|
730
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
847
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
|
|
731
848
|
}
|
|
732
849
|
}
|
|
733
850
|
// Probably a queue error or something unwanted happened, mark as pending to try again latter
|
|
@@ -748,7 +865,6 @@ export class BlockInputSync {
|
|
|
748
865
|
this.pendingPayloads.delete(rootHex);
|
|
749
866
|
return;
|
|
750
867
|
}
|
|
751
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
752
868
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
753
869
|
// Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
|
|
754
870
|
// non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
|
|
@@ -762,8 +878,9 @@ export class BlockInputSync {
|
|
|
762
878
|
}
|
|
763
879
|
return;
|
|
764
880
|
}
|
|
881
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
765
882
|
if (!payloadInput) {
|
|
766
|
-
this.logger.debug("
|
|
883
|
+
this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
|
|
767
884
|
root: rootHex,
|
|
768
885
|
});
|
|
769
886
|
return;
|
|
@@ -775,6 +892,7 @@ export class BlockInputSync {
|
|
|
775
892
|
const pendingPayloadByRoot = {
|
|
776
893
|
status: PendingPayloadInputStatus.pending,
|
|
777
894
|
rootHex,
|
|
895
|
+
slot: pendingPayload.envelope.message.payload.slotNumber,
|
|
778
896
|
timeAddedSec: pendingPayload.timeAddedSec,
|
|
779
897
|
peerIdStrings: new Set(pendingPayload.peerIdStrings),
|
|
780
898
|
};
|
|
@@ -806,6 +924,9 @@ export class BlockInputSync {
|
|
|
806
924
|
if (payload.status !== PendingPayloadInputStatus.pending) {
|
|
807
925
|
return;
|
|
808
926
|
}
|
|
927
|
+
if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
|
|
928
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
929
|
+
}
|
|
809
930
|
const payloadSlot = getPayloadSyncCacheItemSlot(payload);
|
|
810
931
|
const logCtx = {
|
|
811
932
|
slot: payloadSlot,
|
|
@@ -818,7 +939,13 @@ export class BlockInputSync {
|
|
|
818
939
|
const res = await wrapError(this.fetchPayloadInput(payload));
|
|
819
940
|
if (!res.err) {
|
|
820
941
|
const pendingPayload = res.result;
|
|
821
|
-
|
|
942
|
+
const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
943
|
+
// finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
|
|
944
|
+
if (!this.pendingPayloads.has(resultRootHex)) {
|
|
945
|
+
this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
this.pendingPayloads.set(resultRootHex, pendingPayload);
|
|
822
949
|
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
823
950
|
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
824
951
|
}
|
|
@@ -885,16 +1012,24 @@ export class BlockInputSync {
|
|
|
885
1012
|
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
886
1013
|
break;
|
|
887
1014
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1015
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1016
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1017
|
+
break;
|
|
888
1018
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1019
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1020
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1021
|
+
break;
|
|
889
1022
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
890
1023
|
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
891
1024
|
// reintroducing envelope replacement / recreation flows.
|
|
892
1025
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
893
|
-
this.removePendingPayloadAndDescendants(rootHex);
|
|
1026
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
894
1027
|
break;
|
|
895
1028
|
default:
|
|
896
1029
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
897
|
-
this.pendingPayloads.delete(rootHex)
|
|
1030
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1031
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.unknown }, 1);
|
|
1032
|
+
}
|
|
898
1033
|
}
|
|
899
1034
|
return;
|
|
900
1035
|
}
|
|
@@ -913,7 +1048,7 @@ export class BlockInputSync {
|
|
|
913
1048
|
let slot = getPayloadSyncCacheItemSlot(cacheItem);
|
|
914
1049
|
let payloadInput = isPendingPayloadInput(cacheItem)
|
|
915
1050
|
? cacheItem.payloadInput
|
|
916
|
-
: this.chain.seenPayloadEnvelopeInputCache.
|
|
1051
|
+
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
917
1052
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
918
1053
|
let i = 0;
|
|
919
1054
|
let deferredByRateLimit = false;
|
|
@@ -921,12 +1056,16 @@ export class BlockInputSync {
|
|
|
921
1056
|
const pendingColumns = payloadInput?.hasAllData()
|
|
922
1057
|
? new Set()
|
|
923
1058
|
: new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
|
|
924
|
-
|
|
1059
|
+
// prefer peers that gossiped this payload root to us (#9923)
|
|
1060
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers, cacheItem.peerIdStrings);
|
|
925
1061
|
if (peerMeta === null) {
|
|
926
1062
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
927
1063
|
throw new UnknownBlockRateLimitedError(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`);
|
|
928
1064
|
}
|
|
929
|
-
|
|
1065
|
+
const reason = pendingColumns.size > 0
|
|
1066
|
+
? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
|
|
1067
|
+
: "no peer available to download pending payload";
|
|
1068
|
+
throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
|
|
930
1069
|
}
|
|
931
1070
|
const { peerId, client: peerClient } = peerMeta;
|
|
932
1071
|
cacheItem.peerIdStrings.add(peerId);
|
|
@@ -935,7 +1074,7 @@ export class BlockInputSync {
|
|
|
935
1074
|
envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
|
|
936
1075
|
slot = envelope.message.payload.slotNumber;
|
|
937
1076
|
}
|
|
938
|
-
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.
|
|
1077
|
+
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
939
1078
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
940
1079
|
// Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
|
|
941
1080
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
@@ -1080,7 +1219,8 @@ export class BlockInputSync {
|
|
|
1080
1219
|
const pendingColumns = isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
|
|
1081
1220
|
? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
|
|
1082
1221
|
: defaultPendingColumns;
|
|
1083
|
-
|
|
1222
|
+
// prefer peers that gossiped this block root to us (#9923)
|
|
1223
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers, cacheItem.peerIdStrings);
|
|
1084
1224
|
if (peerMeta === null) {
|
|
1085
1225
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
1086
1226
|
throw new UnknownBlockRateLimitedError(`Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`);
|
|
@@ -1206,9 +1346,9 @@ export class BlockInputSync {
|
|
|
1206
1346
|
* Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
|
|
1207
1347
|
* referenced more than one bad block.
|
|
1208
1348
|
*/
|
|
1209
|
-
removeAndDownScoreAllDescendants(block) {
|
|
1349
|
+
removeAndDownScoreAllDescendants(block, headReason) {
|
|
1210
1350
|
// Get all blocks that are a descendant of this one
|
|
1211
|
-
const badPendingBlocks = this.removeAllDescendants(block);
|
|
1351
|
+
const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
|
|
1212
1352
|
// just console log and do not penalize on pending/bad blocks for debugging
|
|
1213
1353
|
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
1214
1354
|
for (const block of badPendingBlocks) {
|
|
@@ -1232,16 +1372,21 @@ export class BlockInputSync {
|
|
|
1232
1372
|
pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
|
|
1233
1373
|
}
|
|
1234
1374
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1235
|
-
removePendingPayloadAndDescendants(rootHex) {
|
|
1375
|
+
removePendingPayloadAndDescendants(rootHex, headReason) {
|
|
1236
1376
|
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1237
1377
|
// later validation/finalization logic decides when it can leave memory.
|
|
1238
|
-
this.pendingPayloads.delete(rootHex)
|
|
1378
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1379
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: headReason }, 1);
|
|
1380
|
+
}
|
|
1239
1381
|
const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
|
|
1240
|
-
this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
|
|
1241
1382
|
for (const block of badPendingBlocks) {
|
|
1242
1383
|
const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1243
|
-
this.pendingBlocks.delete(descendantRootHex)
|
|
1244
|
-
|
|
1384
|
+
if (this.pendingBlocks.delete(descendantRootHex)) {
|
|
1385
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.invalidParent }, 1);
|
|
1386
|
+
}
|
|
1387
|
+
if (this.pendingPayloads.delete(descendantRootHex)) {
|
|
1388
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.invalidParent }, 1);
|
|
1389
|
+
}
|
|
1245
1390
|
this.chain.seenBlockInputCache.prune(descendantRootHex);
|
|
1246
1391
|
this.logger.debug("Removing pending descendant after invalid parent payload", {
|
|
1247
1392
|
slot: getBlockInputSyncCacheItemSlot(block),
|
|
@@ -1250,16 +1395,20 @@ export class BlockInputSync {
|
|
|
1250
1395
|
});
|
|
1251
1396
|
}
|
|
1252
1397
|
}
|
|
1253
|
-
removeAllDescendants(block) {
|
|
1398
|
+
removeAllDescendants(block, headReason, descendantReason = DroppedItemReason.invalidParent) {
|
|
1254
1399
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1255
1400
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
1256
|
-
// Get all blocks that are a descendant of this one
|
|
1401
|
+
// Get all blocks that are a descendant of this one (index 0 is the head item itself)
|
|
1257
1402
|
const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
|
|
1258
|
-
|
|
1259
|
-
for (const block of badPendingBlocks) {
|
|
1403
|
+
for (const [i, block] of badPendingBlocks.entries()) {
|
|
1260
1404
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1261
|
-
|
|
1262
|
-
this.
|
|
1405
|
+
const reason = i === 0 ? headReason : descendantReason;
|
|
1406
|
+
if (this.pendingBlocks.delete(rootHex)) {
|
|
1407
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason }, 1);
|
|
1408
|
+
}
|
|
1409
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1410
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason }, 1);
|
|
1411
|
+
}
|
|
1263
1412
|
this.chain.seenBlockInputCache.prune(rootHex);
|
|
1264
1413
|
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1265
1414
|
// importBlock()-owned lifecycle.
|
|
@@ -1303,6 +1452,12 @@ export class UnknownBlockPeerBalancer {
|
|
|
1303
1452
|
this.activeRequests.delete(peerId);
|
|
1304
1453
|
this.rateLimitedUntilByPeer.delete(peerId);
|
|
1305
1454
|
}
|
|
1455
|
+
/** Reset all tracked peer state, e.g. when BlockInputSync unsubscribes from the network. */
|
|
1456
|
+
clear() {
|
|
1457
|
+
this.peersMeta.clear();
|
|
1458
|
+
this.activeRequests.clear();
|
|
1459
|
+
this.rateLimitedUntilByPeer.clear();
|
|
1460
|
+
}
|
|
1306
1461
|
onRateLimited(peerId, rateLimitedUntilMs) {
|
|
1307
1462
|
this.rateLimitedUntilByPeer.set(peerId, rateLimitedUntilMs);
|
|
1308
1463
|
}
|
|
@@ -1330,17 +1485,21 @@ export class UnknownBlockPeerBalancer {
|
|
|
1330
1485
|
return retryAt;
|
|
1331
1486
|
}
|
|
1332
1487
|
/**
|
|
1333
|
-
* called from fetchBlockInput()
|
|
1488
|
+
* called from fetchBlockInput()/fetchPayloadInput() with a block/payload root.
|
|
1334
1489
|
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
1335
1490
|
* pendingColumns is empty for prefulu, or the 1st time we we download a block by root
|
|
1491
|
+
* preferredPeers are the peers NetworkProcessor told us gossiped this root (from
|
|
1492
|
+
* pendingBlock/cacheItem.peerIdStrings). They are preferred but not required.
|
|
1336
1493
|
*/
|
|
1337
|
-
bestPeerForPendingColumns(pendingColumns, excludedPeers) {
|
|
1494
|
+
bestPeerForPendingColumns(pendingColumns, excludedPeers, preferredPeers) {
|
|
1338
1495
|
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
1339
1496
|
if (eligiblePeers.length === 0) {
|
|
1340
1497
|
return null;
|
|
1341
1498
|
}
|
|
1342
1499
|
const sortedEligiblePeers = sortBy(shuffle(eligiblePeers),
|
|
1343
|
-
// prefer peers
|
|
1500
|
+
// 1st key: prefer peers we heard about this root from
|
|
1501
|
+
(peerId) => (preferredPeers?.has(peerId) ? 0 : 1),
|
|
1502
|
+
// 2nd key (tiebreak): prefer peers with least active req
|
|
1344
1503
|
(peerId) => this.activeRequests.get(peerId) ?? 0);
|
|
1345
1504
|
const bestPeerId = sortedEligiblePeers[0];
|
|
1346
1505
|
this.onRequest(bestPeerId);
|