@lodestar/beacon-node 1.43.0-dev.aef3645690 → 1.43.0-dev.b05ea98d04
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 +17 -9
- 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 +45 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -2
- 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/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +0 -1
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +68 -5
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/GetBlobsTracker.d.ts +1 -1
- package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
- package/lib/chain/GetBlobsTracker.js +1 -2
- package/lib/chain/GetBlobsTracker.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/interface.d.ts +4 -4
- package/lib/chain/archiveStore/interface.d.ts.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts +4 -4
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js +2 -4
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +2 -2
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +110 -58
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.d.ts +3 -0
- package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.js +4 -1
- package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +38 -58
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +28 -14
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +89 -89
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts +5 -3
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +59 -26
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +7 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +29 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.js +7 -5
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +16 -21
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +23 -2
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +89 -12
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts +5 -3
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +50 -7
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +0 -4
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -2
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +2 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +25 -5
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +24 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +79 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +14 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +30 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -0
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +2 -11
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts +8 -6
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +46 -50
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +16 -15
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +5 -4
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/attestationError.d.ts +8 -1
- package/lib/chain/errors/attestationError.d.ts.map +1 -1
- package/lib/chain/errors/attestationError.js +4 -0
- package/lib/chain/errors/attestationError.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +8 -1
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +2 -0
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +1 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +5 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/errors/index.d.ts +1 -0
- package/lib/chain/errors/index.d.ts.map +1 -1
- package/lib/chain/errors/index.js +1 -0
- package/lib/chain/errors/index.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +40 -0
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
- package/lib/chain/errors/proposerPreferences.js +14 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -0
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +11 -15
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/initState.d.ts.map +1 -1
- package/lib/chain/initState.js +6 -1
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/interface.d.ts +7 -5
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +26 -4
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +47 -23
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts +3 -9
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +5 -32
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +13 -8
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +68 -25
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/errors.d.ts +1 -11
- package/lib/chain/regen/errors.d.ts.map +1 -1
- package/lib/chain/regen/errors.js +0 -2
- package/lib/chain/regen/errors.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +7 -12
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +1 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts +6 -11
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +9 -44
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +0 -5
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +8 -38
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +1 -0
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +1 -0
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +22 -6
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +53 -17
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +16 -0
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
- package/lib/chain/seenCache/seenProposerPreferences.js +26 -0
- package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
- package/lib/chain/stateCache/datastore/db.d.ts +5 -4
- package/lib/chain/stateCache/datastore/db.d.ts.map +1 -1
- package/lib/chain/stateCache/datastore/db.js +10 -32
- package/lib/chain/stateCache/datastore/db.js.map +1 -1
- package/lib/chain/stateCache/datastore/file.d.ts +1 -1
- package/lib/chain/stateCache/datastore/file.d.ts.map +1 -1
- package/lib/chain/stateCache/datastore/file.js +5 -5
- package/lib/chain/stateCache/datastore/file.js.map +1 -1
- package/lib/chain/stateCache/datastore/types.d.ts +1 -1
- package/lib/chain/stateCache/datastore/types.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +1 -7
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +0 -8
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +13 -30
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +120 -216
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +8 -15
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/stateCache/types.js.map +1 -1
- package/lib/chain/validation/aggregateAndProof.js +12 -0
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +12 -0
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +1 -0
- 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 +24 -9
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +21 -11
- 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 +4 -3
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts +8 -0
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -0
- package/lib/chain/validation/proposerPreferences.js +91 -0
- package/lib/chain/validation/proposerPreferences.js.map +1 -0
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +21 -14
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +1 -0
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +6 -0
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +20 -0
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +18 -0
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +1 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +4 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/interface.d.ts +7 -1
- package/lib/network/gossip/interface.d.ts.map +1 -1
- package/lib/network/gossip/interface.js +1 -0
- package/lib/network/gossip/interface.js.map +1 -1
- package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
- package/lib/network/gossip/scoringParameters.js +12 -1
- package/lib/network/gossip/scoringParameters.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +32 -748
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +6 -0
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/interface.d.ts +1 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +1 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +6 -1
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +60 -22
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
- package/lib/network/processor/gossipQueues/index.js +5 -0
- package/lib/network/processor/gossipQueues/index.js.map +1 -1
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +6 -5
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -6
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js +11 -5
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +17 -5
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +7 -4
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +6 -4
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/constants.d.ts +3 -1
- package/lib/sync/constants.d.ts.map +1 -1
- package/lib/sync/constants.js +3 -4
- package/lib/sync/constants.js.map +1 -1
- package/lib/sync/range/batch.d.ts +28 -2
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +146 -44
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +12 -2
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +53 -9
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +17 -6
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/types.d.ts +34 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +34 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +22 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +604 -53
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +46 -10
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +162 -24
- 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 +16 -2
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/sync/utils/pendingBlocksTree.d.ts +0 -1
- package/lib/sync/utils/pendingBlocksTree.d.ts.map +1 -1
- package/lib/sync/utils/pendingBlocksTree.js +0 -9
- package/lib/sync/utils/pendingBlocksTree.js.map +1 -1
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +20 -5
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +17 -16
- package/src/api/impl/beacon/blocks/index.ts +22 -9
- package/src/api/impl/beacon/pool/index.ts +83 -1
- package/src/api/impl/beacon/state/utils.ts +2 -2
- package/src/api/impl/debug/index.ts +0 -1
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/validator/index.ts +84 -6
- package/src/chain/GetBlobsTracker.ts +1 -2
- package/src/chain/archiveStore/archiveStore.ts +5 -5
- package/src/chain/archiveStore/interface.ts +4 -4
- package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +6 -8
- package/src/chain/archiveStore/utils/archiveBlocks.ts +153 -94
- package/src/chain/blocks/blockInput/blockInput.ts +4 -1
- package/src/chain/blocks/importBlock.ts +38 -83
- package/src/chain/blocks/importExecutionPayload.ts +110 -102
- package/src/chain/blocks/index.ts +74 -24
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +37 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -0
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +7 -6
- package/src/chain/blocks/types.ts +16 -26
- package/src/chain/blocks/utils/chainSegment.ts +114 -17
- package/src/chain/blocks/verifyBlock.ts +70 -9
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +26 -7
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +134 -0
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +41 -0
- package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +9 -18
- package/src/chain/chain.ts +63 -72
- package/src/chain/emitter.ts +15 -14
- package/src/chain/errors/attestationError.ts +6 -1
- package/src/chain/errors/blockError.ts +4 -1
- package/src/chain/errors/executionPayloadBid.ts +6 -0
- package/src/chain/errors/executionPayloadEnvelope.ts +6 -0
- package/src/chain/errors/index.ts +1 -0
- package/src/chain/errors/proposerPreferences.ts +47 -0
- package/src/chain/forkChoice/index.ts +8 -20
- package/src/chain/initState.ts +9 -1
- package/src/chain/interface.ts +11 -3
- package/src/chain/opPools/payloadAttestationPool.ts +29 -8
- package/src/chain/prepareNextSlot.ts +55 -24
- package/src/chain/produceBlock/computeNewStateRoot.ts +6 -43
- package/src/chain/produceBlock/produceBlockBody.ts +91 -27
- package/src/chain/regen/errors.ts +1 -6
- package/src/chain/regen/interface.ts +7 -12
- package/src/chain/regen/queued.ts +14 -55
- package/src/chain/regen/regen.ts +10 -43
- package/src/chain/seenCache/index.ts +1 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +71 -20
- package/src/chain/seenCache/seenProposerPreferences.ts +32 -0
- package/src/chain/stateCache/datastore/db.ts +10 -33
- package/src/chain/stateCache/datastore/file.ts +5 -6
- package/src/chain/stateCache/datastore/types.ts +2 -3
- package/src/chain/stateCache/fifoBlockStateCache.ts +1 -10
- package/src/chain/stateCache/persistentCheckpointsCache.ts +139 -247
- package/src/chain/stateCache/types.ts +8 -14
- package/src/chain/validation/aggregateAndProof.ts +13 -0
- package/src/chain/validation/attestation.ts +13 -0
- package/src/chain/validation/block.ts +1 -0
- package/src/chain/validation/executionPayloadBid.ts +25 -8
- package/src/chain/validation/executionPayloadEnvelope.ts +22 -12
- package/src/chain/validation/payloadAttestationMessage.ts +5 -3
- package/src/chain/validation/proposerPreferences.ts +110 -0
- package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
- package/src/execution/engine/http.ts +21 -14
- package/src/execution/engine/interface.ts +1 -0
- package/src/execution/engine/mock.ts +8 -1
- package/src/execution/engine/types.ts +41 -0
- package/src/metrics/metrics/lodestar.ts +4 -0
- package/src/network/gossip/interface.ts +6 -0
- package/src/network/gossip/scoringParameters.ts +14 -1
- package/src/network/gossip/topic.ts +6 -0
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +12 -1
- package/src/network/processor/gossipHandlers.ts +79 -27
- package/src/network/processor/gossipQueues/index.ts +5 -0
- package/src/network/processor/index.ts +6 -5
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +14 -6
- package/src/network/reqresp/handlers/blobSidecarsByRange.ts +11 -5
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -5
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +7 -4
- package/src/node/nodejs.ts +6 -4
- package/src/sync/constants.ts +4 -4
- package/src/sync/range/batch.ts +204 -49
- package/src/sync/range/chain.ts +69 -11
- package/src/sync/range/range.ts +18 -6
- package/src/sync/types.ts +72 -0
- package/src/sync/unknownBlock.ts +762 -57
- package/src/sync/utils/downloadByRange.ts +272 -39
- package/src/sync/utils/downloadByRoot.ts +24 -2
- package/src/sync/utils/pendingBlocksTree.ts +0 -15
- package/src/util/sszBytes.ts +25 -5
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
import {routes} from "@lodestar/api";
|
|
1
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
3
|
import {ForkSeq} from "@lodestar/params";
|
|
3
4
|
import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
|
|
4
5
|
import {computeTimeAtSlot} from "@lodestar/state-transition";
|
|
5
|
-
import {RootHex} from "@lodestar/types";
|
|
6
|
-
import {Logger, prettyPrintIndices, pruneSetToMax, sleep} from "@lodestar/utils";
|
|
6
|
+
import {RootHex, gloas} from "@lodestar/types";
|
|
7
|
+
import {Logger, fromHex, prettyPrintIndices, pruneSetToMax, sleep, toRootHex} from "@lodestar/utils";
|
|
7
8
|
import {isBlockInputBlobs, isBlockInputColumns} from "../chain/blocks/blockInput/blockInput.js";
|
|
8
9
|
import {BlockInputSource, IBlockInput} from "../chain/blocks/blockInput/types.js";
|
|
10
|
+
import {PayloadError, PayloadErrorCode} from "../chain/blocks/importExecutionPayload.js";
|
|
11
|
+
import {PayloadEnvelopeInput, PayloadEnvelopeInputSource} from "../chain/blocks/payloadEnvelopeInput/index.js";
|
|
9
12
|
import {BlockError, BlockErrorCode} from "../chain/errors/index.js";
|
|
10
13
|
import {ChainEvent, ChainEventData, IBeaconChain} from "../chain/index.js";
|
|
14
|
+
import {validateGloasBlockDataColumnSidecars} from "../chain/validation/dataColumnSidecar.js";
|
|
15
|
+
import {validateGossipExecutionPayloadEnvelope} from "../chain/validation/executionPayloadEnvelope.js";
|
|
11
16
|
import {Metrics} from "../metrics/index.js";
|
|
12
17
|
import {INetwork, NetworkEvent, NetworkEventData, prettyPrintPeerIdStr} from "../network/index.js";
|
|
13
18
|
import {PeerSyncMeta} from "../network/peers/peersData.js";
|
|
@@ -19,20 +24,37 @@ import {MAX_CONCURRENT_REQUESTS} from "./constants.js";
|
|
|
19
24
|
import {SyncOptions} from "./options.js";
|
|
20
25
|
import {
|
|
21
26
|
BlockInputSyncCacheItem,
|
|
27
|
+
PayloadSyncCacheItem,
|
|
22
28
|
PendingBlockInput,
|
|
23
29
|
PendingBlockInputStatus,
|
|
24
30
|
PendingBlockType,
|
|
31
|
+
PendingPayloadEnvelope,
|
|
32
|
+
PendingPayloadInput,
|
|
33
|
+
PendingPayloadInputStatus,
|
|
34
|
+
PendingPayloadRootHex,
|
|
25
35
|
getBlockInputSyncCacheItemRootHex,
|
|
26
36
|
getBlockInputSyncCacheItemSlot,
|
|
37
|
+
getPayloadSyncCacheItemRootHex,
|
|
38
|
+
getPayloadSyncCacheItemSlot,
|
|
27
39
|
isPendingBlockInput,
|
|
40
|
+
isPendingPayloadEnvelope,
|
|
41
|
+
isPendingPayloadInput,
|
|
28
42
|
} from "./types.js";
|
|
29
43
|
import {DownloadByRootError, downloadByRoot} from "./utils/downloadByRoot.js";
|
|
30
|
-
import {getAllDescendantBlocks,
|
|
44
|
+
import {getAllDescendantBlocks, getUnknownAndAncestorBlocks} from "./utils/pendingBlocksTree.js";
|
|
31
45
|
|
|
32
46
|
const MAX_ATTEMPTS_PER_BLOCK = 5;
|
|
33
47
|
const MAX_KNOWN_BAD_BLOCKS = 500;
|
|
34
48
|
const MAX_PENDING_BLOCKS = 100;
|
|
35
49
|
|
|
50
|
+
type AdvancePendingBlockResult =
|
|
51
|
+
| "ready"
|
|
52
|
+
| "queued_block"
|
|
53
|
+
| "queued_parent_block"
|
|
54
|
+
| "queued_parent_payload"
|
|
55
|
+
| "blocked"
|
|
56
|
+
| "removed";
|
|
57
|
+
|
|
36
58
|
enum FetchResult {
|
|
37
59
|
SuccessResolved = "success_resolved",
|
|
38
60
|
SuccessMissingParent = "success_missing_parent",
|
|
@@ -78,6 +100,8 @@ export class BlockInputSync {
|
|
|
78
100
|
* block RootHex -> PendingBlock. To avoid finding same root at the same time
|
|
79
101
|
*/
|
|
80
102
|
private readonly pendingBlocks = new Map<RootHex, BlockInputSyncCacheItem>();
|
|
103
|
+
// Payload sync is keyed by beacon block root as well, so block and payload queues can unblock each other.
|
|
104
|
+
private readonly pendingPayloads = new Map<RootHex, PayloadSyncCacheItem>();
|
|
81
105
|
private readonly knownBadBlocks = new Set<RootHex>();
|
|
82
106
|
private readonly maxPendingBlocks;
|
|
83
107
|
private subscribedToNetworkEvents = false;
|
|
@@ -98,6 +122,9 @@ export class BlockInputSync {
|
|
|
98
122
|
metrics.blockInputSync.pendingBlocks.addCollect(() =>
|
|
99
123
|
metrics.blockInputSync.pendingBlocks.set(this.pendingBlocks.size)
|
|
100
124
|
);
|
|
125
|
+
metrics.blockInputSync.pendingPayloads.addCollect(() =>
|
|
126
|
+
metrics.blockInputSync.pendingPayloads.set(this.pendingPayloads.size)
|
|
127
|
+
);
|
|
101
128
|
metrics.blockInputSync.knownBadBlocks.addCollect(() =>
|
|
102
129
|
metrics.blockInputSync.knownBadBlocks.set(this.knownBadBlocks.size)
|
|
103
130
|
);
|
|
@@ -114,8 +141,12 @@ export class BlockInputSync {
|
|
|
114
141
|
if (!this.subscribedToNetworkEvents) {
|
|
115
142
|
this.logger.verbose("BlockInputSync enabled.");
|
|
116
143
|
this.chain.emitter.on(ChainEvent.unknownBlockRoot, this.onUnknownBlockRoot);
|
|
144
|
+
this.chain.emitter.on(ChainEvent.unknownEnvelopeBlockRoot, this.onUnknownEnvelopeBlockRoot);
|
|
117
145
|
this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
146
|
+
this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
118
147
|
this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
148
|
+
this.chain.emitter.on(routes.events.EventType.block, this.onBlockImported);
|
|
149
|
+
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
119
150
|
this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
120
151
|
this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
121
152
|
this.subscribedToNetworkEvents = true;
|
|
@@ -125,8 +156,12 @@ export class BlockInputSync {
|
|
|
125
156
|
unsubscribeFromNetwork(): void {
|
|
126
157
|
this.logger.verbose("BlockInputSync disabled.");
|
|
127
158
|
this.chain.emitter.off(ChainEvent.unknownBlockRoot, this.onUnknownBlockRoot);
|
|
159
|
+
this.chain.emitter.off(ChainEvent.unknownEnvelopeBlockRoot, this.onUnknownEnvelopeBlockRoot);
|
|
128
160
|
this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
161
|
+
this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
129
162
|
this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
163
|
+
this.chain.emitter.off(routes.events.EventType.block, this.onBlockImported);
|
|
164
|
+
this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
130
165
|
this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
131
166
|
this.network.events.off(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
132
167
|
this.subscribedToNetworkEvents = false;
|
|
@@ -168,12 +203,55 @@ export class BlockInputSync {
|
|
|
168
203
|
}
|
|
169
204
|
};
|
|
170
205
|
|
|
206
|
+
private onUnknownEnvelopeBlockRoot = (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRoot]): void => {
|
|
207
|
+
try {
|
|
208
|
+
this.addByPayloadRootHex(data.rootHex, data.peer);
|
|
209
|
+
this.triggerUnknownBlockSearch();
|
|
210
|
+
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_DATA});
|
|
211
|
+
this.metrics?.blockInputSync.source.inc({source: data.source});
|
|
212
|
+
} catch (e) {
|
|
213
|
+
this.logger.debug("Error handling unknownEnvelopeBlockRoot event", {}, e as Error);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
private onIncompletePayloadEnvelope = (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]): void => {
|
|
218
|
+
try {
|
|
219
|
+
this.addByPayloadInput(data.payloadInput, data.peer);
|
|
220
|
+
this.triggerUnknownBlockSearch();
|
|
221
|
+
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_DATA});
|
|
222
|
+
this.metrics?.blockInputSync.source.inc({source: data.source});
|
|
223
|
+
} catch (e) {
|
|
224
|
+
this.logger.debug("Error handling incompletePayloadEnvelope event", {}, e as Error);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
171
228
|
/**
|
|
172
229
|
* Process an unknownBlockParent event and register the block in `pendingBlocks` Map.
|
|
173
230
|
*/
|
|
174
231
|
private onUnknownParent = (data: ChainEventData[ChainEvent.blockUnknownParent]): void => {
|
|
175
232
|
try {
|
|
176
|
-
this.
|
|
233
|
+
const missingDependency = this.getMissingBlockDependency(data.blockInput);
|
|
234
|
+
if (missingDependency.kind === "invalidParentPayload") {
|
|
235
|
+
this.addByBlockInput(data.blockInput, data.peer);
|
|
236
|
+
|
|
237
|
+
const pendingBlock = this.pendingBlocks.get(data.blockInput.blockRootHex);
|
|
238
|
+
if (pendingBlock && isPendingBlockInput(pendingBlock)) {
|
|
239
|
+
this.logger.debug("Ignoring block with conflicting parent payload hash", {
|
|
240
|
+
slot: pendingBlock.blockInput.slot,
|
|
241
|
+
root: pendingBlock.blockInput.blockRootHex,
|
|
242
|
+
parentRoot: missingDependency.parentRootHex,
|
|
243
|
+
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
244
|
+
});
|
|
245
|
+
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (missingDependency.kind === "parentPayload") {
|
|
251
|
+
this.addByPayloadRootHex(missingDependency.rootHex, data.peer);
|
|
252
|
+
} else if (missingDependency.kind === "parentBlock") {
|
|
253
|
+
this.addByRootHex(missingDependency.rootHex, data.peer);
|
|
254
|
+
}
|
|
177
255
|
this.addByBlockInput(data.blockInput, data.peer);
|
|
178
256
|
this.triggerUnknownBlockSearch();
|
|
179
257
|
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_PARENT});
|
|
@@ -183,8 +261,22 @@ export class BlockInputSync {
|
|
|
183
261
|
}
|
|
184
262
|
};
|
|
185
263
|
|
|
186
|
-
private
|
|
264
|
+
private onBlockImported = (): void => {
|
|
265
|
+
if (this.pendingPayloads.size > 0) {
|
|
266
|
+
this.triggerUnknownBlockSearch();
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
private onPayloadImported = ({
|
|
271
|
+
blockRoot,
|
|
272
|
+
}: routes.events.EventData[routes.events.EventType.executionPayload]): void => {
|
|
273
|
+
this.pendingPayloads.delete(blockRoot);
|
|
274
|
+
this.triggerUnknownBlockSearch();
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
private addByRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr): boolean => {
|
|
187
278
|
let pendingBlock = this.pendingBlocks.get(rootHex);
|
|
279
|
+
let added = false;
|
|
188
280
|
if (!pendingBlock) {
|
|
189
281
|
pendingBlock = {
|
|
190
282
|
status: PendingBlockInputStatus.pending,
|
|
@@ -193,6 +285,7 @@ export class BlockInputSync {
|
|
|
193
285
|
timeAddedSec: Date.now() / 1000,
|
|
194
286
|
};
|
|
195
287
|
this.pendingBlocks.set(rootHex, pendingBlock);
|
|
288
|
+
added = true;
|
|
196
289
|
|
|
197
290
|
this.logger.verbose("Added new rootHex to BlockInputSync.pendingBlocks", {
|
|
198
291
|
root: pendingBlock.rootHex,
|
|
@@ -210,6 +303,7 @@ export class BlockInputSync {
|
|
|
210
303
|
if (prunedItemCount > 0) {
|
|
211
304
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
212
305
|
}
|
|
306
|
+
return added;
|
|
213
307
|
};
|
|
214
308
|
|
|
215
309
|
private addByBlockInput = (blockInput: IBlockInput, peerIdStr?: string): void => {
|
|
@@ -242,6 +336,58 @@ export class BlockInputSync {
|
|
|
242
336
|
}
|
|
243
337
|
};
|
|
244
338
|
|
|
339
|
+
private addByPayloadRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr): boolean => {
|
|
340
|
+
let pendingPayload = this.pendingPayloads.get(rootHex);
|
|
341
|
+
let added = false;
|
|
342
|
+
if (!pendingPayload) {
|
|
343
|
+
pendingPayload = {
|
|
344
|
+
status: PendingPayloadInputStatus.pending,
|
|
345
|
+
rootHex,
|
|
346
|
+
peerIdStrings: new Set(),
|
|
347
|
+
timeAddedSec: Date.now() / 1000,
|
|
348
|
+
};
|
|
349
|
+
this.pendingPayloads.set(rootHex, pendingPayload);
|
|
350
|
+
added = true;
|
|
351
|
+
|
|
352
|
+
this.logger.verbose("Added new payload rootHex to BlockInputSync.pendingPayloads", {
|
|
353
|
+
root: rootHex,
|
|
354
|
+
peerIdStr: peerIdStr ?? "unknown peer",
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (peerIdStr) {
|
|
359
|
+
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
363
|
+
if (prunedItemCount > 0) {
|
|
364
|
+
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
365
|
+
}
|
|
366
|
+
return added;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
private addByPayloadInput = (
|
|
370
|
+
payloadInput: PayloadEnvelopeInput,
|
|
371
|
+
peerIdStr?: PeerIdStr,
|
|
372
|
+
envelope?: gloas.SignedExecutionPayloadEnvelope
|
|
373
|
+
): void => {
|
|
374
|
+
const pendingPayload = this.toPendingPayloadInput(
|
|
375
|
+
payloadInput,
|
|
376
|
+
this.pendingPayloads.get(payloadInput.blockRootHex),
|
|
377
|
+
envelope
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
if (peerIdStr) {
|
|
381
|
+
pendingPayload.peerIdStrings.add(peerIdStr);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
this.pendingPayloads.set(payloadInput.blockRootHex, pendingPayload);
|
|
385
|
+
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
386
|
+
if (prunedItemCount > 0) {
|
|
387
|
+
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
|
|
245
391
|
private onPeerConnected = (data: NetworkEventData[NetworkEvent.peerConnected]): void => {
|
|
246
392
|
try {
|
|
247
393
|
const peerId = data.peer;
|
|
@@ -258,52 +404,207 @@ export class BlockInputSync {
|
|
|
258
404
|
this.peerBalancer.onPeerDisconnected(peerId);
|
|
259
405
|
};
|
|
260
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Post-gloas, a locally complete block can still be blocked on its parent's execution payload lineage.
|
|
409
|
+
* Distinguish which dependency is missing so the scheduler can enqueue the right follow-up work.
|
|
410
|
+
*/
|
|
411
|
+
private getMissingBlockDependency(
|
|
412
|
+
blockInput: IBlockInput
|
|
413
|
+
):
|
|
414
|
+
| {kind: "ready"}
|
|
415
|
+
| {kind: "block" | "parentBlock" | "parentPayload"; rootHex: RootHex}
|
|
416
|
+
| {kind: "invalidParentPayload"; parentRootHex: RootHex; parentBlockHashHex: RootHex} {
|
|
417
|
+
const parentRootHex = blockInput.parentRootHex;
|
|
418
|
+
if (!this.chain.forkChoice.hasBlockHex(parentRootHex)) {
|
|
419
|
+
return {kind: "parentBlock", rootHex: parentRootHex};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (!blockInput.hasBlock()) {
|
|
423
|
+
return {kind: "block", rootHex: blockInput.blockRootHex};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (this.config.getForkSeq(blockInput.slot) < ForkSeq.gloas) {
|
|
427
|
+
return {kind: "ready"};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const block = blockInput.getBlock() as gloas.SignedBeaconBlock;
|
|
431
|
+
const parentBlockHashHex = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
|
|
432
|
+
if (this.chain.forkChoice.getBlockHexAndBlockHash(parentRootHex, parentBlockHashHex) !== null) {
|
|
433
|
+
return {kind: "ready"};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (this.chain.forkChoice.hasPayloadHexUnsafe(parentRootHex)) {
|
|
437
|
+
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const parentPayloadInput = this.chain.seenPayloadEnvelopeInputCache.get(parentRootHex);
|
|
441
|
+
if (parentPayloadInput) {
|
|
442
|
+
if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
|
|
443
|
+
return {kind: "parentPayload", rootHex: parentRootHex};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return {kind: "parentPayload", rootHex: parentRootHex};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
private advancePendingBlock(pendingBlock: PendingBlockInput): AdvancePendingBlockResult {
|
|
453
|
+
const missingDependency = this.getMissingBlockDependency(pendingBlock.blockInput);
|
|
454
|
+
|
|
455
|
+
switch (missingDependency.kind) {
|
|
456
|
+
case "ready":
|
|
457
|
+
return "ready";
|
|
458
|
+
|
|
459
|
+
case "block":
|
|
460
|
+
pendingBlock.status = PendingBlockInputStatus.pending;
|
|
461
|
+
return "queued_block";
|
|
462
|
+
|
|
463
|
+
case "parentBlock": {
|
|
464
|
+
let added = this.addByRootHex(missingDependency.rootHex);
|
|
465
|
+
for (const peerIdStr of pendingBlock.peerIdStrings) {
|
|
466
|
+
added = this.addByRootHex(missingDependency.rootHex, peerIdStr) || added;
|
|
467
|
+
}
|
|
468
|
+
return added ? "queued_parent_block" : "blocked";
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
case "parentPayload": {
|
|
472
|
+
let added = this.addByPayloadRootHex(missingDependency.rootHex);
|
|
473
|
+
for (const peerIdStr of pendingBlock.peerIdStrings) {
|
|
474
|
+
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr) || added;
|
|
475
|
+
}
|
|
476
|
+
return added ? "queued_parent_payload" : "blocked";
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
case "invalidParentPayload":
|
|
480
|
+
this.logger.debug("Removing block with conflicting parent payload hash", {
|
|
481
|
+
slot: pendingBlock.blockInput.slot,
|
|
482
|
+
root: pendingBlock.blockInput.blockRootHex,
|
|
483
|
+
parentRoot: missingDependency.parentRootHex,
|
|
484
|
+
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
485
|
+
});
|
|
486
|
+
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
487
|
+
return "removed";
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
private toPendingPayloadInput(
|
|
492
|
+
payloadInput: PayloadEnvelopeInput,
|
|
493
|
+
previous?: PayloadSyncCacheItem,
|
|
494
|
+
envelope?: gloas.SignedExecutionPayloadEnvelope
|
|
495
|
+
): PendingPayloadInput {
|
|
496
|
+
// Normalize every payload queueing path into the same cache shape while preserving first-seen
|
|
497
|
+
// timing and peer provenance from any earlier by-root or envelope-only entry.
|
|
498
|
+
const queuedEnvelope = envelope ?? (previous && isPendingPayloadEnvelope(previous) ? previous.envelope : undefined);
|
|
499
|
+
|
|
500
|
+
if (queuedEnvelope && !payloadInput.hasPayloadEnvelope()) {
|
|
501
|
+
payloadInput.addPayloadEnvelope({
|
|
502
|
+
envelope: queuedEnvelope,
|
|
503
|
+
source: PayloadEnvelopeInputSource.byRoot,
|
|
504
|
+
seenTimestampSec: Date.now() / 1000,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
return {
|
|
509
|
+
status: payloadInput.isComplete() ? PendingPayloadInputStatus.downloaded : PendingPayloadInputStatus.pending,
|
|
510
|
+
payloadInput,
|
|
511
|
+
timeAddedSec: previous?.timeAddedSec ?? Date.now() / 1000,
|
|
512
|
+
timeSyncedSec: payloadInput.isComplete() ? Date.now() / 1000 : undefined,
|
|
513
|
+
peerIdStrings: new Set(previous?.peerIdStrings ?? []),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
261
517
|
/**
|
|
262
518
|
* Gather tip parent blocks with unknown parent and do a search for all of them
|
|
263
519
|
*/
|
|
264
520
|
private triggerUnknownBlockSearch = (): void => {
|
|
265
521
|
// Cheap early stop to prevent calling the network.getConnectedPeers()
|
|
266
|
-
if (this.pendingBlocks.size === 0) {
|
|
522
|
+
if (this.pendingBlocks.size === 0 && this.pendingPayloads.size === 0) {
|
|
267
523
|
return;
|
|
268
524
|
}
|
|
269
525
|
|
|
270
|
-
// If the node loses all peers with pending unknown blocks, the sync will stall
|
|
526
|
+
// If the node loses all peers with pending unknown blocks or payloads, the sync will stall
|
|
271
527
|
const connectedPeers = this.network.getConnectedPeers();
|
|
272
|
-
|
|
273
|
-
this.logger.debug("No connected peers, skipping unknown block search.");
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
528
|
+
const hasConnectedPeers = connectedPeers.length > 0;
|
|
276
529
|
|
|
277
530
|
const {unknowns, ancestors} = getUnknownAndAncestorBlocks(this.pendingBlocks);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (this.chain.forkChoice.hasBlockHex(block.blockInput.parentRootHex)) {
|
|
531
|
+
let processedBlocks = 0;
|
|
532
|
+
let shouldRerunBlockSearch = false;
|
|
533
|
+
|
|
534
|
+
for (const block of ancestors) {
|
|
535
|
+
const advanceResult = this.advancePendingBlock(block);
|
|
536
|
+
switch (advanceResult) {
|
|
537
|
+
case "ready":
|
|
286
538
|
processedBlocks++;
|
|
287
|
-
this.
|
|
539
|
+
this.processReadyBlock(block).catch((e) => {
|
|
288
540
|
this.logger.debug("Unexpected error - process old downloaded block", {}, e);
|
|
289
541
|
});
|
|
290
|
-
|
|
542
|
+
break;
|
|
543
|
+
|
|
544
|
+
case "queued_block":
|
|
545
|
+
case "queued_parent_block":
|
|
546
|
+
shouldRerunBlockSearch = true;
|
|
547
|
+
break;
|
|
548
|
+
|
|
549
|
+
case "queued_parent_payload":
|
|
550
|
+
case "blocked":
|
|
551
|
+
case "removed":
|
|
552
|
+
break;
|
|
291
553
|
}
|
|
554
|
+
}
|
|
292
555
|
|
|
556
|
+
if (unknowns.length > 0) {
|
|
557
|
+
if (!hasConnectedPeers) {
|
|
558
|
+
this.logger.debug("No connected peers, skipping unknown block download.");
|
|
559
|
+
} else {
|
|
560
|
+
// Most of the time there is exactly 1 unknown block
|
|
561
|
+
for (const block of unknowns) {
|
|
562
|
+
this.downloadBlock(block).catch((e) => {
|
|
563
|
+
this.logger.debug("Unexpected error - downloadBlock", {root: getBlockInputSyncCacheItemRootHex(block)}, e);
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
} else if (ancestors.length > 0) {
|
|
568
|
+
// It's rare when there is no unknown block
|
|
569
|
+
// see https://github.com/ChainSafe/lodestar/issues/5649#issuecomment-1594213550
|
|
293
570
|
this.logger.verbose("No unknown block, process ancestor downloaded blocks", {
|
|
294
571
|
pendingBlocks: this.pendingBlocks.size,
|
|
295
572
|
ancestorBlocks: ancestors.length,
|
|
296
573
|
processedBlocks,
|
|
297
574
|
});
|
|
298
|
-
return;
|
|
299
575
|
}
|
|
300
576
|
|
|
301
|
-
//
|
|
302
|
-
for (const
|
|
303
|
-
|
|
304
|
-
this.
|
|
577
|
+
// Blocks can unblock payloads and payloads can unblock blocks, so every scheduler pass services both queues.
|
|
578
|
+
for (const payload of Array.from(this.pendingPayloads.values())) {
|
|
579
|
+
if (isPendingPayloadInput(payload) && payload.status === PendingPayloadInputStatus.downloaded) {
|
|
580
|
+
this.processPayload(payload).catch((e) => {
|
|
581
|
+
this.logger.debug("Unexpected error - process downloaded payload", {}, e);
|
|
582
|
+
});
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (isPendingPayloadEnvelope(payload)) {
|
|
587
|
+
this.reconcilePayloadEnvelope(payload).catch((e) => {
|
|
588
|
+
this.logger.debug("Unexpected error - reconcile pending payload envelope", {}, e);
|
|
589
|
+
});
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (!hasConnectedPeers) {
|
|
594
|
+
this.logger.debug("No connected peers, skipping unknown payload download.", {
|
|
595
|
+
root: getPayloadSyncCacheItemRootHex(payload),
|
|
596
|
+
});
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
this.downloadPayload(payload).catch((e) => {
|
|
601
|
+
this.logger.debug("Unexpected error - downloadPayload", {root: getPayloadSyncCacheItemRootHex(payload)}, e);
|
|
305
602
|
});
|
|
306
603
|
}
|
|
604
|
+
|
|
605
|
+
if (shouldRerunBlockSearch) {
|
|
606
|
+
this.triggerUnknownBlockSearch();
|
|
607
|
+
}
|
|
307
608
|
};
|
|
308
609
|
|
|
309
610
|
private async downloadBlock(block: BlockInputSyncCacheItem): Promise<void> {
|
|
@@ -342,10 +643,26 @@ export class BlockInputSync {
|
|
|
342
643
|
this.logger.verbose("Downloaded unknown block", logCtx2);
|
|
343
644
|
|
|
344
645
|
if (parentInForkChoice) {
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
646
|
+
// If the direct parent is already in fork choice, let the block state machine decide if
|
|
647
|
+
// the next step is block import, parent payload download, or branch removal.
|
|
648
|
+
const advanceResult = this.advancePendingBlock(pending);
|
|
649
|
+
switch (advanceResult) {
|
|
650
|
+
case "ready":
|
|
651
|
+
this.processReadyBlock(pending).catch((e) => {
|
|
652
|
+
this.logger.debug("Unexpected error - process newly downloaded block", logCtx2, e);
|
|
653
|
+
});
|
|
654
|
+
break;
|
|
655
|
+
|
|
656
|
+
case "queued_block":
|
|
657
|
+
case "queued_parent_block":
|
|
658
|
+
case "queued_parent_payload":
|
|
659
|
+
this.triggerUnknownBlockSearch();
|
|
660
|
+
break;
|
|
661
|
+
|
|
662
|
+
case "blocked":
|
|
663
|
+
case "removed":
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
349
666
|
} else if (blockSlot <= finalizedSlot) {
|
|
350
667
|
// the common ancestor of the downloading chain and canonical chain should be at least the finalized slot and
|
|
351
668
|
// we should found it through forkchoice. If not, we should penalize all peers sending us this block chain
|
|
@@ -368,26 +685,11 @@ export class BlockInputSync {
|
|
|
368
685
|
}
|
|
369
686
|
|
|
370
687
|
/**
|
|
371
|
-
*
|
|
372
|
-
* On error, remove and downscore
|
|
373
|
-
* This function could run recursively for all descendant blocks
|
|
688
|
+
* Import a block that has already passed the local dependency checks in BlockInputSync.
|
|
689
|
+
* On error, remove and downscore descendants as appropriate for the failure type.
|
|
374
690
|
*/
|
|
375
|
-
private async
|
|
376
|
-
// pending block status is `downloaded` right after `downloadBlock`
|
|
377
|
-
// but could be `pending` if added by `onUnknownBlockParent` event and this function is called recursively
|
|
691
|
+
private async processReadyBlock(pendingBlock: PendingBlockInput): Promise<void> {
|
|
378
692
|
if (pendingBlock.status !== PendingBlockInputStatus.downloaded) {
|
|
379
|
-
if (pendingBlock.status === PendingBlockInputStatus.pending) {
|
|
380
|
-
const connectedPeers = this.network.getConnectedPeers();
|
|
381
|
-
if (connectedPeers.length === 0) {
|
|
382
|
-
this.logger.debug("No connected peers, skipping download block", {
|
|
383
|
-
slot: pendingBlock.blockInput.slot,
|
|
384
|
-
blockRoot: pendingBlock.blockInput.blockRootHex,
|
|
385
|
-
});
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
// if the download is a success we'll call `processBlock()` for this block
|
|
389
|
-
await this.downloadBlock(pendingBlock);
|
|
390
|
-
}
|
|
391
693
|
return;
|
|
392
694
|
}
|
|
393
695
|
|
|
@@ -432,15 +734,9 @@ export class BlockInputSync {
|
|
|
432
734
|
if (!res.err) {
|
|
433
735
|
// no need to update status to "processed", delete anyway
|
|
434
736
|
this.pendingBlocks.delete(pendingBlock.blockInput.blockRootHex);
|
|
435
|
-
|
|
436
|
-
//
|
|
437
|
-
|
|
438
|
-
if (isPendingBlockInput(descendantBlock)) {
|
|
439
|
-
this.processBlock(descendantBlock).catch((e) => {
|
|
440
|
-
this.logger.debug("Unexpected error - process descendant block", {}, e);
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
}
|
|
737
|
+
// Re-enter the scheduler so descendants blocked on either parent blocks or parent payloads
|
|
738
|
+
// are advanced through the same dependency checks as every other pending item.
|
|
739
|
+
this.triggerUnknownBlockSearch();
|
|
444
740
|
} else {
|
|
445
741
|
const errorData = {slot: pendingBlock.blockInput.slot, root: pendingBlock.blockInput.blockRootHex};
|
|
446
742
|
if (res.err instanceof BlockError) {
|
|
@@ -456,6 +752,19 @@ export class BlockInputSync {
|
|
|
456
752
|
pendingBlock.status = PendingBlockInputStatus.downloaded;
|
|
457
753
|
break;
|
|
458
754
|
|
|
755
|
+
case BlockErrorCode.PARENT_PAYLOAD_UNKNOWN:
|
|
756
|
+
this.logger.error(
|
|
757
|
+
"processReadyBlock() hit unexpected parent payload dependency after readiness checks",
|
|
758
|
+
{
|
|
759
|
+
...errorData,
|
|
760
|
+
parentRoot: pendingBlock.blockInput.parentRootHex,
|
|
761
|
+
parentBlockHash: res.err.type.parentBlockHash,
|
|
762
|
+
},
|
|
763
|
+
res.err
|
|
764
|
+
);
|
|
765
|
+
pendingBlock.status = PendingBlockInputStatus.downloaded;
|
|
766
|
+
break;
|
|
767
|
+
|
|
459
768
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
460
769
|
// Removing the block(s) without penalizing the peers, hoping for EL to
|
|
461
770
|
// recover on a latter download + verify attempt
|
|
@@ -477,6 +786,375 @@ export class BlockInputSync {
|
|
|
477
786
|
}
|
|
478
787
|
}
|
|
479
788
|
|
|
789
|
+
/**
|
|
790
|
+
* Reconcile an envelope-first payload entry once the block import path has seeded its
|
|
791
|
+
* PayloadEnvelopeInput. This may queue block download, validate the speculative envelope, or
|
|
792
|
+
* downgrade back to by-root fetching when the cached envelope does not match the imported block.
|
|
793
|
+
*/
|
|
794
|
+
private async reconcilePayloadEnvelope(pendingPayload: PendingPayloadEnvelope): Promise<void> {
|
|
795
|
+
const rootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
796
|
+
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
797
|
+
this.pendingPayloads.delete(rootHex);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
802
|
+
if (!payloadInput) {
|
|
803
|
+
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
804
|
+
// Column commitments live on the block body, so an envelope-only entry has to pull the block first.
|
|
805
|
+
if (!this.pendingBlocks.has(rootHex)) {
|
|
806
|
+
this.addByRootHex(rootHex);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const pendingBlock = this.pendingBlocks.get(rootHex);
|
|
810
|
+
if (pendingBlock && this.network.getConnectedPeers().length > 0) {
|
|
811
|
+
await this.downloadBlock(pendingBlock);
|
|
812
|
+
}
|
|
813
|
+
} else {
|
|
814
|
+
this.logger.debug("Missing PayloadEnvelopeInput for known block while reconciling payload envelope", {
|
|
815
|
+
root: rootHex,
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (!payloadInput.hasPayloadEnvelope()) {
|
|
822
|
+
const validationResult = await wrapError(
|
|
823
|
+
validateGossipExecutionPayloadEnvelope(this.chain, pendingPayload.envelope)
|
|
824
|
+
);
|
|
825
|
+
if (validationResult.err) {
|
|
826
|
+
this.logger.debug(
|
|
827
|
+
"Pending payload envelope failed validation after block import, refetching by root",
|
|
828
|
+
{slot: pendingPayload.envelope.message.payload.slotNumber, root: rootHex},
|
|
829
|
+
validationResult.err
|
|
830
|
+
);
|
|
831
|
+
|
|
832
|
+
const pendingPayloadByRoot: PendingPayloadRootHex = {
|
|
833
|
+
status: PendingPayloadInputStatus.pending,
|
|
834
|
+
rootHex,
|
|
835
|
+
timeAddedSec: pendingPayload.timeAddedSec,
|
|
836
|
+
peerIdStrings: new Set(pendingPayload.peerIdStrings),
|
|
837
|
+
};
|
|
838
|
+
this.pendingPayloads.set(rootHex, pendingPayloadByRoot);
|
|
839
|
+
|
|
840
|
+
if (this.network.getConnectedPeers().length > 0) {
|
|
841
|
+
await this.downloadPayload(pendingPayloadByRoot);
|
|
842
|
+
}
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const upgradedPayload = this.toPendingPayloadInput(payloadInput, pendingPayload, pendingPayload.envelope);
|
|
848
|
+
this.pendingPayloads.set(rootHex, upgradedPayload);
|
|
849
|
+
|
|
850
|
+
if (upgradedPayload.status === PendingPayloadInputStatus.downloaded) {
|
|
851
|
+
await this.processPayload(upgradedPayload);
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
await this.downloadPayload(upgradedPayload);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
private async downloadPayload(payload: PayloadSyncCacheItem): Promise<void> {
|
|
859
|
+
if (isPendingPayloadEnvelope(payload)) {
|
|
860
|
+
await this.reconcilePayloadEnvelope(payload);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const rootHex = getPayloadSyncCacheItemRootHex(payload);
|
|
865
|
+
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
866
|
+
this.pendingPayloads.delete(rootHex);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (payload.status !== PendingPayloadInputStatus.pending) {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const logCtx = {
|
|
875
|
+
slot: getPayloadSyncCacheItemSlot(payload),
|
|
876
|
+
root: rootHex,
|
|
877
|
+
pendingPayloads: this.pendingPayloads.size,
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
this.logger.verbose("BlockInputSync.downloadPayload()", logCtx);
|
|
881
|
+
|
|
882
|
+
payload.status = PendingPayloadInputStatus.fetching;
|
|
883
|
+
|
|
884
|
+
const res = await wrapError(this.fetchPayloadInput(payload));
|
|
885
|
+
if (!res.err) {
|
|
886
|
+
const pendingPayload = res.result;
|
|
887
|
+
this.pendingPayloads.set(getPayloadSyncCacheItemRootHex(pendingPayload), pendingPayload);
|
|
888
|
+
|
|
889
|
+
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
890
|
+
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
891
|
+
} else if (pendingPayload.status === PendingPayloadInputStatus.downloaded) {
|
|
892
|
+
await this.processPayload(pendingPayload);
|
|
893
|
+
}
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
this.logger.debug("Ignoring unknown payload root after failed download", logCtx, res.err);
|
|
898
|
+
if (!isPendingPayloadEnvelope(payload)) {
|
|
899
|
+
payload.status = PendingPayloadInputStatus.pending;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
private async processPayload(pendingPayload: PendingPayloadInput): Promise<void> {
|
|
904
|
+
const rootHex = pendingPayload.payloadInput.blockRootHex;
|
|
905
|
+
const logCtx = {slot: pendingPayload.payloadInput.slot, root: rootHex};
|
|
906
|
+
|
|
907
|
+
if (pendingPayload.status !== PendingPayloadInputStatus.downloaded) {
|
|
908
|
+
this.logger.debug("Skipping payload processing before payload input is downloaded", {
|
|
909
|
+
...logCtx,
|
|
910
|
+
status: pendingPayload.status,
|
|
911
|
+
});
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
916
|
+
this.logger.debug("Payload already imported while processing unknown payload", logCtx);
|
|
917
|
+
this.pendingPayloads.delete(rootHex);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
922
|
+
this.logger.debug("Payload input is ready before its block is in fork choice", logCtx);
|
|
923
|
+
const added = this.addByRootHex(rootHex);
|
|
924
|
+
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
925
|
+
if (added) {
|
|
926
|
+
this.triggerUnknownBlockSearch();
|
|
927
|
+
}
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
pendingPayload.status = PendingPayloadInputStatus.processing;
|
|
932
|
+
|
|
933
|
+
const res = await wrapError(this.chain.processExecutionPayload(pendingPayload.payloadInput));
|
|
934
|
+
if (!res.err) {
|
|
935
|
+
this.logger.debug("Processed payload from unknown sync", logCtx);
|
|
936
|
+
this.pendingPayloads.delete(rootHex);
|
|
937
|
+
this.triggerUnknownBlockSearch();
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if (res.err instanceof PayloadError) {
|
|
942
|
+
switch (res.err.type.code) {
|
|
943
|
+
case PayloadErrorCode.BLOCK_NOT_IN_FORK_CHOICE:
|
|
944
|
+
// Payload sync discovered the block dependency before the block queue did. Re-enqueue the
|
|
945
|
+
// block and keep the payload ready so the scheduler can retry once the block reaches fork choice.
|
|
946
|
+
if (this.addByRootHex(rootHex)) {
|
|
947
|
+
this.triggerUnknownBlockSearch();
|
|
948
|
+
}
|
|
949
|
+
// Keep the payload out of any synchronous requeue pass; a later scheduler pass will retry it.
|
|
950
|
+
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
951
|
+
break;
|
|
952
|
+
|
|
953
|
+
case PayloadErrorCode.EXECUTION_ENGINE_ERROR:
|
|
954
|
+
this.logger.debug("Execution engine error while processing payload from unknown sync", logCtx, res.err);
|
|
955
|
+
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
956
|
+
break;
|
|
957
|
+
|
|
958
|
+
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
959
|
+
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
960
|
+
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
961
|
+
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
962
|
+
// reintroducing envelope replacement / recreation flows.
|
|
963
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
964
|
+
this.removePendingPayloadAndDescendants(rootHex);
|
|
965
|
+
break;
|
|
966
|
+
|
|
967
|
+
default:
|
|
968
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
969
|
+
this.pendingPayloads.delete(rootHex);
|
|
970
|
+
}
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
this.logger.debug("Unknown error processing payload from unknown sync", logCtx, res.err);
|
|
975
|
+
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Download payload material keyed by beacon block root. Unlike block download, payload sync may
|
|
980
|
+
* already have a locally cached envelope or partial columns, so each attempt starts from local state
|
|
981
|
+
* and only asks peers for the remaining pieces.
|
|
982
|
+
*/
|
|
983
|
+
private async fetchPayloadInput(
|
|
984
|
+
cacheItem: PendingPayloadInput | PendingPayloadRootHex
|
|
985
|
+
): Promise<PendingPayloadInput | PendingPayloadEnvelope> {
|
|
986
|
+
const rootHex = getPayloadSyncCacheItemRootHex(cacheItem);
|
|
987
|
+
const blockRoot = fromHex(rootHex);
|
|
988
|
+
const excludedPeers = new Set<PeerIdStr>();
|
|
989
|
+
|
|
990
|
+
let slot = getPayloadSyncCacheItemSlot(cacheItem);
|
|
991
|
+
let payloadInput = isPendingPayloadInput(cacheItem)
|
|
992
|
+
? cacheItem.payloadInput
|
|
993
|
+
: this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
994
|
+
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
995
|
+
|
|
996
|
+
let i = 0;
|
|
997
|
+
while (i++ < this.getMaxDownloadAttempts()) {
|
|
998
|
+
const pendingColumns = payloadInput?.hasAllData()
|
|
999
|
+
? new Set<number>()
|
|
1000
|
+
: new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
|
|
1001
|
+
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers);
|
|
1002
|
+
if (peerMeta === null) {
|
|
1003
|
+
throw Error(
|
|
1004
|
+
`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
const {peerId, client: peerClient} = peerMeta;
|
|
1009
|
+
cacheItem.peerIdStrings.add(peerId);
|
|
1010
|
+
|
|
1011
|
+
try {
|
|
1012
|
+
if (!envelope) {
|
|
1013
|
+
envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
|
|
1014
|
+
slot = envelope.message.payload.slotNumber;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
1018
|
+
if (!payloadInput) {
|
|
1019
|
+
if (this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
1020
|
+
throw new Error(`Missing PayloadEnvelopeInput for known block ${rootHex}`);
|
|
1021
|
+
}
|
|
1022
|
+
// Keep the validated envelope around, but wait for the block body before turning it into a full payload input.
|
|
1023
|
+
return {
|
|
1024
|
+
status: PendingPayloadInputStatus.waitingForBlock,
|
|
1025
|
+
envelope,
|
|
1026
|
+
timeAddedSec: cacheItem.timeAddedSec,
|
|
1027
|
+
peerIdStrings: cacheItem.peerIdStrings,
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
if (!payloadInput.hasPayloadEnvelope()) {
|
|
1032
|
+
await validateGossipExecutionPayloadEnvelope(this.chain, envelope);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
let pendingPayload = this.toPendingPayloadInput(payloadInput, cacheItem, envelope);
|
|
1036
|
+
if (!pendingPayload.payloadInput.hasAllData()) {
|
|
1037
|
+
const missing = pendingPayload.payloadInput.getMissingSampledColumnMeta().missing;
|
|
1038
|
+
if (missing.length > 0) {
|
|
1039
|
+
const columnSidecars = await this.fetchPayloadColumns(peerMeta, pendingPayload.payloadInput, missing);
|
|
1040
|
+
const seenTimestampSec = Date.now() / 1000;
|
|
1041
|
+
for (const columnSidecar of columnSidecars) {
|
|
1042
|
+
if (pendingPayload.payloadInput.hasColumn(columnSidecar.index)) {
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
pendingPayload.payloadInput.addColumn({
|
|
1047
|
+
columnSidecar,
|
|
1048
|
+
source: PayloadEnvelopeInputSource.byRoot,
|
|
1049
|
+
seenTimestampSec,
|
|
1050
|
+
peerIdStr: peerId,
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
pendingPayload = this.toPendingPayloadInput(pendingPayload.payloadInput, pendingPayload);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
this.logger.verbose("BlockInputSync.fetchPayloadInput: successful download", {
|
|
1058
|
+
slot,
|
|
1059
|
+
rootHex,
|
|
1060
|
+
peerId,
|
|
1061
|
+
peerClient,
|
|
1062
|
+
hasPayload: pendingPayload.payloadInput.hasPayloadEnvelope(),
|
|
1063
|
+
hasAllData: pendingPayload.payloadInput.hasAllData(),
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
if (pendingPayload.status === PendingPayloadInputStatus.downloaded) {
|
|
1067
|
+
return pendingPayload;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
cacheItem = pendingPayload;
|
|
1071
|
+
payloadInput = pendingPayload.payloadInput;
|
|
1072
|
+
} catch (e) {
|
|
1073
|
+
this.logger.debug(
|
|
1074
|
+
"Error downloading payload in BlockInputSync.fetchPayloadInput",
|
|
1075
|
+
{slot, rootHex, attempt: i, peer: peerId, peerClient},
|
|
1076
|
+
e as Error
|
|
1077
|
+
);
|
|
1078
|
+
|
|
1079
|
+
if (e instanceof RequestError) {
|
|
1080
|
+
switch (e.type.code) {
|
|
1081
|
+
case RequestErrorCode.REQUEST_RATE_LIMITED:
|
|
1082
|
+
case RequestErrorCode.REQUEST_TIMEOUT:
|
|
1083
|
+
break;
|
|
1084
|
+
default:
|
|
1085
|
+
excludedPeers.add(peerId);
|
|
1086
|
+
break;
|
|
1087
|
+
}
|
|
1088
|
+
} else {
|
|
1089
|
+
excludedPeers.add(peerId);
|
|
1090
|
+
}
|
|
1091
|
+
} finally {
|
|
1092
|
+
this.peerBalancer.onRequestCompleted(peerId);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
throw Error(`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts.`);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
private async fetchExecutionPayloadEnvelope(
|
|
1100
|
+
peerIdStr: PeerIdStr,
|
|
1101
|
+
blockRoot: Uint8Array,
|
|
1102
|
+
rootHex: RootHex
|
|
1103
|
+
): Promise<gloas.SignedExecutionPayloadEnvelope> {
|
|
1104
|
+
const response = await this.network.sendExecutionPayloadEnvelopesByRoot(peerIdStr, [blockRoot]);
|
|
1105
|
+
const envelope = response.at(0);
|
|
1106
|
+
if (!envelope) {
|
|
1107
|
+
throw new Error(`Missing execution payload envelope for root=${rootHex}`);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
const receivedRootHex = toRootHex(envelope.message.beaconBlockRoot);
|
|
1111
|
+
if (receivedRootHex !== rootHex) {
|
|
1112
|
+
throw new Error(`Execution payload envelope root mismatch requested=${rootHex} received=${receivedRootHex}`);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
return envelope;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
private async fetchPayloadColumns(
|
|
1119
|
+
peerMeta: PeerSyncMeta,
|
|
1120
|
+
payloadInput: PayloadEnvelopeInput,
|
|
1121
|
+
missing: number[]
|
|
1122
|
+
): Promise<gloas.DataColumnSidecar[]> {
|
|
1123
|
+
const {peerId: peerIdStr} = peerMeta;
|
|
1124
|
+
const peerColumns = new Set(peerMeta.custodyColumns ?? []);
|
|
1125
|
+
const requestedColumns = missing.filter((columnIndex) => peerColumns.has(columnIndex));
|
|
1126
|
+
if (requestedColumns.length === 0) {
|
|
1127
|
+
return [];
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
const columnSidecars = (await this.network.sendDataColumnSidecarsByRoot(peerIdStr, [
|
|
1131
|
+
{blockRoot: fromHex(payloadInput.blockRootHex), columns: requestedColumns},
|
|
1132
|
+
])) as gloas.DataColumnSidecar[];
|
|
1133
|
+
|
|
1134
|
+
if (columnSidecars.length === 0) {
|
|
1135
|
+
throw new Error(`No data column sidecars returned for payload root=${payloadInput.blockRootHex}`);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const requestedColumnsSet = new Set(requestedColumns);
|
|
1139
|
+
const extraColumns = columnSidecars.filter((columnSidecar) => !requestedColumnsSet.has(columnSidecar.index));
|
|
1140
|
+
if (extraColumns.length > 0) {
|
|
1141
|
+
throw new Error(
|
|
1142
|
+
`Received unexpected payload data columns indices=${prettyPrintIndices(extraColumns.map((column) => column.index))}`
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// PayloadEnvelopeInput already carries the block slot, root, and commitments, so reuse the
|
|
1147
|
+
// block-based Gloas validator rather than maintaining a second payload-specific variant.
|
|
1148
|
+
await validateGloasBlockDataColumnSidecars(
|
|
1149
|
+
payloadInput.slot,
|
|
1150
|
+
fromHex(payloadInput.blockRootHex),
|
|
1151
|
+
payloadInput.getBlobKzgCommitments(),
|
|
1152
|
+
columnSidecars,
|
|
1153
|
+
this.chain.metrics?.peerDas
|
|
1154
|
+
);
|
|
1155
|
+
return columnSidecars;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
480
1158
|
/**
|
|
481
1159
|
* From a set of shuffled peers:
|
|
482
1160
|
* - fetch the block
|
|
@@ -562,6 +1240,8 @@ export class BlockInputSync {
|
|
|
562
1240
|
downloadByRootMetrics?.error.inc({code: "req_resp", client: peerClient});
|
|
563
1241
|
switch (e.type.code) {
|
|
564
1242
|
case RequestErrorCode.REQUEST_RATE_LIMITED:
|
|
1243
|
+
case RequestErrorCode.RESP_RATE_LIMITED:
|
|
1244
|
+
case RequestErrorCode.REQUEST_SELF_RATE_LIMITED:
|
|
565
1245
|
case RequestErrorCode.REQUEST_TIMEOUT:
|
|
566
1246
|
// do not exclude peer for these errors
|
|
567
1247
|
break;
|
|
@@ -660,6 +1340,28 @@ export class BlockInputSync {
|
|
|
660
1340
|
pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
|
|
661
1341
|
}
|
|
662
1342
|
|
|
1343
|
+
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1344
|
+
private removePendingPayloadAndDescendants(rootHex: RootHex): void {
|
|
1345
|
+
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1346
|
+
// later validation/finalization logic decides when it can leave memory.
|
|
1347
|
+
this.pendingPayloads.delete(rootHex);
|
|
1348
|
+
|
|
1349
|
+
const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
|
|
1350
|
+
this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
|
|
1351
|
+
|
|
1352
|
+
for (const block of badPendingBlocks) {
|
|
1353
|
+
const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1354
|
+
this.pendingBlocks.delete(descendantRootHex);
|
|
1355
|
+
this.pendingPayloads.delete(descendantRootHex);
|
|
1356
|
+
this.chain.seenBlockInputCache.prune(descendantRootHex);
|
|
1357
|
+
this.logger.debug("Removing pending descendant after invalid parent payload", {
|
|
1358
|
+
slot: getBlockInputSyncCacheItemSlot(block),
|
|
1359
|
+
blockRoot: descendantRootHex,
|
|
1360
|
+
parentPayloadRoot: rootHex,
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
|
|
663
1365
|
private removeAllDescendants(block: BlockInputSyncCacheItem): BlockInputSyncCacheItem[] {
|
|
664
1366
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
665
1367
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
@@ -671,7 +1373,10 @@ export class BlockInputSync {
|
|
|
671
1373
|
for (const block of badPendingBlocks) {
|
|
672
1374
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
673
1375
|
this.pendingBlocks.delete(rootHex);
|
|
1376
|
+
this.pendingPayloads.delete(rootHex);
|
|
674
1377
|
this.chain.seenBlockInputCache.prune(rootHex);
|
|
1378
|
+
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1379
|
+
// importBlock()-owned lifecycle.
|
|
675
1380
|
this.logger.debug("Removing bad/unknown/incomplete BlockInputSyncCacheItem", {
|
|
676
1381
|
slot,
|
|
677
1382
|
blockRoot: rootHex,
|