@lodestar/beacon-node 1.50.0-dev.fef46df5ba → 1.50.0-rc.0
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 -1
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/index.js +13 -0
- package/lib/api/impl/beacon/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +4 -4
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +28 -6
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +26 -18
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts +1 -0
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +49 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +5 -0
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +14 -24
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +2 -2
- package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -7
- package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
- package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
- package/lib/chain/archiveStore/interface.d.ts +1 -1
- package/lib/chain/archiveStore/interface.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +2 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/beaconProposerCache.d.ts +6 -0
- package/lib/chain/beaconProposerCache.d.ts.map +1 -1
- package/lib/chain/beaconProposerCache.js +16 -0
- package/lib/chain/beaconProposerCache.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +18 -8
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +7 -7
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +5 -4
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/chain.d.ts +10 -5
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +105 -36
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +7 -1
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +5 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
- package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.js +2 -0
- package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
- package/lib/chain/errors/gossipValidation.d.ts +3 -1
- package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.js +2 -0
- package/lib/chain/errors/gossipValidation.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +2 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +5 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts +4 -2
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +30 -8
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/opPool.d.ts +5 -4
- package/lib/chain/opPools/opPool.d.ts.map +1 -1
- package/lib/chain/opPools/opPool.js +60 -38
- package/lib/chain/opPools/opPool.js.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +8 -4
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +27 -12
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +15 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +33 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/options.d.ts +2 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -1
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +16 -5
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +11 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +46 -30
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +2 -0
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +10 -4
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts +7 -0
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.js +21 -0
- package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/shufflingCache.d.ts +18 -8
- package/lib/chain/shufflingCache.d.ts.map +1 -1
- package/lib/chain/shufflingCache.js +72 -18
- package/lib/chain/shufflingCache.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts +4 -4
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +3 -0
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.d.ts +3 -3
- package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +3 -0
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +5 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +64 -54
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/db/beacon.d.ts +4 -1
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +6 -1
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/buckets.d.ts +3 -1
- package/lib/db/buckets.d.ts.map +1 -1
- package/lib/db/buckets.js +2 -0
- package/lib/db/buckets.js.map +1 -1
- package/lib/db/interface.d.ts +4 -1
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
- package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
- package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
- package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
- package/lib/db/repositories/earliestAvailableSlot.js +19 -0
- package/lib/db/repositories/earliestAvailableSlot.js.map +1 -0
- package/lib/db/repositories/index.d.ts +1 -0
- package/lib/db/repositories/index.d.ts.map +1 -1
- package/lib/db/repositories/index.js +1 -0
- package/lib/db/repositories/index.js.map +1 -1
- package/lib/db/repositories/proposerPreferences.d.ts +14 -0
- package/lib/db/repositories/proposerPreferences.d.ts.map +1 -0
- package/lib/db/repositories/proposerPreferences.js +21 -0
- package/lib/db/repositories/proposerPreferences.js.map +1 -0
- package/lib/db/repositories/proposerSlashing.d.ts +1 -0
- package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
- package/lib/db/repositories/proposerSlashing.js +5 -1
- package/lib/db/repositories/proposerSlashing.js.map +1 -1
- package/lib/execution/engine/blobVersionedHashes.d.ts +9 -0
- package/lib/execution/engine/blobVersionedHashes.d.ts.map +1 -0
- package/lib/execution/engine/blobVersionedHashes.js +74 -0
- package/lib/execution/engine/blobVersionedHashes.js.map +1 -0
- package/lib/execution/engine/disabled.d.ts +0 -2
- package/lib/execution/engine/disabled.d.ts.map +1 -1
- package/lib/execution/engine/disabled.js +0 -6
- package/lib/execution/engine/disabled.js.map +1 -1
- package/lib/execution/engine/http.d.ts +46 -16
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +236 -213
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/index.d.ts +3 -2
- package/lib/execution/engine/index.d.ts.map +1 -1
- package/lib/execution/engine/index.js +33 -8
- package/lib/execution/engine/index.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +2 -4
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/jsonRpcTransport.d.ts +25 -0
- package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -0
- package/lib/execution/engine/jsonRpcTransport.js +158 -0
- package/lib/execution/engine/jsonRpcTransport.js.map +1 -0
- package/lib/execution/engine/mock.d.ts +0 -2
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +0 -8
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/restHttpClient.d.ts +71 -0
- package/lib/execution/engine/restHttpClient.d.ts.map +1 -0
- package/lib/execution/engine/restHttpClient.js +201 -0
- package/lib/execution/engine/restHttpClient.js.map +1 -0
- package/lib/execution/engine/restTransport.d.ts +46 -0
- package/lib/execution/engine/restTransport.d.ts.map +1 -0
- package/lib/execution/engine/restTransport.js +399 -0
- package/lib/execution/engine/restTransport.js.map +1 -0
- package/lib/execution/engine/sszTypes.d.ts +1261 -0
- package/lib/execution/engine/sszTypes.d.ts.map +1 -0
- package/lib/execution/engine/sszTypes.js +216 -0
- package/lib/execution/engine/sszTypes.js.map +1 -0
- package/lib/execution/engine/transport.d.ts +39 -0
- package/lib/execution/engine/transport.d.ts.map +1 -0
- package/lib/execution/engine/transport.js +2 -0
- package/lib/execution/engine/transport.js.map +1 -0
- package/lib/execution/engine/types.d.ts +2 -8
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +24 -57
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/execution/engine/utils.d.ts.map +1 -1
- package/lib/execution/engine/utils.js +9 -2
- package/lib/execution/engine/utils.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +10 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +22 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/metrics/metrics.d.ts +7 -3
- package/lib/metrics/metrics.d.ts.map +1 -1
- package/lib/metrics/metrics.js +4 -4
- package/lib/metrics/metrics.js.map +1 -1
- package/lib/network/core/events.d.ts +5 -1
- package/lib/network/core/events.d.ts.map +1 -1
- package/lib/network/core/events.js +8 -0
- package/lib/network/core/events.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +67 -68
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +7 -3
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/lib/network/processor/index.d.ts +9 -0
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +77 -10
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
- package/lib/node/nodejs.d.ts +3 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +27 -4
- package/lib/node/nodejs.js.map +1 -1
- package/lib/node/options.d.ts +3 -2
- package/lib/node/options.d.ts.map +1 -1
- package/lib/node/options.js +2 -2
- package/lib/node/options.js.map +1 -1
- package/lib/sync/range/batch.d.ts +11 -5
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +21 -17
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +4 -4
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +12 -6
- 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 +75 -29
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +5 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +49 -24
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +38 -14
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +84 -39
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/util/asyncIterableToEvents.d.ts +10 -2
- package/lib/util/asyncIterableToEvents.d.ts.map +1 -1
- package/lib/util/asyncIterableToEvents.js +29 -3
- package/lib/util/asyncIterableToEvents.js.map +1 -1
- package/lib/util/execution.d.ts +9 -11
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +5 -1
- package/lib/util/execution.js.map +1 -1
- package/lib/util/metadata.d.ts.map +1 -1
- package/lib/util/metadata.js +2 -1
- package/lib/util/metadata.js.map +1 -1
- package/package.json +18 -17
- package/src/api/impl/beacon/blocks/index.ts +7 -4
- package/src/api/impl/beacon/index.ts +15 -0
- package/src/api/impl/beacon/pool/index.ts +4 -4
- package/src/api/impl/beacon/state/index.ts +43 -6
- package/src/api/impl/debug/index.ts +28 -19
- package/src/api/impl/validator/index.ts +61 -2
- package/src/chain/archiveStore/archiveStore.ts +19 -25
- package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -7
- package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -1
- package/src/chain/archiveStore/historicalState/types.ts +2 -2
- package/src/chain/archiveStore/historicalState/worker.ts +1 -2
- package/src/chain/archiveStore/interface.ts +1 -1
- package/src/chain/archiveStore/utils/pruneHistory.ts +4 -1
- package/src/chain/beaconProposerCache.ts +19 -1
- package/src/chain/blocks/importBlock.ts +21 -7
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
- package/src/chain/blocks/verifyBlock.ts +8 -6
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +6 -3
- package/src/chain/chain.ts +135 -38
- package/src/chain/emitter.ts +7 -1
- package/src/chain/errors/envelopeReconstructionError.ts +3 -0
- package/src/chain/errors/gossipValidation.ts +2 -0
- package/src/chain/forkChoice/index.ts +2 -0
- package/src/chain/interface.ts +5 -2
- package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
- package/src/chain/opPools/opPool.ts +89 -46
- package/src/chain/opPools/proposerPreferencesPool.ts +34 -11
- package/src/chain/opPools/utils.ts +62 -3
- package/src/chain/options.ts +3 -3
- package/src/chain/prepareNextSlot.ts +21 -6
- package/src/chain/produceBlock/computeNewStateRoot.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +76 -35
- package/src/chain/regen/regen.ts +12 -4
- package/src/chain/seenCache/seenGossipBlockInput.ts +23 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
- package/src/chain/shufflingCache.ts +84 -22
- package/src/chain/validation/attesterSlashing.ts +7 -4
- package/src/chain/validation/proposerSlashing.ts +7 -4
- package/src/chain/validatorMonitor.ts +75 -57
- package/src/db/beacon.ts +6 -0
- package/src/db/buckets.ts +2 -0
- package/src/db/interface.ts +4 -0
- package/src/db/repositories/earliestAvailableSlot.ts +19 -0
- package/src/db/repositories/index.ts +1 -0
- package/src/db/repositories/proposerPreferences.ts +23 -0
- package/src/db/repositories/proposerSlashing.ts +6 -1
- package/src/execution/engine/blobVersionedHashes.ts +72 -0
- package/src/execution/engine/disabled.ts +0 -8
- package/src/execution/engine/http.ts +304 -300
- package/src/execution/engine/index.ts +41 -7
- package/src/execution/engine/interface.ts +2 -6
- package/src/execution/engine/jsonRpcTransport.ts +247 -0
- package/src/execution/engine/mock.ts +1 -19
- package/src/execution/engine/restHttpClient.ts +269 -0
- package/src/execution/engine/restTransport.ts +544 -0
- package/src/execution/engine/sszTypes.ts +354 -0
- package/src/execution/engine/transport.ts +61 -0
- package/src/execution/engine/types.ts +34 -84
- package/src/execution/engine/utils.ts +12 -2
- package/src/metrics/metrics/lodestar.ts +23 -0
- package/src/metrics/metrics.ts +21 -6
- package/src/network/core/events.ts +16 -1
- package/src/network/processor/gossipHandlers.ts +98 -81
- package/src/network/processor/gossipValidatorFn.ts +13 -3
- package/src/network/processor/index.ts +89 -10
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +1 -1
- package/src/node/nodejs.ts +35 -3
- package/src/node/options.ts +10 -1
- package/src/sync/range/batch.ts +25 -19
- package/src/sync/range/chain.ts +15 -8
- package/src/sync/range/range.ts +76 -30
- package/src/sync/unknownBlock.ts +62 -25
- package/src/sync/utils/downloadByRange.ts +123 -58
- package/src/util/asyncIterableToEvents.ts +44 -5
- package/src/util/execution.ts +16 -8
- package/src/util/metadata.ts +2 -1
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -73,7 +73,11 @@ type AdvancePendingBlockResult =
|
|
|
73
73
|
| "removed";
|
|
74
74
|
|
|
75
75
|
class UnknownBlockRateLimitedError extends Error {
|
|
76
|
-
constructor(
|
|
76
|
+
constructor(
|
|
77
|
+
message: string,
|
|
78
|
+
/** Earliest backoff expiry seen when the download was deferred, it is retried once that has passed */
|
|
79
|
+
readonly retryAt: number
|
|
80
|
+
) {
|
|
77
81
|
super(message);
|
|
78
82
|
this.name = "UnknownBlockRateLimitedError";
|
|
79
83
|
}
|
|
@@ -849,17 +853,20 @@ export class BlockInputSync {
|
|
|
849
853
|
}
|
|
850
854
|
};
|
|
851
855
|
|
|
852
|
-
|
|
856
|
+
/**
|
|
857
|
+
* Returns false if there is no backoff left to wait for, in which case no retry is scheduled.
|
|
858
|
+
*/
|
|
859
|
+
private scheduleRateLimitBackoffRetry(): boolean {
|
|
853
860
|
this.clearRateLimitBackoffTimer();
|
|
854
861
|
|
|
855
862
|
if (!this.subscribedToNetworkEvents || (this.pendingBlocks.size === 0 && this.pendingPayloads.size === 0)) {
|
|
856
|
-
return;
|
|
863
|
+
return false;
|
|
857
864
|
}
|
|
858
865
|
|
|
859
866
|
const now = Date.now();
|
|
860
867
|
const retryAt = this.peerBalancer.getNextRateLimitRetryAt();
|
|
861
868
|
if (retryAt === null) {
|
|
862
|
-
return;
|
|
869
|
+
return false;
|
|
863
870
|
}
|
|
864
871
|
|
|
865
872
|
this.rateLimitBackoffTimeout = setTimeout(
|
|
@@ -870,6 +877,7 @@ export class BlockInputSync {
|
|
|
870
877
|
},
|
|
871
878
|
Math.max(0, retryAt - now)
|
|
872
879
|
);
|
|
880
|
+
return true;
|
|
873
881
|
}
|
|
874
882
|
|
|
875
883
|
private clearRateLimitBackoffTimer(): void {
|
|
@@ -1081,7 +1089,11 @@ export class BlockInputSync {
|
|
|
1081
1089
|
pendingBlock.status = PendingBlockInputStatus.pending;
|
|
1082
1090
|
}
|
|
1083
1091
|
this.logger.debug("Deferring unknown block download due to peer rate limit", logCtx, res.err);
|
|
1084
|
-
this.scheduleRateLimitBackoffRetry()
|
|
1092
|
+
if (!this.scheduleRateLimitBackoffRetry() || res.err.retryAt <= Date.now()) {
|
|
1093
|
+
// The backoff this download waited for expired in the meantime, a timer is only armed if another
|
|
1094
|
+
// peer is still in backoff and would retry it later than needed
|
|
1095
|
+
this.triggerUnknownBlockSearch();
|
|
1096
|
+
}
|
|
1085
1097
|
return;
|
|
1086
1098
|
}
|
|
1087
1099
|
|
|
@@ -1364,6 +1376,14 @@ export class BlockInputSync {
|
|
|
1364
1376
|
if (!isPendingPayloadEnvelope(payload)) {
|
|
1365
1377
|
payload.status = PendingPayloadInputStatus.pending;
|
|
1366
1378
|
}
|
|
1379
|
+
if (
|
|
1380
|
+
res.err instanceof UnknownBlockRateLimitedError &&
|
|
1381
|
+
(!this.scheduleRateLimitBackoffRetry() || res.err.retryAt <= Date.now())
|
|
1382
|
+
) {
|
|
1383
|
+
// The backoff this download waited for expired in the meantime, a timer is only armed if another
|
|
1384
|
+
// peer is still in backoff and would retry it later than needed
|
|
1385
|
+
this.triggerUnknownBlockSearch();
|
|
1386
|
+
}
|
|
1367
1387
|
}
|
|
1368
1388
|
|
|
1369
1389
|
private async processPayload(pendingPayload: PendingPayloadInput): Promise<void> {
|
|
@@ -1426,20 +1446,23 @@ export class BlockInputSync {
|
|
|
1426
1446
|
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
1427
1447
|
break;
|
|
1428
1448
|
|
|
1449
|
+
// The invalid envelope is evicted from the seen cache in the cases below, otherwise the next
|
|
1450
|
+
// attempt would reuse it instead of fetching another envelope from peers
|
|
1429
1451
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1430
1452
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1453
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1431
1454
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1432
1455
|
break;
|
|
1433
1456
|
|
|
1434
1457
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1435
1458
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1459
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1436
1460
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1437
1461
|
break;
|
|
1438
1462
|
|
|
1439
1463
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1440
|
-
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
1441
|
-
// reintroducing envelope replacement / recreation flows.
|
|
1442
1464
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1465
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1443
1466
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
1444
1467
|
break;
|
|
1445
1468
|
|
|
@@ -1485,16 +1508,21 @@ export class BlockInputSync {
|
|
|
1485
1508
|
const pendingColumns = payloadInput?.hasAllData()
|
|
1486
1509
|
? new Set<number>()
|
|
1487
1510
|
: new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
|
|
1511
|
+
// Same `now` for both calls, a backoff expiring in between would be reported as no peer available
|
|
1512
|
+
const now = Date.now();
|
|
1488
1513
|
// prefer peers that gossiped this payload root to us (#9923)
|
|
1489
1514
|
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
|
|
1490
1515
|
pendingColumns,
|
|
1491
1516
|
excludedPeers,
|
|
1492
|
-
cacheItem.peerIdStrings
|
|
1517
|
+
cacheItem.peerIdStrings,
|
|
1518
|
+
now
|
|
1493
1519
|
);
|
|
1494
1520
|
if (peerMeta === null) {
|
|
1495
|
-
|
|
1521
|
+
const retryAt = this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers, now);
|
|
1522
|
+
if (retryAt !== null) {
|
|
1496
1523
|
throw new UnknownBlockRateLimitedError(
|
|
1497
|
-
`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited
|
|
1524
|
+
`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`,
|
|
1525
|
+
retryAt
|
|
1498
1526
|
);
|
|
1499
1527
|
}
|
|
1500
1528
|
|
|
@@ -1633,9 +1661,11 @@ export class BlockInputSync {
|
|
|
1633
1661
|
);
|
|
1634
1662
|
this.metrics?.blockInputSync.payloadFetchPeers.set({result: FetchResult.FailureMaxAttempts}, i - 1);
|
|
1635
1663
|
|
|
1636
|
-
|
|
1664
|
+
const retryAt = deferredByRateLimit ? this.peerBalancer.getNextRateLimitRetryAt() : null;
|
|
1665
|
+
if (retryAt !== null) {
|
|
1637
1666
|
throw new UnknownBlockRateLimitedError(
|
|
1638
|
-
`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts: peers are rate-limited
|
|
1667
|
+
`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts: peers are rate-limited`,
|
|
1668
|
+
retryAt
|
|
1639
1669
|
);
|
|
1640
1670
|
}
|
|
1641
1671
|
throw Error(`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts.`);
|
|
@@ -1727,16 +1757,21 @@ export class BlockInputSync {
|
|
|
1727
1757
|
isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
|
|
1728
1758
|
? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
|
|
1729
1759
|
: defaultPendingColumns;
|
|
1760
|
+
// Same `now` for both calls, a backoff expiring in between would be reported as no peer available
|
|
1761
|
+
const now = Date.now();
|
|
1730
1762
|
// prefer peers that gossiped this block root to us (#9923)
|
|
1731
1763
|
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
|
|
1732
1764
|
pendingColumns,
|
|
1733
1765
|
excludedPeers,
|
|
1734
|
-
cacheItem.peerIdStrings
|
|
1766
|
+
cacheItem.peerIdStrings,
|
|
1767
|
+
now
|
|
1735
1768
|
);
|
|
1736
1769
|
if (peerMeta === null) {
|
|
1737
|
-
|
|
1770
|
+
const retryAt = this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers, now);
|
|
1771
|
+
if (retryAt !== null) {
|
|
1738
1772
|
throw new UnknownBlockRateLimitedError(
|
|
1739
|
-
`Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited
|
|
1773
|
+
`Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`,
|
|
1774
|
+
retryAt
|
|
1740
1775
|
);
|
|
1741
1776
|
}
|
|
1742
1777
|
|
|
@@ -1842,8 +1877,9 @@ export class BlockInputSync {
|
|
|
1842
1877
|
|
|
1843
1878
|
const message = `Error fetching BlockInput with slot=${slot} root=${rootHex} after ${i - 1} attempts.`;
|
|
1844
1879
|
|
|
1845
|
-
|
|
1846
|
-
|
|
1880
|
+
const retryAt = deferredByRateLimit ? this.peerBalancer.getNextRateLimitRetryAt() : null;
|
|
1881
|
+
if (retryAt !== null) {
|
|
1882
|
+
throw new UnknownBlockRateLimitedError(`${message} Peers are rate-limited.`, retryAt);
|
|
1847
1883
|
}
|
|
1848
1884
|
|
|
1849
1885
|
if (!isPendingBlockInput(cacheItem)) {
|
|
@@ -1879,8 +1915,6 @@ export class BlockInputSync {
|
|
|
1879
1915
|
|
|
1880
1916
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1881
1917
|
private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
|
|
1882
|
-
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1883
|
-
// later validation/finalization logic decides when it can leave memory.
|
|
1884
1918
|
if (this.pendingPayloads.delete(rootHex)) {
|
|
1885
1919
|
this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
|
|
1886
1920
|
}
|
|
@@ -2036,8 +2070,11 @@ export class UnknownBlockPeerBalancer {
|
|
|
2036
2070
|
this.rateLimitedUntilByPeer.set(peerId, rateLimitedUntilMs);
|
|
2037
2071
|
}
|
|
2038
2072
|
|
|
2039
|
-
getNextRateLimitRetryAt(
|
|
2040
|
-
|
|
2073
|
+
getNextRateLimitRetryAt(
|
|
2074
|
+
pendingColumns?: Set<number>,
|
|
2075
|
+
excludedPeers?: Set<PeerIdStr>,
|
|
2076
|
+
now = Date.now()
|
|
2077
|
+
): number | null {
|
|
2041
2078
|
let retryAt: number | null = null;
|
|
2042
2079
|
|
|
2043
2080
|
for (const [peerId, rateLimitedUntil] of this.rateLimitedUntilByPeer.entries()) {
|
|
@@ -2076,9 +2113,10 @@ export class UnknownBlockPeerBalancer {
|
|
|
2076
2113
|
bestPeerForPendingColumns(
|
|
2077
2114
|
pendingColumns: Set<number>,
|
|
2078
2115
|
excludedPeers: Set<PeerIdStr>,
|
|
2079
|
-
preferredPeers?: Set<PeerIdStr
|
|
2116
|
+
preferredPeers?: Set<PeerIdStr>,
|
|
2117
|
+
now = Date.now()
|
|
2080
2118
|
): PeerSyncMeta | null {
|
|
2081
|
-
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
2119
|
+
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers, now);
|
|
2082
2120
|
if (eligiblePeers.length === 0) {
|
|
2083
2121
|
return null;
|
|
2084
2122
|
}
|
|
@@ -2119,8 +2157,7 @@ export class UnknownBlockPeerBalancer {
|
|
|
2119
2157
|
return totalActiveRequests;
|
|
2120
2158
|
}
|
|
2121
2159
|
|
|
2122
|
-
private filterPeers(pendingDataColumns: Set<number>, excludedPeers: Set<PeerIdStr
|
|
2123
|
-
const now = Date.now();
|
|
2160
|
+
private filterPeers(pendingDataColumns: Set<number>, excludedPeers: Set<PeerIdStr>, now: number): PeerIdStr[] {
|
|
2124
2161
|
let maxColumnCount = 0;
|
|
2125
2162
|
const considerPeers: {peerId: PeerIdStr; columnCount: number}[] = [];
|
|
2126
2163
|
for (const [peerId, syncMeta] of this.peersMeta.entries()) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
2
|
import {
|
|
3
|
+
ForkName,
|
|
3
4
|
ForkPostDeneb,
|
|
4
5
|
ForkPostFulu,
|
|
5
6
|
ForkPostGloas,
|
|
@@ -13,9 +14,11 @@ import {
|
|
|
13
14
|
RootHex,
|
|
14
15
|
SignedBeaconBlock,
|
|
15
16
|
Slot,
|
|
17
|
+
ValidatorIndex,
|
|
16
18
|
deneb,
|
|
17
19
|
fulu,
|
|
18
20
|
gloas,
|
|
21
|
+
isGloasBeaconBlock,
|
|
19
22
|
isGloasDataColumnSidecar,
|
|
20
23
|
phase0,
|
|
21
24
|
} from "@lodestar/types";
|
|
@@ -59,10 +62,17 @@ export type DownloadByRangeRequests = {
|
|
|
59
62
|
};
|
|
60
63
|
};
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
/**
|
|
66
|
+
* The head block when the `SyncChain` was created. The first block of the first batch may build on its FULL variant
|
|
67
|
+
* while the payload is not imported yet, the batch then downloads the payload by root as its dangling parent.
|
|
68
|
+
*/
|
|
69
|
+
export type ParentPayload = {
|
|
63
70
|
blockRoot: Uint8Array;
|
|
64
71
|
blockRootHex: RootHex;
|
|
65
|
-
|
|
72
|
+
slot: Slot;
|
|
73
|
+
proposerIndex: ValidatorIndex;
|
|
74
|
+
forkName: ForkName;
|
|
75
|
+
bid: gloas.ExecutionPayloadBid;
|
|
66
76
|
};
|
|
67
77
|
|
|
68
78
|
export type DownloadByRangeResponses = {
|
|
@@ -78,14 +88,14 @@ export type DownloadAndCacheByRangeProps = DownloadByRangeRequests & {
|
|
|
78
88
|
logger: Logger;
|
|
79
89
|
peerIdStr: string;
|
|
80
90
|
batchBlocks?: IBlockInput[];
|
|
81
|
-
/** Required when `parentPayloadRequest` is set
|
|
82
|
-
|
|
91
|
+
/** Required when `parentPayloadRequest` is set */
|
|
92
|
+
parentPayload?: ParentPayload;
|
|
83
93
|
peerDasMetrics?: BeaconMetrics["peerDas"] | null;
|
|
84
94
|
};
|
|
85
95
|
|
|
86
96
|
export type CacheByRangeResponsesProps = {
|
|
87
|
-
cache: SeenBlockInput
|
|
88
|
-
seenPayloadEnvelopeInputCache: SeenPayloadEnvelopeInput
|
|
97
|
+
cache: Pick<SeenBlockInput, "getByBlock">;
|
|
98
|
+
seenPayloadEnvelopeInputCache: Pick<SeenPayloadEnvelopeInput, "add" | "addFromBid">;
|
|
89
99
|
peerIdStr: string;
|
|
90
100
|
responses: ValidatedResponses;
|
|
91
101
|
batchBlocks: IBlockInput[];
|
|
@@ -93,6 +103,8 @@ export type CacheByRangeResponsesProps = {
|
|
|
93
103
|
downloadedPayloadEnvelopes: Map<Slot, gloas.SignedExecutionPayloadEnvelope> | null;
|
|
94
104
|
/** Envelopes already wrapped from previous partial downloads on this batch */
|
|
95
105
|
existingPayloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
|
|
106
|
+
/** Set when `downloadedPayloadEnvelopes` may contain the dangling parent envelope */
|
|
107
|
+
parentPayload?: ParentPayload;
|
|
96
108
|
/** Sampled/custody column indices for building PayloadEnvelopeInputs */
|
|
97
109
|
custodyConfig: Pick<CustodyConfig, "sampledColumns" | "custodyColumns">;
|
|
98
110
|
seenTimestampSec: number;
|
|
@@ -130,6 +142,7 @@ export function cacheByRangeResponses({
|
|
|
130
142
|
batchBlocks,
|
|
131
143
|
downloadedPayloadEnvelopes,
|
|
132
144
|
existingPayloadEnvelopes,
|
|
145
|
+
parentPayload,
|
|
133
146
|
custodyConfig,
|
|
134
147
|
seenTimestampSec,
|
|
135
148
|
}: CacheByRangeResponsesProps): {blocks: IBlockInput[]; payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null} {
|
|
@@ -228,20 +241,29 @@ export function cacheByRangeResponses({
|
|
|
228
241
|
|
|
229
242
|
if (downloadedPayloadEnvelopes !== null) {
|
|
230
243
|
for (const [slot, envelope] of downloadedPayloadEnvelopes) {
|
|
231
|
-
// the only PayloadEnvelopeInput miss is the dangling parent
|
|
244
|
+
// the only PayloadEnvelopeInput miss is the dangling parent
|
|
232
245
|
let payloadInput = payloadEnvelopes.get(slot);
|
|
233
246
|
if (payloadInput === undefined) {
|
|
247
|
+
const blockRootHex = toRootHex(envelope.message.beaconBlockRoot);
|
|
234
248
|
if (updatedBatchBlocks.has(slot)) {
|
|
235
|
-
throw new Error(
|
|
236
|
-
`Missing PayloadEnvelopeInput for in-batch slot ${slot} root ${toRootHex(envelope.message.beaconBlockRoot)}`
|
|
237
|
-
);
|
|
249
|
+
throw new Error(`Missing PayloadEnvelopeInput for in-batch slot ${slot} root ${blockRootHex}`);
|
|
238
250
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
251
|
+
if (parentPayload === undefined || blockRootHex !== parentPayload.blockRootHex) {
|
|
252
|
+
throw new Error(`Coding error: envelope for slot ${slot} root ${blockRootHex} is not the dangling parent`);
|
|
253
|
+
}
|
|
254
|
+
// Recreates the entry if it was evicted, by the cache cap or after an invalid envelope failed the import,
|
|
255
|
+
// the parent block is not part of the batch to recreate it from
|
|
256
|
+
payloadInput = seenPayloadEnvelopeInputCache.addFromBid({
|
|
257
|
+
blockRootHex,
|
|
258
|
+
slot: parentPayload.slot,
|
|
259
|
+
forkName: parentPayload.forkName,
|
|
260
|
+
proposerIndex: parentPayload.proposerIndex,
|
|
261
|
+
bid: parentPayload.bid,
|
|
262
|
+
sampledColumns: custodyConfig.sampledColumns,
|
|
263
|
+
custodyColumns: custodyConfig.custodyColumns,
|
|
264
|
+
seenTimestampSec,
|
|
265
|
+
source: PayloadEnvelopeInputSource.byRange,
|
|
266
|
+
});
|
|
245
267
|
}
|
|
246
268
|
|
|
247
269
|
if (!payloadInput.hasPayloadEnvelope()) {
|
|
@@ -332,7 +354,7 @@ export async function downloadByRange({
|
|
|
332
354
|
columnsRequest,
|
|
333
355
|
envelopesRequest,
|
|
334
356
|
parentPayloadRequest,
|
|
335
|
-
|
|
357
|
+
parentPayload,
|
|
336
358
|
peerDasMetrics,
|
|
337
359
|
}: DownloadAndCacheByRangeProps): Promise<
|
|
338
360
|
WarnResult<
|
|
@@ -367,7 +389,7 @@ export async function downloadByRange({
|
|
|
367
389
|
columnsRequest,
|
|
368
390
|
envelopesRequest,
|
|
369
391
|
parentPayloadRequest,
|
|
370
|
-
|
|
392
|
+
parentPayload,
|
|
371
393
|
peerDasMetrics,
|
|
372
394
|
...response,
|
|
373
395
|
});
|
|
@@ -471,7 +493,7 @@ export async function validateResponses({
|
|
|
471
493
|
columnsRequest,
|
|
472
494
|
envelopesRequest,
|
|
473
495
|
parentPayloadRequest,
|
|
474
|
-
|
|
496
|
+
parentPayload,
|
|
475
497
|
blocks,
|
|
476
498
|
blobSidecars,
|
|
477
499
|
columnSidecars,
|
|
@@ -481,7 +503,7 @@ export async function validateResponses({
|
|
|
481
503
|
DownloadByRangeResponses & {
|
|
482
504
|
config: ChainForkConfig;
|
|
483
505
|
batchBlocks?: IBlockInput[];
|
|
484
|
-
|
|
506
|
+
parentPayload?: ParentPayload;
|
|
485
507
|
peerDasMetrics?: BeaconMetrics["peerDas"] | null;
|
|
486
508
|
}): Promise<
|
|
487
509
|
WarnResult<
|
|
@@ -502,9 +524,9 @@ export async function validateResponses({
|
|
|
502
524
|
);
|
|
503
525
|
}
|
|
504
526
|
|
|
505
|
-
// `parentPayloadRequest` and `
|
|
506
|
-
if ((parentPayloadRequest === undefined) !== (
|
|
507
|
-
throw new Error("Coding error: parentPayloadRequest and
|
|
527
|
+
// `parentPayloadRequest` and `parentPayload` must be supplied together
|
|
528
|
+
if ((parentPayloadRequest === undefined) !== (parentPayload === undefined)) {
|
|
529
|
+
throw new Error("Coding error: parentPayloadRequest and parentPayload must be both set or both unset");
|
|
508
530
|
}
|
|
509
531
|
|
|
510
532
|
const validatedResponses: ValidatedResponses = {};
|
|
@@ -518,7 +540,7 @@ export async function validateResponses({
|
|
|
518
540
|
validatedResponses.validatedBlocks = result.result;
|
|
519
541
|
}
|
|
520
542
|
|
|
521
|
-
const needsEnvelopeValidation = !!envelopesRequest ||
|
|
543
|
+
const needsEnvelopeValidation = !!envelopesRequest || parentPayload !== undefined;
|
|
522
544
|
const dataRequest = blobsRequest ?? columnsRequest;
|
|
523
545
|
if (!dataRequest && !needsEnvelopeValidation) {
|
|
524
546
|
return {result: {responses: validatedResponses, payloadEnvelopes: null}, warnings};
|
|
@@ -526,12 +548,8 @@ export async function validateResponses({
|
|
|
526
548
|
|
|
527
549
|
if (!dataRequest) {
|
|
528
550
|
// Only envelope and/or parent-by-root validation needed
|
|
529
|
-
if (
|
|
530
|
-
const parentValidated = await validateParentPayloadColumns(
|
|
531
|
-
parentPayloadCommitments,
|
|
532
|
-
columnSidecars ?? [],
|
|
533
|
-
peerDasMetrics
|
|
534
|
-
);
|
|
551
|
+
if (parentPayload !== undefined) {
|
|
552
|
+
const parentValidated = await validateParentPayloadColumns(parentPayload, columnSidecars ?? [], peerDasMetrics);
|
|
535
553
|
if (parentValidated) {
|
|
536
554
|
validatedResponses.validatedColumnSidecars = [parentValidated];
|
|
537
555
|
}
|
|
@@ -540,7 +558,7 @@ export async function validateResponses({
|
|
|
540
558
|
validatedResponses.validatedBlocks ?? [],
|
|
541
559
|
batchBlocks,
|
|
542
560
|
payloadEnvelopes ?? [],
|
|
543
|
-
|
|
561
|
+
parentPayload
|
|
544
562
|
);
|
|
545
563
|
return {result: {responses: validatedResponses, payloadEnvelopes: validatedPayloadEnvelopes}, warnings};
|
|
546
564
|
}
|
|
@@ -601,12 +619,8 @@ export async function validateResponses({
|
|
|
601
619
|
}
|
|
602
620
|
|
|
603
621
|
// Parent columns (by-root): KZG-validate against parent's bid commitments and append.
|
|
604
|
-
if (
|
|
605
|
-
const parentValidated = await validateParentPayloadColumns(
|
|
606
|
-
parentPayloadCommitments,
|
|
607
|
-
columnSidecars ?? [],
|
|
608
|
-
peerDasMetrics
|
|
609
|
-
);
|
|
622
|
+
if (parentPayload !== undefined) {
|
|
623
|
+
const parentValidated = await validateParentPayloadColumns(parentPayload, columnSidecars ?? [], peerDasMetrics);
|
|
610
624
|
if (parentValidated) {
|
|
611
625
|
validatedResponses.validatedColumnSidecars = [
|
|
612
626
|
...(validatedResponses.validatedColumnSidecars ?? []),
|
|
@@ -621,7 +635,7 @@ export async function validateResponses({
|
|
|
621
635
|
validatedResponses.validatedBlocks ?? [],
|
|
622
636
|
batchBlocks,
|
|
623
637
|
payloadEnvelopes ?? [],
|
|
624
|
-
|
|
638
|
+
parentPayload
|
|
625
639
|
);
|
|
626
640
|
}
|
|
627
641
|
|
|
@@ -629,13 +643,13 @@ export async function validateResponses({
|
|
|
629
643
|
}
|
|
630
644
|
|
|
631
645
|
async function validateParentPayloadColumns(
|
|
632
|
-
|
|
646
|
+
parentPayload: ParentPayload,
|
|
633
647
|
columnSidecars: DataColumnSidecar[],
|
|
634
648
|
peerDasMetrics?: BeaconMetrics["peerDas"] | null
|
|
635
649
|
): Promise<ValidatedColumnSidecars | null> {
|
|
636
650
|
const parentColumns: gloas.DataColumnSidecar[] = [];
|
|
637
651
|
for (const cs of columnSidecars) {
|
|
638
|
-
if (isGloasDataColumnSidecar(cs) && byteArrayEquals(cs.beaconBlockRoot,
|
|
652
|
+
if (isGloasDataColumnSidecar(cs) && byteArrayEquals(cs.beaconBlockRoot, parentPayload.blockRoot)) {
|
|
639
653
|
parentColumns.push(cs);
|
|
640
654
|
}
|
|
641
655
|
}
|
|
@@ -645,17 +659,16 @@ async function validateParentPayloadColumns(
|
|
|
645
659
|
}
|
|
646
660
|
|
|
647
661
|
parentColumns.sort((a, b) => a.index - b.index);
|
|
648
|
-
const parentSlot = parentColumns[0].slot;
|
|
649
662
|
|
|
650
663
|
await validateGloasBlockDataColumnSidecars(
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
664
|
+
parentPayload.slot,
|
|
665
|
+
parentPayload.blockRoot,
|
|
666
|
+
parentPayload.bid.blobKzgCommitments,
|
|
654
667
|
parentColumns,
|
|
655
668
|
peerDasMetrics
|
|
656
669
|
);
|
|
657
670
|
|
|
658
|
-
return {blockRoot:
|
|
671
|
+
return {blockRoot: parentPayload.blockRoot, columnSidecars: parentColumns};
|
|
659
672
|
}
|
|
660
673
|
|
|
661
674
|
/**
|
|
@@ -1171,6 +1184,10 @@ export enum DownloadByRangeErrorCode {
|
|
|
1171
1184
|
|
|
1172
1185
|
/** Envelope beaconBlockRoot does not match the block's root */
|
|
1173
1186
|
INVALID_ENVELOPE_BEACON_BLOCK_ROOT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_ENVELOPE_BEACON_BLOCK_ROOT",
|
|
1187
|
+
/** Envelope payload.blockHash does not match the block's bid */
|
|
1188
|
+
INVALID_ENVELOPE_BLOCK_HASH = "DOWNLOAD_BY_RANGE_ERROR_INVALID_ENVELOPE_BLOCK_HASH",
|
|
1189
|
+
/** Dangling parent envelope payload.slotNumber does not match the parent's slot */
|
|
1190
|
+
INVALID_ENVELOPE_SLOT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_ENVELOPE_SLOT",
|
|
1174
1191
|
|
|
1175
1192
|
/** Block segment + envelopes failed chain-segment linearity / FULL-chain checks */
|
|
1176
1193
|
INVALID_CHAIN_SEGMENT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_CHAIN_SEGMENT",
|
|
@@ -1272,6 +1289,17 @@ export type DownloadByRangeErrorType =
|
|
|
1272
1289
|
expected: string;
|
|
1273
1290
|
actual: string;
|
|
1274
1291
|
}
|
|
1292
|
+
| {
|
|
1293
|
+
code: DownloadByRangeErrorCode.INVALID_ENVELOPE_BLOCK_HASH;
|
|
1294
|
+
slot: Slot;
|
|
1295
|
+
expected: string;
|
|
1296
|
+
actual: string;
|
|
1297
|
+
}
|
|
1298
|
+
| {
|
|
1299
|
+
code: DownloadByRangeErrorCode.INVALID_ENVELOPE_SLOT;
|
|
1300
|
+
expected: Slot;
|
|
1301
|
+
actual: Slot;
|
|
1302
|
+
}
|
|
1275
1303
|
| {
|
|
1276
1304
|
code: DownloadByRangeErrorCode.INVALID_CHAIN_SEGMENT;
|
|
1277
1305
|
slot: Slot;
|
|
@@ -1285,57 +1313,94 @@ export class DownloadByRangeError extends LodestarError<DownloadByRangeErrorType
|
|
|
1285
1313
|
*
|
|
1286
1314
|
* Three categories of envelope slots:
|
|
1287
1315
|
* - In-batch slot whose block we have: verify envelope.beaconBlockRoot matches.
|
|
1288
|
-
* - Dangling-parent envelope (only when `
|
|
1289
|
-
* batch of a `SyncChain`): keep if `envelope.beaconBlockRoot ===
|
|
1316
|
+
* - Dangling-parent envelope (only when `parentPayload` is set, i.e. the first
|
|
1317
|
+
* batch of a `SyncChain`): keep if `envelope.beaconBlockRoot === parentPayload.blockRoot`, its slot and
|
|
1318
|
+
* block hash must match the parent's bid.
|
|
1290
1319
|
* - Other "orphan" envelopes (e.g. unrelated slots): ignored.
|
|
1291
1320
|
*/
|
|
1292
1321
|
export function validateEnvelopesByRangeResponse(
|
|
1293
1322
|
validatedBlocks: ValidatedBlock[],
|
|
1294
1323
|
batchBlocks: IBlockInput[] | undefined,
|
|
1295
1324
|
payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[],
|
|
1296
|
-
|
|
1325
|
+
parentPayload?: ParentPayload
|
|
1297
1326
|
): Map<Slot, gloas.SignedExecutionPayloadEnvelope> {
|
|
1298
|
-
// Build a map of slot ->
|
|
1299
|
-
const
|
|
1327
|
+
// Build a map of slot -> block root and bid block hash for all blocks in the batch
|
|
1328
|
+
const batchBlocksBySlot = new Map<Slot, {blockRoot: Uint8Array; block: SignedBeaconBlock | null}>();
|
|
1300
1329
|
if (batchBlocks) {
|
|
1301
1330
|
for (const blockInput of batchBlocks) {
|
|
1302
|
-
|
|
1331
|
+
batchBlocksBySlot.set(blockInput.slot, {
|
|
1332
|
+
blockRoot: fromHex(blockInput.blockRootHex),
|
|
1333
|
+
block: blockInput.hasBlock() ? blockInput.getBlock() : null,
|
|
1334
|
+
});
|
|
1303
1335
|
}
|
|
1304
1336
|
}
|
|
1305
1337
|
for (const {block, blockRoot} of validatedBlocks) {
|
|
1306
|
-
|
|
1338
|
+
batchBlocksBySlot.set(block.message.slot, {blockRoot, block});
|
|
1307
1339
|
}
|
|
1308
1340
|
|
|
1309
1341
|
const payloadEnvelopeMap = new Map<Slot, gloas.SignedExecutionPayloadEnvelope>();
|
|
1310
1342
|
|
|
1311
1343
|
for (const payloadEnvelope of payloadEnvelopes) {
|
|
1312
1344
|
const slot = payloadEnvelope.message.payload.slotNumber;
|
|
1313
|
-
const
|
|
1345
|
+
const batchBlock = batchBlocksBySlot.get(slot);
|
|
1314
1346
|
|
|
1315
|
-
if (
|
|
1347
|
+
if (batchBlock === undefined) {
|
|
1316
1348
|
// Keep the requested dangling-parent envelope only when its beaconBlockRoot matches
|
|
1317
1349
|
// exactly. All other unrelated envelopes are dropped.
|
|
1318
1350
|
if (
|
|
1319
|
-
|
|
1320
|
-
byteArrayEquals(payloadEnvelope.message.beaconBlockRoot,
|
|
1351
|
+
parentPayload !== undefined &&
|
|
1352
|
+
byteArrayEquals(payloadEnvelope.message.beaconBlockRoot, parentPayload.blockRoot)
|
|
1321
1353
|
) {
|
|
1354
|
+
if (slot !== parentPayload.slot) {
|
|
1355
|
+
throw new DownloadByRangeError({
|
|
1356
|
+
code: DownloadByRangeErrorCode.INVALID_ENVELOPE_SLOT,
|
|
1357
|
+
expected: parentPayload.slot,
|
|
1358
|
+
actual: slot,
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
assertEnvelopeBlockHash(payloadEnvelope, parentPayload.bid.blockHash);
|
|
1322
1362
|
payloadEnvelopeMap.set(slot, payloadEnvelope);
|
|
1323
1363
|
}
|
|
1324
1364
|
continue;
|
|
1325
1365
|
}
|
|
1326
1366
|
|
|
1327
1367
|
// Verify beaconBlockRoot matches the block's root
|
|
1328
|
-
if (!byteArrayEquals(payloadEnvelope.message.beaconBlockRoot,
|
|
1368
|
+
if (!byteArrayEquals(payloadEnvelope.message.beaconBlockRoot, batchBlock.blockRoot)) {
|
|
1329
1369
|
throw new DownloadByRangeError({
|
|
1330
1370
|
code: DownloadByRangeErrorCode.INVALID_ENVELOPE_BEACON_BLOCK_ROOT,
|
|
1331
1371
|
slot,
|
|
1332
|
-
expected: toRootHex(
|
|
1372
|
+
expected: toRootHex(batchBlock.blockRoot),
|
|
1333
1373
|
actual: toRootHex(payloadEnvelope.message.beaconBlockRoot),
|
|
1334
1374
|
});
|
|
1335
1375
|
}
|
|
1336
1376
|
|
|
1377
|
+
if (batchBlock.block !== null && isGloasBeaconBlock(batchBlock.block.message)) {
|
|
1378
|
+
assertEnvelopeBlockHash(
|
|
1379
|
+
payloadEnvelope,
|
|
1380
|
+
batchBlock.block.message.body.signedExecutionPayloadBid.message.blockHash
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1337
1384
|
payloadEnvelopeMap.set(slot, payloadEnvelope);
|
|
1338
1385
|
}
|
|
1339
1386
|
|
|
1340
1387
|
return payloadEnvelopeMap;
|
|
1341
1388
|
}
|
|
1389
|
+
|
|
1390
|
+
/**
|
|
1391
|
+
* Rejected before it is cached. In-batch, `assertLinearChainSegment` fails on a wrong hash without evicting the
|
|
1392
|
+
* envelope. For the dangling parent, the mismatch would only be caught at import.
|
|
1393
|
+
*/
|
|
1394
|
+
function assertEnvelopeBlockHash(
|
|
1395
|
+
payloadEnvelope: gloas.SignedExecutionPayloadEnvelope,
|
|
1396
|
+
bidBlockHash: Uint8Array
|
|
1397
|
+
): void {
|
|
1398
|
+
if (!byteArrayEquals(payloadEnvelope.message.payload.blockHash, bidBlockHash)) {
|
|
1399
|
+
throw new DownloadByRangeError({
|
|
1400
|
+
code: DownloadByRangeErrorCode.INVALID_ENVELOPE_BLOCK_HASH,
|
|
1401
|
+
slot: payloadEnvelope.message.payload.slotNumber,
|
|
1402
|
+
expected: toRootHex(bidBlockHash),
|
|
1403
|
+
actual: toRootHex(payloadEnvelope.message.payload.blockHash),
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
}
|