@lodestar/beacon-node 1.36.0-dev.f259361847 → 1.36.0-dev.f3703b7882

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 (197) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +41 -22
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/lodestar/index.d.ts +5 -0
  5. package/lib/api/impl/lodestar/index.d.ts.map +1 -1
  6. package/lib/api/impl/lodestar/index.js +35 -10
  7. package/lib/api/impl/lodestar/index.js.map +1 -1
  8. package/lib/api/impl/node/utils.js +1 -1
  9. package/lib/api/impl/node/utils.js.map +1 -1
  10. package/lib/chain/chain.d.ts +5 -2
  11. package/lib/chain/chain.d.ts.map +1 -1
  12. package/lib/chain/chain.js +32 -16
  13. package/lib/chain/chain.js.map +1 -1
  14. package/lib/chain/errors/blobSidecarError.d.ts +5 -0
  15. package/lib/chain/errors/blobSidecarError.d.ts.map +1 -1
  16. package/lib/chain/errors/blobSidecarError.js.map +1 -1
  17. package/lib/chain/errors/dataColumnSidecarError.d.ts +21 -14
  18. package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
  19. package/lib/chain/errors/dataColumnSidecarError.js +4 -0
  20. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
  21. package/lib/chain/forkChoice/index.d.ts +9 -1
  22. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  23. package/lib/chain/forkChoice/index.js +109 -4
  24. package/lib/chain/forkChoice/index.js.map +1 -1
  25. package/lib/chain/interface.d.ts +2 -0
  26. package/lib/chain/interface.d.ts.map +1 -1
  27. package/lib/chain/options.d.ts +0 -2
  28. package/lib/chain/options.d.ts.map +1 -1
  29. package/lib/chain/options.js +2 -2
  30. package/lib/chain/options.js.map +1 -1
  31. package/lib/chain/stateCache/datastore/db.d.ts +12 -0
  32. package/lib/chain/stateCache/datastore/db.d.ts.map +1 -1
  33. package/lib/chain/stateCache/datastore/db.js +70 -0
  34. package/lib/chain/stateCache/datastore/db.js.map +1 -1
  35. package/lib/chain/stateCache/datastore/file.d.ts +1 -0
  36. package/lib/chain/stateCache/datastore/file.d.ts.map +1 -1
  37. package/lib/chain/stateCache/datastore/file.js +7 -0
  38. package/lib/chain/stateCache/datastore/file.js.map +1 -1
  39. package/lib/chain/stateCache/datastore/types.d.ts +1 -0
  40. package/lib/chain/stateCache/datastore/types.d.ts.map +1 -1
  41. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +16 -1
  42. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  43. package/lib/chain/stateCache/persistentCheckpointsCache.js +31 -1
  44. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  45. package/lib/chain/validation/blobSidecar.d.ts +4 -1
  46. package/lib/chain/validation/blobSidecar.d.ts.map +1 -1
  47. package/lib/chain/validation/blobSidecar.js +46 -11
  48. package/lib/chain/validation/blobSidecar.js.map +1 -1
  49. package/lib/chain/validation/dataColumnSidecar.d.ts +4 -1
  50. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  51. package/lib/chain/validation/dataColumnSidecar.js +64 -19
  52. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  53. package/lib/index.d.ts +2 -0
  54. package/lib/index.d.ts.map +1 -1
  55. package/lib/index.js +2 -0
  56. package/lib/index.js.map +1 -1
  57. package/lib/metrics/metrics/lodestar.js +1 -1
  58. package/lib/metrics/metrics/lodestar.js.map +1 -1
  59. package/lib/network/core/networkCore.d.ts.map +1 -1
  60. package/lib/network/core/networkCore.js +5 -1
  61. package/lib/network/core/networkCore.js.map +1 -1
  62. package/lib/network/core/networkCoreWorker.js +8 -8
  63. package/lib/network/core/networkCoreWorker.js.map +1 -1
  64. package/lib/network/core/networkCoreWorkerHandler.js +1 -1
  65. package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
  66. package/lib/network/discv5/worker.js +2 -7
  67. package/lib/network/discv5/worker.js.map +1 -1
  68. package/lib/network/events.d.ts +1 -0
  69. package/lib/network/events.d.ts.map +1 -1
  70. package/lib/network/gossip/encoding.js +1 -1
  71. package/lib/network/gossip/encoding.js.map +1 -1
  72. package/lib/network/gossip/gossipsub.d.ts.map +1 -1
  73. package/lib/network/gossip/gossipsub.js +6 -1
  74. package/lib/network/gossip/gossipsub.js.map +1 -1
  75. package/lib/network/gossip/interface.d.ts +2 -0
  76. package/lib/network/gossip/interface.d.ts.map +1 -1
  77. package/lib/network/gossip/snappy_bun.d.ts +3 -0
  78. package/lib/network/gossip/snappy_bun.d.ts.map +1 -0
  79. package/lib/network/gossip/snappy_bun.js +3 -0
  80. package/lib/network/gossip/snappy_bun.js.map +1 -0
  81. package/lib/network/metadata.d.ts +1 -1
  82. package/lib/network/metadata.d.ts.map +1 -1
  83. package/lib/network/metadata.js +1 -0
  84. package/lib/network/metadata.js.map +1 -1
  85. package/lib/network/options.d.ts +0 -1
  86. package/lib/network/options.d.ts.map +1 -1
  87. package/lib/network/options.js.map +1 -1
  88. package/lib/network/peers/discover.js +2 -2
  89. package/lib/network/peers/discover.js.map +1 -1
  90. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  91. package/lib/network/processor/gossipHandlers.js +15 -1
  92. package/lib/network/processor/gossipHandlers.js.map +1 -1
  93. package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
  94. package/lib/network/processor/gossipValidatorFn.js +3 -2
  95. package/lib/network/processor/gossipValidatorFn.js.map +1 -1
  96. package/lib/network/processor/types.d.ts +2 -0
  97. package/lib/network/processor/types.d.ts.map +1 -1
  98. package/lib/network/reqresp/ReqRespBeaconNode.d.ts.map +1 -1
  99. package/lib/network/reqresp/ReqRespBeaconNode.js +3 -1
  100. package/lib/network/reqresp/ReqRespBeaconNode.js.map +1 -1
  101. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts +2 -1
  102. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  103. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -3
  104. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  105. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts +2 -1
  106. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  107. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +9 -1
  108. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  109. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts +2 -1
  110. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
  111. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +9 -1
  112. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  113. package/lib/network/reqresp/handlers/index.js +6 -6
  114. package/lib/network/reqresp/handlers/index.js.map +1 -1
  115. package/lib/network/reqresp/types.d.ts +1 -0
  116. package/lib/network/reqresp/types.d.ts.map +1 -1
  117. package/lib/node/nodejs.d.ts +2 -1
  118. package/lib/node/nodejs.d.ts.map +1 -1
  119. package/lib/node/nodejs.js +2 -1
  120. package/lib/node/nodejs.js.map +1 -1
  121. package/lib/sync/range/range.d.ts.map +1 -1
  122. package/lib/sync/range/range.js +2 -1
  123. package/lib/sync/range/range.js.map +1 -1
  124. package/lib/sync/unknownBlock.js +1 -1
  125. package/lib/sync/unknownBlock.js.map +1 -1
  126. package/lib/sync/utils/downloadByRange.d.ts +59 -15
  127. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  128. package/lib/sync/utils/downloadByRange.js +204 -83
  129. package/lib/sync/utils/downloadByRange.js.map +1 -1
  130. package/lib/sync/utils/downloadByRoot.d.ts +8 -14
  131. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  132. package/lib/sync/utils/downloadByRoot.js +18 -33
  133. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  134. package/lib/sync/utils/remoteSyncType.d.ts +2 -1
  135. package/lib/sync/utils/remoteSyncType.d.ts.map +1 -1
  136. package/lib/sync/utils/remoteSyncType.js +19 -4
  137. package/lib/sync/utils/remoteSyncType.js.map +1 -1
  138. package/lib/util/blobs.d.ts +1 -1
  139. package/lib/util/blobs.d.ts.map +1 -1
  140. package/lib/util/blobs.js +53 -20
  141. package/lib/util/blobs.js.map +1 -1
  142. package/lib/util/profile.d.ts +6 -4
  143. package/lib/util/profile.d.ts.map +1 -1
  144. package/lib/util/profile.js +40 -3
  145. package/lib/util/profile.js.map +1 -1
  146. package/lib/util/sszBytes.d.ts +2 -0
  147. package/lib/util/sszBytes.d.ts.map +1 -1
  148. package/lib/util/sszBytes.js +25 -0
  149. package/lib/util/sszBytes.js.map +1 -1
  150. package/package.json +32 -25
  151. package/src/api/impl/beacon/blocks/index.ts +47 -25
  152. package/src/api/impl/lodestar/index.ts +42 -10
  153. package/src/api/impl/node/utils.ts +1 -1
  154. package/src/chain/chain.ts +48 -23
  155. package/src/chain/errors/blobSidecarError.ts +12 -2
  156. package/src/chain/errors/dataColumnSidecarError.ts +31 -16
  157. package/src/chain/forkChoice/index.ts +178 -2
  158. package/src/chain/interface.ts +2 -0
  159. package/src/chain/options.ts +2 -3
  160. package/src/chain/stateCache/datastore/db.ts +89 -1
  161. package/src/chain/stateCache/datastore/file.ts +8 -0
  162. package/src/chain/stateCache/datastore/types.ts +1 -0
  163. package/src/chain/stateCache/persistentCheckpointsCache.ts +45 -2
  164. package/src/chain/validation/blobSidecar.ts +54 -10
  165. package/src/chain/validation/dataColumnSidecar.ts +76 -19
  166. package/src/index.ts +2 -0
  167. package/src/metrics/metrics/lodestar.ts +1 -1
  168. package/src/network/core/networkCore.ts +5 -1
  169. package/src/network/core/networkCoreWorker.ts +9 -9
  170. package/src/network/core/networkCoreWorkerHandler.ts +1 -1
  171. package/src/network/discv5/worker.ts +2 -7
  172. package/src/network/events.ts +1 -1
  173. package/src/network/gossip/encoding.ts +1 -1
  174. package/src/network/gossip/gossipsub.ts +7 -1
  175. package/src/network/gossip/interface.ts +2 -0
  176. package/src/network/gossip/snappy_bun.ts +2 -0
  177. package/src/network/metadata.ts +3 -1
  178. package/src/network/options.ts +0 -1
  179. package/src/network/peers/discover.ts +2 -2
  180. package/src/network/processor/gossipHandlers.ts +16 -1
  181. package/src/network/processor/gossipValidatorFn.ts +15 -2
  182. package/src/network/processor/types.ts +2 -0
  183. package/src/network/reqresp/ReqRespBeaconNode.ts +3 -1
  184. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +18 -3
  185. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +13 -1
  186. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +13 -1
  187. package/src/network/reqresp/handlers/index.ts +6 -6
  188. package/src/network/reqresp/types.ts +1 -0
  189. package/src/node/nodejs.ts +3 -0
  190. package/src/sync/range/range.ts +2 -1
  191. package/src/sync/unknownBlock.ts +1 -1
  192. package/src/sync/utils/downloadByRange.ts +273 -108
  193. package/src/sync/utils/downloadByRoot.ts +22 -56
  194. package/src/sync/utils/remoteSyncType.ts +23 -4
  195. package/src/util/blobs.ts +64 -20
  196. package/src/util/profile.ts +45 -3
  197. package/src/util/sszBytes.ts +30 -0
@@ -9,11 +9,11 @@ import {
9
9
  isForkPostFulu,
10
10
  } from "@lodestar/params";
11
11
  import {BeaconBlockBody, BlobIndex, ColumnIndex, SignedBeaconBlock, Slot, deneb, fulu} from "@lodestar/types";
12
- import {LodestarError, fromHex, prettyBytes, prettyPrintIndices, toHex, toRootHex} from "@lodestar/utils";
12
+ import {LodestarError, fromHex, prettyPrintIndices, toHex, toRootHex} from "@lodestar/utils";
13
13
  import {isBlockInputBlobs, isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
14
14
  import {BlockInputSource, IBlockInput} from "../../chain/blocks/blockInput/types.js";
15
15
  import {ChainEventEmitter} from "../../chain/emitter.js";
16
- import {SeenBlockInput} from "../../chain/seenCache/seenGossipBlockInput.js";
16
+ import {IBeaconChain} from "../../chain/interface.ts";
17
17
  import {validateBlockBlobSidecars} from "../../chain/validation/blobSidecar.js";
18
18
  import {validateBlockDataColumnSidecars} from "../../chain/validation/dataColumnSidecar.js";
19
19
  import {INetwork} from "../../network/interface.js";
@@ -32,6 +32,7 @@ import {
32
32
 
33
33
  export type FetchByRootCoreProps = {
34
34
  config: ChainForkConfig;
35
+ chain: IBeaconChain | null; // null for testing purposes
35
36
  network: INetwork;
36
37
  peerMeta: PeerSyncMeta;
37
38
  };
@@ -63,13 +64,13 @@ export type FetchByRootResponses = {
63
64
 
64
65
  export type DownloadByRootProps = FetchByRootCoreProps & {
65
66
  cacheItem: BlockInputSyncCacheItem;
66
- seenCache: SeenBlockInput;
67
+ chain: IBeaconChain;
67
68
  emitter: ChainEventEmitter;
68
69
  };
69
70
 
70
71
  export async function downloadByRoot({
71
72
  config,
72
- seenCache,
73
+ chain,
73
74
  network,
74
75
  emitter,
75
76
  peerMeta,
@@ -84,6 +85,7 @@ export async function downloadByRoot({
84
85
  warnings,
85
86
  } = await fetchByRoot({
86
87
  config,
88
+ chain,
87
89
  network,
88
90
  cacheItem,
89
91
  blockRoot,
@@ -103,7 +105,7 @@ export async function downloadByRoot({
103
105
  });
104
106
  }
105
107
  } else {
106
- blockInput = seenCache.getByBlock({
108
+ blockInput = chain.seenBlockInputCache.getByBlock({
107
109
  block,
108
110
  peerIdStr,
109
111
  blockRootHex: rootHex,
@@ -119,7 +121,7 @@ export async function downloadByRoot({
119
121
  if (!blobSidecars) {
120
122
  throw new DownloadByRootError({
121
123
  code: DownloadByRootErrorCode.MISSING_BLOB_RESPONSE,
122
- blockRoot: prettyBytes(rootHex),
124
+ blockRoot: rootHex,
123
125
  peer: peerIdStr,
124
126
  });
125
127
  }
@@ -157,7 +159,7 @@ export async function downloadByRoot({
157
159
  if (!columnSidecars) {
158
160
  throw new DownloadByRootError({
159
161
  code: DownloadByRootErrorCode.MISSING_COLUMN_RESPONSE,
160
- blockRoot: prettyBytes(rootHex),
162
+ blockRoot: rootHex,
161
163
  peer: peerIdStr,
162
164
  });
163
165
  }
@@ -210,6 +212,7 @@ export async function downloadByRoot({
210
212
 
211
213
  export async function fetchByRoot({
212
214
  config,
215
+ chain,
213
216
  network,
214
217
  peerMeta,
215
218
  blockRoot,
@@ -237,6 +240,7 @@ export async function fetchByRoot({
237
240
  if (isBlockInputBlobs(cacheItem.blockInput)) {
238
241
  blobSidecars = await fetchAndValidateBlobs({
239
242
  config,
243
+ chain,
240
244
  network,
241
245
  peerIdStr,
242
246
  forkName: forkName as ForkPreFulu,
@@ -248,6 +252,7 @@ export async function fetchByRoot({
248
252
  if (isBlockInputColumns(cacheItem.blockInput)) {
249
253
  columnSidecarResult = await fetchAndValidateColumns({
250
254
  config,
255
+ chain,
251
256
  network,
252
257
  peerMeta,
253
258
  forkName: forkName as ForkPostFulu,
@@ -268,6 +273,7 @@ export async function fetchByRoot({
268
273
  if (isForkPostFulu(forkName)) {
269
274
  columnSidecarResult = await fetchAndValidateColumns({
270
275
  config,
276
+ chain,
271
277
  network,
272
278
  peerMeta,
273
279
  forkName,
@@ -280,6 +286,7 @@ export async function fetchByRoot({
280
286
  const blobCount = commitments.length;
281
287
  blobSidecars = await fetchAndValidateBlobs({
282
288
  config,
289
+ chain,
283
290
  network,
284
291
  peerIdStr,
285
292
  forkName: forkName as ForkPreFulu,
@@ -305,14 +312,14 @@ export async function fetchAndValidateBlock({
305
312
  network,
306
313
  peerIdStr,
307
314
  blockRoot,
308
- }: FetchByRootAndValidateBlockProps): Promise<SignedBeaconBlock> {
315
+ }: Omit<FetchByRootAndValidateBlockProps, "chain">): Promise<SignedBeaconBlock> {
309
316
  const response = await network.sendBeaconBlocksByRoot(peerIdStr, [blockRoot]);
310
317
  const block = response.at(0)?.data;
311
318
  if (!block) {
312
319
  throw new DownloadByRootError({
313
320
  code: DownloadByRootErrorCode.MISSING_BLOCK_RESPONSE,
314
321
  peer: prettyPrintPeerIdStr(peerIdStr),
315
- blockRoot: prettyBytes(blockRoot),
322
+ blockRoot: toRootHex(blockRoot),
316
323
  });
317
324
  }
318
325
  const receivedRoot = config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message);
@@ -321,8 +328,8 @@ export async function fetchAndValidateBlock({
321
328
  {
322
329
  code: DownloadByRootErrorCode.MISMATCH_BLOCK_ROOT,
323
330
  peer: prettyPrintPeerIdStr(peerIdStr),
324
- requestedBlockRoot: prettyBytes(blockRoot),
325
- receivedBlockRoot: prettyBytes(toRootHex(receivedRoot)),
331
+ requestedBlockRoot: toRootHex(blockRoot),
332
+ receivedBlockRoot: toRootHex(receivedRoot),
326
333
  },
327
334
  "block does not match requested root"
328
335
  );
@@ -331,6 +338,7 @@ export async function fetchAndValidateBlock({
331
338
  }
332
339
 
333
340
  export async function fetchAndValidateBlobs({
341
+ chain,
334
342
  network,
335
343
  peerIdStr,
336
344
  blockRoot,
@@ -344,7 +352,7 @@ export async function fetchAndValidateBlobs({
344
352
  missing,
345
353
  });
346
354
 
347
- await validateBlockBlobSidecars(block.message.slot, blockRoot, missing.length, blobSidecars);
355
+ await validateBlockBlobSidecars(chain, block.message.slot, blockRoot, missing.length, blobSidecars);
348
356
 
349
357
  return blobSidecars;
350
358
  }
@@ -368,6 +376,7 @@ export async function fetchBlobsByRoot({
368
376
  }
369
377
 
370
378
  export async function fetchAndValidateColumns({
379
+ chain,
371
380
  network,
372
381
  peerMeta,
373
382
  block,
@@ -438,7 +447,7 @@ export async function fetchAndValidateColumns({
438
447
  );
439
448
  }
440
449
 
441
- await validateBlockDataColumnSidecars(slot, blockRoot, blobCount, columnSidecars);
450
+ await validateBlockDataColumnSidecars(chain, slot, blockRoot, blobCount, columnSidecars);
442
451
 
443
452
  return {result: columnSidecars, warnings: warnings.length > 0 ? warnings : null};
444
453
  }
@@ -456,49 +465,6 @@ export async function fetchColumnsByRoot({
456
465
  return await network.sendDataColumnSidecarsByRoot(peerMeta.peerId, [{blockRoot, columns: missing}]);
457
466
  }
458
467
 
459
- // TODO(fulu) not in use, remove?
460
- export type ValidateColumnSidecarsProps = Pick<
461
- FetchByRootAndValidateColumnsProps,
462
- "config" | "peerMeta" | "blockRoot" | "missing"
463
- > & {
464
- slot: number;
465
- blobCount: number;
466
- needed?: fulu.DataColumnSidecars;
467
- needToPublish?: fulu.DataColumnSidecars;
468
- };
469
-
470
- // TODO(fulu) not in use, remove?
471
- export async function validateColumnSidecars({
472
- peerMeta,
473
- slot,
474
- blockRoot,
475
- blobCount,
476
- missing,
477
- needed = [],
478
- needToPublish = [],
479
- }: ValidateColumnSidecarsProps): Promise<void> {
480
- const requestedIndices = missing;
481
- const extraIndices: number[] = [];
482
- for (const columnSidecar of needed) {
483
- if (!requestedIndices.includes(columnSidecar.index)) {
484
- extraIndices.push(columnSidecar.index);
485
- }
486
- }
487
- if (extraIndices.length > 0) {
488
- throw new DownloadByRootError(
489
- {
490
- code: DownloadByRootErrorCode.EXTRA_SIDECAR_RECEIVED,
491
- peer: prettyPrintPeerIdStr(peerMeta.peerId),
492
- slot,
493
- blockRoot: prettyBytes(blockRoot),
494
- invalidIndices: prettyPrintIndices(extraIndices),
495
- },
496
- "Received a columnSidecar that was not requested"
497
- );
498
- }
499
- await validateBlockDataColumnSidecars(slot, blockRoot, blobCount, [...needed, ...needToPublish]);
500
- }
501
-
502
468
  export enum DownloadByRootErrorCode {
503
469
  MISMATCH_BLOCK_ROOT = "DOWNLOAD_BY_ROOT_ERROR_MISMATCH_BLOCK_ROOT",
504
470
  EXTRA_SIDECAR_RECEIVED = "DOWNLOAD_BY_ROOT_ERROR_EXTRA_SIDECAR_RECEIVED",
@@ -1,6 +1,7 @@
1
1
  import {IForkChoice} from "@lodestar/fork-choice";
2
2
  import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
3
3
  import {Slot, Status} from "@lodestar/types";
4
+ import {IBeaconChain} from "../../chain/interface.ts";
4
5
  import {ChainTarget} from "../range/utils/index.js";
5
6
 
6
7
  /** The type of peer relative to our current state */
@@ -103,8 +104,11 @@ export function getRangeSyncType(local: Status, remote: Status, forkChoice: IFor
103
104
  export function getRangeSyncTarget(
104
105
  local: Status,
105
106
  remote: Status,
106
- forkChoice: IForkChoice
107
+ chain: IBeaconChain
107
108
  ): {syncType: RangeSyncType; startEpoch: Slot; target: ChainTarget} {
109
+ const forkChoice = chain.forkChoice;
110
+
111
+ // finalized sync
108
112
  if (remote.finalizedEpoch > local.finalizedEpoch && !forkChoice.hasBlock(remote.finalizedRoot)) {
109
113
  return {
110
114
  // If RangeSyncType.Finalized, the range of blocks fetchable from startEpoch and target must allow to switch
@@ -131,11 +135,26 @@ export function getRangeSyncTarget(
131
135
  },
132
136
  };
133
137
  }
138
+
139
+ // we don't want to sync from epoch < minEpoch
140
+ // if we boot from an unfinalized checkpoint state, we don't want to sync before anchorStateLatestBlockSlot
141
+ // if we boot from a finalized checkpoint state, anchorStateLatestBlockSlot is trusted and we also don't want to sync before it
142
+ const minEpoch = Math.max(remote.finalizedEpoch, computeEpochAtSlot(chain.anchorStateLatestBlockSlot));
143
+
144
+ // head sync
134
145
  return {
135
146
  syncType: RangeSyncType.Head,
136
- // The new peer has the same finalized (earlier filters should prevent a peer with an
137
- // earlier finalized chain from reaching here) and local head will always be >= local finalized.
138
- startEpoch: computeEpochAtSlot(local.headSlot),
147
+ // The new peer has the same finalized `remote.finalizedEpoch == local.finalizedEpoch` since
148
+ // previous filters should prevent a peer with an earlier finalized chain from reaching here.
149
+ //
150
+ // By default and during stable network conditions, the head sync always starts from
151
+ // the finalized epoch (even though it's the head sync) because finalized epoch is < local head.
152
+ // This is to prevent the issue noted here https://github.com/ChainSafe/lodestar/pull/7509#discussion_r1984353063.
153
+ //
154
+ // During non-finality of the network, when starting from an unfinalized checkpoint state, we don't want
155
+ // to sync before anchorStateLatestBlockSlot as finalized epoch is too far away. Local head will also be
156
+ // the same to that value at startup, the head sync always starts from anchorStateLatestBlockSlot in this case.
157
+ startEpoch: Math.min(computeEpochAtSlot(local.headSlot), minEpoch),
139
158
  target: {
140
159
  slot: remote.headSlot,
141
160
  root: remote.headRoot,
package/src/util/blobs.ts CHANGED
@@ -149,41 +149,85 @@ export async function dataColumnMatrixRecovery(
149
149
  * Reconstruct blobs from a set of data columns, at least 50%+ of all the columns
150
150
  * must be provided to allow to reconstruct the full data matrix
151
151
  */
152
- export async function reconstructBlobs(sidecars: fulu.DataColumnSidecars): Promise<deneb.Blobs> {
152
+ export async function reconstructBlobs(sidecars: fulu.DataColumnSidecars, indices?: number[]): Promise<deneb.Blobs> {
153
153
  if (sidecars.length < NUMBER_OF_COLUMNS / 2) {
154
154
  throw Error(
155
155
  `Expected at least ${NUMBER_OF_COLUMNS / 2} data columns to reconstruct blobs, received ${sidecars.length}`
156
156
  );
157
157
  }
158
+ const blobCount = sidecars[0].column.length;
158
159
 
159
- let fullSidecars: fulu.DataColumnSidecars;
160
-
161
- if (sidecars.length === NUMBER_OF_COLUMNS) {
162
- // Full columns, no need to recover
163
- fullSidecars = sidecars;
164
- } else {
165
- const sidecarsByIndex = new Map<number, fulu.DataColumnSidecar>(sidecars.map((sc) => [sc.index, sc]));
166
- const recoveredSidecars = await dataColumnMatrixRecovery(sidecarsByIndex);
167
- if (recoveredSidecars === null) {
168
- // Should not happen because we check the column count above
169
- throw Error("Failed to reconstruct the full data matrix");
160
+ for (const index of indices ?? []) {
161
+ if (index < 0 || index >= blobCount) {
162
+ throw Error(`Invalid blob index ${index}, must be between 0 and ${blobCount - 1}`);
170
163
  }
171
- fullSidecars = recoveredSidecars;
164
+ }
165
+ const indicesToReconstruct = indices ?? Array.from({length: blobCount}, (_, i) => i);
166
+
167
+ const recoveredCells = await recoverBlobCells(sidecars, indicesToReconstruct);
168
+ if (recoveredCells === null) {
169
+ // Should not happen because we check the column count above
170
+ throw Error("Failed to recover cells to reconstruct blobs");
172
171
  }
173
172
 
174
- const blobCount = fullSidecars[0].column.length;
175
- const blobs: deneb.Blobs = new Array(blobCount);
173
+ const blobs: deneb.Blobs = new Array(indicesToReconstruct.length);
176
174
 
177
- const ordered = fullSidecars.slice().sort((a, b) => a.index - b.index);
178
- for (let row = 0; row < blobCount; row++) {
179
- // 128 cells that make up one "extended blob" row
180
- const cells = ordered.map((col) => col.column[row]);
181
- blobs[row] = cellsToBlob(cells);
175
+ for (let i = 0; i < indicesToReconstruct.length; i++) {
176
+ const blobIndex = indicesToReconstruct[i];
177
+ const cells = recoveredCells.get(blobIndex);
178
+ if (!cells) {
179
+ throw Error(`Failed to get recovered cells for blob index ${blobIndex}`);
180
+ }
181
+ blobs[i] = cellsToBlob(cells);
182
182
  }
183
183
 
184
184
  return blobs;
185
185
  }
186
186
 
187
+ /**
188
+ * Recover cells for specific blob indices from a set of data columns
189
+ */
190
+ async function recoverBlobCells(
191
+ partialSidecars: fulu.DataColumnSidecar[],
192
+ blobIndices: number[]
193
+ ): Promise<Map<number, fulu.Cell[]> | null> {
194
+ const columnCount = partialSidecars.length;
195
+ if (columnCount < NUMBER_OF_COLUMNS / 2) {
196
+ // We don't have enough columns to recover
197
+ return null;
198
+ }
199
+
200
+ const recoveredCells = new Map<number, fulu.Cell[]>();
201
+ // Sort data columns by index in ascending order
202
+ const partialSidecarsSorted = partialSidecars.slice().sort((a, b) => a.index - b.index);
203
+
204
+ if (columnCount === NUMBER_OF_COLUMNS) {
205
+ // Full columns, no need to recover
206
+ for (const blobIndex of blobIndices) {
207
+ // 128 cells that make up one "extended blob" row
208
+ const cells = partialSidecarsSorted.map((col) => col.column[blobIndex]);
209
+ recoveredCells.set(blobIndex, cells);
210
+ }
211
+ return recoveredCells;
212
+ }
213
+
214
+ await Promise.all(
215
+ blobIndices.map(async (blobIndex) => {
216
+ const cellIndices: number[] = [];
217
+ const cells: fulu.Cell[] = [];
218
+ for (const dataColumn of partialSidecarsSorted) {
219
+ cellIndices.push(dataColumn.index);
220
+ cells.push(dataColumn.column[blobIndex]);
221
+ }
222
+ // Recover cells for this specific blob row
223
+ const recovered = await kzg.asyncRecoverCellsAndKzgProofs(cellIndices, cells);
224
+ recoveredCells.set(blobIndex, recovered.cells);
225
+ })
226
+ );
227
+
228
+ return recoveredCells;
229
+ }
230
+
187
231
  /**
188
232
  * Concatenate the systematic half (columns 0‑63) of a row of cells into
189
233
  * the original 131072 byte blob. The parity half (64‑127) is ignored as
@@ -1,13 +1,32 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
1
3
  import {sleep} from "@lodestar/utils";
2
4
 
5
+ export enum ProfileThread {
6
+ MAIN = "main",
7
+ NETWORK = "network",
8
+ DISC5 = "discv5",
9
+ }
10
+
11
+ /**
12
+ * The time to take a Bun profile.
13
+ * If we increase this time it'll potentiall cause the app to crash.
14
+ * If we decrease this time, profile recorded will be fragmented and hard to analyze.
15
+ */
16
+ const BUN_PROFILE_MS = 3 * 1000;
17
+
18
+ export async function profileThread(thread: ProfileThread, durationMs: number, dirpath: string): Promise<string> {
19
+ return globalThis.Bun ? profileBun(thread, durationMs) : profileNodeJS(thread, durationMs, dirpath);
20
+ }
21
+
3
22
  /**
4
- * Take 10m profile of the current thread without promise tracking.
23
+ * Take `durationMs` profile of the current thread and return the persisted file path.
5
24
  */
6
- export async function profileNodeJS(durationMs: number): Promise<string> {
25
+ async function profileNodeJS(thread: ProfileThread, durationMs: number, dirpath: string): Promise<string> {
7
26
  const inspector = await import("node:inspector");
8
27
 
9
28
  // due to some typing issues, not able to use promisify here
10
- return new Promise<string>((resolve, reject) => {
29
+ const profile = await new Promise<string>((resolve, reject) => {
11
30
  // Start the inspector and connect to it
12
31
  const session = new inspector.Session();
13
32
  session.connect();
@@ -29,6 +48,29 @@ export async function profileNodeJS(durationMs: number): Promise<string> {
29
48
  });
30
49
  });
31
50
  });
51
+
52
+ const filePath = path.join(dirpath, `${thread}_thread_${new Date().toISOString()}.cpuprofile`);
53
+ fs.writeFileSync(filePath, profile);
54
+ return filePath;
55
+ }
56
+
57
+ /**
58
+ * Unlike NodeJS, Bun console.profile() api flush data to the inspector,
59
+ * so this api returns ms taken of this profile instead of file path.
60
+ */
61
+ async function profileBun(thread: ProfileThread, durationMs: number): Promise<string> {
62
+ const start = Date.now();
63
+ let now = Date.now();
64
+ while (now - start < durationMs) {
65
+ // biome-ignore lint/suspicious/noConsole: need to use console api to profile in Bun
66
+ console.profile(String(now));
67
+ await sleep(BUN_PROFILE_MS);
68
+ // biome-ignore lint/suspicious/noConsole: need to use console api to profile in Bun
69
+ console.profileEnd(String(now));
70
+ now = Date.now();
71
+ }
72
+
73
+ return `Successfully take Bun ${thread} thread profile in ${now - start}ms. Check your inspector to see the profile.`;
32
74
  }
33
75
 
34
76
  /**
@@ -417,6 +417,36 @@ export function getSlotFromDataColumnSidecarSerialized(data: Uint8Array): Slot |
417
417
  return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR);
418
418
  }
419
419
 
420
+ /**
421
+ * BeaconState of all forks (up until Electra, check with new forks)
422
+ * class BeaconState(Container):
423
+ * genesis_time: uint64 - 8 bytes
424
+ * genesis_validators_root: Root - 32 bytes
425
+ * slot: Slot - 8 bytes
426
+ * fork: Fork - 16 bytes
427
+ * latest_block_header: BeaconBlockHeader - fixed size
428
+ * slot: Slot - 8 bytes
429
+ *
430
+ */
431
+
432
+ const BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE = 8 + 32 + 8 + 16;
433
+ export function getLastProcessedSlotFromBeaconStateSerialized(data: Uint8Array): Slot | null {
434
+ if (data.length < BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE + SLOT_SIZE) {
435
+ return null;
436
+ }
437
+
438
+ return getSlotFromOffset(data, BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE);
439
+ }
440
+
441
+ const SLOT_BYTES_POSITION_IN_BEACON_STATE = 8 + 32;
442
+ export function getSlotFromBeaconStateSerialized(data: Uint8Array): Slot | null {
443
+ if (data.length < SLOT_BYTES_POSITION_IN_BEACON_STATE) {
444
+ return null;
445
+ }
446
+
447
+ return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_BEACON_STATE);
448
+ }
449
+
420
450
  /**
421
451
  * Read only the first 4 bytes of Slot, max value is 4,294,967,295 will be reached 1634 years after genesis
422
452
  *