@lodestar/beacon-node 1.47.0-dev.d9c16ea16f → 1.47.0-dev.e73f1f5165
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.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +12 -22
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/impl/validator/utils.d.ts +5 -0
- package/lib/api/impl/validator/utils.d.ts.map +1 -1
- package/lib/api/impl/validator/utils.js +18 -12
- 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/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 +15 -7
- 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 +5 -3
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +18 -7
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +2 -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/produceBlock/produceBlockBody.d.ts +2 -0
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +6 -1
- package/lib/chain/produceBlock/produceBlockBody.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/seenBlockProposers.d.ts +5 -1
- package/lib/chain/seenCache/seenBlockProposers.d.ts.map +1 -1
- package/lib/chain/seenCache/seenBlockProposers.js +10 -3
- package/lib/chain/seenCache/seenBlockProposers.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 +21 -7
- package/lib/chain/validation/block.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 +37 -6
- 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 +15 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +234 -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 +22 -27
- package/src/api/impl/validator/utils.ts +30 -15
- 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 +19 -7
- 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 +19 -6
- package/src/chain/errors/blockError.ts +9 -3
- 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/produceBlock/produceBlockBody.ts +13 -0
- 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/seenBlockProposers.ts +13 -3
- 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 +21 -7
- 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 +40 -4
- 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 +273 -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,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
|
/**
|
|
@@ -637,9 +754,10 @@ export class BlockInputSync {
|
|
|
637
754
|
// this prevents unbundling attack
|
|
638
755
|
// see https://lighthouse-blog.sigmaprime.io/mev-unbundling-rpc.html
|
|
639
756
|
const { slot: blockSlot, proposerIndex } = pendingBlock.blockInput.getBlock().message;
|
|
757
|
+
const blockRootHex = pendingBlock.blockInput.blockRootHex;
|
|
640
758
|
const logCtx = {
|
|
641
759
|
slot: blockSlot,
|
|
642
|
-
root:
|
|
760
|
+
root: blockRootHex,
|
|
643
761
|
delaySec: this.chain.clock.secFromSlot(blockSlot),
|
|
644
762
|
};
|
|
645
763
|
this.logger.verbose("Processing downloaded block", logCtx);
|
|
@@ -651,7 +769,7 @@ export class BlockInputSync {
|
|
|
651
769
|
// eligible for proposer boost to prevent unbundling attack
|
|
652
770
|
this.logger.verbose("Avoid proposer boost for this block of known proposer", {
|
|
653
771
|
slot: blockSlot,
|
|
654
|
-
root:
|
|
772
|
+
root: blockRootHex,
|
|
655
773
|
proposerIndex,
|
|
656
774
|
});
|
|
657
775
|
await sleep(proposerBoostWindowMs);
|
|
@@ -676,13 +794,24 @@ export class BlockInputSync {
|
|
|
676
794
|
if (!res.err) {
|
|
677
795
|
this.logger.verbose("Processed block from unknown sync", logCtx);
|
|
678
796
|
// no need to update status to "processed", delete anyway
|
|
679
|
-
this.pendingBlocks.delete(
|
|
797
|
+
this.pendingBlocks.delete(blockRootHex);
|
|
798
|
+
if (isForkPostGloas(fork)) {
|
|
799
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
|
|
800
|
+
if (!payloadInput) {
|
|
801
|
+
this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
// Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
|
|
805
|
+
// The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
|
|
806
|
+
this.chain.getBlobsTracker.triggerGetBlobs(payloadInput);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
680
809
|
// Re-enter the scheduler so descendants blocked on either parent blocks or parent payloads
|
|
681
810
|
// are advanced through the same dependency checks as every other pending item.
|
|
682
811
|
this.triggerUnknownBlockSearch();
|
|
683
812
|
}
|
|
684
813
|
else {
|
|
685
|
-
const errorData = { slot: pendingBlock.blockInput.slot, root:
|
|
814
|
+
const errorData = { slot: pendingBlock.blockInput.slot, root: blockRootHex };
|
|
686
815
|
if (res.err instanceof BlockError) {
|
|
687
816
|
switch (res.err.type.code) {
|
|
688
817
|
// This cases are already handled with `{ignoreIfKnown: true}`
|
|
@@ -705,17 +834,17 @@ export class BlockInputSync {
|
|
|
705
834
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
706
835
|
// Removing the block(s) without penalizing the peers, hoping for EL to
|
|
707
836
|
// recover on a latter download + verify attempt.
|
|
708
|
-
this.removeAllDescendants(pendingBlock);
|
|
837
|
+
this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
|
|
709
838
|
break;
|
|
710
839
|
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
711
840
|
// the peer served a bad block
|
|
712
841
|
this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
|
|
713
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
842
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
|
|
714
843
|
break;
|
|
715
844
|
default:
|
|
716
845
|
// Block is not correct with respect to our chain. Log error loudly
|
|
717
846
|
this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
|
|
718
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
847
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
|
|
719
848
|
}
|
|
720
849
|
}
|
|
721
850
|
// Probably a queue error or something unwanted happened, mark as pending to try again latter
|
|
@@ -736,7 +865,6 @@ export class BlockInputSync {
|
|
|
736
865
|
this.pendingPayloads.delete(rootHex);
|
|
737
866
|
return;
|
|
738
867
|
}
|
|
739
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
740
868
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
741
869
|
// Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
|
|
742
870
|
// non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
|
|
@@ -750,8 +878,9 @@ export class BlockInputSync {
|
|
|
750
878
|
}
|
|
751
879
|
return;
|
|
752
880
|
}
|
|
881
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
753
882
|
if (!payloadInput) {
|
|
754
|
-
this.logger.debug("
|
|
883
|
+
this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
|
|
755
884
|
root: rootHex,
|
|
756
885
|
});
|
|
757
886
|
return;
|
|
@@ -763,6 +892,7 @@ export class BlockInputSync {
|
|
|
763
892
|
const pendingPayloadByRoot = {
|
|
764
893
|
status: PendingPayloadInputStatus.pending,
|
|
765
894
|
rootHex,
|
|
895
|
+
slot: pendingPayload.envelope.message.payload.slotNumber,
|
|
766
896
|
timeAddedSec: pendingPayload.timeAddedSec,
|
|
767
897
|
peerIdStrings: new Set(pendingPayload.peerIdStrings),
|
|
768
898
|
};
|
|
@@ -794,6 +924,9 @@ export class BlockInputSync {
|
|
|
794
924
|
if (payload.status !== PendingPayloadInputStatus.pending) {
|
|
795
925
|
return;
|
|
796
926
|
}
|
|
927
|
+
if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
|
|
928
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
929
|
+
}
|
|
797
930
|
const payloadSlot = getPayloadSyncCacheItemSlot(payload);
|
|
798
931
|
const logCtx = {
|
|
799
932
|
slot: payloadSlot,
|
|
@@ -806,7 +939,13 @@ export class BlockInputSync {
|
|
|
806
939
|
const res = await wrapError(this.fetchPayloadInput(payload));
|
|
807
940
|
if (!res.err) {
|
|
808
941
|
const pendingPayload = res.result;
|
|
809
|
-
|
|
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);
|
|
810
949
|
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
811
950
|
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
812
951
|
}
|
|
@@ -873,16 +1012,24 @@ export class BlockInputSync {
|
|
|
873
1012
|
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
874
1013
|
break;
|
|
875
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;
|
|
876
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;
|
|
877
1022
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
878
1023
|
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
879
1024
|
// reintroducing envelope replacement / recreation flows.
|
|
880
1025
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
881
|
-
this.removePendingPayloadAndDescendants(rootHex);
|
|
1026
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
882
1027
|
break;
|
|
883
1028
|
default:
|
|
884
1029
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
885
|
-
this.pendingPayloads.delete(rootHex)
|
|
1030
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1031
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.unknown }, 1);
|
|
1032
|
+
}
|
|
886
1033
|
}
|
|
887
1034
|
return;
|
|
888
1035
|
}
|
|
@@ -901,7 +1048,7 @@ export class BlockInputSync {
|
|
|
901
1048
|
let slot = getPayloadSyncCacheItemSlot(cacheItem);
|
|
902
1049
|
let payloadInput = isPendingPayloadInput(cacheItem)
|
|
903
1050
|
? cacheItem.payloadInput
|
|
904
|
-
: this.chain.seenPayloadEnvelopeInputCache.
|
|
1051
|
+
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
905
1052
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
906
1053
|
let i = 0;
|
|
907
1054
|
let deferredByRateLimit = false;
|
|
@@ -909,12 +1056,16 @@ export class BlockInputSync {
|
|
|
909
1056
|
const pendingColumns = payloadInput?.hasAllData()
|
|
910
1057
|
? new Set()
|
|
911
1058
|
: new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
|
|
912
|
-
|
|
1059
|
+
// prefer peers that gossiped this payload root to us (#9923)
|
|
1060
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers, cacheItem.peerIdStrings);
|
|
913
1061
|
if (peerMeta === null) {
|
|
914
1062
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
915
1063
|
throw new UnknownBlockRateLimitedError(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`);
|
|
916
1064
|
}
|
|
917
|
-
|
|
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}`);
|
|
918
1069
|
}
|
|
919
1070
|
const { peerId, client: peerClient } = peerMeta;
|
|
920
1071
|
cacheItem.peerIdStrings.add(peerId);
|
|
@@ -923,7 +1074,7 @@ export class BlockInputSync {
|
|
|
923
1074
|
envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
|
|
924
1075
|
slot = envelope.message.payload.slotNumber;
|
|
925
1076
|
}
|
|
926
|
-
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.
|
|
1077
|
+
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
927
1078
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
928
1079
|
// Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
|
|
929
1080
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
@@ -1068,7 +1219,8 @@ export class BlockInputSync {
|
|
|
1068
1219
|
const pendingColumns = isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
|
|
1069
1220
|
? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
|
|
1070
1221
|
: defaultPendingColumns;
|
|
1071
|
-
|
|
1222
|
+
// prefer peers that gossiped this block root to us (#9923)
|
|
1223
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers, cacheItem.peerIdStrings);
|
|
1072
1224
|
if (peerMeta === null) {
|
|
1073
1225
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
1074
1226
|
throw new UnknownBlockRateLimitedError(`Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`);
|
|
@@ -1194,9 +1346,9 @@ export class BlockInputSync {
|
|
|
1194
1346
|
* Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
|
|
1195
1347
|
* referenced more than one bad block.
|
|
1196
1348
|
*/
|
|
1197
|
-
removeAndDownScoreAllDescendants(block) {
|
|
1349
|
+
removeAndDownScoreAllDescendants(block, headReason) {
|
|
1198
1350
|
// Get all blocks that are a descendant of this one
|
|
1199
|
-
const badPendingBlocks = this.removeAllDescendants(block);
|
|
1351
|
+
const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
|
|
1200
1352
|
// just console log and do not penalize on pending/bad blocks for debugging
|
|
1201
1353
|
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
1202
1354
|
for (const block of badPendingBlocks) {
|
|
@@ -1220,16 +1372,21 @@ export class BlockInputSync {
|
|
|
1220
1372
|
pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
|
|
1221
1373
|
}
|
|
1222
1374
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1223
|
-
removePendingPayloadAndDescendants(rootHex) {
|
|
1375
|
+
removePendingPayloadAndDescendants(rootHex, headReason) {
|
|
1224
1376
|
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1225
1377
|
// later validation/finalization logic decides when it can leave memory.
|
|
1226
|
-
this.pendingPayloads.delete(rootHex)
|
|
1378
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1379
|
+
this.metrics?.blockInputSync.removedPayloads.inc({ reason: headReason }, 1);
|
|
1380
|
+
}
|
|
1227
1381
|
const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
|
|
1228
|
-
this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
|
|
1229
1382
|
for (const block of badPendingBlocks) {
|
|
1230
1383
|
const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1231
|
-
this.pendingBlocks.delete(descendantRootHex)
|
|
1232
|
-
|
|
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
|
+
}
|
|
1233
1390
|
this.chain.seenBlockInputCache.prune(descendantRootHex);
|
|
1234
1391
|
this.logger.debug("Removing pending descendant after invalid parent payload", {
|
|
1235
1392
|
slot: getBlockInputSyncCacheItemSlot(block),
|
|
@@ -1238,16 +1395,20 @@ export class BlockInputSync {
|
|
|
1238
1395
|
});
|
|
1239
1396
|
}
|
|
1240
1397
|
}
|
|
1241
|
-
removeAllDescendants(block) {
|
|
1398
|
+
removeAllDescendants(block, headReason, descendantReason = DroppedItemReason.invalidParent) {
|
|
1242
1399
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1243
1400
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
1244
|
-
// 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)
|
|
1245
1402
|
const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
|
|
1246
|
-
|
|
1247
|
-
for (const block of badPendingBlocks) {
|
|
1403
|
+
for (const [i, block] of badPendingBlocks.entries()) {
|
|
1248
1404
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1249
|
-
|
|
1250
|
-
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
|
+
}
|
|
1251
1412
|
this.chain.seenBlockInputCache.prune(rootHex);
|
|
1252
1413
|
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1253
1414
|
// importBlock()-owned lifecycle.
|
|
@@ -1291,6 +1452,12 @@ export class UnknownBlockPeerBalancer {
|
|
|
1291
1452
|
this.activeRequests.delete(peerId);
|
|
1292
1453
|
this.rateLimitedUntilByPeer.delete(peerId);
|
|
1293
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
|
+
}
|
|
1294
1461
|
onRateLimited(peerId, rateLimitedUntilMs) {
|
|
1295
1462
|
this.rateLimitedUntilByPeer.set(peerId, rateLimitedUntilMs);
|
|
1296
1463
|
}
|
|
@@ -1318,17 +1485,21 @@ export class UnknownBlockPeerBalancer {
|
|
|
1318
1485
|
return retryAt;
|
|
1319
1486
|
}
|
|
1320
1487
|
/**
|
|
1321
|
-
* called from fetchBlockInput()
|
|
1488
|
+
* called from fetchBlockInput()/fetchPayloadInput() with a block/payload root.
|
|
1322
1489
|
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
1323
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.
|
|
1324
1493
|
*/
|
|
1325
|
-
bestPeerForPendingColumns(pendingColumns, excludedPeers) {
|
|
1494
|
+
bestPeerForPendingColumns(pendingColumns, excludedPeers, preferredPeers) {
|
|
1326
1495
|
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
1327
1496
|
if (eligiblePeers.length === 0) {
|
|
1328
1497
|
return null;
|
|
1329
1498
|
}
|
|
1330
1499
|
const sortedEligiblePeers = sortBy(shuffle(eligiblePeers),
|
|
1331
|
-
// 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
|
|
1332
1503
|
(peerId) => this.activeRequests.get(peerId) ?? 0);
|
|
1333
1504
|
const bestPeerId = sortedEligiblePeers[0];
|
|
1334
1505
|
this.onRequest(bestPeerId);
|