@lodestar/beacon-node 1.49.0-dev.4d35c8b82e → 1.49.0-dev.57ffc39074
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.js +2 -2
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +9 -7
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.js +4 -0
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +14 -3
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/blocks/index.js +2 -2
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.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/attesterSlashingError.d.ts +4 -1
- package/lib/chain/errors/attesterSlashingError.d.ts.map +1 -1
- package/lib/chain/errors/attesterSlashingError.js +1 -0
- package/lib/chain/errors/attesterSlashingError.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/errors/proposerSlashingError.d.ts +4 -1
- package/lib/chain/errors/proposerSlashingError.d.ts.map +1 -1
- package/lib/chain/errors/proposerSlashingError.js +1 -0
- package/lib/chain/errors/proposerSlashingError.js.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/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +6 -6
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +2 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +3 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +1 -2
- package/lib/chain/validation/attesterSlashing.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/chain/validation/proposerSlashing.js +1 -2
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +2 -2
- package/lib/chain/validatorMonitor.js.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/metrics/metrics/lodestar.d.ts +19 -4
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +38 -1
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts +1 -2
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +5 -10
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +16 -1
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +9 -6
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +23 -8
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +16 -5
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +2 -0
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +88 -7
- package/lib/network/processor/gossipValidatorFn.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/protocols.d.ts.map +1 -1
- package/lib/network/reqresp/protocols.js +19 -5
- package/lib/network/reqresp/protocols.js.map +1 -1
- package/lib/sync/types.d.ts +39 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +42 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +16 -0
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +186 -13
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/sszBytes.d.ts +5 -0
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +13 -1
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +14 -14
- package/src/api/impl/beacon/blocks/index.ts +2 -2
- package/src/api/impl/validator/index.ts +9 -7
- package/src/chain/archiveStore/historicalState/metrics.ts +4 -0
- package/src/chain/archiveStore/utils/pruneHistory.ts +14 -4
- package/src/chain/blocks/index.ts +2 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -1
- package/src/chain/emitter.ts +7 -2
- package/src/chain/errors/attesterSlashingError.ts +3 -1
- package/src/chain/errors/payloadAttestation.ts +2 -0
- package/src/chain/errors/proposerSlashingError.ts +3 -1
- package/src/chain/options.ts +0 -5
- package/src/chain/produceBlock/produceBlockBody.ts +17 -6
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +3 -2
- package/src/chain/validation/attesterSlashing.ts +1 -2
- package/src/chain/validation/payloadAttestationMessage.ts +9 -0
- package/src/chain/validation/proposerSlashing.ts +1 -2
- package/src/chain/validatorMonitor.ts +12 -2
- package/src/db/repositories/blockArchive.ts +38 -1
- package/src/metrics/metrics/lodestar.ts +46 -3
- package/src/network/gossip/encoding.ts +5 -8
- package/src/network/gossip/gossipsub.ts +19 -2
- package/src/network/gossip/topic.ts +25 -7
- package/src/network/processor/gossipHandlers.ts +18 -5
- package/src/network/processor/gossipValidatorFn.ts +111 -7
- package/src/network/processor/index.ts +81 -24
- package/src/network/reqresp/protocols.ts +31 -6
- package/src/sync/types.ts +44 -3
- package/src/sync/unknownBlock.ts +209 -13
- package/src/util/sszBytes.ts +18 -0
|
@@ -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,12 @@ export declare class BlockInputSync {
|
|
|
104
114
|
private triggerUnknownBlockSearch;
|
|
105
115
|
private scheduleRateLimitBackoffRetry;
|
|
106
116
|
private clearRateLimitBackoffTimer;
|
|
117
|
+
private onClockSlot;
|
|
118
|
+
/**
|
|
119
|
+
* Poll unknown payloads of the current slot.
|
|
120
|
+
*/
|
|
121
|
+
private pollPayloadsAtSlot;
|
|
122
|
+
private addDeferredPayloadRoot;
|
|
107
123
|
private downloadBlock;
|
|
108
124
|
/**
|
|
109
125
|
* 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 { DroppedItemReason, 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,14 +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;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
FetchResult["SuccessLate"] = "success_late";
|
|
36
|
-
FetchResult["FailureTriedAllPeers"] = "failure_tried_all_peers";
|
|
37
|
-
FetchResult["FailureMaxAttempts"] = "failure_max_attempts";
|
|
38
|
-
})(FetchResult || (FetchResult = {}));
|
|
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;
|
|
39
36
|
class UnknownBlockRateLimitedError extends Error {
|
|
40
37
|
constructor(message) {
|
|
41
38
|
super(message);
|
|
@@ -92,6 +89,11 @@ export class BlockInputSync {
|
|
|
92
89
|
subscribedToNetworkEvents = false;
|
|
93
90
|
peerBalancer;
|
|
94
91
|
rateLimitBackoffTimeout;
|
|
92
|
+
/**
|
|
93
|
+
* Search unknown payload starting at PAYLOAD_DUE_BPS.
|
|
94
|
+
*/
|
|
95
|
+
deferredPayloadRoots = new Map();
|
|
96
|
+
payloadPollAbortController;
|
|
95
97
|
constructor(config, network, chain, logger, metrics, opts) {
|
|
96
98
|
this.config = config;
|
|
97
99
|
this.network = network;
|
|
@@ -117,6 +119,7 @@ export class BlockInputSync {
|
|
|
117
119
|
this.logger.verbose("BlockInputSync enabled.");
|
|
118
120
|
this.chain.emitter.on(ChainEvent.unknownBlockRoot, this.onUnknownBlockRoot);
|
|
119
121
|
this.chain.emitter.on(ChainEvent.unknownEnvelopeBlockRoot, this.onUnknownEnvelopeBlockRoot);
|
|
122
|
+
this.chain.emitter.on(ChainEvent.unknownEnvelopeBlockRootSlot, this.onUnknownEnvelopeBlockRootSlot);
|
|
120
123
|
this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
121
124
|
this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
122
125
|
this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
@@ -125,6 +128,7 @@ export class BlockInputSync {
|
|
|
125
128
|
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
126
129
|
this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
127
130
|
this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
131
|
+
this.chain.clock.on(ClockEvent.slot, this.onClockSlot);
|
|
128
132
|
// Seed the balancer with peers that connected before we subscribed
|
|
129
133
|
for (const peerId of this.network.getConnectedPeers()) {
|
|
130
134
|
try {
|
|
@@ -135,13 +139,21 @@ export class BlockInputSync {
|
|
|
135
139
|
}
|
|
136
140
|
}
|
|
137
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.onClockSlot(this.chain.clock.currentSlot);
|
|
138
145
|
}
|
|
139
146
|
}
|
|
140
147
|
unsubscribeFromNetwork() {
|
|
141
148
|
this.logger.verbose("BlockInputSync disabled.");
|
|
142
149
|
this.clearRateLimitBackoffTimer();
|
|
150
|
+
this.chain.clock.off(ClockEvent.slot, this.onClockSlot);
|
|
151
|
+
this.payloadPollAbortController?.abort();
|
|
152
|
+
this.payloadPollAbortController = undefined;
|
|
153
|
+
this.deferredPayloadRoots.clear();
|
|
143
154
|
this.chain.emitter.off(ChainEvent.unknownBlockRoot, this.onUnknownBlockRoot);
|
|
144
155
|
this.chain.emitter.off(ChainEvent.unknownEnvelopeBlockRoot, this.onUnknownEnvelopeBlockRoot);
|
|
156
|
+
this.chain.emitter.off(ChainEvent.unknownEnvelopeBlockRootSlot, this.onUnknownEnvelopeBlockRootSlot);
|
|
145
157
|
this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
146
158
|
this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
147
159
|
this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
@@ -202,6 +214,33 @@ export class BlockInputSync {
|
|
|
202
214
|
this.logger.debug("Error handling unknownEnvelopeBlockRoot event", {}, e);
|
|
203
215
|
}
|
|
204
216
|
};
|
|
217
|
+
onUnknownEnvelopeBlockRootSlot = (data) => {
|
|
218
|
+
try {
|
|
219
|
+
if (data.slot === this.chain.clock.currentSlot) {
|
|
220
|
+
// we receive the optimistic search, the poll loop searches it at PAYLOAD_DUE or on its next
|
|
221
|
+
// tick. If that loop already returned - every root it watched was imported - this root waits
|
|
222
|
+
// for the next slot, where onClockSlot moves it to pendingPayloads. That is not the normal
|
|
223
|
+
// scenario, and in a forking condition it only delays the search of the extra root
|
|
224
|
+
const deferred = this.deferredPayloadRoots.get(data.rootHex);
|
|
225
|
+
if (deferred === undefined) {
|
|
226
|
+
this.deferredPayloadRoots.set(data.rootHex, { slot: data.slot, source: data.source, peer: data.peer });
|
|
227
|
+
}
|
|
228
|
+
else if (deferred.peer === undefined) {
|
|
229
|
+
deferred.peer = data.peer;
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const isNewRoot = this.addByPayloadRootHex(data.rootHex, data.peer, data.slot);
|
|
234
|
+
if (isNewRoot) {
|
|
235
|
+
this.triggerUnknownBlockSearch();
|
|
236
|
+
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT_SLOT });
|
|
237
|
+
this.metrics?.blockInputSync.payloadSource.inc({ source: data.source });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
this.logger.debug("Error handling unknownEnvelopeBlockRootSlot event", {}, e);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
205
244
|
onIncompletePayloadEnvelope = (data) => {
|
|
206
245
|
try {
|
|
207
246
|
this.addByPayloadInput(data.payloadInput, data.peer);
|
|
@@ -660,6 +699,111 @@ export class BlockInputSync {
|
|
|
660
699
|
this.rateLimitBackoffTimeout = undefined;
|
|
661
700
|
}
|
|
662
701
|
}
|
|
702
|
+
onClockSlot = (slot) => {
|
|
703
|
+
if (!isForkPostGloas(this.config.getForkName(slot))) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
// end the previous slot's loop, wherever it is: its sleep is bound to this same controller
|
|
707
|
+
this.payloadPollAbortController?.abort();
|
|
708
|
+
this.payloadPollAbortController = new AbortController();
|
|
709
|
+
// entries from an earlier slot is not polled
|
|
710
|
+
for (const [rootHex, { slot: deferredSlot, source, peer }] of this.deferredPayloadRoots) {
|
|
711
|
+
if (deferredSlot < slot) {
|
|
712
|
+
if (!this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
713
|
+
this.addDeferredPayloadRoot(rootHex, deferredSlot, source, peer);
|
|
714
|
+
}
|
|
715
|
+
this.deferredPayloadRoots.delete(rootHex);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
this.pollPayloadsAtSlot(slot, this.payloadPollAbortController.signal).catch((e) => {
|
|
719
|
+
if (!isErrorAborted(e)) {
|
|
720
|
+
this.logger.debug("Error polling payloads", { slot }, e);
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
};
|
|
724
|
+
/**
|
|
725
|
+
* Poll unknown payloads of the current slot.
|
|
726
|
+
*/
|
|
727
|
+
async pollPayloadsAtSlot(slot, signal) {
|
|
728
|
+
// fixed tick count so the loop cannot outlive its slot: for mainnet, it's 6 ticks
|
|
729
|
+
const tickCount = Math.floor((this.config.SLOT_DURATION_MS - this.config.getPayloadDueMs()) / PAYLOAD_POLL_INTERVAL_MS);
|
|
730
|
+
// a root still missing on several ticks is searched on each of them, recorded once
|
|
731
|
+
const searchedRoots = new Set();
|
|
732
|
+
let polls = 0;
|
|
733
|
+
try {
|
|
734
|
+
await sleep(Math.max(0, this.config.getPayloadDueMs() - this.chain.clock.msFromSlot(slot)), signal);
|
|
735
|
+
for (polls = 0; polls < tickCount; polls++) {
|
|
736
|
+
let importedCount = 0;
|
|
737
|
+
for (const [rootHex, { slot: deferredSlot, source, peer }] of this.deferredPayloadRoots) {
|
|
738
|
+
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
739
|
+
importedCount++;
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
// copy to the regular pendingPayloads so that we can search
|
|
743
|
+
this.addDeferredPayloadRoot(rootHex, deferredSlot, source, peer);
|
|
744
|
+
searchedRoots.add(rootHex);
|
|
745
|
+
}
|
|
746
|
+
// an empty deferredPayloadRoots means there is no optimistic search comes before PAYLOAD_DUE
|
|
747
|
+
// we have to poll until the payload of current slot is imported or slot end
|
|
748
|
+
if (this.deferredPayloadRoots.size > 0 && importedCount === this.deferredPayloadRoots.size) {
|
|
749
|
+
if (searchedRoots.size > 0) {
|
|
750
|
+
// the backup search was needed
|
|
751
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({
|
|
752
|
+
result: DeferredPayloadResult.ImportedAfterSearch,
|
|
753
|
+
});
|
|
754
|
+
this.metrics?.blockInputSync.deferredPayloadPolls.observe(polls);
|
|
755
|
+
this.logger.verbose("Payload imported after unknown sync search", {
|
|
756
|
+
slot,
|
|
757
|
+
roots: Array.from(searchedRoots).join(","),
|
|
758
|
+
polls,
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
// this happens most of the time on devnet
|
|
763
|
+
// gossip delivered everything before PAYLOAD_DUE
|
|
764
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({
|
|
765
|
+
result: DeferredPayloadResult.ImportedBeforeSearch,
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
this.triggerUnknownBlockSearch();
|
|
771
|
+
if (polls < tickCount - 1) {
|
|
772
|
+
// no sleep after the last tick: the loop would otherwise end right at the slot boundary,
|
|
773
|
+
// where onClockSlot aborts it
|
|
774
|
+
await sleep(PAYLOAD_POLL_INTERVAL_MS, signal);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
// reaching here means the early return never fired
|
|
778
|
+
if (this.deferredPayloadRoots.size > 0) {
|
|
779
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({ result: DeferredPayloadResult.Unresolved });
|
|
780
|
+
this.logger.debug("Deferred payloads still missing at end of slot", {
|
|
781
|
+
slot,
|
|
782
|
+
roots: Array.from(this.deferredPayloadRoots.keys()).join(","),
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
// no optimistic search and no payload import ended the loop, eg a skipped slot
|
|
787
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({ result: DeferredPayloadResult.ResolvedNoRoot });
|
|
788
|
+
this.logger.debug("No deferred payloads at slot", {
|
|
789
|
+
slot,
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
catch (e) {
|
|
794
|
+
if (!isErrorAborted(e)) {
|
|
795
|
+
throw e;
|
|
796
|
+
}
|
|
797
|
+
this.logger.verbose("Payload poll aborted", { slot, polls, msIntoSlot: this.chain.clock.msFromSlot(slot) });
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
addDeferredPayloadRoot(rootHex, slot, source, peer) {
|
|
801
|
+
const isNewRoot = this.addByPayloadRootHex(rootHex, peer, slot);
|
|
802
|
+
if (isNewRoot) {
|
|
803
|
+
this.metrics?.blockInputSync.requests.inc({ type: PendingBlockType.DEFERRED_PAYLOAD_BLOCK_ROOT });
|
|
804
|
+
this.metrics?.blockInputSync.payloadSource.inc({ source });
|
|
805
|
+
}
|
|
806
|
+
}
|
|
663
807
|
async downloadBlock(block) {
|
|
664
808
|
if (block.status !== PendingBlockInputStatus.pending) {
|
|
665
809
|
return;
|
|
@@ -946,14 +1090,22 @@ export class BlockInputSync {
|
|
|
946
1090
|
return;
|
|
947
1091
|
}
|
|
948
1092
|
this.pendingPayloads.set(resultRootHex, pendingPayload);
|
|
1093
|
+
const result = isPendingPayloadEnvelope(pendingPayload)
|
|
1094
|
+
? DownloadResult.WaitingForBlock
|
|
1095
|
+
: this.chain.forkChoice.hasPayloadHexUnsafe(resultRootHex)
|
|
1096
|
+
? DownloadResult.Late
|
|
1097
|
+
: DownloadResult.Resolved;
|
|
1098
|
+
this.metrics?.blockInputSync.downloadedPayloadsResult.inc({ result });
|
|
949
1099
|
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
950
1100
|
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
951
1101
|
}
|
|
952
1102
|
else if (pendingPayload.status === PendingPayloadInputStatus.downloaded) {
|
|
1103
|
+
this.metrics?.blockInputSync.elapsedTimeTillPayloadReceived.observe(Date.now() / 1000 - computeTimeAtSlot(this.config, pendingPayload.payloadInput.slot, this.chain.genesisTime));
|
|
953
1104
|
await this.processPayload(pendingPayload);
|
|
954
1105
|
}
|
|
955
1106
|
return;
|
|
956
1107
|
}
|
|
1108
|
+
this.metrics?.blockInputSync.downloadedPayloadsResult.inc({ result: DownloadResult.Failed });
|
|
957
1109
|
this.logger.debug("Ignoring unknown payload root after failed download", logCtx, res.err);
|
|
958
1110
|
if (!isPendingPayloadEnvelope(payload)) {
|
|
959
1111
|
payload.status = PendingPayloadInputStatus.pending;
|
|
@@ -1050,6 +1202,10 @@ export class BlockInputSync {
|
|
|
1050
1202
|
? cacheItem.payloadInput
|
|
1051
1203
|
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1052
1204
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
1205
|
+
const fetchStartSec = Date.now() / 1000;
|
|
1206
|
+
if (typeof slot === "number") {
|
|
1207
|
+
this.metrics?.blockInputSync.payloadFetchBegin.observe(this.chain.clock.secFromSlot(slot, fetchStartSec));
|
|
1208
|
+
}
|
|
1053
1209
|
let i = 0;
|
|
1054
1210
|
let deferredByRateLimit = false;
|
|
1055
1211
|
while (i++ < this.getMaxDownloadAttempts()) {
|
|
@@ -1065,6 +1221,8 @@ export class BlockInputSync {
|
|
|
1065
1221
|
const reason = pendingColumns.size > 0
|
|
1066
1222
|
? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
|
|
1067
1223
|
: "no peer available to download pending payload";
|
|
1224
|
+
this.metrics?.blockInputSync.payloadFetchTime.observe({ result: FetchResult.FailureTriedAllPeers }, Date.now() / 1000 - fetchStartSec);
|
|
1225
|
+
this.metrics?.blockInputSync.payloadFetchPeers.set({ result: FetchResult.FailureTriedAllPeers }, i);
|
|
1068
1226
|
throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
|
|
1069
1227
|
}
|
|
1070
1228
|
const { peerId, client: peerClient } = peerMeta;
|
|
@@ -1072,7 +1230,12 @@ export class BlockInputSync {
|
|
|
1072
1230
|
try {
|
|
1073
1231
|
if (!envelope) {
|
|
1074
1232
|
envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
|
|
1233
|
+
const slotWasUnknown = typeof slot !== "number";
|
|
1075
1234
|
slot = envelope.message.payload.slotNumber;
|
|
1235
|
+
if (slotWasUnknown) {
|
|
1236
|
+
// we were not able to observe the time into slot when starting the fetch, do it now
|
|
1237
|
+
this.metrics?.blockInputSync.payloadFetchBegin.observe(this.chain.clock.secFromSlot(slot, fetchStartSec));
|
|
1238
|
+
}
|
|
1076
1239
|
}
|
|
1077
1240
|
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1078
1241
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
@@ -1080,6 +1243,8 @@ export class BlockInputSync {
|
|
|
1080
1243
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
1081
1244
|
// payloadInput may be seeded from the block body during download, so a non-null payloadInput does not
|
|
1082
1245
|
// imply the block is imported.
|
|
1246
|
+
this.metrics?.blockInputSync.payloadFetchTime.observe({ result: FetchResult.SuccessWaitingForBlock }, Date.now() / 1000 - fetchStartSec);
|
|
1247
|
+
this.metrics?.blockInputSync.payloadFetchPeers.set({ result: FetchResult.SuccessWaitingForBlock }, i);
|
|
1083
1248
|
return {
|
|
1084
1249
|
status: PendingPayloadInputStatus.waitingForBlock,
|
|
1085
1250
|
envelope,
|
|
@@ -1124,6 +1289,12 @@ export class BlockInputSync {
|
|
|
1124
1289
|
hasAllData: pendingPayload.payloadInput.hasAllData(),
|
|
1125
1290
|
});
|
|
1126
1291
|
if (pendingPayload.status === PendingPayloadInputStatus.downloaded) {
|
|
1292
|
+
// late = the payload got imported (via gossip) while we were downloading, same as the block path
|
|
1293
|
+
const result = this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)
|
|
1294
|
+
? FetchResult.SuccessLate
|
|
1295
|
+
: FetchResult.SuccessResolved;
|
|
1296
|
+
this.metrics?.blockInputSync.payloadFetchTime.observe({ result }, Date.now() / 1000 - fetchStartSec);
|
|
1297
|
+
this.metrics?.blockInputSync.payloadFetchPeers.set({ result }, i);
|
|
1127
1298
|
return pendingPayload;
|
|
1128
1299
|
}
|
|
1129
1300
|
cacheItem = pendingPayload;
|
|
@@ -1155,6 +1326,8 @@ export class BlockInputSync {
|
|
|
1155
1326
|
this.peerBalancer.onRequestCompleted(peerId);
|
|
1156
1327
|
}
|
|
1157
1328
|
}
|
|
1329
|
+
this.metrics?.blockInputSync.payloadFetchTime.observe({ result: FetchResult.FailureMaxAttempts }, Date.now() / 1000 - fetchStartSec);
|
|
1330
|
+
this.metrics?.blockInputSync.payloadFetchPeers.set({ result: FetchResult.FailureMaxAttempts }, i - 1);
|
|
1158
1331
|
if (deferredByRateLimit && this.peerBalancer.getNextRateLimitRetryAt() !== null) {
|
|
1159
1332
|
throw new UnknownBlockRateLimitedError(`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts: peers are rate-limited`);
|
|
1160
1333
|
}
|
|
@@ -1227,7 +1400,7 @@ export class BlockInputSync {
|
|
|
1227
1400
|
}
|
|
1228
1401
|
// no more peer with needed columns to try, throw error
|
|
1229
1402
|
const message = `Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`;
|
|
1230
|
-
this.metrics?.blockInputSync.
|
|
1403
|
+
this.metrics?.blockInputSync.fetchTime.observe({ result: FetchResult.FailureTriedAllPeers }, Date.now() / 1000 - fetchStartSec);
|
|
1231
1404
|
this.metrics?.blockInputSync.fetchPeers.set({ result: FetchResult.FailureTriedAllPeers }, i);
|
|
1232
1405
|
throw Error(message);
|
|
1233
1406
|
}
|
|
@@ -1309,7 +1482,7 @@ export class BlockInputSync {
|
|
|
1309
1482
|
: this.chain.forkChoice.hasBlockHex(cacheItem.blockInput.parentRootHex)
|
|
1310
1483
|
? FetchResult.SuccessResolved
|
|
1311
1484
|
: FetchResult.SuccessMissingParent;
|
|
1312
|
-
this.metrics?.blockInputSync.
|
|
1485
|
+
this.metrics?.blockInputSync.fetchTime.observe({ result }, Date.now() / 1000 - fetchStartSec);
|
|
1313
1486
|
this.metrics?.blockInputSync.fetchPeers.set({ result }, i);
|
|
1314
1487
|
return cacheItem;
|
|
1315
1488
|
}
|
|
@@ -1336,7 +1509,7 @@ export class BlockInputSync {
|
|
|
1336
1509
|
throw new Error(`${message} Missing column indices=${prettyPrintIndices(missing)}.`);
|
|
1337
1510
|
}
|
|
1338
1511
|
}
|
|
1339
|
-
this.metrics?.blockInputSync.
|
|
1512
|
+
this.metrics?.blockInputSync.fetchTime.observe({ result: FetchResult.FailureMaxAttempts }, Date.now() / 1000 - fetchStartSec);
|
|
1340
1513
|
this.metrics?.blockInputSync.fetchPeers.set({ result: FetchResult.FailureMaxAttempts }, i - 1);
|
|
1341
1514
|
throw Error(message);
|
|
1342
1515
|
}
|