@lodestar/beacon-node 1.43.0-rc.2 → 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.
Files changed (97) hide show
  1. package/lib/chain/blocks/utils/chainSegment.d.ts +1 -3
  2. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  3. package/lib/chain/blocks/utils/chainSegment.js +29 -26
  4. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  5. package/lib/metrics/metrics/lodestar.d.ts +4 -0
  6. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  7. package/lib/metrics/metrics/lodestar.js +5 -0
  8. package/lib/metrics/metrics/lodestar.js.map +1 -1
  9. package/lib/network/gossip/topic.d.ts +749 -2
  10. package/lib/network/gossip/topic.d.ts.map +1 -1
  11. package/lib/network/interface.d.ts +1 -0
  12. package/lib/network/interface.d.ts.map +1 -1
  13. package/lib/network/network.d.ts +1 -0
  14. package/lib/network/network.d.ts.map +1 -1
  15. package/lib/network/network.js +3 -0
  16. package/lib/network/network.js.map +1 -1
  17. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  18. package/lib/network/processor/gossipHandlers.js +31 -1
  19. package/lib/network/processor/gossipHandlers.js.map +1 -1
  20. package/lib/network/reqresp/ReqRespBeaconNode.d.ts.map +1 -1
  21. package/lib/network/reqresp/ReqRespBeaconNode.js +1 -1
  22. package/lib/network/reqresp/ReqRespBeaconNode.js.map +1 -1
  23. package/lib/network/reqresp/handlers/beaconBlocksByHead.d.ts +9 -0
  24. package/lib/network/reqresp/handlers/beaconBlocksByHead.d.ts.map +1 -0
  25. package/lib/network/reqresp/handlers/beaconBlocksByHead.js +61 -0
  26. package/lib/network/reqresp/handlers/beaconBlocksByHead.js.map +1 -0
  27. package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
  28. package/lib/network/reqresp/handlers/index.js +5 -0
  29. package/lib/network/reqresp/handlers/index.js.map +1 -1
  30. package/lib/network/reqresp/interface.d.ts +1 -1
  31. package/lib/network/reqresp/interface.js +1 -1
  32. package/lib/network/reqresp/protocols.d.ts +1 -0
  33. package/lib/network/reqresp/protocols.d.ts.map +1 -1
  34. package/lib/network/reqresp/protocols.js +5 -0
  35. package/lib/network/reqresp/protocols.js.map +1 -1
  36. package/lib/network/reqresp/rateLimit.d.ts.map +1 -1
  37. package/lib/network/reqresp/rateLimit.js +4 -0
  38. package/lib/network/reqresp/rateLimit.js.map +1 -1
  39. package/lib/network/reqresp/score.d.ts.map +1 -1
  40. package/lib/network/reqresp/score.js +1 -0
  41. package/lib/network/reqresp/score.js.map +1 -1
  42. package/lib/network/reqresp/types.d.ts +3 -0
  43. package/lib/network/reqresp/types.d.ts.map +1 -1
  44. package/lib/network/reqresp/types.js +3 -0
  45. package/lib/network/reqresp/types.js.map +1 -1
  46. package/lib/sync/constants.d.ts +5 -1
  47. package/lib/sync/constants.d.ts.map +1 -1
  48. package/lib/sync/constants.js +5 -1
  49. package/lib/sync/constants.js.map +1 -1
  50. package/lib/sync/range/batch.d.ts +20 -3
  51. package/lib/sync/range/batch.d.ts.map +1 -1
  52. package/lib/sync/range/batch.js +54 -10
  53. package/lib/sync/range/batch.js.map +1 -1
  54. package/lib/sync/range/chain.d.ts +3 -0
  55. package/lib/sync/range/chain.d.ts.map +1 -1
  56. package/lib/sync/range/chain.js +44 -5
  57. package/lib/sync/range/chain.js.map +1 -1
  58. package/lib/sync/range/range.d.ts.map +1 -1
  59. package/lib/sync/range/range.js +40 -2
  60. package/lib/sync/range/range.js.map +1 -1
  61. package/lib/sync/range/utils/peerBalancer.d.ts +2 -1
  62. package/lib/sync/range/utils/peerBalancer.d.ts.map +1 -1
  63. package/lib/sync/range/utils/peerBalancer.js +8 -4
  64. package/lib/sync/range/utils/peerBalancer.js.map +1 -1
  65. package/lib/sync/unknownBlock.d.ts.map +1 -1
  66. package/lib/sync/unknownBlock.js +1 -12
  67. package/lib/sync/unknownBlock.js.map +1 -1
  68. package/lib/sync/utils/downloadByRange.d.ts +7 -1
  69. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  70. package/lib/sync/utils/downloadByRange.js +2 -0
  71. package/lib/sync/utils/downloadByRange.js.map +1 -1
  72. package/lib/sync/utils/rateLimit.d.ts +2 -0
  73. package/lib/sync/utils/rateLimit.d.ts.map +1 -0
  74. package/lib/sync/utils/rateLimit.js +15 -0
  75. package/lib/sync/utils/rateLimit.js.map +1 -0
  76. package/package.json +15 -15
  77. package/src/chain/blocks/utils/chainSegment.ts +30 -27
  78. package/src/metrics/metrics/lodestar.ts +6 -0
  79. package/src/network/interface.ts +1 -0
  80. package/src/network/network.ts +12 -0
  81. package/src/network/processor/gossipHandlers.ts +35 -1
  82. package/src/network/reqresp/ReqRespBeaconNode.ts +1 -0
  83. package/src/network/reqresp/handlers/beaconBlocksByHead.ts +91 -0
  84. package/src/network/reqresp/handlers/index.ts +5 -0
  85. package/src/network/reqresp/interface.ts +1 -1
  86. package/src/network/reqresp/protocols.ts +6 -0
  87. package/src/network/reqresp/rateLimit.ts +4 -0
  88. package/src/network/reqresp/score.ts +1 -0
  89. package/src/network/reqresp/types.ts +5 -0
  90. package/src/sync/constants.ts +5 -1
  91. package/src/sync/range/batch.ts +71 -11
  92. package/src/sync/range/chain.ts +52 -10
  93. package/src/sync/range/range.ts +52 -2
  94. package/src/sync/range/utils/peerBalancer.ts +9 -3
  95. package/src/sync/unknownBlock.ts +1 -14
  96. package/src/sync/utils/downloadByRange.ts +8 -0
  97. package/src/sync/utils/rateLimit.ts +16 -0
@@ -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 {cacheByRangeResponses, downloadByRange} from "../utils/downloadByRange.js";
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) no failed request (2) less active requests, then pick first
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
@@ -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
+ }