@lodestar/beacon-node 1.43.0-dev.4358217e12 → 1.43.0-dev.4451fec75a
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 +10 -0
- 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/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/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +68 -2
- package/lib/api/impl/validator/index.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 +16 -31
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +6 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +19 -10
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +35 -21
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +12 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +28 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +17 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/types.d.ts +2 -1
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +1 -3
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +34 -23
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts +2 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +30 -12
- 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 +16 -7
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +10 -6
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +25 -8
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +0 -11
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +0 -4
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +8 -1
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/errors/proposerPreferences.js +1 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -1
- package/lib/chain/initState.d.ts.map +1 -1
- package/lib/chain/initState.js +6 -1
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/lightClient/index.d.ts.map +1 -1
- package/lib/chain/lightClient/index.js +1 -1
- package/lib/chain/lightClient/index.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts +2 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +52 -10
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +16 -18
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +12 -3
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +33 -20
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +1 -4
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +1 -4
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +21 -11
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +70 -20
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +8 -7
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.js +11 -10
- package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +11 -8
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +5 -3
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +39 -17
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +4 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +5 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +751 -2
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/interface.d.ts +2 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +2 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +8 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +61 -13
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.js +5 -5
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.d.ts.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.js +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByHead.d.ts +9 -0
- package/lib/network/reqresp/handlers/beaconBlocksByHead.d.ts.map +1 -0
- package/lib/network/reqresp/handlers/beaconBlocksByHead.js +61 -0
- package/lib/network/reqresp/handlers/beaconBlocksByHead.js.map +1 -0
- package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/index.js +5 -0
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/interface.d.ts +1 -1
- package/lib/network/reqresp/interface.js +1 -1
- package/lib/network/reqresp/protocols.d.ts +1 -0
- package/lib/network/reqresp/protocols.d.ts.map +1 -1
- package/lib/network/reqresp/protocols.js +5 -0
- package/lib/network/reqresp/protocols.js.map +1 -1
- package/lib/network/reqresp/rateLimit.d.ts.map +1 -1
- package/lib/network/reqresp/rateLimit.js +4 -0
- package/lib/network/reqresp/rateLimit.js.map +1 -1
- package/lib/network/reqresp/score.d.ts.map +1 -1
- package/lib/network/reqresp/score.js +1 -0
- package/lib/network/reqresp/score.js.map +1 -1
- package/lib/network/reqresp/types.d.ts +3 -0
- package/lib/network/reqresp/types.d.ts.map +1 -1
- package/lib/network/reqresp/types.js +3 -0
- package/lib/network/reqresp/types.js.map +1 -1
- package/lib/node/nodejs.js +2 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/node/notifier.js +1 -7
- package/lib/node/notifier.js.map +1 -1
- package/lib/sync/constants.d.ts +7 -1
- package/lib/sync/constants.d.ts.map +1 -1
- package/lib/sync/constants.js +7 -4
- package/lib/sync/constants.js.map +1 -1
- package/lib/sync/range/batch.d.ts +43 -6
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +243 -44
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +16 -2
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +100 -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 +54 -5
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/range/utils/peerBalancer.d.ts +2 -1
- package/lib/sync/range/utils/peerBalancer.d.ts.map +1 -1
- package/lib/sync/range/utils/peerBalancer.js +8 -4
- package/lib/sync/range/utils/peerBalancer.js.map +1 -1
- package/lib/sync/sync.d.ts.map +1 -1
- package/lib/sync/sync.js +13 -0
- package/lib/sync/sync.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +7 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +127 -57
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +36 -9
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +106 -42
- 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 +10 -0
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/sync/utils/rateLimit.d.ts +2 -0
- package/lib/sync/utils/rateLimit.d.ts.map +1 -0
- package/lib/sync/utils/rateLimit.js +15 -0
- package/lib/sync/utils/rateLimit.js.map +1 -0
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +8 -6
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +15 -16
- package/src/api/impl/beacon/blocks/index.ts +13 -0
- package/src/api/impl/beacon/pool/index.ts +83 -1
- package/src/api/impl/debug/index.ts +0 -1
- package/src/api/impl/validator/index.ts +82 -1
- package/src/chain/blocks/blockInput/blockInput.ts +4 -1
- package/src/chain/blocks/importBlock.ts +16 -50
- package/src/chain/blocks/importExecutionPayload.ts +32 -15
- package/src/chain/blocks/index.ts +32 -15
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +37 -3
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +18 -0
- package/src/chain/blocks/types.ts +2 -1
- package/src/chain/blocks/utils/chainSegment.ts +35 -24
- package/src/chain/blocks/verifyBlock.ts +45 -13
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -6
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +14 -6
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
- package/src/chain/chain.ts +29 -7
- package/src/chain/emitter.ts +0 -11
- package/src/chain/errors/proposerPreferences.ts +9 -1
- package/src/chain/initState.ts +9 -1
- package/src/chain/lightClient/index.ts +6 -6
- package/src/chain/opPools/payloadAttestationPool.ts +59 -10
- package/src/chain/prepareNextSlot.ts +21 -29
- package/src/chain/produceBlock/produceBlockBody.ts +41 -25
- package/src/chain/regen/queued.ts +2 -7
- package/src/chain/regen/regen.ts +2 -7
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +90 -24
- package/src/chain/seenCache/seenProposerPreferences.ts +14 -11
- package/src/chain/validation/executionPayloadBid.ts +11 -8
- package/src/chain/validation/payloadAttestationMessage.ts +6 -4
- package/src/chain/validation/proposerPreferences.ts +37 -18
- package/src/metrics/metrics/lodestar.ts +6 -0
- package/src/network/interface.ts +2 -0
- package/src/network/network.ts +23 -0
- package/src/network/processor/gossipHandlers.ts +75 -14
- package/src/network/processor/index.ts +5 -5
- package/src/network/reqresp/ReqRespBeaconNode.ts +1 -0
- package/src/network/reqresp/handlers/beaconBlocksByHead.ts +91 -0
- package/src/network/reqresp/handlers/index.ts +5 -0
- package/src/network/reqresp/interface.ts +1 -1
- package/src/network/reqresp/protocols.ts +6 -0
- package/src/network/reqresp/rateLimit.ts +4 -0
- package/src/network/reqresp/score.ts +1 -0
- package/src/network/reqresp/types.ts +5 -0
- package/src/node/nodejs.ts +2 -2
- package/src/node/notifier.ts +1 -8
- package/src/sync/constants.ts +8 -4
- package/src/sync/range/batch.ts +309 -51
- package/src/sync/range/chain.ts +119 -10
- package/src/sync/range/range.ts +68 -5
- package/src/sync/range/utils/peerBalancer.ts +9 -3
- package/src/sync/sync.ts +13 -1
- package/src/sync/unknownBlock.ts +157 -60
- package/src/sync/utils/downloadByRange.ts +174 -44
- package/src/sync/utils/downloadByRoot.ts +12 -0
- package/src/sync/utils/rateLimit.ts +16 -0
- package/src/util/sszBytes.ts +8 -6
package/src/sync/range/chain.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {Epoch, Root, Slot} from "@lodestar/types";
|
|
3
|
-
import {ErrorAborted, LodestarError, Logger, toRootHex} from "@lodestar/utils";
|
|
2
|
+
import {Epoch, Root, Slot, gloas} from "@lodestar/types";
|
|
3
|
+
import {ErrorAborted, LodestarError, Logger, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
4
4
|
import {isBlockInputBlobs, isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
|
|
5
5
|
import {BlockInputErrorCode} from "../../chain/blocks/blockInput/errors.js";
|
|
6
6
|
import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
|
|
@@ -17,6 +17,7 @@ import {PeerIdStr} from "../../util/peerId.js";
|
|
|
17
17
|
import {WarnResult, wrapError} from "../../util/wrapError.js";
|
|
18
18
|
import {BATCH_BUFFER_SIZE, EPOCHS_PER_BATCH, MAX_LOOK_AHEAD_EPOCHS} from "../constants.js";
|
|
19
19
|
import {DownloadByRangeError, DownloadByRangeErrorCode} from "../utils/downloadByRange.js";
|
|
20
|
+
import {getRateLimitedUntilMs} from "../utils/rateLimit.js";
|
|
20
21
|
import {RangeSyncType} from "../utils/remoteSyncType.js";
|
|
21
22
|
import {Batch, BatchError, BatchErrorCode, BatchMetadata, BatchStatus} from "./batch.js";
|
|
22
23
|
import {
|
|
@@ -139,20 +140,33 @@ export class SyncChain {
|
|
|
139
140
|
private readonly batchProcessor = new ItTrigger();
|
|
140
141
|
/** Sorted map of batches undergoing some kind of processing. */
|
|
141
142
|
private readonly batches = new Map<Epoch, Batch>();
|
|
143
|
+
/**
|
|
144
|
+
* `true` until the first `Batch` is constructed via `includeNextBatch`
|
|
145
|
+
*/
|
|
146
|
+
private isFirstBatch = true;
|
|
142
147
|
private readonly peerset = new Map<PeerIdStr, ChainTarget>();
|
|
148
|
+
/**
|
|
149
|
+
* Tracks peers that have rate-limited us, mapped to the timestamp (ms) until which we should avoid them.
|
|
150
|
+
* This is a sync-layer optimization to avoid assigning batches to backed-off peers.
|
|
151
|
+
* The reqresp SelfRateLimiter independently enforces backoff at the protocol level as a safety net.
|
|
152
|
+
*/
|
|
153
|
+
private readonly rateLimitedPeers = new Map<PeerIdStr, number>();
|
|
154
|
+
private rateLimitBackoffTimeout: NodeJS.Timeout | undefined;
|
|
143
155
|
|
|
144
156
|
private readonly logger: Logger;
|
|
145
157
|
private readonly config: ChainForkConfig;
|
|
146
158
|
private readonly clock: IClock;
|
|
147
159
|
private readonly metrics: Metrics | null;
|
|
148
160
|
private readonly custodyConfig: CustodyConfig;
|
|
161
|
+
private readonly latestBid: gloas.ExecutionPayloadBid | undefined;
|
|
149
162
|
|
|
150
163
|
constructor(
|
|
151
164
|
initialBatchEpoch: Epoch,
|
|
152
165
|
initialTarget: ChainTarget,
|
|
153
166
|
syncType: RangeSyncType,
|
|
154
167
|
fns: SyncChainFns,
|
|
155
|
-
modules: SyncChainModules
|
|
168
|
+
modules: SyncChainModules,
|
|
169
|
+
latestBid: gloas.ExecutionPayloadBid | undefined
|
|
156
170
|
) {
|
|
157
171
|
const {config, clock, custodyConfig, logger, metrics} = modules;
|
|
158
172
|
this.firstBatchEpoch = initialBatchEpoch;
|
|
@@ -168,6 +182,7 @@ export class SyncChain {
|
|
|
168
182
|
this.clock = clock;
|
|
169
183
|
this.metrics = metrics;
|
|
170
184
|
this.custodyConfig = custodyConfig;
|
|
185
|
+
this.latestBid = latestBid;
|
|
171
186
|
this.logger = logger;
|
|
172
187
|
this.logId = `${syncType}-${nextChainId++}`;
|
|
173
188
|
|
|
@@ -222,12 +237,16 @@ export class SyncChain {
|
|
|
222
237
|
*/
|
|
223
238
|
stopSyncing(): void {
|
|
224
239
|
this.status = SyncChainStatus.Stopped;
|
|
240
|
+
this.clearRateLimitBackoffTimer();
|
|
241
|
+
this.logger.debug("SyncChain stopSyncing", {id: this.logId});
|
|
225
242
|
}
|
|
226
243
|
|
|
227
244
|
/**
|
|
228
245
|
* Permanently remove this chain. Throws the main AsyncIterable
|
|
229
246
|
*/
|
|
230
247
|
remove(): void {
|
|
248
|
+
this.logger.debug("SyncChain remove", {id: this.logId});
|
|
249
|
+
this.clearRateLimitBackoffTimer();
|
|
231
250
|
this.batchProcessor.end(new ErrorAborted("SyncChain"));
|
|
232
251
|
}
|
|
233
252
|
|
|
@@ -246,6 +265,7 @@ export class SyncChain {
|
|
|
246
265
|
*/
|
|
247
266
|
removePeer(peerId: PeerIdStr): boolean {
|
|
248
267
|
const deleted = this.peerset.delete(peerId);
|
|
268
|
+
this.rateLimitedPeers.delete(peerId);
|
|
249
269
|
this.computeTarget();
|
|
250
270
|
return deleted;
|
|
251
271
|
}
|
|
@@ -349,6 +369,8 @@ export class SyncChain {
|
|
|
349
369
|
}
|
|
350
370
|
|
|
351
371
|
throw e;
|
|
372
|
+
} finally {
|
|
373
|
+
this.clearRateLimitBackoffTimer();
|
|
352
374
|
}
|
|
353
375
|
}
|
|
354
376
|
|
|
@@ -372,6 +394,44 @@ export class SyncChain {
|
|
|
372
394
|
}
|
|
373
395
|
}
|
|
374
396
|
|
|
397
|
+
private scheduleRateLimitBackoffRetry(): void {
|
|
398
|
+
this.clearRateLimitBackoffTimer();
|
|
399
|
+
|
|
400
|
+
if (this.status !== SyncChainStatus.Syncing || this.rateLimitedPeers.size === 0) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const now = Date.now();
|
|
405
|
+
let retryAt: number | null = null;
|
|
406
|
+
for (const [peerId, rateLimitedUntil] of this.rateLimitedPeers.entries()) {
|
|
407
|
+
if (rateLimitedUntil <= now) {
|
|
408
|
+
this.rateLimitedPeers.delete(peerId);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
retryAt = Math.min(retryAt ?? rateLimitedUntil, rateLimitedUntil);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (retryAt === null) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
this.rateLimitBackoffTimeout = setTimeout(
|
|
419
|
+
() => {
|
|
420
|
+
this.rateLimitBackoffTimeout = undefined;
|
|
421
|
+
this.triggerBatchDownloader();
|
|
422
|
+
this.scheduleRateLimitBackoffRetry();
|
|
423
|
+
},
|
|
424
|
+
Math.max(0, retryAt - now)
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
private clearRateLimitBackoffTimer(): void {
|
|
429
|
+
if (this.rateLimitBackoffTimeout !== undefined) {
|
|
430
|
+
clearTimeout(this.rateLimitBackoffTimeout);
|
|
431
|
+
this.rateLimitBackoffTimeout = undefined;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
375
435
|
/**
|
|
376
436
|
* Attempts to request the next required batches from the peer pool if the chain is syncing.
|
|
377
437
|
* It will exhaust the peer pool and left over batches until the batch buffer is reached.
|
|
@@ -381,8 +441,18 @@ export class SyncChain {
|
|
|
381
441
|
return;
|
|
382
442
|
}
|
|
383
443
|
|
|
444
|
+
const now = Date.now();
|
|
384
445
|
const peersSyncInfo: PeerSyncInfo[] = [];
|
|
385
446
|
for (const [peerId, target] of this.peerset.entries()) {
|
|
447
|
+
// Skip peers that are currently in rate-limit backoff
|
|
448
|
+
const rateLimitedUntil = this.rateLimitedPeers.get(peerId);
|
|
449
|
+
if (rateLimitedUntil !== undefined) {
|
|
450
|
+
if (now < rateLimitedUntil) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
this.rateLimitedPeers.delete(peerId);
|
|
454
|
+
}
|
|
455
|
+
|
|
386
456
|
try {
|
|
387
457
|
peersSyncInfo.push({...this.getConnectedPeerSyncMeta(peerId), target});
|
|
388
458
|
} catch (e) {
|
|
@@ -456,7 +526,17 @@ export class SyncChain {
|
|
|
456
526
|
return null;
|
|
457
527
|
}
|
|
458
528
|
|
|
459
|
-
const batch = new Batch(
|
|
529
|
+
const batch = new Batch(
|
|
530
|
+
startEpoch,
|
|
531
|
+
this.config,
|
|
532
|
+
this.clock,
|
|
533
|
+
this.custodyConfig,
|
|
534
|
+
this.isFirstBatch,
|
|
535
|
+
// `latestBid` is only meaningful for the first batch's parent-payload check
|
|
536
|
+
this.isFirstBatch ? this.latestBid : undefined,
|
|
537
|
+
this.target.slot
|
|
538
|
+
);
|
|
539
|
+
this.isFirstBatch = false;
|
|
460
540
|
this.batches.set(startEpoch, batch);
|
|
461
541
|
return batch;
|
|
462
542
|
}
|
|
@@ -471,7 +551,7 @@ export class SyncChain {
|
|
|
471
551
|
peer: prettyPrintPeerIdStr(peer.peerId),
|
|
472
552
|
});
|
|
473
553
|
try {
|
|
474
|
-
batch.startDownloading(peer
|
|
554
|
+
batch.startDownloading(peer);
|
|
475
555
|
|
|
476
556
|
// wrapError ensures to never call both batch success() and batch error()
|
|
477
557
|
const res = await wrapError(this.downloadByRange(peer, batch, this.syncType));
|
|
@@ -501,6 +581,8 @@ export class SyncChain {
|
|
|
501
581
|
case DownloadByRangeErrorCode.OUT_OF_ORDER_BLOCKS:
|
|
502
582
|
case DownloadByRangeErrorCode.OUT_OF_RANGE_BLOCKS:
|
|
503
583
|
case DownloadByRangeErrorCode.PARENT_ROOT_MISMATCH:
|
|
584
|
+
case DownloadByRangeErrorCode.INVALID_ENVELOPE_BEACON_BLOCK_ROOT:
|
|
585
|
+
case DownloadByRangeErrorCode.INVALID_CHAIN_SEGMENT:
|
|
504
586
|
case BlobSidecarErrorCode.INCLUSION_PROOF_INVALID:
|
|
505
587
|
case BlobSidecarErrorCode.INVALID_KZG_PROOF_BATCH:
|
|
506
588
|
case DataColumnSidecarErrorCode.INCORRECT_KZG_COMMITMENTS_COUNT:
|
|
@@ -514,7 +596,16 @@ export class SyncChain {
|
|
|
514
596
|
{id: this.logId, ...batch.getMetadata(), peer: prettyPrintPeerIdStr(peer.peerId)},
|
|
515
597
|
res.err
|
|
516
598
|
);
|
|
517
|
-
|
|
599
|
+
const rateLimitedUntilMs = getRateLimitedUntilMs(res.err);
|
|
600
|
+
if (rateLimitedUntilMs !== null) {
|
|
601
|
+
// Peer rate-limited us — don't count as a failed download attempt and mark peer for backoff
|
|
602
|
+
this.rateLimitedPeers.set(peer.peerId, rateLimitedUntilMs);
|
|
603
|
+
this.scheduleRateLimitBackoffRetry();
|
|
604
|
+
batch.downloadingRateLimited();
|
|
605
|
+
this.triggerBatchDownloader();
|
|
606
|
+
} else {
|
|
607
|
+
batch.downloadingError(peer.peerId); // Throws after MAX_DOWNLOAD_ATTEMPTS
|
|
608
|
+
}
|
|
518
609
|
} else {
|
|
519
610
|
this.logger.verbose("Batch download success", {
|
|
520
611
|
id: this.logId,
|
|
@@ -534,7 +625,7 @@ export class SyncChain {
|
|
|
534
625
|
this.metrics?.syncRange.downloadByRange.warn.inc({client: peer.client, code: warning.type.code});
|
|
535
626
|
this.logger.debug(
|
|
536
627
|
"Batch downloaded with warning",
|
|
537
|
-
{id: this.logId,
|
|
628
|
+
{id: this.logId, ...batch.getMetadata(), ...logMeta, peer: prettyPrintPeerIdStr(peer.peerId)},
|
|
538
629
|
warning
|
|
539
630
|
);
|
|
540
631
|
}
|
|
@@ -560,10 +651,17 @@ export class SyncChain {
|
|
|
560
651
|
// the flow will continue to call triggerBatchDownloader() below
|
|
561
652
|
}
|
|
562
653
|
|
|
654
|
+
const blockSlots = downloadSuccessOutput.blocks.map((b) => b.slot);
|
|
655
|
+
const envelopeSlots = downloadSuccessOutput.payloadEnvelopes
|
|
656
|
+
? Array.from(downloadSuccessOutput.payloadEnvelopes.keys())
|
|
657
|
+
: null;
|
|
658
|
+
|
|
563
659
|
this.logger.debug(logMessage, {
|
|
564
660
|
id: this.logId,
|
|
565
|
-
|
|
661
|
+
...batch.getMetadata(),
|
|
566
662
|
...logMeta,
|
|
663
|
+
blockSlots: prettyPrintIndices(blockSlots),
|
|
664
|
+
...(envelopeSlots ? {envelopeSlots: prettyPrintIndices(envelopeSlots)} : {}),
|
|
567
665
|
peer: prettyPrintPeerIdStr(peer.peerId),
|
|
568
666
|
});
|
|
569
667
|
}
|
|
@@ -586,13 +684,24 @@ export class SyncChain {
|
|
|
586
684
|
* Sends `batch` to the processor. Note: batch may be empty
|
|
587
685
|
*/
|
|
588
686
|
private async processBatch(batch: Batch): Promise<void> {
|
|
589
|
-
const {blocks, payloadEnvelopes} = batch.startProcessing();
|
|
687
|
+
const {blocks, payloadEnvelopes, peers} = batch.startProcessing();
|
|
688
|
+
|
|
689
|
+
const logCtx = {
|
|
690
|
+
id: this.logId,
|
|
691
|
+
...batch.getMetadata(),
|
|
692
|
+
blockCount: blocks.length,
|
|
693
|
+
blockSlots: prettyPrintIndices(blocks.map((b) => b.slot)),
|
|
694
|
+
...(payloadEnvelopes ? {envelopeSlots: prettyPrintIndices(Array.from(payloadEnvelopes.keys()))} : {}),
|
|
695
|
+
peers: peers.map(prettyPrintPeerIdStr).join(","),
|
|
696
|
+
};
|
|
697
|
+
this.logger.verbose("Processing batch", logCtx);
|
|
590
698
|
|
|
591
699
|
// wrapError ensures to never call both batch success() and batch error()
|
|
592
700
|
const res = await wrapError(this.processChainSegment(blocks, payloadEnvelopes, this.syncType));
|
|
593
701
|
|
|
594
702
|
if (!res.err) {
|
|
595
703
|
batch.processingSuccess();
|
|
704
|
+
this.logger.verbose("Processed batch", {...logCtx, ...batch.getMetadata()});
|
|
596
705
|
|
|
597
706
|
// If the processed batch is not empty, validate previous AwaitingValidation blocks.
|
|
598
707
|
if (blocks.length > 0) {
|
|
@@ -602,7 +711,7 @@ export class SyncChain {
|
|
|
602
711
|
// Potentially process next AwaitingProcessing batch
|
|
603
712
|
this.triggerBatchProcessor();
|
|
604
713
|
} else {
|
|
605
|
-
this.logger.verbose("Batch process error",
|
|
714
|
+
this.logger.verbose("Batch process error", logCtx, res.err);
|
|
606
715
|
batch.processingError(res.err); // Throws after MAX_BATCH_PROCESSING_ATTEMPTS
|
|
607
716
|
|
|
608
717
|
// At least one block was successfully verified and imported, so we can be sure all
|
package/src/sync/range/range.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import {EventEmitter} from "node:events";
|
|
2
2
|
import {StrictEventEmitter} from "strict-event-emitter-types";
|
|
3
3
|
import {BeaconConfig} from "@lodestar/config";
|
|
4
|
-
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
|
+
import {IBeaconStateViewGloas, computeStartSlotAtEpoch, isStatePostGloas} from "@lodestar/state-transition";
|
|
5
5
|
import {Epoch, Status, fulu} from "@lodestar/types";
|
|
6
|
-
import {Logger, toRootHex} from "@lodestar/utils";
|
|
6
|
+
import {Logger, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
7
7
|
import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
|
|
8
8
|
import {AttestationImportOpt, ImportBlockOpts} from "../../chain/blocks/index.js";
|
|
9
|
+
import {assertLinearChainSegment} from "../../chain/blocks/utils/chainSegment.js";
|
|
10
|
+
import {BlockError} from "../../chain/errors/index.js";
|
|
9
11
|
import {IBeaconChain} from "../../chain/index.js";
|
|
10
12
|
import {Metrics} from "../../metrics/index.js";
|
|
11
13
|
import {INetwork} from "../../network/index.js";
|
|
12
14
|
import {PeerIdStr} from "../../util/peerId.js";
|
|
13
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
DownloadByRangeError,
|
|
17
|
+
DownloadByRangeErrorCode,
|
|
18
|
+
cacheByRangeResponses,
|
|
19
|
+
downloadByRange,
|
|
20
|
+
} from "../utils/downloadByRange.js";
|
|
14
21
|
import {RangeSyncType, getRangeSyncTarget, rangeSyncTypes} from "../utils/remoteSyncType.js";
|
|
15
22
|
import {ChainTarget, SyncChain, SyncChainDebugState, SyncChainFns} from "./chain.js";
|
|
16
23
|
import {updateChains} from "./utils/index.js";
|
|
@@ -206,14 +213,18 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
206
213
|
|
|
207
214
|
private downloadByRange: SyncChainFns["downloadByRange"] = async (peer, batch) => {
|
|
208
215
|
const batchBlocks = batch.getBlocks();
|
|
216
|
+
const requests = batch.getRequestsForPeer(peer);
|
|
217
|
+
const parentRoot = requests.parentPayloadRequest?.envelopeBlockRoot ?? requests.parentPayloadRequest?.blockRoot;
|
|
218
|
+
const parentPayloadCommitments = parentRoot ? batch.getParentPayloadCommitments(parentRoot) : undefined;
|
|
209
219
|
const {result, warnings} = await downloadByRange({
|
|
210
220
|
config: this.config,
|
|
211
221
|
network: this.network,
|
|
212
222
|
logger: this.logger,
|
|
213
223
|
peerIdStr: peer.peerId,
|
|
214
224
|
batchBlocks,
|
|
225
|
+
parentPayloadCommitments,
|
|
215
226
|
peerDasMetrics: this.chain.metrics?.peerDas,
|
|
216
|
-
...
|
|
227
|
+
...requests,
|
|
217
228
|
});
|
|
218
229
|
const {responses, payloadEnvelopes: downloadedPayloadEnvelopes} = result;
|
|
219
230
|
const {blocks, payloadEnvelopes} = cacheByRangeResponses({
|
|
@@ -227,6 +238,49 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
227
238
|
custodyConfig: this.chain.custodyConfig,
|
|
228
239
|
seenTimestampSec: Date.now() / 1000,
|
|
229
240
|
});
|
|
241
|
+
|
|
242
|
+
const segmentBlocks = blocks.filter((b) => b.hasBlock()).sort((a, b) => a.slot - b.slot);
|
|
243
|
+
const envelopeSlots = payloadEnvelopes
|
|
244
|
+
? Array.from(payloadEnvelopes.entries())
|
|
245
|
+
.filter(([, pi]) => pi.hasPayloadEnvelope())
|
|
246
|
+
.map(([slot]) => slot)
|
|
247
|
+
.sort((a, b) => a - b)
|
|
248
|
+
: [];
|
|
249
|
+
this.logger.verbose("downloadByRange batch ready", {
|
|
250
|
+
peer: peer.peerId,
|
|
251
|
+
blockSlots: prettyPrintIndices(segmentBlocks.map((b) => b.slot)),
|
|
252
|
+
envelopeSlots: prettyPrintIndices(envelopeSlots),
|
|
253
|
+
...batch.getMetadata(),
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
if (segmentBlocks.length > 1) {
|
|
257
|
+
try {
|
|
258
|
+
assertLinearChainSegment(this.config, segmentBlocks, payloadEnvelopes, null);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
if (err instanceof BlockError) {
|
|
261
|
+
this.logger.debug(
|
|
262
|
+
"downloadByRange segment validation failed",
|
|
263
|
+
{
|
|
264
|
+
peer: peer.peerId,
|
|
265
|
+
reason: err.type.code,
|
|
266
|
+
slot: err.signedBlock.message.slot,
|
|
267
|
+
detail: JSON.stringify(err.type),
|
|
268
|
+
...batch.getMetadata(),
|
|
269
|
+
},
|
|
270
|
+
err
|
|
271
|
+
);
|
|
272
|
+
// with this error, the peer will be penalized inside SyncChain
|
|
273
|
+
throw new DownloadByRangeError({
|
|
274
|
+
code: DownloadByRangeErrorCode.INVALID_CHAIN_SEGMENT,
|
|
275
|
+
slot: err.signedBlock.message.slot,
|
|
276
|
+
reason: err.type.code,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
throw err;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
230
284
|
return {result: {blocks, payloadEnvelopes}, warnings};
|
|
231
285
|
};
|
|
232
286
|
|
|
@@ -258,6 +312,14 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
258
312
|
private addPeerOrCreateChain(startEpoch: Epoch, target: ChainTarget, peer: PeerIdStr, syncType: RangeSyncType): void {
|
|
259
313
|
let syncChain = this.chains.get(syncType);
|
|
260
314
|
if (!syncChain) {
|
|
315
|
+
// The first batch of a new sync chain may need to detect whether the parent block was an
|
|
316
|
+
// gloas "empty" block (no envelope produced). It does so by comparing the first
|
|
317
|
+
// downloaded block's `bid.parentBlockHash` against the head state's `latestExecutionPayloadBid.blockHash`.
|
|
318
|
+
const headState = this.chain.getHeadState();
|
|
319
|
+
const latestBid = isStatePostGloas(headState)
|
|
320
|
+
? (headState as IBeaconStateViewGloas).latestExecutionPayloadBid
|
|
321
|
+
: undefined;
|
|
322
|
+
|
|
261
323
|
syncChain = new SyncChain(
|
|
262
324
|
startEpoch,
|
|
263
325
|
target,
|
|
@@ -276,7 +338,8 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
276
338
|
logger: this.logger,
|
|
277
339
|
custodyConfig: this.chain.custodyConfig,
|
|
278
340
|
metrics: this.metrics,
|
|
279
|
-
}
|
|
341
|
+
},
|
|
342
|
+
latestBid
|
|
280
343
|
);
|
|
281
344
|
this.chains.set(syncType, syncChain);
|
|
282
345
|
|
|
@@ -52,7 +52,8 @@ export class ChainPeersBalancer {
|
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* Return the most suitable peer to retry
|
|
55
|
-
* Sort peers by (1)
|
|
55
|
+
* Sort peers by (1) less active requests (2) most columns we need.
|
|
56
|
+
* Peers that failed this batch or already succeeded for the same request are excluded inside `filterPeers`.
|
|
56
57
|
*/
|
|
57
58
|
bestPeerToRetryBatch(batch: Batch): PeerSyncMeta | undefined {
|
|
58
59
|
if (batch.state.status !== BatchStatus.AwaitingDownload) {
|
|
@@ -63,10 +64,8 @@ export class ChainPeersBalancer {
|
|
|
63
64
|
const pendingDataColumns = columnsRequest?.columns ?? this.custodyConfig.sampledColumns;
|
|
64
65
|
const eligiblePeers = this.filterPeers(batch, pendingDataColumns, false);
|
|
65
66
|
|
|
66
|
-
const failedPeers = new Set(batch.getFailedPeers());
|
|
67
67
|
const sortedBestPeers = sortBy(
|
|
68
68
|
eligiblePeers,
|
|
69
|
-
({syncInfo}) => (failedPeers.has(syncInfo.peerId) ? 1 : 0), // prefer peers without failed requests
|
|
70
69
|
({syncInfo}) => this.activeRequestsByPeer.get(syncInfo.peerId) ?? 0, // prefer peers with least active req
|
|
71
70
|
({columns}) => -1 * columns // prefer peers with the most columns
|
|
72
71
|
);
|
|
@@ -117,9 +116,16 @@ export class ChainPeersBalancer {
|
|
|
117
116
|
return eligiblePeers;
|
|
118
117
|
}
|
|
119
118
|
|
|
119
|
+
// Skip peers that failed this batch, or that already returned the exact current request shape.
|
|
120
|
+
const failedPeers = new Set<PeerIdStr>(batch.getFailedPeers());
|
|
121
|
+
|
|
120
122
|
for (const peer of this.peers) {
|
|
121
123
|
const {earliestAvailableSlot, target, peerId} = peer;
|
|
122
124
|
|
|
125
|
+
if (failedPeers.has(peerId) || batch.hasPeerSucceededCurrentRequest(peer)) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
123
129
|
const activeRequest = this.activeRequestsByPeer.get(peerId) ?? 0;
|
|
124
130
|
if (noActiveRequest && activeRequest > 0) {
|
|
125
131
|
// consumer wants to find peer with no active request, but this peer has active request
|
package/src/sync/sync.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
2
|
import {Slot} from "@lodestar/types";
|
|
3
|
-
import {Logger} from "@lodestar/utils";
|
|
3
|
+
import {Logger, toRootHex} from "@lodestar/utils";
|
|
4
4
|
import {IBeaconChain} from "../chain/index.js";
|
|
5
5
|
import {GENESIS_SLOT} from "../constants/constants.js";
|
|
6
6
|
import {ExecutionEngineState} from "../execution/index.js";
|
|
@@ -188,6 +188,18 @@ export class BeaconSync implements IBeaconSync {
|
|
|
188
188
|
private addPeer = (data: NetworkEventData[NetworkEvent.peerConnected]): void => {
|
|
189
189
|
const localStatus = this.chain.getStatus();
|
|
190
190
|
const syncType = getPeerSyncType(localStatus, data.status, this.chain.forkChoice, this.slotImportTolerance);
|
|
191
|
+
this.logger.verbose("Peer sync type classified", {
|
|
192
|
+
peer: data.peer,
|
|
193
|
+
syncType,
|
|
194
|
+
localFinalizedEpoch: localStatus.finalizedEpoch,
|
|
195
|
+
localFinalizedRoot: toRootHex(localStatus.finalizedRoot),
|
|
196
|
+
localHeadSlot: localStatus.headSlot,
|
|
197
|
+
localHeadRoot: toRootHex(localStatus.headRoot),
|
|
198
|
+
remoteFinalizedEpoch: data.status.finalizedEpoch,
|
|
199
|
+
remoteFinalizedRoot: toRootHex(data.status.finalizedRoot),
|
|
200
|
+
remoteHeadSlot: data.status.headSlot,
|
|
201
|
+
remoteHeadRoot: toRootHex(data.status.headRoot),
|
|
202
|
+
});
|
|
191
203
|
|
|
192
204
|
// For metrics only
|
|
193
205
|
this.peerSyncType.set(data.peer, syncType);
|