@lodestar/beacon-node 1.46.0 → 1.47.0-dev.02d2dbea03
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.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 +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 +3 -8
- 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 +12 -10
- 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 +17 -11
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +11 -16
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/bls/index.d.ts +1 -1
- package/lib/chain/bls/index.d.ts.map +1 -1
- package/lib/chain/bls/interface.d.ts +5 -5
- package/lib/chain/bls/interface.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts +5 -11
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +116 -101
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
- package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
- package/lib/chain/bls/multithread/jobItem.js +10 -65
- package/lib/chain/bls/multithread/jobItem.js.map +1 -1
- package/lib/chain/bls/multithread/types.d.ts +29 -8
- package/lib/chain/bls/multithread/types.d.ts.map +1 -1
- package/lib/chain/bls/multithread/types.js +12 -0
- package/lib/chain/bls/multithread/types.js.map +1 -1
- package/lib/chain/bls/multithread/utils.d.ts +4 -0
- package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
- package/lib/chain/bls/multithread/utils.js +10 -0
- package/lib/chain/bls/multithread/utils.js.map +1 -1
- package/lib/chain/bls/multithread/worker.js +49 -23
- package/lib/chain/bls/multithread/worker.js.map +1 -1
- package/lib/chain/bls/singleThread.d.ts +4 -10
- package/lib/chain/bls/singleThread.d.ts.map +1 -1
- package/lib/chain/bls/singleThread.js +8 -45
- package/lib/chain/bls/singleThread.js.map +1 -1
- package/lib/chain/bls/utils.d.ts +4 -4
- package/lib/chain/bls/utils.d.ts.map +1 -1
- package/lib/chain/bls/utils.js +18 -19
- package/lib/chain/bls/utils.js.map +1 -1
- package/lib/chain/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 +24 -8
- 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/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 -3
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +2 -2
- 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 +8 -3
- 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 +17 -7
- 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 +71 -22
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +126 -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/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/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 +62 -23
- 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/range/utils/hashBlocks.d.ts +16 -4
- package/lib/sync/range/utils/hashBlocks.d.ts.map +1 -1
- package/lib/sync/range/utils/hashBlocks.js +49 -18
- package/lib/sync/range/utils/hashBlocks.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 +9 -0
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +187 -47
- 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 +37 -37
- 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 +12 -13
- 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 +2 -7
- package/src/chain/blocks/importExecutionPayload.ts +13 -12
- package/src/chain/blocks/index.ts +21 -11
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
- package/src/chain/blocks/verifyBlock.ts +21 -25
- 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 +25 -7
- package/src/chain/errors/blockError.ts +9 -3
- package/src/chain/errors/proposerPreferences.ts +7 -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 -3
- package/src/chain/prepareNextSlot.ts +2 -2
- package/src/chain/produceBlock/produceBlockBody.ts +15 -2
- 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 +24 -7
- 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 +138 -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/gossipHandlers.ts +40 -4
- 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 +84 -29
- package/src/sync/range/chain.ts +26 -11
- package/src/sync/range/utils/hashBlocks.ts +56 -21
- package/src/sync/types.ts +36 -1
- package/src/sync/unknownBlock.ts +212 -53
- 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
|
@@ -196,7 +196,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
196
196
|
forkName: fork,
|
|
197
197
|
sampledColumns: chain.custodyConfig.sampledColumns,
|
|
198
198
|
custodyColumns: chain.custodyConfig.custodyColumns,
|
|
199
|
-
|
|
199
|
+
seenTimestampSec,
|
|
200
|
+
source: PayloadEnvelopeInputSource.gossip,
|
|
200
201
|
});
|
|
201
202
|
}
|
|
202
203
|
|
|
@@ -640,6 +641,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
640
641
|
metrics?.gossipBlock.elapsedTimeTillProcessed.observe(delaySec);
|
|
641
642
|
|
|
642
643
|
if (isForkPostGloas(blockInput.forkName)) {
|
|
644
|
+
// we should have the payloadInput in the seen cache so no need getOrReload() here
|
|
643
645
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockInput.blockRootHex);
|
|
644
646
|
// This payloadInput should have been created just after gossip validation
|
|
645
647
|
if (!payloadInput) {
|
|
@@ -713,9 +715,32 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
713
715
|
const {serializedData} = gossipData;
|
|
714
716
|
|
|
715
717
|
const signedBlock = sszDeserialize(topic, serializedData);
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
718
|
+
try {
|
|
719
|
+
const blockInput = await validateBeaconBlock(signedBlock, topic.boundary.fork, peerIdStr, seenTimestampSec);
|
|
720
|
+
chain.serializedCache.set(signedBlock, serializedData);
|
|
721
|
+
handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
|
|
722
|
+
} catch (e) {
|
|
723
|
+
// Spec: IGNORE the block, ie not to re-publish to peers
|
|
724
|
+
// but we should still import an equivocating (REPEAT_PROPOSAL) block into fork choice because we don't
|
|
725
|
+
// know if this block (or the 1st known block with same slot) will become canonical yet
|
|
726
|
+
if (
|
|
727
|
+
e instanceof BlockGossipError &&
|
|
728
|
+
e.type.code === BlockErrorCode.REPEAT_PROPOSAL &&
|
|
729
|
+
// this is make sure the block's proposer signature was verified, it should be true anyway
|
|
730
|
+
chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root)
|
|
731
|
+
) {
|
|
732
|
+
// blockInput was optimistically seeded in validateBeaconBlock and retained on IGNORE
|
|
733
|
+
const blockInput = chain.seenBlockInputCache.get(e.type.root);
|
|
734
|
+
if (blockInput) {
|
|
735
|
+
chain.serializedCache.set(signedBlock, serializedData);
|
|
736
|
+
// this is technically not a valid gossip block but gossip validation is a cheap subset of checks
|
|
737
|
+
// this runs the full state transition, so importing an equivocating-but-valid block here is safe.
|
|
738
|
+
handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
// rethrow so gossipValidatorFn maps IGNORE -> TopicValidatorResult.Ignore (message not forwarded)
|
|
742
|
+
throw e;
|
|
743
|
+
}
|
|
719
744
|
},
|
|
720
745
|
|
|
721
746
|
[GossipType.blob_sidecar]: async ({
|
|
@@ -1166,6 +1191,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1166
1191
|
chain.validatorMonitor?.registerExecutionPayloadEnvelope(OpSource.gossip, delaySec, signedEnvelope);
|
|
1167
1192
|
|
|
1168
1193
|
const blockRootHex = toRootHex(envelope.beaconBlockRoot);
|
|
1194
|
+
// a gossip payload cannot be `MAX_PAYLOAD_ENVELOPE_INPUT_CACHE_SIZE` slots after the block
|
|
1195
|
+
// otherwise it'll get to UnknownBlockInput flow
|
|
1169
1196
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
1170
1197
|
|
|
1171
1198
|
if (!payloadInput) {
|
|
@@ -1232,11 +1259,15 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1232
1259
|
[GossipType.payload_attestation_message]: async ({
|
|
1233
1260
|
gossipData,
|
|
1234
1261
|
topic,
|
|
1262
|
+
seenTimestampSec,
|
|
1235
1263
|
}: GossipHandlerParamGeneric<GossipType.payload_attestation_message>) => {
|
|
1236
1264
|
const {serializedData} = gossipData;
|
|
1237
1265
|
const payloadAttestationMessage = sszDeserialize(topic, serializedData);
|
|
1238
1266
|
const validationResult = await validateGossipPayloadAttestationMessage(chain, payloadAttestationMessage);
|
|
1239
1267
|
|
|
1268
|
+
const delaySec = chain.clock.secFromSlot(payloadAttestationMessage.data.slot, seenTimestampSec);
|
|
1269
|
+
metrics?.gossipPayloadAttestationMessage.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
|
|
1270
|
+
|
|
1240
1271
|
try {
|
|
1241
1272
|
const insertOutcome = chain.payloadAttestationPool.add(
|
|
1242
1273
|
payloadAttestationMessage,
|
|
@@ -1263,11 +1294,16 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1263
1294
|
[GossipType.execution_payload_bid]: async ({
|
|
1264
1295
|
gossipData,
|
|
1265
1296
|
topic,
|
|
1297
|
+
seenTimestampSec,
|
|
1266
1298
|
}: GossipHandlerParamGeneric<GossipType.execution_payload_bid>) => {
|
|
1267
1299
|
const {serializedData} = gossipData;
|
|
1268
1300
|
const executionPayloadBid = sszDeserialize(topic, serializedData);
|
|
1269
1301
|
const {proposerIndex} = await validateGossipExecutionPayloadBid(chain, executionPayloadBid);
|
|
1270
1302
|
|
|
1303
|
+
// this could be negative, because it's most likely the bid of next slot comes at this clock slot
|
|
1304
|
+
const elapsedSec = chain.clock.secFromSlot(executionPayloadBid.message.slot, seenTimestampSec);
|
|
1305
|
+
metrics?.gossipExecutionPayloadBid.elapsedTimeTillReceived.observe({source: OpSource.gossip}, elapsedSec);
|
|
1306
|
+
|
|
1271
1307
|
// Handle valid payload bid by storing in a bid pool
|
|
1272
1308
|
try {
|
|
1273
1309
|
const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid);
|
|
@@ -44,17 +44,11 @@ export function onOutgoingReqRespError(e: RequestError, method: ReqRespMethod):
|
|
|
44
44
|
// failure, so keep the stronger penalty (Fatal for Ping, as before).
|
|
45
45
|
return method === ReqRespMethod.Ping ? PeerAction.Fatal : PeerAction.LowToleranceError;
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
case ReqRespMethod.Ping:
|
|
53
|
-
case ReqRespMethod.Status:
|
|
54
|
-
return PeerAction.HighToleranceError;
|
|
55
|
-
default:
|
|
56
|
-
return PeerAction.LowToleranceError;
|
|
57
|
-
}
|
|
47
|
+
// A dial failure means we could not open a stream. That is evidence about the transport, not
|
|
48
|
+
// about the peer's behavior, and it is dominated by transient congestion on our side, so
|
|
49
|
+
// penalize leniently for every method to avoid self-inflicted peer starvation (#9562). A
|
|
50
|
+
// genuinely dead peer still accumulates penalty and is eventually evicted, freeing the slot.
|
|
51
|
+
return PeerAction.HighToleranceError;
|
|
58
52
|
// TODO: Detect SSZDecodeError and return PeerAction.Fatal
|
|
59
53
|
|
|
60
54
|
case RequestErrorCode.RESP_TIMEOUT:
|
package/src/network/util.ts
CHANGED
|
@@ -23,11 +23,6 @@ export function getConnection(libp2p: Libp2p, peerIdStr: string): Connection | u
|
|
|
23
23
|
return getConnectionsMap(libp2p).get(peerIdStr)?.value[0] ?? undefined;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
// https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
|
|
27
|
-
export function isPublishToZeroPeersError(e: Error): boolean {
|
|
28
|
-
return e.message.includes("PublishError.NoPeersSubscribedToTopic");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
26
|
// https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
|
|
32
27
|
export function isPublishDuplicateError(e: Error): boolean {
|
|
33
28
|
return e.message.includes("PublishError.Duplicate");
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {routes} from "@lodestar/api";
|
|
2
|
+
import {Logger} from "@lodestar/utils";
|
|
3
|
+
import {IBeaconChain} from "../chain/index.js";
|
|
4
|
+
import {INetwork} from "../network/index.js";
|
|
5
|
+
import {ClockEvent} from "../util/clock.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* On every epoch, drain the deferred voluntary exit pool and publish the exits that have become
|
|
9
|
+
* processable. Lives at the node layer rather than inside `BeaconChain` because publishing needs
|
|
10
|
+
* `network`, which the chain does not (and should not) depend on.
|
|
11
|
+
*/
|
|
12
|
+
export function startDeferredVoluntaryExitPublisher({
|
|
13
|
+
chain,
|
|
14
|
+
network,
|
|
15
|
+
logger,
|
|
16
|
+
signal,
|
|
17
|
+
}: {
|
|
18
|
+
chain: IBeaconChain;
|
|
19
|
+
network: INetwork;
|
|
20
|
+
logger: Logger;
|
|
21
|
+
signal: AbortSignal;
|
|
22
|
+
}): void {
|
|
23
|
+
const onEpoch = async (): Promise<void> => {
|
|
24
|
+
try {
|
|
25
|
+
// Use the cached head state directly rather than getHeadStateAtCurrentEpoch, which would regen
|
|
26
|
+
// the head forward to the current epoch. At init from a stale db-finalized state that regen can
|
|
27
|
+
// be many epoch transitions. The head state is fine here: this is a best-effort drain that
|
|
28
|
+
// re-runs every epoch, so a just-processable exit is republished next tick.
|
|
29
|
+
const state = chain.getHeadState();
|
|
30
|
+
const exits = chain.deferredVoluntaryExitPool.drainProcessableExits(state);
|
|
31
|
+
for (const exit of exits) {
|
|
32
|
+
try {
|
|
33
|
+
chain.opPool.insertVoluntaryExit(exit);
|
|
34
|
+
chain.emitter.emit(routes.events.EventType.voluntaryExit, exit);
|
|
35
|
+
await network.publishVoluntaryExit(exit);
|
|
36
|
+
logger.info("Voluntary exit successfully published for validator", {
|
|
37
|
+
validatorIndex: exit.message.validatorIndex,
|
|
38
|
+
});
|
|
39
|
+
} catch (e) {
|
|
40
|
+
logger.warn(
|
|
41
|
+
"Failed to publish deferred voluntary exit",
|
|
42
|
+
{validatorIndex: exit.message.validatorIndex},
|
|
43
|
+
e as Error
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
logger.warn("Failed to drain deferred voluntary exit pool", {}, e as Error);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
chain.clock.addListener(ClockEvent.epoch, onEpoch);
|
|
53
|
+
signal.addEventListener("abort", () => chain.clock.removeListener(ClockEvent.epoch, onEpoch), {once: true});
|
|
54
|
+
}
|
package/src/node/nodejs.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {setMaxListeners} from "node:events";
|
|
2
2
|
import {PrivateKey} from "@libp2p/interface";
|
|
3
3
|
import {Registry} from "prom-client";
|
|
4
|
+
import {type PubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
|
|
4
5
|
import {hasher} from "@chainsafe/persistent-merkle-tree";
|
|
5
6
|
import {BeaconApiMethods} from "@lodestar/api/beacon/server";
|
|
6
7
|
import {BeaconConfig} from "@lodestar/config";
|
|
7
8
|
import type {LoggerNode} from "@lodestar/logger/node";
|
|
8
9
|
import {ZERO_HASH_HEX} from "@lodestar/params";
|
|
9
|
-
import {IBeaconStateView,
|
|
10
|
+
import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
|
|
10
11
|
import {phase0} from "@lodestar/types";
|
|
11
12
|
import {sleep, toRootHex} from "@lodestar/utils";
|
|
12
13
|
import {ProcessShutdownCallback} from "@lodestar/validator";
|
|
@@ -21,6 +22,7 @@ import {Network, getReqRespHandlers} from "../network/index.js";
|
|
|
21
22
|
import {BackfillSync} from "../sync/backfill/index.js";
|
|
22
23
|
import {BeaconSync, IBeaconSync} from "../sync/index.js";
|
|
23
24
|
import {Clock} from "../util/clock.js";
|
|
25
|
+
import {startDeferredVoluntaryExitPublisher} from "./deferredVoluntaryExitPublisher.js";
|
|
24
26
|
import {runNodeNotifier} from "./notifier.js";
|
|
25
27
|
import {IBeaconNodeOptions} from "./options.js";
|
|
26
28
|
|
|
@@ -334,6 +336,8 @@ export class BeaconNode {
|
|
|
334
336
|
|
|
335
337
|
void runNodeNotifier({network, chain, sync, config, logger, signal});
|
|
336
338
|
|
|
339
|
+
startDeferredVoluntaryExitPublisher({chain, network, logger, signal});
|
|
340
|
+
|
|
337
341
|
return new BeaconNode({
|
|
338
342
|
opts,
|
|
339
343
|
config,
|
package/src/sync/range/batch.ts
CHANGED
|
@@ -39,6 +39,7 @@ export enum BatchStatus {
|
|
|
39
39
|
AwaitingValidation = "AwaitingValidation",
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/** Identity of one download attempt*/
|
|
42
43
|
export type Attempt = {
|
|
43
44
|
/** The peer that made the attempt */
|
|
44
45
|
peers: PeerIdStr[];
|
|
@@ -46,6 +47,16 @@ export type Attempt = {
|
|
|
46
47
|
hash: RootHex;
|
|
47
48
|
};
|
|
48
49
|
|
|
50
|
+
/**
|
|
51
|
+
* A failed attempt that has been classified for blame.
|
|
52
|
+
*/
|
|
53
|
+
export type FailedAttempt = Attempt & {
|
|
54
|
+
/**
|
|
55
|
+
* True when the failure is evidence the peers served bad data, so they may be downscored..
|
|
56
|
+
*/
|
|
57
|
+
peerAttributable: boolean;
|
|
58
|
+
};
|
|
59
|
+
|
|
49
60
|
type TrackedRequest = {
|
|
50
61
|
/** only happen for the 1st batch in checkpoint sync */
|
|
51
62
|
parentPayload: boolean;
|
|
@@ -108,10 +119,12 @@ export type BatchMetadata = {
|
|
|
108
119
|
// Retry counters
|
|
109
120
|
downloadAttempts: number;
|
|
110
121
|
processingAttempts: number;
|
|
122
|
+
executionErrorAttempts: number;
|
|
111
123
|
|
|
112
124
|
// Cumulative peer attribution for failed attempts (only present when non-empty)
|
|
113
125
|
failedDownloadPeers?: string;
|
|
114
126
|
failedProcessingPeers?: string;
|
|
127
|
+
executionErrorPeers?: string;
|
|
115
128
|
};
|
|
116
129
|
|
|
117
130
|
function formatRangeReq(req: {startSlot: Slot; count: number}): string {
|
|
@@ -152,10 +165,10 @@ export class Batch {
|
|
|
152
165
|
state: BatchState = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
|
|
153
166
|
/** Peers that provided good data, with column coverage for by_range requests */
|
|
154
167
|
private readonly successfulDownloads = new Map<PeerIdStr, TrackedRequest>();
|
|
155
|
-
/** The
|
|
156
|
-
readonly failedProcessingAttempts:
|
|
157
|
-
/** The
|
|
158
|
-
readonly executionErrorAttempts:
|
|
168
|
+
/** The attempts that have been made and failed to send us this batch. */
|
|
169
|
+
readonly failedProcessingAttempts: FailedAttempt[] = [];
|
|
170
|
+
/** The attempts that have been made and failed because of execution malfunction. */
|
|
171
|
+
readonly executionErrorAttempts: FailedAttempt[] = [];
|
|
159
172
|
/** The number of download retries this batch has undergone due to a failed request. */
|
|
160
173
|
private readonly failedDownloadAttempts: PeerIdStr[] = [];
|
|
161
174
|
private readonly config: ChainForkConfig;
|
|
@@ -419,10 +432,18 @@ export class Batch {
|
|
|
419
432
|
}
|
|
420
433
|
|
|
421
434
|
/**
|
|
422
|
-
* Gives a list of peers from which this batch has had a failed download or
|
|
435
|
+
* Gives a list of peers from which this batch has had a failed download attempt, or a
|
|
436
|
+
* peer-attributable failed processing/execution attempt.
|
|
423
437
|
*/
|
|
424
438
|
getFailedPeers(): PeerIdStr[] {
|
|
425
|
-
return [...this.failedDownloadAttempts, ...this.
|
|
439
|
+
return [...this.failedDownloadAttempts, ...this.getPeerAttributableAttempts().flatMap((a) => a.peers)];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Attempts whose failure is peer-attributable, across both the processing and execution error
|
|
444
|
+
*/
|
|
445
|
+
getPeerAttributableAttempts(): FailedAttempt[] {
|
|
446
|
+
return [...this.failedProcessingAttempts, ...this.executionErrorAttempts].filter((a) => a.peerAttributable);
|
|
426
447
|
}
|
|
427
448
|
|
|
428
449
|
/**
|
|
@@ -457,6 +478,7 @@ export class Batch {
|
|
|
457
478
|
getMetadata(): BatchMetadata {
|
|
458
479
|
const {blocksRequest, blobsRequest, columnsRequest, envelopesRequest} = this.requests;
|
|
459
480
|
const failedProcessingPeerList = this.failedProcessingAttempts.flatMap((a) => a.peers);
|
|
481
|
+
const executionErrorPeerList = this.executionErrorAttempts.flatMap((a) => a.peers);
|
|
460
482
|
return {
|
|
461
483
|
startEpoch: this.startEpoch,
|
|
462
484
|
startSlot: this.startSlot,
|
|
@@ -468,12 +490,16 @@ export class Batch {
|
|
|
468
490
|
...(envelopesRequest && {envelopesReq: formatRangeReq(envelopesRequest)}),
|
|
469
491
|
downloadAttempts: this.failedDownloadAttempts.length,
|
|
470
492
|
processingAttempts: this.failedProcessingAttempts.length,
|
|
493
|
+
executionErrorAttempts: this.executionErrorAttempts.length,
|
|
471
494
|
...(this.failedDownloadAttempts.length > 0 && {
|
|
472
495
|
failedDownloadPeers: this.failedDownloadAttempts.join(","),
|
|
473
496
|
}),
|
|
474
497
|
...(failedProcessingPeerList.length > 0 && {
|
|
475
498
|
failedProcessingPeers: failedProcessingPeerList.join(","),
|
|
476
499
|
}),
|
|
500
|
+
...(executionErrorPeerList.length > 0 && {
|
|
501
|
+
executionErrorPeers: executionErrorPeerList.join(","),
|
|
502
|
+
}),
|
|
477
503
|
};
|
|
478
504
|
}
|
|
479
505
|
|
|
@@ -593,7 +619,7 @@ export class Batch {
|
|
|
593
619
|
if (allComplete) {
|
|
594
620
|
const attempt: Attempt = {
|
|
595
621
|
peers: this.getSuccessfulPeers(),
|
|
596
|
-
hash: hashBlocks(blocks,
|
|
622
|
+
hash: hashBlocks(blocks, payloadEnvelopes),
|
|
597
623
|
};
|
|
598
624
|
this.state = {status: BatchStatus.AwaitingProcessing, blocks, payloadEnvelopes: newPayloadEnvelopes, attempt};
|
|
599
625
|
} else {
|
|
@@ -703,11 +729,7 @@ export class Batch {
|
|
|
703
729
|
throw new BatchError(this.wrongStatusErrorType(BatchStatus.Processing));
|
|
704
730
|
}
|
|
705
731
|
|
|
706
|
-
|
|
707
|
-
this.onExecutionEngineError(this.state.attempt);
|
|
708
|
-
} else {
|
|
709
|
-
this.onProcessingError(this.state.attempt);
|
|
710
|
-
}
|
|
732
|
+
this.routeProcessingFailure(err, this.state.attempt);
|
|
711
733
|
}
|
|
712
734
|
|
|
713
735
|
/**
|
|
@@ -718,10 +740,17 @@ export class Batch {
|
|
|
718
740
|
throw new BatchError(this.wrongStatusErrorType(BatchStatus.AwaitingValidation));
|
|
719
741
|
}
|
|
720
742
|
|
|
721
|
-
|
|
722
|
-
|
|
743
|
+
this.routeProcessingFailure(err, this.state.attempt);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
private routeProcessingFailure(err: Error, attempt: Attempt): void {
|
|
747
|
+
const code = err instanceof BlockError || err instanceof PayloadError ? err.type.code : null;
|
|
748
|
+
const failedAttempt: FailedAttempt = {...attempt, peerAttributable: isPeerAttributableFailure(code)};
|
|
749
|
+
|
|
750
|
+
if (isExecutionEngineFailure(code)) {
|
|
751
|
+
this.onExecutionEngineError(failedAttempt);
|
|
723
752
|
} else {
|
|
724
|
-
this.onProcessingError(
|
|
753
|
+
this.onProcessingError(failedAttempt);
|
|
725
754
|
}
|
|
726
755
|
}
|
|
727
756
|
|
|
@@ -735,7 +764,7 @@ export class Batch {
|
|
|
735
764
|
return this.state.attempt;
|
|
736
765
|
}
|
|
737
766
|
|
|
738
|
-
private onExecutionEngineError(attempt:
|
|
767
|
+
private onExecutionEngineError(attempt: FailedAttempt): void {
|
|
739
768
|
this.executionErrorAttempts.push(attempt);
|
|
740
769
|
if (this.executionErrorAttempts.length > MAX_BATCH_PROCESSING_ATTEMPTS) {
|
|
741
770
|
throw new BatchError(this.errorType({code: BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS}));
|
|
@@ -746,7 +775,7 @@ export class Batch {
|
|
|
746
775
|
this.state = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
|
|
747
776
|
}
|
|
748
777
|
|
|
749
|
-
private onProcessingError(attempt:
|
|
778
|
+
private onProcessingError(attempt: FailedAttempt): void {
|
|
750
779
|
this.failedProcessingAttempts.push(attempt);
|
|
751
780
|
if (this.failedProcessingAttempts.length > MAX_BATCH_PROCESSING_ATTEMPTS) {
|
|
752
781
|
throw new BatchError(this.errorType({code: BatchErrorCode.MAX_PROCESSING_ATTEMPTS}));
|
|
@@ -789,18 +818,44 @@ type BatchErrorMetadata = {
|
|
|
789
818
|
|
|
790
819
|
export class BatchError extends LodestarError<BatchErrorType & BatchErrorMetadata> {}
|
|
791
820
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
if (err.type.code === BlockErrorCode.EXECUTION_ENGINE_ERROR) {
|
|
798
|
-
return true;
|
|
799
|
-
}
|
|
800
|
-
|
|
821
|
+
/**
|
|
822
|
+
* Whether a processing failure is an execution-engine failure. Pre-gloas, all EL errors stay in
|
|
823
|
+
* BlockErrorCode, post-gloas, they stay in PayloadErrorCode.
|
|
824
|
+
*/
|
|
825
|
+
function isExecutionEngineFailure(code: BlockErrorCode | PayloadErrorCode | null): boolean {
|
|
801
826
|
return (
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
827
|
+
code === BlockErrorCode.EXECUTION_ENGINE_INVALID ||
|
|
828
|
+
code === BlockErrorCode.EXECUTION_ENGINE_ERROR ||
|
|
829
|
+
code === PayloadErrorCode.EXECUTION_ENGINE_INVALID ||
|
|
830
|
+
code === PayloadErrorCode.EXECUTION_ENGINE_ERROR
|
|
805
831
|
);
|
|
806
832
|
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Based on an error code, return if peer is attributable for FailedAttempt.
|
|
836
|
+
*/
|
|
837
|
+
function isPeerAttributableFailure(code: BlockErrorCode | PayloadErrorCode | null): boolean {
|
|
838
|
+
switch (code) {
|
|
839
|
+
// EL returned a definitive INVALID verdict on the payload
|
|
840
|
+
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
841
|
+
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
842
|
+
// a block or envelope signature is invalid
|
|
843
|
+
case BlockErrorCode.INVALID_SIGNATURE:
|
|
844
|
+
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
845
|
+
// the block's state transition produced an invalid state root, or failed per_slot/per_block processing
|
|
846
|
+
case BlockErrorCode.INVALID_STATE_ROOT:
|
|
847
|
+
case BlockErrorCode.PER_BLOCK_PROCESSING_ERROR:
|
|
848
|
+
// the segment this peer served is internally inconsistent (in-segment link breaks, see PR #9684)
|
|
849
|
+
case BlockErrorCode.NON_LINEAR_SLOTS:
|
|
850
|
+
case BlockErrorCode.NON_LINEAR_PARENT_ROOTS:
|
|
851
|
+
case BlockErrorCode.NON_LINEAR_PAYLOAD_ROOTS:
|
|
852
|
+
// the envelope references a mismatched block root, or fails field verification
|
|
853
|
+
case BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH:
|
|
854
|
+
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
855
|
+
// the peer served a block we have explicitly blacklisted
|
|
856
|
+
case BlockErrorCode.BLACKLISTED_BLOCK:
|
|
857
|
+
return true;
|
|
858
|
+
default:
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
}
|
package/src/sync/range/chain.ts
CHANGED
|
@@ -358,9 +358,10 @@ export class SyncChain {
|
|
|
358
358
|
// If a batch exceeds it's retry limit, maybe downscore peers.
|
|
359
359
|
// shouldDownscoreOnBatchError() functions enforces that all BatchErrorCode values are covered
|
|
360
360
|
if (e instanceof BatchError) {
|
|
361
|
-
const shouldReportPeer = shouldReportPeerOnBatchError(e.type.code);
|
|
361
|
+
const shouldReportPeer = shouldReportPeerOnBatchError(e.type.code, this.batches.get(e.type.startEpoch));
|
|
362
362
|
if (shouldReportPeer) {
|
|
363
|
-
|
|
363
|
+
// Only the peers actually at fault are reported, never the whole peerset.
|
|
364
|
+
for (const peer of shouldReportPeer.peers) {
|
|
364
365
|
this.reportPeer(peer, shouldReportPeer.action, shouldReportPeer.reason);
|
|
365
366
|
}
|
|
366
367
|
}
|
|
@@ -768,8 +769,11 @@ export class SyncChain {
|
|
|
768
769
|
|
|
769
770
|
// The last batch attempt is right, all others are wrong. Penalize other peers
|
|
770
771
|
const attemptOk = batch.validationSuccess();
|
|
771
|
-
for (const attempt of batch.
|
|
772
|
+
for (const attempt of batch.getPeerAttributableAttempts()) {
|
|
772
773
|
if (attempt.hash !== attemptOk.hash) {
|
|
774
|
+
// attempt.peers contains block/column/envelope peers.
|
|
775
|
+
// And if there is one bad envelope we will downscore block and column peers to
|
|
776
|
+
// TODO: resolve this
|
|
773
777
|
for (const badAttemptPeer of attempt.peers) {
|
|
774
778
|
if (attemptOk.peers.find((goodPeer) => goodPeer === badAttemptPeer)) {
|
|
775
779
|
// The same peer corrected its previous attempt
|
|
@@ -830,21 +834,32 @@ export class SyncChain {
|
|
|
830
834
|
* If peer should not be downscored, returns null.
|
|
831
835
|
*/
|
|
832
836
|
export function shouldReportPeerOnBatchError(
|
|
833
|
-
code: BatchErrorCode
|
|
834
|
-
|
|
837
|
+
code: BatchErrorCode,
|
|
838
|
+
batch: Batch | undefined
|
|
839
|
+
): {action: PeerAction.LowToleranceError; reason: string; peers: PeerIdStr[]} | null {
|
|
835
840
|
switch (code) {
|
|
836
|
-
//
|
|
837
|
-
//
|
|
838
|
-
// We drop the chain and report all peers.
|
|
839
|
-
// There are some edge cases with forks that could cause this situation, but it's unlikely.
|
|
841
|
+
// A batch could not be processed after max retry limit. Report
|
|
842
|
+
// only the peers that served a peer-attributable failed attempt.
|
|
840
843
|
case BatchErrorCode.MAX_PROCESSING_ATTEMPTS:
|
|
841
|
-
|
|
844
|
+
case BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS: {
|
|
845
|
+
// penalize both the processing-error and execution-error peers
|
|
846
|
+
const attributablePeers = [
|
|
847
|
+
...new Set(batch?.getPeerAttributableAttempts().flatMap((attempt) => attempt.peers) ?? []),
|
|
848
|
+
];
|
|
849
|
+
if (attributablePeers.length === 0) {
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
const reason =
|
|
853
|
+
code === BatchErrorCode.MAX_PROCESSING_ATTEMPTS
|
|
854
|
+
? "SyncChainMaxProcessingAttempts"
|
|
855
|
+
: "SyncChainMaxExecutionEngineErrorAttempts";
|
|
856
|
+
return {action: PeerAction.LowToleranceError, reason, peers: attributablePeers};
|
|
857
|
+
}
|
|
842
858
|
|
|
843
859
|
// TODO: Should peers be reported for MAX_DOWNLOAD_ATTEMPTS?
|
|
844
860
|
case BatchErrorCode.MAX_DOWNLOAD_ATTEMPTS:
|
|
845
861
|
case BatchErrorCode.INVALID_COUNT:
|
|
846
862
|
case BatchErrorCode.WRONG_STATUS:
|
|
847
|
-
case BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS:
|
|
848
863
|
return null;
|
|
849
864
|
}
|
|
850
865
|
}
|
|
@@ -1,27 +1,62 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {RootHex,
|
|
3
|
-
import {toRootHex} from "@lodestar/utils";
|
|
1
|
+
import {digest} from "@chainsafe/as-sha256";
|
|
2
|
+
import {RootHex, Slot, ssz} from "@lodestar/types";
|
|
3
|
+
import {fromHex, toRootHex} from "@lodestar/utils";
|
|
4
4
|
import {IBlockInput} from "../../../chain/blocks/blockInput/types.js";
|
|
5
|
+
import {PayloadEnvelopeInput} from "../../../chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
6
|
+
|
|
7
|
+
const ROOT_SIZE = 32;
|
|
8
|
+
const SIGNATURE_SIZE = 96;
|
|
9
|
+
/** Every block and every payload envelope contributes a fixed-size (root, signature) entry */
|
|
10
|
+
const ENTRY_SIZE = ROOT_SIZE + SIGNATURE_SIZE;
|
|
11
|
+
/** uint32 LE counts of block and envelope entries, so the variable-length sections are self-describing */
|
|
12
|
+
const COUNT_SIZE = 4;
|
|
13
|
+
const HEADER_SIZE = COUNT_SIZE * 2;
|
|
5
14
|
|
|
6
15
|
/**
|
|
7
|
-
*
|
|
16
|
+
* Root uniquely identifying a batch attempt (its blocks AND payloads). Used for peer scoring and to
|
|
17
|
+
* compare if two attempts are equivalent.
|
|
18
|
+
*
|
|
19
|
+
* Signatures are part of the id for BOTH blocks and payload envelopes, not just their message roots:
|
|
20
|
+
* a peer serving a correct message with a garbage signature would otherwise produce the same id as
|
|
21
|
+
* the honest attempt and escape scoring in `SyncChain.advanceChain`. Signatures are not verified at
|
|
22
|
+
* download time, only during processChainSegment (block signatures in verifyBlocksSignatures,
|
|
23
|
+
* envelope signatures in importExecutionPayload).
|
|
24
|
+
*
|
|
25
|
+
* Entries are written as raw bytes into a single exactly-sized buffer and digested once, so an
|
|
26
|
+
* `Attempt` retains 32 bytes rather than the ~16 KB a full 32-slot gloas batch would need if the
|
|
27
|
+
* roots and signatures were concatenated as hex. sha256 (not a 64-bit hash) because a peer that
|
|
28
|
+
* could force a collision with the winning attempt would escape peer scoring.
|
|
8
29
|
*/
|
|
9
|
-
export function hashBlocks(blocks: IBlockInput[],
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
export function hashBlocks(blocks: IBlockInput[], payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null): RootHex {
|
|
31
|
+
// Envelopes without a payload carry no attributable data, so they are skipped entirely. A `null`
|
|
32
|
+
// map and a map whose entries all lack a payload are therefore equivalent, which is intended.
|
|
33
|
+
const envelopes =
|
|
34
|
+
payloadEnvelopes && payloadEnvelopes.size > 0
|
|
35
|
+
? Array.from(payloadEnvelopes.entries())
|
|
36
|
+
.filter(([, envelope]) => envelope.hasPayloadEnvelope())
|
|
37
|
+
.sort(([slotA], [slotB]) => slotA - slotB)
|
|
38
|
+
: [];
|
|
39
|
+
|
|
40
|
+
const buf = Buffer.allocUnsafe(HEADER_SIZE + (blocks.length + envelopes.length) * ENTRY_SIZE);
|
|
41
|
+
buf.writeUInt32LE(blocks.length, 0);
|
|
42
|
+
buf.writeUInt32LE(envelopes.length, COUNT_SIZE);
|
|
43
|
+
let offset = HEADER_SIZE;
|
|
44
|
+
|
|
45
|
+
for (const block of blocks) {
|
|
46
|
+
buf.set(fromHex(block.blockRootHex), offset);
|
|
47
|
+
offset += ROOT_SIZE;
|
|
48
|
+
buf.set(block.getBlock().signature, offset);
|
|
49
|
+
offset += SIGNATURE_SIZE;
|
|
26
50
|
}
|
|
51
|
+
|
|
52
|
+
for (const [, envelope] of envelopes) {
|
|
53
|
+
const signedEnvelope = envelope.getPayloadEnvelope();
|
|
54
|
+
// envelope's root is cached thanks to cachePermanentRootStruct, so this avoids re-hashing
|
|
55
|
+
buf.set(ssz.gloas.ExecutionPayloadEnvelope.hashTreeRoot(signedEnvelope.message), offset);
|
|
56
|
+
offset += ROOT_SIZE;
|
|
57
|
+
buf.set(signedEnvelope.signature, offset);
|
|
58
|
+
offset += SIGNATURE_SIZE;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return toRootHex(digest(buf));
|
|
27
62
|
}
|
package/src/sync/types.ts
CHANGED
|
@@ -36,6 +36,39 @@ export enum PendingBlockInputStatus {
|
|
|
36
36
|
processing = "processing",
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Why a pending item was dropped from BlockInputSync WITHOUT completing.
|
|
41
|
+
* The happy path — a successfully imported/processed block or payload — is removed by the import
|
|
42
|
+
* flow (onPayloadImported, processReadyBlock/processPayload success) and is deliberately NOT tracked
|
|
43
|
+
* here
|
|
44
|
+
*/
|
|
45
|
+
export enum DroppedItemReason {
|
|
46
|
+
/** prune: item's own slot is below the finalized slot (routine cleanup), or a downloaded block landed at/below finality */
|
|
47
|
+
belowFinalized = "below_finalized",
|
|
48
|
+
/** prune: slot-less, unresolvable payload evicted after PRUNE_UNRESOLVED_SLOT_EPOCHS */
|
|
49
|
+
agedOut = "aged_out",
|
|
50
|
+
/** pruneSetToMax evicted the oldest entry because the cache hit its capacity (DoS guard) */
|
|
51
|
+
capacity = "capacity",
|
|
52
|
+
/** descendant removed because an ancestor was invalid/removed */
|
|
53
|
+
invalidParent = "invalid_parent",
|
|
54
|
+
/** transient execution-engine error removal (EXECUTION_ENGINE_ERROR) */
|
|
55
|
+
elError = "el_error",
|
|
56
|
+
/** the execution engine declared the item invalid (EXECUTION_ENGINE_INVALID) */
|
|
57
|
+
elInvalid = "el_invalid",
|
|
58
|
+
/** a block failed our own checks (not correct w.r.t. our chain) */
|
|
59
|
+
invalidBlock = "invalid_block",
|
|
60
|
+
/** a block declares a parent payload hash that conflicts with the parent's actual payload (gloas) */
|
|
61
|
+
invalidParentPayload = "invalid_parent_payload",
|
|
62
|
+
/** a payload envelope failed gossip verification (ENVELOPE_VERIFICATION_ERROR) */
|
|
63
|
+
invalidEnvelope = "invalid_envelope",
|
|
64
|
+
/** a payload had an invalid signature (INVALID_SIGNATURE) */
|
|
65
|
+
invalidSignature = "invalid_signature",
|
|
66
|
+
/** a block root was given up on after exhausting download attempts (data could not be fetched) */
|
|
67
|
+
unavailable = "unavailable",
|
|
68
|
+
/** an unexpected / unhandled error code led to removal */
|
|
69
|
+
unknown = "unknown",
|
|
70
|
+
}
|
|
71
|
+
|
|
39
72
|
export enum PendingPayloadInputStatus {
|
|
40
73
|
pending = "pending",
|
|
41
74
|
fetching = "fetching",
|
|
@@ -77,7 +110,9 @@ export type PendingPayloadInput = {
|
|
|
77
110
|
export type PendingPayloadRootHex = {
|
|
78
111
|
status: PendingPayloadInputStatus.pending | PendingPayloadInputStatus.fetching;
|
|
79
112
|
rootHex: RootHex;
|
|
80
|
-
//
|
|
113
|
+
// Trusted slot only (fork choice / validated data), may be missing until resolved. NOT the gossip
|
|
114
|
+
// message slot from ChainEvent.unknownEnvelopeBlockRoot, which is untrusted and not necessarily the
|
|
115
|
+
// payload/block slot. See BlockInputSync.resolvePayloadSlot.
|
|
81
116
|
slot?: Slot;
|
|
82
117
|
timeAddedSec: number;
|
|
83
118
|
timeSyncedSec?: number;
|