@lodestar/beacon-node 1.47.0-dev.d2f4b96574 → 1.47.0-dev.d79d81f994
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 +47 -18
- 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.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +12 -13
- 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 +238 -65
- 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 +10 -5
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +24 -9
- 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 +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/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 +25 -7
- 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.d.ts +2 -0
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +7 -2
- 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/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 +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 +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/apiClient.d.ts +64 -0
- package/lib/execution/builder/apiClient.d.ts.map +1 -0
- package/lib/execution/builder/apiClient.js +184 -0
- package/lib/execution/builder/apiClient.js.map +1 -0
- 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/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/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 +88 -23
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +164 -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 +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 +38 -7
- 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 +41 -17
- 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 +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 +55 -21
- 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 +12 -13
- package/src/api/impl/validator/index.ts +300 -69
- 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 +27 -7
- 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 +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/options.ts +1 -1
- package/src/chain/prepareNextSlot.ts +25 -7
- package/src/chain/produceBlock/computeNewStateRoot.ts +3 -38
- package/src/chain/produceBlock/produceBlockBody.ts +14 -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/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 +168 -14
- 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/apiClient.ts +244 -0
- package/src/execution/builder/http.ts +11 -11
- package/src/execution/builder/validateBid.ts +166 -0
- package/src/metrics/metrics/beacon.ts +9 -9
- package/src/metrics/metrics/lodestar.ts +178 -58
- package/src/network/gossip/topic.ts +38 -0
- package/src/network/libp2p/index.ts +11 -2
- package/src/network/network.ts +29 -54
- package/src/network/processor/constants.ts +6 -0
- package/src/network/processor/gossipHandlers.ts +41 -5
- package/src/network/processor/gossipQueues/indexed.ts +0 -1
- package/src/network/processor/index.ts +53 -21
- 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 +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/src/sync/unknownBlock.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
|
-
import {
|
|
3
|
+
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
4
|
+
import {ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas} from "@lodestar/params";
|
|
4
5
|
import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
|
|
5
6
|
import {computeTimeAtSlot} from "@lodestar/state-transition";
|
|
6
7
|
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
@@ -16,6 +17,7 @@ import {validateGossipExecutionPayloadEnvelope} from "../chain/validation/execut
|
|
|
16
17
|
import {Metrics} from "../metrics/index.js";
|
|
17
18
|
import {INetwork, NetworkEvent, NetworkEventData, prettyPrintPeerIdStr} from "../network/index.js";
|
|
18
19
|
import {PeerSyncMeta} from "../network/peers/peersData.js";
|
|
20
|
+
import {MAX_PEERS_PER_ROOT} from "../network/processor/constants.js";
|
|
19
21
|
import {PeerIdStr} from "../util/peerId.js";
|
|
20
22
|
import {shuffle} from "../util/shuffle.js";
|
|
21
23
|
import {sortBy} from "../util/sortBy.js";
|
|
@@ -24,6 +26,7 @@ import {MAX_CONCURRENT_REQUESTS} from "./constants.js";
|
|
|
24
26
|
import {SyncOptions} from "./options.js";
|
|
25
27
|
import {
|
|
26
28
|
BlockInputSyncCacheItem,
|
|
29
|
+
DroppedItemReason,
|
|
27
30
|
PayloadSyncCacheItem,
|
|
28
31
|
PendingBlockInput,
|
|
29
32
|
PendingBlockInputStatus,
|
|
@@ -47,6 +50,10 @@ import {getRateLimitedUntilMs} from "./utils/rateLimit.js";
|
|
|
47
50
|
const MAX_ATTEMPTS_PER_BLOCK = 5;
|
|
48
51
|
const MAX_KNOWN_BAD_BLOCKS = 500;
|
|
49
52
|
const MAX_PENDING_BLOCKS = 100;
|
|
53
|
+
/**
|
|
54
|
+
* A pending payload without slot can only stay at most 2 epochs.
|
|
55
|
+
*/
|
|
56
|
+
const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
|
|
50
57
|
|
|
51
58
|
type AdvancePendingBlockResult =
|
|
52
59
|
| "ready"
|
|
@@ -154,10 +161,21 @@ export class BlockInputSync {
|
|
|
154
161
|
this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
155
162
|
this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
156
163
|
this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
164
|
+
this.chain.emitter.on(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
157
165
|
this.chain.emitter.on(routes.events.EventType.block, this.onBlockImported);
|
|
158
166
|
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
159
167
|
this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
160
168
|
this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
169
|
+
|
|
170
|
+
// Seed the balancer with peers that connected before we subscribed
|
|
171
|
+
for (const peerId of this.network.getConnectedPeers()) {
|
|
172
|
+
try {
|
|
173
|
+
this.peerBalancer.onPeerConnected(peerId, this.network.getConnectedPeerSyncMeta(peerId));
|
|
174
|
+
} catch (e) {
|
|
175
|
+
this.logger.debug("Failed to seed connected peer into BlockInputSync balancer", {peerId}, e as Error);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
161
179
|
this.subscribedToNetworkEvents = true;
|
|
162
180
|
}
|
|
163
181
|
}
|
|
@@ -170,10 +188,12 @@ export class BlockInputSync {
|
|
|
170
188
|
this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
171
189
|
this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
172
190
|
this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
191
|
+
this.chain.emitter.off(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
173
192
|
this.chain.emitter.off(routes.events.EventType.block, this.onBlockImported);
|
|
174
193
|
this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
175
194
|
this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
176
195
|
this.network.events.off(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
196
|
+
this.peerBalancer.clear();
|
|
177
197
|
this.subscribedToNetworkEvents = false;
|
|
178
198
|
}
|
|
179
199
|
|
|
@@ -190,10 +210,12 @@ export class BlockInputSync {
|
|
|
190
210
|
*/
|
|
191
211
|
private onUnknownBlockRoot = (data: ChainEventData[ChainEvent.unknownBlockRoot]): void => {
|
|
192
212
|
try {
|
|
193
|
-
this.addByRootHex(data.rootHex, data.peer);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
213
|
+
const isNewRoot = this.addByRootHex(data.rootHex, data.peer);
|
|
214
|
+
if (isNewRoot) {
|
|
215
|
+
this.triggerUnknownBlockSearch();
|
|
216
|
+
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_BLOCK_ROOT});
|
|
217
|
+
this.metrics?.blockInputSync.source.inc({source: data.source});
|
|
218
|
+
}
|
|
197
219
|
} catch (e) {
|
|
198
220
|
this.logger.debug("Error handling unknownBlockRoot event", {}, e as Error);
|
|
199
221
|
}
|
|
@@ -215,10 +237,12 @@ export class BlockInputSync {
|
|
|
215
237
|
|
|
216
238
|
private onUnknownEnvelopeBlockRoot = (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRoot]): void => {
|
|
217
239
|
try {
|
|
218
|
-
this.addByPayloadRootHex(data.rootHex, data.peer, data.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
240
|
+
const isNewRoot = this.addByPayloadRootHex(data.rootHex, data.peer, this.resolvePayloadSlot(data.rootHex));
|
|
241
|
+
if (isNewRoot) {
|
|
242
|
+
this.triggerUnknownBlockSearch();
|
|
243
|
+
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT});
|
|
244
|
+
this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
|
|
245
|
+
}
|
|
222
246
|
} catch (e) {
|
|
223
247
|
this.logger.debug("Error handling unknownEnvelopeBlockRoot event", {}, e as Error);
|
|
224
248
|
}
|
|
@@ -252,13 +276,13 @@ export class BlockInputSync {
|
|
|
252
276
|
parentRoot: missingDependency.parentRootHex,
|
|
253
277
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
254
278
|
});
|
|
255
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
279
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
256
280
|
}
|
|
257
281
|
return;
|
|
258
282
|
}
|
|
259
283
|
|
|
260
284
|
if (missingDependency.kind === "parentPayload") {
|
|
261
|
-
this.addByPayloadRootHex(missingDependency.rootHex, data.peer);
|
|
285
|
+
this.addByPayloadRootHex(missingDependency.rootHex, data.peer, missingDependency.slot);
|
|
262
286
|
} else if (missingDependency.kind === "parentBlock") {
|
|
263
287
|
this.addByRootHex(missingDependency.rootHex, data.peer);
|
|
264
288
|
}
|
|
@@ -284,6 +308,80 @@ export class BlockInputSync {
|
|
|
284
308
|
this.triggerUnknownBlockSearch();
|
|
285
309
|
};
|
|
286
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Drop pending payloads and blocks that fell below the finalized slot.
|
|
313
|
+
* See https://github.com/ChainSafe/lodestar/issues/9800
|
|
314
|
+
*/
|
|
315
|
+
private pruneFinalized = (checkpoint: CheckpointWithHex): void => {
|
|
316
|
+
// Use the actual finalized block slot, not computeStartSlotAtEpoch(checkpoint.epoch)
|
|
317
|
+
// because the payload at the finalized block is not finalized
|
|
318
|
+
const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
|
|
319
|
+
const nowSec = Date.now() / 1000;
|
|
320
|
+
const maxSecInCache = (PRUNE_UNRESOLVED_SLOT_EPOCHS * SLOTS_PER_EPOCH * this.config.SLOT_DURATION_MS) / 1000;
|
|
321
|
+
|
|
322
|
+
let belowFinalizedPayloads = 0;
|
|
323
|
+
let agedOutPayloads = 0;
|
|
324
|
+
let unresolvedSlotPayloads = 0;
|
|
325
|
+
|
|
326
|
+
for (const [rootHex, payload] of this.pendingPayloads) {
|
|
327
|
+
// Last-effort resolve for a slot-less PendingPayloadRootHex, and store it back for later passes.
|
|
328
|
+
if (!isPendingPayloadInput(payload) && !isPendingPayloadEnvelope(payload) && payload.slot === undefined) {
|
|
329
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
330
|
+
if (payload.slot === undefined) {
|
|
331
|
+
// Still unresolvable: age it out so the MAX_PENDING_BLOCKS cap cannot stay saturated.
|
|
332
|
+
if (nowSec - payload.timeAddedSec > maxSecInCache) {
|
|
333
|
+
this.pendingPayloads.delete(rootHex);
|
|
334
|
+
agedOutPayloads++;
|
|
335
|
+
} else {
|
|
336
|
+
unresolvedSlotPayloads++;
|
|
337
|
+
this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
|
|
338
|
+
root: rootHex,
|
|
339
|
+
finalizedSlot,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const slot = getPayloadSyncCacheItemSlot(payload);
|
|
347
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
348
|
+
this.pendingPayloads.delete(rootHex);
|
|
349
|
+
belowFinalizedPayloads++;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
let deletedBlocks = 0;
|
|
354
|
+
for (const [rootHex, block] of this.pendingBlocks) {
|
|
355
|
+
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
356
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
357
|
+
this.pendingBlocks.delete(rootHex);
|
|
358
|
+
deletedBlocks++;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (belowFinalizedPayloads > 0) {
|
|
363
|
+
this.metrics?.blockInputSync.removedPayloads.inc(
|
|
364
|
+
{reason: DroppedItemReason.belowFinalized},
|
|
365
|
+
belowFinalizedPayloads
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
if (agedOutPayloads > 0) {
|
|
369
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.agedOut}, agedOutPayloads);
|
|
370
|
+
}
|
|
371
|
+
if (deletedBlocks > 0) {
|
|
372
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.belowFinalized}, deletedBlocks);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
this.logger.debug("BlockInputSync.pruneFinalized dropped pre-finalized pending items", {
|
|
376
|
+
finalizedSlot,
|
|
377
|
+
finalizedRoot: checkpoint.rootHex,
|
|
378
|
+
belowFinalizedPayloads,
|
|
379
|
+
agedOutPayloads,
|
|
380
|
+
deletedBlocks,
|
|
381
|
+
unresolvedSlotPayloads,
|
|
382
|
+
});
|
|
383
|
+
};
|
|
384
|
+
|
|
287
385
|
private addByRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr): boolean => {
|
|
288
386
|
let pendingBlock = this.pendingBlocks.get(rootHex);
|
|
289
387
|
let added = false;
|
|
@@ -303,7 +401,9 @@ export class BlockInputSync {
|
|
|
303
401
|
});
|
|
304
402
|
}
|
|
305
403
|
|
|
306
|
-
|
|
404
|
+
// bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
|
|
405
|
+
// Once at the cap, ignore further peers rather than evicting already-tracked ones.
|
|
406
|
+
if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
307
407
|
pendingBlock.peerIdStrings.add(peerIdStr);
|
|
308
408
|
}
|
|
309
409
|
|
|
@@ -311,6 +411,7 @@ export class BlockInputSync {
|
|
|
311
411
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
312
412
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
313
413
|
if (prunedItemCount > 0) {
|
|
414
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
314
415
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
315
416
|
}
|
|
316
417
|
return added;
|
|
@@ -337,7 +438,9 @@ export class BlockInputSync {
|
|
|
337
438
|
});
|
|
338
439
|
}
|
|
339
440
|
|
|
340
|
-
|
|
441
|
+
// bound peers tracked per root: propagationSource is recorded pre-validation, so cap the set (#9923).
|
|
442
|
+
// Once at the cap, ignore further peers rather than evicting already-tracked ones.
|
|
443
|
+
if (peerIdStr && pendingBlock.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
341
444
|
pendingBlock.peerIdStrings.add(peerIdStr);
|
|
342
445
|
}
|
|
343
446
|
|
|
@@ -345,6 +448,7 @@ export class BlockInputSync {
|
|
|
345
448
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
346
449
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
347
450
|
if (prunedItemCount > 0) {
|
|
451
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
348
452
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
349
453
|
}
|
|
350
454
|
};
|
|
@@ -369,19 +473,50 @@ export class BlockInputSync {
|
|
|
369
473
|
root: rootHex,
|
|
370
474
|
peerIdStr: peerIdStr ?? "unknown peer",
|
|
371
475
|
});
|
|
476
|
+
} else if (
|
|
477
|
+
// update a previously slot-less entry once a slot becomes available
|
|
478
|
+
slot !== undefined &&
|
|
479
|
+
!isPendingPayloadInput(pendingPayload) &&
|
|
480
|
+
!isPendingPayloadEnvelope(pendingPayload) &&
|
|
481
|
+
pendingPayload.slot === undefined
|
|
482
|
+
) {
|
|
483
|
+
pendingPayload.slot = slot;
|
|
372
484
|
}
|
|
373
|
-
|
|
374
|
-
if (peerIdStr) {
|
|
485
|
+
if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
375
486
|
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
376
487
|
}
|
|
377
488
|
|
|
378
489
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
379
490
|
if (prunedItemCount > 0) {
|
|
491
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
380
492
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
381
493
|
}
|
|
382
494
|
return added;
|
|
383
495
|
};
|
|
384
496
|
|
|
497
|
+
/**
|
|
498
|
+
* Resolve the block slot for a payload root.
|
|
499
|
+
*/
|
|
500
|
+
private resolvePayloadSlot(rootHex: RootHex): Slot | undefined {
|
|
501
|
+
// a pending block of the same root
|
|
502
|
+
const pendingBlock = this.pendingBlocks.get(rootHex);
|
|
503
|
+
if (pendingBlock) {
|
|
504
|
+
const pendingBlockSlot = getBlockInputSyncCacheItemSlot(pendingBlock);
|
|
505
|
+
if (typeof pendingBlockSlot === "number") {
|
|
506
|
+
return pendingBlockSlot;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// the block is in fork choice
|
|
511
|
+
const forkChoiceSlot = this.chain.forkChoice.getBlockHexDefaultStatus(rootHex)?.slot;
|
|
512
|
+
if (forkChoiceSlot !== undefined) {
|
|
513
|
+
return forkChoiceSlot;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// a seen payload envelope input
|
|
517
|
+
return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
|
|
518
|
+
}
|
|
519
|
+
|
|
385
520
|
private addByPayloadInput = (
|
|
386
521
|
payloadInput: PayloadEnvelopeInput,
|
|
387
522
|
peerIdStr?: PeerIdStr,
|
|
@@ -393,7 +528,7 @@ export class BlockInputSync {
|
|
|
393
528
|
envelope
|
|
394
529
|
);
|
|
395
530
|
|
|
396
|
-
if (peerIdStr) {
|
|
531
|
+
if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
|
|
397
532
|
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
398
533
|
}
|
|
399
534
|
|
|
@@ -407,6 +542,7 @@ export class BlockInputSync {
|
|
|
407
542
|
|
|
408
543
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
409
544
|
if (prunedItemCount > 0) {
|
|
545
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
410
546
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
411
547
|
}
|
|
412
548
|
};
|
|
@@ -436,10 +572,12 @@ export class BlockInputSync {
|
|
|
436
572
|
blockInput: IBlockInput
|
|
437
573
|
):
|
|
438
574
|
| {kind: "ready"}
|
|
439
|
-
| {kind: "block" | "parentBlock"
|
|
575
|
+
| {kind: "block" | "parentBlock"; rootHex: RootHex}
|
|
576
|
+
| {kind: "parentPayload"; rootHex: RootHex; slot: Slot}
|
|
440
577
|
| {kind: "invalidParentPayload"; parentRootHex: RootHex; parentBlockHashHex: RootHex} {
|
|
441
578
|
const parentRootHex = blockInput.parentRootHex;
|
|
442
|
-
|
|
579
|
+
const parentBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRootHex);
|
|
580
|
+
if (!parentBlock) {
|
|
443
581
|
return {kind: "parentBlock", rootHex: parentRootHex};
|
|
444
582
|
}
|
|
445
583
|
|
|
@@ -461,16 +599,8 @@ export class BlockInputSync {
|
|
|
461
599
|
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
462
600
|
}
|
|
463
601
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
|
|
467
|
-
return {kind: "parentPayload", rootHex: parentRootHex};
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return {kind: "parentPayload", rootHex: parentRootHex};
|
|
602
|
+
// Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
|
|
603
|
+
return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
|
|
474
604
|
}
|
|
475
605
|
|
|
476
606
|
private advancePendingBlock(pendingBlock: PendingBlockInput): AdvancePendingBlockResult {
|
|
@@ -493,9 +623,9 @@ export class BlockInputSync {
|
|
|
493
623
|
}
|
|
494
624
|
|
|
495
625
|
case "parentPayload": {
|
|
496
|
-
let added = this.addByPayloadRootHex(missingDependency.rootHex);
|
|
626
|
+
let added = this.addByPayloadRootHex(missingDependency.rootHex, undefined, missingDependency.slot);
|
|
497
627
|
for (const peerIdStr of pendingBlock.peerIdStrings) {
|
|
498
|
-
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr) || added;
|
|
628
|
+
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr, missingDependency.slot) || added;
|
|
499
629
|
}
|
|
500
630
|
return added ? "queued_parent_payload" : "blocked";
|
|
501
631
|
}
|
|
@@ -507,7 +637,7 @@ export class BlockInputSync {
|
|
|
507
637
|
parentRoot: missingDependency.parentRootHex,
|
|
508
638
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
509
639
|
});
|
|
510
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
640
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
511
641
|
return "removed";
|
|
512
642
|
}
|
|
513
643
|
}
|
|
@@ -730,7 +860,7 @@ export class BlockInputSync {
|
|
|
730
860
|
...logCtx2,
|
|
731
861
|
finalizedSlot,
|
|
732
862
|
});
|
|
733
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
863
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
|
|
734
864
|
} else {
|
|
735
865
|
this.onUnknownBlockRoot({rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot});
|
|
736
866
|
}
|
|
@@ -747,7 +877,7 @@ export class BlockInputSync {
|
|
|
747
877
|
|
|
748
878
|
this.metrics?.blockInputSync.downloadedBlocksError.inc();
|
|
749
879
|
this.logger.debug("Ignoring unknown block root after many failed downloads", logCtx, res.err);
|
|
750
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
880
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.unavailable);
|
|
751
881
|
}
|
|
752
882
|
}
|
|
753
883
|
|
|
@@ -764,9 +894,10 @@ export class BlockInputSync {
|
|
|
764
894
|
// this prevents unbundling attack
|
|
765
895
|
// see https://lighthouse-blog.sigmaprime.io/mev-unbundling-rpc.html
|
|
766
896
|
const {slot: blockSlot, proposerIndex} = pendingBlock.blockInput.getBlock().message;
|
|
897
|
+
const blockRootHex = pendingBlock.blockInput.blockRootHex;
|
|
767
898
|
const logCtx = {
|
|
768
899
|
slot: blockSlot,
|
|
769
|
-
root:
|
|
900
|
+
root: blockRootHex,
|
|
770
901
|
delaySec: this.chain.clock.secFromSlot(blockSlot),
|
|
771
902
|
};
|
|
772
903
|
this.logger.verbose("Processing downloaded block", logCtx);
|
|
@@ -780,7 +911,7 @@ export class BlockInputSync {
|
|
|
780
911
|
// eligible for proposer boost to prevent unbundling attack
|
|
781
912
|
this.logger.verbose("Avoid proposer boost for this block of known proposer", {
|
|
782
913
|
slot: blockSlot,
|
|
783
|
-
root:
|
|
914
|
+
root: blockRootHex,
|
|
784
915
|
proposerIndex,
|
|
785
916
|
});
|
|
786
917
|
await sleep(proposerBoostWindowMs);
|
|
@@ -807,12 +938,24 @@ export class BlockInputSync {
|
|
|
807
938
|
if (!res.err) {
|
|
808
939
|
this.logger.verbose("Processed block from unknown sync", logCtx);
|
|
809
940
|
// no need to update status to "processed", delete anyway
|
|
810
|
-
this.pendingBlocks.delete(
|
|
941
|
+
this.pendingBlocks.delete(blockRootHex);
|
|
942
|
+
|
|
943
|
+
if (isForkPostGloas(fork)) {
|
|
944
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
|
|
945
|
+
if (!payloadInput) {
|
|
946
|
+
this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
|
|
947
|
+
} else {
|
|
948
|
+
// Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
|
|
949
|
+
// The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
|
|
950
|
+
this.chain.getBlobsTracker.triggerGetBlobs(payloadInput);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
811
954
|
// Re-enter the scheduler so descendants blocked on either parent blocks or parent payloads
|
|
812
955
|
// are advanced through the same dependency checks as every other pending item.
|
|
813
956
|
this.triggerUnknownBlockSearch();
|
|
814
957
|
} else {
|
|
815
|
-
const errorData = {slot: pendingBlock.blockInput.slot, root:
|
|
958
|
+
const errorData = {slot: pendingBlock.blockInput.slot, root: blockRootHex};
|
|
816
959
|
if (res.err instanceof BlockError) {
|
|
817
960
|
switch (res.err.type.code) {
|
|
818
961
|
// This cases are already handled with `{ignoreIfKnown: true}`
|
|
@@ -842,19 +985,19 @@ export class BlockInputSync {
|
|
|
842
985
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
843
986
|
// Removing the block(s) without penalizing the peers, hoping for EL to
|
|
844
987
|
// recover on a latter download + verify attempt.
|
|
845
|
-
this.removeAllDescendants(pendingBlock);
|
|
988
|
+
this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
|
|
846
989
|
break;
|
|
847
990
|
|
|
848
991
|
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
849
992
|
// the peer served a bad block
|
|
850
993
|
this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
|
|
851
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
994
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
|
|
852
995
|
break;
|
|
853
996
|
|
|
854
997
|
default:
|
|
855
998
|
// Block is not correct with respect to our chain. Log error loudly
|
|
856
999
|
this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
|
|
857
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
1000
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
|
|
858
1001
|
}
|
|
859
1002
|
}
|
|
860
1003
|
|
|
@@ -873,12 +1016,12 @@ export class BlockInputSync {
|
|
|
873
1016
|
*/
|
|
874
1017
|
private async reconcilePayloadEnvelope(pendingPayload: PendingPayloadEnvelope): Promise<void> {
|
|
875
1018
|
const rootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
1019
|
+
|
|
876
1020
|
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
877
1021
|
this.pendingPayloads.delete(rootHex);
|
|
878
1022
|
return;
|
|
879
1023
|
}
|
|
880
1024
|
|
|
881
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
882
1025
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
883
1026
|
// Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
|
|
884
1027
|
// non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
|
|
@@ -894,8 +1037,9 @@ export class BlockInputSync {
|
|
|
894
1037
|
return;
|
|
895
1038
|
}
|
|
896
1039
|
|
|
1040
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
897
1041
|
if (!payloadInput) {
|
|
898
|
-
this.logger.debug("
|
|
1042
|
+
this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
|
|
899
1043
|
root: rootHex,
|
|
900
1044
|
});
|
|
901
1045
|
return;
|
|
@@ -915,6 +1059,7 @@ export class BlockInputSync {
|
|
|
915
1059
|
const pendingPayloadByRoot: PendingPayloadRootHex = {
|
|
916
1060
|
status: PendingPayloadInputStatus.pending,
|
|
917
1061
|
rootHex,
|
|
1062
|
+
slot: pendingPayload.envelope.message.payload.slotNumber,
|
|
918
1063
|
timeAddedSec: pendingPayload.timeAddedSec,
|
|
919
1064
|
peerIdStrings: new Set(pendingPayload.peerIdStrings),
|
|
920
1065
|
};
|
|
@@ -954,6 +1099,10 @@ export class BlockInputSync {
|
|
|
954
1099
|
return;
|
|
955
1100
|
}
|
|
956
1101
|
|
|
1102
|
+
if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
|
|
1103
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
957
1106
|
const payloadSlot = getPayloadSyncCacheItemSlot(payload);
|
|
958
1107
|
const logCtx = {
|
|
959
1108
|
slot: payloadSlot,
|
|
@@ -969,7 +1118,13 @@ export class BlockInputSync {
|
|
|
969
1118
|
const res = await wrapError(this.fetchPayloadInput(payload));
|
|
970
1119
|
if (!res.err) {
|
|
971
1120
|
const pendingPayload = res.result;
|
|
972
|
-
|
|
1121
|
+
const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
1122
|
+
// finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
|
|
1123
|
+
if (!this.pendingPayloads.has(resultRootHex)) {
|
|
1124
|
+
this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
this.pendingPayloads.set(resultRootHex, pendingPayload);
|
|
973
1128
|
|
|
974
1129
|
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
975
1130
|
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
@@ -1046,17 +1201,27 @@ export class BlockInputSync {
|
|
|
1046
1201
|
break;
|
|
1047
1202
|
|
|
1048
1203
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1204
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1205
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1206
|
+
break;
|
|
1207
|
+
|
|
1049
1208
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1209
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1210
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1211
|
+
break;
|
|
1212
|
+
|
|
1050
1213
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1051
1214
|
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
1052
1215
|
// reintroducing envelope replacement / recreation flows.
|
|
1053
1216
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1054
|
-
this.removePendingPayloadAndDescendants(rootHex);
|
|
1217
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
1055
1218
|
break;
|
|
1056
1219
|
|
|
1057
1220
|
default:
|
|
1058
1221
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1059
|
-
this.pendingPayloads.delete(rootHex)
|
|
1222
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1223
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.unknown}, 1);
|
|
1224
|
+
}
|
|
1060
1225
|
}
|
|
1061
1226
|
return;
|
|
1062
1227
|
}
|
|
@@ -1080,7 +1245,7 @@ export class BlockInputSync {
|
|
|
1080
1245
|
let slot = getPayloadSyncCacheItemSlot(cacheItem);
|
|
1081
1246
|
let payloadInput = isPendingPayloadInput(cacheItem)
|
|
1082
1247
|
? cacheItem.payloadInput
|
|
1083
|
-
: this.chain.seenPayloadEnvelopeInputCache.
|
|
1248
|
+
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1084
1249
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
1085
1250
|
|
|
1086
1251
|
let i = 0;
|
|
@@ -1089,7 +1254,12 @@ export class BlockInputSync {
|
|
|
1089
1254
|
const pendingColumns = payloadInput?.hasAllData()
|
|
1090
1255
|
? new Set<number>()
|
|
1091
1256
|
: new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
|
|
1092
|
-
|
|
1257
|
+
// prefer peers that gossiped this payload root to us (#9923)
|
|
1258
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
|
|
1259
|
+
pendingColumns,
|
|
1260
|
+
excludedPeers,
|
|
1261
|
+
cacheItem.peerIdStrings
|
|
1262
|
+
);
|
|
1093
1263
|
if (peerMeta === null) {
|
|
1094
1264
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
1095
1265
|
throw new UnknownBlockRateLimitedError(
|
|
@@ -1097,9 +1267,11 @@ export class BlockInputSync {
|
|
|
1097
1267
|
);
|
|
1098
1268
|
}
|
|
1099
1269
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1270
|
+
const reason =
|
|
1271
|
+
pendingColumns.size > 0
|
|
1272
|
+
? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
|
|
1273
|
+
: "no peer available to download pending payload";
|
|
1274
|
+
throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
|
|
1103
1275
|
}
|
|
1104
1276
|
|
|
1105
1277
|
const {peerId, client: peerClient} = peerMeta;
|
|
@@ -1111,7 +1283,7 @@ export class BlockInputSync {
|
|
|
1111
1283
|
slot = envelope.message.payload.slotNumber;
|
|
1112
1284
|
}
|
|
1113
1285
|
|
|
1114
|
-
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.
|
|
1286
|
+
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1115
1287
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
1116
1288
|
// Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
|
|
1117
1289
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
@@ -1296,7 +1468,12 @@ export class BlockInputSync {
|
|
|
1296
1468
|
isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
|
|
1297
1469
|
? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
|
|
1298
1470
|
: defaultPendingColumns;
|
|
1299
|
-
|
|
1471
|
+
// prefer peers that gossiped this block root to us (#9923)
|
|
1472
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
|
|
1473
|
+
pendingColumns,
|
|
1474
|
+
excludedPeers,
|
|
1475
|
+
cacheItem.peerIdStrings
|
|
1476
|
+
);
|
|
1300
1477
|
if (peerMeta === null) {
|
|
1301
1478
|
if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
|
|
1302
1479
|
throw new UnknownBlockRateLimitedError(
|
|
@@ -1444,9 +1621,9 @@ export class BlockInputSync {
|
|
|
1444
1621
|
* Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
|
|
1445
1622
|
* referenced more than one bad block.
|
|
1446
1623
|
*/
|
|
1447
|
-
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem): void {
|
|
1624
|
+
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
|
|
1448
1625
|
// Get all blocks that are a descendant of this one
|
|
1449
|
-
const badPendingBlocks = this.removeAllDescendants(block);
|
|
1626
|
+
const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
|
|
1450
1627
|
// just console log and do not penalize on pending/bad blocks for debugging
|
|
1451
1628
|
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
1452
1629
|
|
|
@@ -1473,18 +1650,22 @@ export class BlockInputSync {
|
|
|
1473
1650
|
}
|
|
1474
1651
|
|
|
1475
1652
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1476
|
-
private removePendingPayloadAndDescendants(rootHex: RootHex): void {
|
|
1653
|
+
private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
|
|
1477
1654
|
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1478
1655
|
// later validation/finalization logic decides when it can leave memory.
|
|
1479
|
-
this.pendingPayloads.delete(rootHex)
|
|
1656
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1657
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
|
|
1658
|
+
}
|
|
1480
1659
|
|
|
1481
1660
|
const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
|
|
1482
|
-
this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
|
|
1483
|
-
|
|
1484
1661
|
for (const block of badPendingBlocks) {
|
|
1485
1662
|
const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1486
|
-
this.pendingBlocks.delete(descendantRootHex)
|
|
1487
|
-
|
|
1663
|
+
if (this.pendingBlocks.delete(descendantRootHex)) {
|
|
1664
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.invalidParent}, 1);
|
|
1665
|
+
}
|
|
1666
|
+
if (this.pendingPayloads.delete(descendantRootHex)) {
|
|
1667
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.invalidParent}, 1);
|
|
1668
|
+
}
|
|
1488
1669
|
this.chain.seenBlockInputCache.prune(descendantRootHex);
|
|
1489
1670
|
this.logger.debug("Removing pending descendant after invalid parent payload", {
|
|
1490
1671
|
slot: getBlockInputSyncCacheItemSlot(block),
|
|
@@ -1494,18 +1675,25 @@ export class BlockInputSync {
|
|
|
1494
1675
|
}
|
|
1495
1676
|
}
|
|
1496
1677
|
|
|
1497
|
-
private removeAllDescendants(
|
|
1678
|
+
private removeAllDescendants(
|
|
1679
|
+
block: BlockInputSyncCacheItem,
|
|
1680
|
+
headReason: DroppedItemReason,
|
|
1681
|
+
descendantReason: DroppedItemReason = DroppedItemReason.invalidParent
|
|
1682
|
+
): BlockInputSyncCacheItem[] {
|
|
1498
1683
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1499
1684
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
1500
|
-
// Get all blocks that are a descendant of this one
|
|
1685
|
+
// Get all blocks that are a descendant of this one (index 0 is the head item itself)
|
|
1501
1686
|
const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
|
|
1502
1687
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
for (const block of badPendingBlocks) {
|
|
1688
|
+
for (const [i, block] of badPendingBlocks.entries()) {
|
|
1506
1689
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1507
|
-
|
|
1508
|
-
this.
|
|
1690
|
+
const reason = i === 0 ? headReason : descendantReason;
|
|
1691
|
+
if (this.pendingBlocks.delete(rootHex)) {
|
|
1692
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason}, 1);
|
|
1693
|
+
}
|
|
1694
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1695
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason}, 1);
|
|
1696
|
+
}
|
|
1509
1697
|
this.chain.seenBlockInputCache.prune(rootHex);
|
|
1510
1698
|
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1511
1699
|
// importBlock()-owned lifecycle.
|
|
@@ -1561,6 +1749,13 @@ export class UnknownBlockPeerBalancer {
|
|
|
1561
1749
|
this.rateLimitedUntilByPeer.delete(peerId);
|
|
1562
1750
|
}
|
|
1563
1751
|
|
|
1752
|
+
/** Reset all tracked peer state, e.g. when BlockInputSync unsubscribes from the network. */
|
|
1753
|
+
clear(): void {
|
|
1754
|
+
this.peersMeta.clear();
|
|
1755
|
+
this.activeRequests.clear();
|
|
1756
|
+
this.rateLimitedUntilByPeer.clear();
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1564
1759
|
onRateLimited(peerId: PeerIdStr, rateLimitedUntilMs: number): void {
|
|
1565
1760
|
this.rateLimitedUntilByPeer.set(peerId, rateLimitedUntilMs);
|
|
1566
1761
|
}
|
|
@@ -1596,11 +1791,17 @@ export class UnknownBlockPeerBalancer {
|
|
|
1596
1791
|
}
|
|
1597
1792
|
|
|
1598
1793
|
/**
|
|
1599
|
-
* called from fetchBlockInput()
|
|
1794
|
+
* called from fetchBlockInput()/fetchPayloadInput() with a block/payload root.
|
|
1600
1795
|
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
1601
1796
|
* pendingColumns is empty for prefulu, or the 1st time we we download a block by root
|
|
1797
|
+
* preferredPeers are the peers NetworkProcessor told us gossiped this root (from
|
|
1798
|
+
* pendingBlock/cacheItem.peerIdStrings). They are preferred but not required.
|
|
1602
1799
|
*/
|
|
1603
|
-
bestPeerForPendingColumns(
|
|
1800
|
+
bestPeerForPendingColumns(
|
|
1801
|
+
pendingColumns: Set<number>,
|
|
1802
|
+
excludedPeers: Set<PeerIdStr>,
|
|
1803
|
+
preferredPeers?: Set<PeerIdStr>
|
|
1804
|
+
): PeerSyncMeta | null {
|
|
1604
1805
|
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
1605
1806
|
if (eligiblePeers.length === 0) {
|
|
1606
1807
|
return null;
|
|
@@ -1608,7 +1809,9 @@ export class UnknownBlockPeerBalancer {
|
|
|
1608
1809
|
|
|
1609
1810
|
const sortedEligiblePeers = sortBy(
|
|
1610
1811
|
shuffle(eligiblePeers),
|
|
1611
|
-
// prefer peers
|
|
1812
|
+
// 1st key: prefer peers we heard about this root from
|
|
1813
|
+
(peerId) => (preferredPeers?.has(peerId) ? 0 : 1),
|
|
1814
|
+
// 2nd key (tiebreak): prefer peers with least active req
|
|
1612
1815
|
(peerId) => this.activeRequests.get(peerId) ?? 0
|
|
1613
1816
|
);
|
|
1614
1817
|
|