@lodestar/beacon-node 1.43.0-rc.1 → 1.43.0-rc.5
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/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 +29 -26
- package/lib/chain/blocks/utils/chainSegment.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 +749 -2
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/interface.d.ts +1 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +1 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +3 -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 +31 -1
- package/lib/network/processor/gossipHandlers.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/sync/constants.d.ts +5 -1
- package/lib/sync/constants.d.ts.map +1 -1
- package/lib/sync/constants.js +5 -1
- package/lib/sync/constants.js.map +1 -1
- package/lib/sync/range/batch.d.ts +20 -3
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +54 -10
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +3 -0
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +44 -5
- 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 +40 -2
- 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/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +1 -12
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +7 -1
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +2 -0
- package/lib/sync/utils/downloadByRange.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/package.json +16 -16
- package/src/chain/blocks/utils/chainSegment.ts +30 -27
- package/src/metrics/metrics/lodestar.ts +6 -0
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +12 -0
- package/src/network/processor/gossipHandlers.ts +35 -1
- 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/sync/constants.ts +5 -1
- package/src/sync/range/batch.ts +71 -11
- package/src/sync/range/chain.ts +52 -10
- package/src/sync/range/range.ts +52 -2
- package/src/sync/range/utils/peerBalancer.ts +9 -3
- package/src/sync/unknownBlock.ts +1 -14
- package/src/sync/utils/downloadByRange.ts +8 -0
- package/src/sync/utils/rateLimit.ts +16 -0
package/src/sync/range/range.ts
CHANGED
|
@@ -3,14 +3,21 @@ import {StrictEventEmitter} from "strict-event-emitter-types";
|
|
|
3
3
|
import {BeaconConfig} from "@lodestar/config";
|
|
4
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";
|
|
@@ -231,6 +238,49 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
231
238
|
custodyConfig: this.chain.custodyConfig,
|
|
232
239
|
seenTimestampSec: Date.now() / 1000,
|
|
233
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
|
+
|
|
234
284
|
return {result: {blocks, payloadEnvelopes}, warnings};
|
|
235
285
|
};
|
|
236
286
|
|
|
@@ -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/unknownBlock.ts
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
} from "./types.js";
|
|
43
43
|
import {DownloadByRootError, downloadByRoot} from "./utils/downloadByRoot.js";
|
|
44
44
|
import {getAllDescendantBlocks, getUnknownAndAncestorBlocks} from "./utils/pendingBlocksTree.js";
|
|
45
|
+
import {getRateLimitedUntilMs} from "./utils/rateLimit.js";
|
|
45
46
|
|
|
46
47
|
const MAX_ATTEMPTS_PER_BLOCK = 5;
|
|
47
48
|
const MAX_KNOWN_BAD_BLOCKS = 500;
|
|
@@ -70,20 +71,6 @@ class UnknownBlockRateLimitedError extends Error {
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
function getRateLimitedUntilMs(e: unknown): number | null {
|
|
74
|
-
if (!(e instanceof RequestError)) {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
switch (e.type.code) {
|
|
79
|
-
case RequestErrorCode.RESP_RATE_LIMITED:
|
|
80
|
-
case RequestErrorCode.REQUEST_SELF_RATE_LIMITED:
|
|
81
|
-
return e.type.rateLimitedUntilMs ?? null;
|
|
82
|
-
default:
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
74
|
/**
|
|
88
75
|
* BlockInputSync is a class that handles ReqResp to find blocks and data related to a specific blockRoot. The
|
|
89
76
|
* blockRoot may have been found via object gossip, or the API. Gossip objects that can trigger a search are block,
|
|
@@ -1155,6 +1155,9 @@ export enum DownloadByRangeErrorCode {
|
|
|
1155
1155
|
|
|
1156
1156
|
/** Envelope beaconBlockRoot does not match the block's root */
|
|
1157
1157
|
INVALID_ENVELOPE_BEACON_BLOCK_ROOT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_ENVELOPE_BEACON_BLOCK_ROOT",
|
|
1158
|
+
|
|
1159
|
+
/** Block segment + envelopes failed chain-segment linearity / FULL-chain checks */
|
|
1160
|
+
INVALID_CHAIN_SEGMENT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_CHAIN_SEGMENT",
|
|
1158
1161
|
}
|
|
1159
1162
|
|
|
1160
1163
|
export type DownloadByRangeErrorType =
|
|
@@ -1252,6 +1255,11 @@ export type DownloadByRangeErrorType =
|
|
|
1252
1255
|
slot: Slot;
|
|
1253
1256
|
expected: string;
|
|
1254
1257
|
actual: string;
|
|
1258
|
+
}
|
|
1259
|
+
| {
|
|
1260
|
+
code: DownloadByRangeErrorCode.INVALID_CHAIN_SEGMENT;
|
|
1261
|
+
slot: Slot;
|
|
1262
|
+
reason: string;
|
|
1255
1263
|
};
|
|
1256
1264
|
|
|
1257
1265
|
export class DownloadByRangeError extends LodestarError<DownloadByRangeErrorType> {}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
|
|
2
|
+
import {RATE_LIMITED_PEER_BACKOFF_MS} from "../constants.js";
|
|
3
|
+
|
|
4
|
+
export function getRateLimitedUntilMs(e: unknown): number | null {
|
|
5
|
+
if (!(e instanceof RequestError)) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
switch (e.type.code) {
|
|
10
|
+
case RequestErrorCode.RESP_RATE_LIMITED:
|
|
11
|
+
case RequestErrorCode.REQUEST_SELF_RATE_LIMITED:
|
|
12
|
+
return e.type.rateLimitedUntilMs ?? Date.now() + RATE_LIMITED_PEER_BACKOFF_MS;
|
|
13
|
+
default:
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|