@lodestar/beacon-node 1.49.0-dev.b27f498c2c → 1.49.0-dev.c256679454
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/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +4 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +3 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +1 -2
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +4 -7
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.js +5 -6
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +2 -2
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +6 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +13 -0
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/emitter.d.ts +10 -0
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +4 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/payloadAttestation.d.ts +4 -0
- package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
- package/lib/chain/errors/payloadAttestation.js +1 -0
- package/lib/chain/errors/payloadAttestation.js.map +1 -1
- package/lib/chain/interface.d.ts +1 -1
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/options.d.ts +0 -4
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +0 -1
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +4 -2
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +8 -1
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/db/beacon.d.ts +1 -2
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +1 -3
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/buckets.d.ts +0 -1
- package/lib/db/buckets.d.ts.map +1 -1
- package/lib/db/buckets.js +1 -1
- package/lib/db/buckets.js.map +1 -1
- package/lib/db/interface.d.ts +1 -2
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/blockArchive.d.ts +5 -0
- package/lib/db/repositories/blockArchive.d.ts.map +1 -1
- package/lib/db/repositories/blockArchive.js +36 -1
- package/lib/db/repositories/blockArchive.js.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/index.d.ts +0 -1
- package/lib/db/repositories/index.d.ts.map +1 -1
- package/lib/db/repositories/index.js +0 -1
- package/lib/db/repositories/index.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +21 -11
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +48 -23
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/metrics/metrics.d.ts +3 -1
- package/lib/metrics/metrics.d.ts.map +1 -1
- package/lib/metrics/metrics.js +2 -2
- package/lib/metrics/metrics.js.map +1 -1
- package/lib/network/processor/index.d.ts +6 -2
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +62 -24
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/index.js +20 -4
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/node/nodejs.d.ts +2 -7
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +2 -22
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/interface.d.ts +1 -2
- package/lib/sync/interface.d.ts.map +1 -1
- package/lib/sync/options.d.ts +0 -6
- package/lib/sync/options.d.ts.map +1 -1
- package/lib/sync/options.js +0 -2
- package/lib/sync/options.js.map +1 -1
- package/lib/sync/types.d.ts +21 -2
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +22 -2
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +20 -0
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +199 -15
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/shuffle.d.ts +0 -4
- package/lib/util/shuffle.d.ts.map +1 -1
- package/lib/util/shuffle.js +0 -8
- package/lib/util/shuffle.js.map +1 -1
- package/package.json +16 -16
- package/src/api/impl/beacon/pool/index.ts +6 -2
- package/src/api/impl/validator/index.ts +3 -1
- package/src/chain/archiveStore/archiveStore.ts +4 -8
- package/src/chain/archiveStore/historicalState/metrics.ts +5 -7
- package/src/chain/archiveStore/utils/pruneHistory.ts +9 -4
- package/src/chain/blocks/importExecutionPayload.ts +14 -0
- package/src/chain/emitter.ts +7 -2
- package/src/chain/errors/payloadAttestation.ts +2 -0
- package/src/chain/interface.ts +1 -1
- package/src/chain/options.ts +0 -5
- package/src/chain/produceBlock/computeNewStateRoot.ts +9 -2
- package/src/chain/validation/payloadAttestationMessage.ts +9 -0
- package/src/db/beacon.ts +0 -5
- package/src/db/buckets.ts +1 -1
- package/src/db/interface.ts +0 -3
- package/src/db/repositories/blockArchive.ts +38 -1
- package/src/db/repositories/index.ts +0 -1
- package/src/metrics/metrics/lodestar.ts +58 -26
- package/src/metrics/metrics.ts +7 -2
- package/src/network/processor/index.ts +81 -24
- package/src/network/reqresp/handlers/index.ts +49 -5
- package/src/node/nodejs.ts +0 -27
- package/src/sync/interface.ts +1 -2
- package/src/sync/options.ts +0 -8
- package/src/sync/types.ts +24 -5
- package/src/sync/unknownBlock.ts +207 -14
- package/src/util/shuffle.ts +0 -9
- package/lib/chain/archiveStore/utils/updateBackfillRange.d.ts +0 -20
- package/lib/chain/archiveStore/utils/updateBackfillRange.d.ts.map +0 -1
- package/lib/chain/archiveStore/utils/updateBackfillRange.js +0 -40
- package/lib/chain/archiveStore/utils/updateBackfillRange.js.map +0 -1
- package/lib/db/repositories/backfilledRanges.d.ts +0 -18
- package/lib/db/repositories/backfilledRanges.d.ts.map +0 -1
- package/lib/db/repositories/backfilledRanges.js +0 -26
- package/lib/db/repositories/backfilledRanges.js.map +0 -1
- package/lib/sync/backfill/backfill.d.ts +0 -169
- package/lib/sync/backfill/backfill.d.ts.map +0 -1
- package/lib/sync/backfill/backfill.js +0 -686
- package/lib/sync/backfill/backfill.js.map +0 -1
- package/lib/sync/backfill/errors.d.ts +0 -30
- package/lib/sync/backfill/errors.d.ts.map +0 -1
- package/lib/sync/backfill/errors.js +0 -15
- package/lib/sync/backfill/errors.js.map +0 -1
- package/lib/sync/backfill/index.d.ts +0 -2
- package/lib/sync/backfill/index.d.ts.map +0 -1
- package/lib/sync/backfill/index.js +0 -2
- package/lib/sync/backfill/index.js.map +0 -1
- package/lib/sync/backfill/verify.d.ts +0 -18
- package/lib/sync/backfill/verify.d.ts.map +0 -1
- package/lib/sync/backfill/verify.js +0 -36
- package/lib/sync/backfill/verify.js.map +0 -1
- package/src/chain/archiveStore/utils/updateBackfillRange.ts +0 -50
- package/src/db/repositories/backfilledRanges.ts +0 -29
- package/src/sync/backfill/backfill.ts +0 -916
- package/src/sync/backfill/errors.ts +0 -23
- package/src/sync/backfill/index.ts +0 -1
- package/src/sync/backfill/verify.ts +0 -58
|
@@ -6,6 +6,10 @@ import { INetwork } from "../network/index.js";
|
|
|
6
6
|
import { PeerSyncMeta } from "../network/peers/peersData.js";
|
|
7
7
|
import { PeerIdStr } from "../util/peerId.js";
|
|
8
8
|
import { SyncOptions } from "./options.js";
|
|
9
|
+
/**
|
|
10
|
+
* Poll pending payloads of the current slot this often, between PAYLOAD_DUE and the end of the slot.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PAYLOAD_POLL_INTERVAL_MS = 1000;
|
|
9
13
|
/**
|
|
10
14
|
* BlockInputSync is a class that handles ReqResp to find blocks and data related to a specific blockRoot. The
|
|
11
15
|
* blockRoot may have been found via object gossip, or the API. Gossip objects that can trigger a search are block,
|
|
@@ -55,6 +59,11 @@ export declare class BlockInputSync {
|
|
|
55
59
|
private subscribedToNetworkEvents;
|
|
56
60
|
private peerBalancer;
|
|
57
61
|
private rateLimitBackoffTimeout;
|
|
62
|
+
/**
|
|
63
|
+
* Search unknown payload starting at PAYLOAD_DUE_BPS.
|
|
64
|
+
*/
|
|
65
|
+
private readonly deferredPayloadRoots;
|
|
66
|
+
private payloadPollAbortController;
|
|
58
67
|
constructor(config: ChainForkConfig, network: INetwork, chain: IBeaconChain, logger: Logger, metrics: Metrics | null, opts?: SyncOptions | undefined);
|
|
59
68
|
subscribeToNetwork(): void;
|
|
60
69
|
unsubscribeFromNetwork(): void;
|
|
@@ -69,6 +78,7 @@ export declare class BlockInputSync {
|
|
|
69
78
|
*/
|
|
70
79
|
private onIncompleteBlockInput;
|
|
71
80
|
private onUnknownEnvelopeBlockRoot;
|
|
81
|
+
private onUnknownEnvelopeBlockRootSlot;
|
|
72
82
|
private onIncompletePayloadEnvelope;
|
|
73
83
|
/**
|
|
74
84
|
* Process an unknownBlockParent event and register the block in `pendingBlocks` Map.
|
|
@@ -104,6 +114,16 @@ export declare class BlockInputSync {
|
|
|
104
114
|
private triggerUnknownBlockSearch;
|
|
105
115
|
private scheduleRateLimitBackoffRetry;
|
|
106
116
|
private clearRateLimitBackoffTimer;
|
|
117
|
+
/**
|
|
118
|
+
* Start this slot's payload poll: end the previous slot's loop, move roots it never got to
|
|
119
|
+
* into pendingPayloads, then poll from PAYLOAD_DUE to the end of the slot.
|
|
120
|
+
*/
|
|
121
|
+
private startPayloadPollAtSlot;
|
|
122
|
+
/**
|
|
123
|
+
* Poll unknown payloads of the current slot.
|
|
124
|
+
*/
|
|
125
|
+
private pollPayloadsAtSlot;
|
|
126
|
+
private addDeferredPayloadRoot;
|
|
107
127
|
private downloadBlock;
|
|
108
128
|
/**
|
|
109
129
|
* Import a block that has already passed the local dependency checks in BlockInputSync.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unknownBlock.d.ts","sourceRoot":"","sources":["../../src/sync/unknownBlock.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAMjD,OAAO,EAAC,MAAM,EAA+
|
|
1
|
+
{"version":3,"file":"unknownBlock.d.ts","sourceRoot":"","sources":["../../src/sync/unknownBlock.ts"],"names":[],"mappings":"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"}
|
package/lib/sync/unknownBlock.js
CHANGED
|
@@ -2,7 +2,7 @@ import { routes } from "@lodestar/api";
|
|
|
2
2
|
import { ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas } from "@lodestar/params";
|
|
3
3
|
import { RequestError, RequestErrorCode } from "@lodestar/reqresp";
|
|
4
4
|
import { computeTimeAtSlot } from "@lodestar/state-transition";
|
|
5
|
-
import { fromHex, prettyPrintIndices, pruneSetToMax, sleep, toRootHex } from "@lodestar/utils";
|
|
5
|
+
import { fromHex, isErrorAborted, prettyPrintIndices, pruneSetToMax, sleep, toRootHex } from "@lodestar/utils";
|
|
6
6
|
import { isBlockInputBlobs, isBlockInputColumns } from "../chain/blocks/blockInput/blockInput.js";
|
|
7
7
|
import { BlockInputSource } from "../chain/blocks/blockInput/types.js";
|
|
8
8
|
import { PayloadError, PayloadErrorCode } from "../chain/blocks/importExecutionPayload.js";
|
|
@@ -13,11 +13,12 @@ import { validateGloasBlockDataColumnSidecars } from "../chain/validation/dataCo
|
|
|
13
13
|
import { validateGossipExecutionPayloadEnvelope } from "../chain/validation/executionPayloadEnvelope.js";
|
|
14
14
|
import { NetworkEvent, prettyPrintPeerIdStr } from "../network/index.js";
|
|
15
15
|
import { MAX_PEERS_PER_ROOT } from "../network/processor/constants.js";
|
|
16
|
+
import { ClockEvent } from "../util/clock.js";
|
|
16
17
|
import { shuffle } from "../util/shuffle.js";
|
|
17
18
|
import { sortBy } from "../util/sortBy.js";
|
|
18
19
|
import { wrapError } from "../util/wrapError.js";
|
|
19
20
|
import { MAX_CONCURRENT_REQUESTS } from "./constants.js";
|
|
20
|
-
import { DownloadResult, DroppedItemReason, FetchResult, PendingBlockInputStatus, PendingBlockType, PendingPayloadInputStatus, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, getPayloadSyncCacheItemRootHex, getPayloadSyncCacheItemSlot, isPendingBlockInput, isPendingPayloadEnvelope, isPendingPayloadInput, } from "./types.js";
|
|
21
|
+
import { DeferredPayloadResult, DownloadResult, DroppedItemReason, FetchResult, PendingBlockInputStatus, PendingBlockType, PendingPayloadInputStatus, getBlockInputSyncCacheItemRootHex, getBlockInputSyncCacheItemSlot, getPayloadSyncCacheItemRootHex, getPayloadSyncCacheItemSlot, isPendingBlockInput, isPendingPayloadEnvelope, isPendingPayloadInput, } from "./types.js";
|
|
21
22
|
import { DownloadByRootError, downloadByRoot } from "./utils/downloadByRoot.js";
|
|
22
23
|
import { getAllDescendantBlocks, getUnknownAndAncestorBlocks } from "./utils/pendingBlocksTree.js";
|
|
23
24
|
import { getRateLimitedUntilMs } from "./utils/rateLimit.js";
|
|
@@ -28,6 +29,10 @@ const MAX_PENDING_BLOCKS = 100;
|
|
|
28
29
|
* A pending payload without slot can only stay at most 2 epochs.
|
|
29
30
|
*/
|
|
30
31
|
const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
|
|
32
|
+
/**
|
|
33
|
+
* Poll pending payloads of the current slot this often, between PAYLOAD_DUE and the end of the slot.
|
|
34
|
+
*/
|
|
35
|
+
export const PAYLOAD_POLL_INTERVAL_MS = 1_000;
|
|
31
36
|
class UnknownBlockRateLimitedError extends Error {
|
|
32
37
|
constructor(message) {
|
|
33
38
|
super(message);
|
|
@@ -84,6 +89,11 @@ export class BlockInputSync {
|
|
|
84
89
|
subscribedToNetworkEvents = false;
|
|
85
90
|
peerBalancer;
|
|
86
91
|
rateLimitBackoffTimeout;
|
|
92
|
+
/**
|
|
93
|
+
* Search unknown payload starting at PAYLOAD_DUE_BPS.
|
|
94
|
+
*/
|
|
95
|
+
deferredPayloadRoots = new Map();
|
|
96
|
+
payloadPollAbortController;
|
|
87
97
|
constructor(config, network, chain, logger, metrics, opts) {
|
|
88
98
|
this.config = config;
|
|
89
99
|
this.network = network;
|
|
@@ -109,6 +119,7 @@ export class BlockInputSync {
|
|
|
109
119
|
this.logger.verbose("BlockInputSync enabled.");
|
|
110
120
|
this.chain.emitter.on(ChainEvent.unknownBlockRoot, this.onUnknownBlockRoot);
|
|
111
121
|
this.chain.emitter.on(ChainEvent.unknownEnvelopeBlockRoot, this.onUnknownEnvelopeBlockRoot);
|
|
122
|
+
this.chain.emitter.on(ChainEvent.unknownEnvelopeBlockRootSlot, this.onUnknownEnvelopeBlockRootSlot);
|
|
112
123
|
this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
113
124
|
this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
114
125
|
this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
@@ -117,6 +128,7 @@ export class BlockInputSync {
|
|
|
117
128
|
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
118
129
|
this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
119
130
|
this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
131
|
+
this.chain.clock.on(ClockEvent.slot, this.startPayloadPollAtSlot);
|
|
120
132
|
// Seed the balancer with peers that connected before we subscribed
|
|
121
133
|
for (const peerId of this.network.getConnectedPeers()) {
|
|
122
134
|
try {
|
|
@@ -127,13 +139,21 @@ export class BlockInputSync {
|
|
|
127
139
|
}
|
|
128
140
|
}
|
|
129
141
|
this.subscribedToNetworkEvents = true;
|
|
142
|
+
// we subscribe mid-slot, and a root can be deferred at any point in the slot, so start this
|
|
143
|
+
// slot's loop now instead of waiting for the next ClockEvent.slot
|
|
144
|
+
this.startPayloadPollAtSlot(this.chain.clock.currentSlot);
|
|
130
145
|
}
|
|
131
146
|
}
|
|
132
147
|
unsubscribeFromNetwork() {
|
|
133
148
|
this.logger.verbose("BlockInputSync disabled.");
|
|
134
149
|
this.clearRateLimitBackoffTimer();
|
|
150
|
+
this.chain.clock.off(ClockEvent.slot, this.startPayloadPollAtSlot);
|
|
151
|
+
this.payloadPollAbortController?.abort();
|
|
152
|
+
this.payloadPollAbortController = undefined;
|
|
153
|
+
this.deferredPayloadRoots.clear();
|
|
135
154
|
this.chain.emitter.off(ChainEvent.unknownBlockRoot, this.onUnknownBlockRoot);
|
|
136
155
|
this.chain.emitter.off(ChainEvent.unknownEnvelopeBlockRoot, this.onUnknownEnvelopeBlockRoot);
|
|
156
|
+
this.chain.emitter.off(ChainEvent.unknownEnvelopeBlockRootSlot, this.onUnknownEnvelopeBlockRootSlot);
|
|
137
157
|
this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
138
158
|
this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
139
159
|
this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
@@ -157,8 +177,11 @@ export class BlockInputSync {
|
|
|
157
177
|
onUnknownBlockRoot = (data) => {
|
|
158
178
|
try {
|
|
159
179
|
const isNewRoot = this.addByRootHex(data.rootHex, data.peer);
|
|
160
|
-
|
|
180
|
+
// A retained entry (an earlier download failed, #10018) is not new but still needs a retry pass.
|
|
181
|
+
if (this.pendingBlocks.get(data.rootHex)?.status === PendingBlockInputStatus.pending) {
|
|
161
182
|
this.triggerUnknownBlockSearch();
|
|
183
|
+
}
|
|
184
|
+
if (isNewRoot) {
|
|
162
185
|
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_BLOCK_ROOT });
|
|
163
186
|
this.metrics?.blockInputSync.source.inc({ source: data.source });
|
|
164
187
|
}
|
|
@@ -194,6 +217,33 @@ export class BlockInputSync {
|
|
|
194
217
|
this.logger.debug("Error handling unknownEnvelopeBlockRoot event", {}, e);
|
|
195
218
|
}
|
|
196
219
|
};
|
|
220
|
+
onUnknownEnvelopeBlockRootSlot = (data) => {
|
|
221
|
+
try {
|
|
222
|
+
if (data.slot === this.chain.clock.currentSlot) {
|
|
223
|
+
// we receive the optimistic search, the poll loop searches it at PAYLOAD_DUE or on its next
|
|
224
|
+
// tick. If that loop already returned - every root it watched was imported - this root waits
|
|
225
|
+
// for the next slot, where startPayloadPollAtSlot moves it to pendingPayloads. That is not the normal
|
|
226
|
+
// scenario, and in a forking condition it only delays the search of the extra root
|
|
227
|
+
const deferred = this.deferredPayloadRoots.get(data.rootHex);
|
|
228
|
+
if (deferred === undefined) {
|
|
229
|
+
this.deferredPayloadRoots.set(data.rootHex, { slot: data.slot, source: data.source, peer: data.peer });
|
|
230
|
+
}
|
|
231
|
+
else if (deferred.peer === undefined) {
|
|
232
|
+
deferred.peer = data.peer;
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const isNewRoot = this.addByPayloadRootHex(data.rootHex, data.peer, data.slot);
|
|
237
|
+
if (isNewRoot) {
|
|
238
|
+
this.triggerUnknownBlockSearch();
|
|
239
|
+
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT_SLOT });
|
|
240
|
+
this.metrics?.blockInputSync.payloadSource.inc({ source: data.source });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
this.logger.debug("Error handling unknownEnvelopeBlockRootSlot event", {}, e);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
197
247
|
onIncompletePayloadEnvelope = (data) => {
|
|
198
248
|
try {
|
|
199
249
|
this.addByPayloadInput(data.payloadInput, data.peer);
|
|
@@ -288,12 +338,21 @@ export class BlockInputSync {
|
|
|
288
338
|
belowFinalizedPayloads++;
|
|
289
339
|
}
|
|
290
340
|
}
|
|
291
|
-
let
|
|
341
|
+
let belowFinalizedBlocks = 0;
|
|
342
|
+
let agedOutBlocks = 0;
|
|
292
343
|
for (const [rootHex, block] of this.pendingBlocks) {
|
|
293
344
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
294
|
-
if (typeof slot
|
|
345
|
+
if (typeof slot !== "number") {
|
|
346
|
+
// we may not be able to download a root, hence there is no slot for it
|
|
347
|
+
if (nowSec - block.timeAddedSec > maxSecInCache) {
|
|
348
|
+
this.pendingBlocks.delete(rootHex);
|
|
349
|
+
agedOutBlocks++;
|
|
350
|
+
}
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (slot < finalizedSlot) {
|
|
295
354
|
this.pendingBlocks.delete(rootHex);
|
|
296
|
-
|
|
355
|
+
belowFinalizedBlocks++;
|
|
297
356
|
}
|
|
298
357
|
}
|
|
299
358
|
if (belowFinalizedPayloads > 0) {
|
|
@@ -302,15 +361,19 @@ export class BlockInputSync {
|
|
|
302
361
|
if (agedOutPayloads > 0) {
|
|
303
362
|
this.metrics?.blockInputSync.removedPayloads.inc({ reason: DroppedItemReason.agedOut }, agedOutPayloads);
|
|
304
363
|
}
|
|
305
|
-
if (
|
|
306
|
-
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.belowFinalized },
|
|
364
|
+
if (belowFinalizedBlocks > 0) {
|
|
365
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.belowFinalized }, belowFinalizedBlocks);
|
|
366
|
+
}
|
|
367
|
+
if (agedOutBlocks > 0) {
|
|
368
|
+
this.metrics?.blockInputSync.removedBlocks.inc({ reason: DroppedItemReason.agedOut }, agedOutBlocks);
|
|
307
369
|
}
|
|
308
|
-
this.logger.debug("BlockInputSync.pruneFinalized dropped
|
|
370
|
+
this.logger.debug("BlockInputSync.pruneFinalized dropped stale pending items", {
|
|
309
371
|
finalizedSlot,
|
|
310
372
|
finalizedRoot: checkpoint.rootHex,
|
|
311
373
|
belowFinalizedPayloads,
|
|
312
374
|
agedOutPayloads,
|
|
313
|
-
|
|
375
|
+
belowFinalizedBlocks,
|
|
376
|
+
agedOutBlocks,
|
|
314
377
|
unresolvedSlotPayloads,
|
|
315
378
|
});
|
|
316
379
|
};
|
|
@@ -652,6 +715,115 @@ export class BlockInputSync {
|
|
|
652
715
|
this.rateLimitBackoffTimeout = undefined;
|
|
653
716
|
}
|
|
654
717
|
}
|
|
718
|
+
/**
|
|
719
|
+
* Start this slot's payload poll: end the previous slot's loop, move roots it never got to
|
|
720
|
+
* into pendingPayloads, then poll from PAYLOAD_DUE to the end of the slot.
|
|
721
|
+
*/
|
|
722
|
+
startPayloadPollAtSlot = (slot) => {
|
|
723
|
+
if (!isForkPostGloas(this.config.getForkName(slot))) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
// end the previous slot's loop, wherever it is: its sleep is bound to this same controller
|
|
727
|
+
this.payloadPollAbortController?.abort();
|
|
728
|
+
this.payloadPollAbortController = new AbortController();
|
|
729
|
+
// entries from an earlier slot is not polled
|
|
730
|
+
for (const [rootHex, { slot: deferredSlot, source, peer }] of this.deferredPayloadRoots) {
|
|
731
|
+
if (deferredSlot < slot) {
|
|
732
|
+
if (!this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
733
|
+
this.addDeferredPayloadRoot(rootHex, deferredSlot, source, peer);
|
|
734
|
+
}
|
|
735
|
+
this.deferredPayloadRoots.delete(rootHex);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
this.pollPayloadsAtSlot(slot, this.payloadPollAbortController.signal).catch((e) => {
|
|
739
|
+
if (!isErrorAborted(e)) {
|
|
740
|
+
this.logger.debug("Error polling payloads", { slot }, e);
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
};
|
|
744
|
+
/**
|
|
745
|
+
* Poll unknown payloads of the current slot.
|
|
746
|
+
*/
|
|
747
|
+
async pollPayloadsAtSlot(slot, signal) {
|
|
748
|
+
// fixed tick count so the loop cannot outlive its slot: for mainnet, it's 6 ticks
|
|
749
|
+
const tickCount = Math.floor((this.config.SLOT_DURATION_MS - this.config.getPayloadDueMs()) / PAYLOAD_POLL_INTERVAL_MS);
|
|
750
|
+
// a root still missing on several ticks is searched on each of them, recorded once
|
|
751
|
+
const searchedRoots = new Set();
|
|
752
|
+
let polls = 0;
|
|
753
|
+
try {
|
|
754
|
+
await sleep(Math.max(0, this.config.getPayloadDueMs() - this.chain.clock.msFromSlot(slot)), signal);
|
|
755
|
+
for (polls = 0; polls < tickCount; polls++) {
|
|
756
|
+
let importedCount = 0;
|
|
757
|
+
for (const [rootHex, { slot: deferredSlot, source, peer }] of this.deferredPayloadRoots) {
|
|
758
|
+
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
759
|
+
importedCount++;
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
// copy to the regular pendingPayloads so that we can search
|
|
763
|
+
this.addDeferredPayloadRoot(rootHex, deferredSlot, source, peer);
|
|
764
|
+
searchedRoots.add(rootHex);
|
|
765
|
+
}
|
|
766
|
+
// an empty deferredPayloadRoots means there is no optimistic search comes before PAYLOAD_DUE
|
|
767
|
+
// we have to poll until the payload of current slot is imported or slot end
|
|
768
|
+
if (this.deferredPayloadRoots.size > 0 && importedCount === this.deferredPayloadRoots.size) {
|
|
769
|
+
if (searchedRoots.size > 0) {
|
|
770
|
+
// the backup search was needed
|
|
771
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({
|
|
772
|
+
result: DeferredPayloadResult.ImportedAfterSearch,
|
|
773
|
+
});
|
|
774
|
+
this.metrics?.blockInputSync.deferredPayloadPolls.observe(polls);
|
|
775
|
+
this.logger.verbose("Payload imported after unknown sync search", {
|
|
776
|
+
slot,
|
|
777
|
+
roots: Array.from(searchedRoots).join(","),
|
|
778
|
+
polls,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
// this happens most of the time on devnet
|
|
783
|
+
// gossip delivered everything before PAYLOAD_DUE
|
|
784
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({
|
|
785
|
+
result: DeferredPayloadResult.ImportedBeforeSearch,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
this.triggerUnknownBlockSearch();
|
|
791
|
+
if (polls < tickCount - 1) {
|
|
792
|
+
// no sleep after the last tick: the loop would otherwise end right at the slot boundary,
|
|
793
|
+
// where startPayloadPollAtSlot aborts it
|
|
794
|
+
await sleep(PAYLOAD_POLL_INTERVAL_MS, signal);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
// reaching here means the early return never fired
|
|
798
|
+
if (this.deferredPayloadRoots.size > 0) {
|
|
799
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({ result: DeferredPayloadResult.Unresolved });
|
|
800
|
+
this.logger.debug("Deferred payloads still missing at end of slot", {
|
|
801
|
+
slot,
|
|
802
|
+
roots: Array.from(this.deferredPayloadRoots.keys()).join(","),
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
// no optimistic search and no payload import ended the loop, eg a skipped slot
|
|
807
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({ result: DeferredPayloadResult.ResolvedNoRoot });
|
|
808
|
+
this.logger.debug("No deferred payloads at slot", {
|
|
809
|
+
slot,
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
catch (e) {
|
|
814
|
+
if (!isErrorAborted(e)) {
|
|
815
|
+
throw e;
|
|
816
|
+
}
|
|
817
|
+
this.logger.verbose("Payload poll aborted", { slot, polls, msIntoSlot: this.chain.clock.msFromSlot(slot) });
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
addDeferredPayloadRoot(rootHex, slot, source, peer) {
|
|
821
|
+
const isNewRoot = this.addByPayloadRootHex(rootHex, peer, slot);
|
|
822
|
+
if (isNewRoot) {
|
|
823
|
+
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.DEFERRED_PAYLOAD_BLOCK_ROOT });
|
|
824
|
+
this.metrics?.blockInputSync.payloadSource.inc({ source });
|
|
825
|
+
}
|
|
826
|
+
}
|
|
655
827
|
async downloadBlock(block) {
|
|
656
828
|
if (block.status !== PendingBlockInputStatus.pending) {
|
|
657
829
|
return;
|
|
@@ -670,7 +842,12 @@ export class BlockInputSync {
|
|
|
670
842
|
if (!res.err) {
|
|
671
843
|
this.metrics?.blockInputSync.downloadedBlocksSuccess.inc();
|
|
672
844
|
const pending = res.result;
|
|
673
|
-
this
|
|
845
|
+
// pruning may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
|
|
846
|
+
if (!this.pendingBlocks.has(rootHex)) {
|
|
847
|
+
this.logger.verbose("Dropping downloaded block, entry pruned during fetch", logCtx);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
this.pendingBlocks.set(rootHex, pending);
|
|
674
851
|
const blockSlot = pending.blockInput.slot;
|
|
675
852
|
const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
|
|
676
853
|
const delaySec = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.chain.genesisTime);
|
|
@@ -730,8 +907,11 @@ export class BlockInputSync {
|
|
|
730
907
|
return;
|
|
731
908
|
}
|
|
732
909
|
this.metrics?.blockInputSync.downloadedBlocksError.inc();
|
|
733
|
-
this.logger.debug("
|
|
734
|
-
this.
|
|
910
|
+
this.logger.debug("Failed to download unknown block root, retained for retry", logCtx, res.err);
|
|
911
|
+
const pendingBlock = this.pendingBlocks.get(rootHex);
|
|
912
|
+
if (pendingBlock && pendingBlock.status === PendingBlockInputStatus.fetching) {
|
|
913
|
+
pendingBlock.status = PendingBlockInputStatus.pending;
|
|
914
|
+
}
|
|
735
915
|
}
|
|
736
916
|
}
|
|
737
917
|
/**
|
|
@@ -954,7 +1134,7 @@ export class BlockInputSync {
|
|
|
954
1134
|
return;
|
|
955
1135
|
}
|
|
956
1136
|
this.metrics?.blockInputSync.downloadedPayloadsResult.inc({ result: DownloadResult.Failed });
|
|
957
|
-
this.logger.debug("
|
|
1137
|
+
this.logger.debug("Failed to download unknown payload root, retained for retry", logCtx, res.err);
|
|
958
1138
|
if (!isPendingPayloadEnvelope(payload)) {
|
|
959
1139
|
payload.status = PendingPayloadInputStatus.pending;
|
|
960
1140
|
}
|
|
@@ -1322,7 +1502,11 @@ export class BlockInputSync {
|
|
|
1322
1502
|
finally {
|
|
1323
1503
|
this.peerBalancer.onRequestCompleted(peerId);
|
|
1324
1504
|
}
|
|
1325
|
-
|
|
1505
|
+
const cacheItemRootHex = getBlockInputSyncCacheItemRootHex(cacheItem);
|
|
1506
|
+
// pruning may have dropped it mid-fetch
|
|
1507
|
+
if (this.pendingBlocks.has(cacheItemRootHex)) {
|
|
1508
|
+
this.pendingBlocks.set(cacheItemRootHex, cacheItem);
|
|
1509
|
+
}
|
|
1326
1510
|
if (cacheItem.status === PendingBlockInputStatus.downloaded) {
|
|
1327
1511
|
// download was successful, no need to go with another peer, return
|
|
1328
1512
|
const result = this.chain.forkChoice.hasBlockHex(cacheItem.blockInput.blockRootHex)
|