@lodestar/beacon-node 1.48.0-dev.a0619b279a → 1.48.0-dev.a427d3dbb5

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 (141) hide show
  1. package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
  2. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  3. package/lib/chain/archiveStore/archiveStore.js +0 -1
  4. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  5. package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
  6. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +0 -1
  7. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  8. package/lib/chain/archiveStore/historicalState/types.d.ts +0 -2
  9. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  10. package/lib/chain/archiveStore/historicalState/types.js.map +1 -1
  11. package/lib/chain/archiveStore/historicalState/worker.js +1 -4
  12. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  13. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  14. package/lib/chain/archiveStore/utils/archiveBlocks.js +34 -31
  15. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  16. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  17. package/lib/chain/blocks/writeBlockInputToDb.js +16 -9
  18. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  19. package/lib/chain/chain.d.ts +1 -2
  20. package/lib/chain/chain.d.ts.map +1 -1
  21. package/lib/chain/chain.js +24 -47
  22. package/lib/chain/chain.js.map +1 -1
  23. package/lib/db/beacon.d.ts +5 -14
  24. package/lib/db/beacon.d.ts.map +1 -1
  25. package/lib/db/beacon.js +3 -17
  26. package/lib/db/beacon.js.map +1 -1
  27. package/lib/db/index.d.ts +0 -1
  28. package/lib/db/index.d.ts.map +1 -1
  29. package/lib/db/interface.d.ts +1 -8
  30. package/lib/db/interface.d.ts.map +1 -1
  31. package/lib/db/repositories/blockArchive.d.ts +1 -8
  32. package/lib/db/repositories/blockArchive.d.ts.map +1 -1
  33. package/lib/db/repositories/blockArchive.js +28 -65
  34. package/lib/db/repositories/blockArchive.js.map +1 -1
  35. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -1
  36. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  37. package/lib/db/repositories/blockArchiveIndex.js +7 -3
  38. package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
  39. package/lib/metrics/metrics/lodestar.d.ts +0 -18
  40. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  41. package/lib/metrics/metrics/lodestar.js +1 -55
  42. package/lib/metrics/metrics/lodestar.js.map +1 -1
  43. package/lib/monitoring/clientStats.js +1 -1
  44. package/lib/monitoring/clientStats.js.map +1 -1
  45. package/lib/network/gossip/encoding.d.ts.map +1 -1
  46. package/lib/network/gossip/encoding.js +1 -0
  47. package/lib/network/gossip/encoding.js.map +1 -1
  48. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  49. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +91 -112
  50. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  51. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
  52. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +0 -2
  53. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  54. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts +1 -2
  55. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
  56. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +9 -14
  57. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  58. package/lib/node/nodejs.d.ts +1 -2
  59. package/lib/node/nodejs.d.ts.map +1 -1
  60. package/lib/node/nodejs.js +2 -4
  61. package/lib/node/nodejs.js.map +1 -1
  62. package/lib/util/sszBytes.d.ts +0 -6
  63. package/lib/util/sszBytes.d.ts.map +1 -1
  64. package/lib/util/sszBytes.js +1 -1
  65. package/lib/util/sszBytes.js.map +1 -1
  66. package/package.json +18 -17
  67. package/src/chain/archiveStore/archiveStore.ts +1 -6
  68. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +0 -1
  69. package/src/chain/archiveStore/historicalState/types.ts +0 -2
  70. package/src/chain/archiveStore/historicalState/worker.ts +1 -4
  71. package/src/chain/archiveStore/utils/archiveBlocks.ts +39 -34
  72. package/src/chain/blocks/writeBlockInputToDb.ts +17 -10
  73. package/src/chain/chain.ts +23 -53
  74. package/src/db/beacon.ts +6 -33
  75. package/src/db/index.ts +0 -1
  76. package/src/db/interface.ts +1 -9
  77. package/src/db/repositories/blockArchive.ts +30 -89
  78. package/src/db/repositories/blockArchiveIndex.ts +9 -4
  79. package/src/metrics/metrics/lodestar.ts +1 -57
  80. package/src/monitoring/clientStats.ts +1 -1
  81. package/src/network/gossip/encoding.ts +1 -0
  82. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +105 -139
  83. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +0 -2
  84. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +7 -15
  85. package/src/node/nodejs.ts +1 -6
  86. package/src/util/sszBytes.ts +1 -1
  87. package/lib/db/dataColumnStore.d.ts +0 -39
  88. package/lib/db/dataColumnStore.d.ts.map +0 -1
  89. package/lib/db/dataColumnStore.js +0 -81
  90. package/lib/db/dataColumnStore.js.map +0 -1
  91. package/lib/db/flatFileStore/atomicWrite.d.ts +0 -8
  92. package/lib/db/flatFileStore/atomicWrite.d.ts.map +0 -1
  93. package/lib/db/flatFileStore/atomicWrite.js +0 -61
  94. package/lib/db/flatFileStore/atomicWrite.js.map +0 -1
  95. package/lib/db/flatFileStore/dcolFormat.d.ts +0 -90
  96. package/lib/db/flatFileStore/dcolFormat.d.ts.map +0 -1
  97. package/lib/db/flatFileStore/dcolFormat.js +0 -286
  98. package/lib/db/flatFileStore/dcolFormat.js.map +0 -1
  99. package/lib/db/flatFileStore/errors.d.ts +0 -82
  100. package/lib/db/flatFileStore/errors.d.ts.map +0 -1
  101. package/lib/db/flatFileStore/errors.js +0 -32
  102. package/lib/db/flatFileStore/errors.js.map +0 -1
  103. package/lib/db/flatFileStore/flatFileStore.d.ts +0 -41
  104. package/lib/db/flatFileStore/flatFileStore.d.ts.map +0 -1
  105. package/lib/db/flatFileStore/flatFileStore.js +0 -251
  106. package/lib/db/flatFileStore/flatFileStore.js.map +0 -1
  107. package/lib/db/flatFileStore/index.d.ts +0 -4
  108. package/lib/db/flatFileStore/index.d.ts.map +0 -1
  109. package/lib/db/flatFileStore/index.js +0 -2
  110. package/lib/db/flatFileStore/index.js.map +0 -1
  111. package/lib/db/flatFileStore/interface.d.ts +0 -25
  112. package/lib/db/flatFileStore/interface.d.ts.map +0 -1
  113. package/lib/db/flatFileStore/interface.js +0 -2
  114. package/lib/db/flatFileStore/interface.js.map +0 -1
  115. package/lib/db/flatFileStore/metrics.d.ts +0 -24
  116. package/lib/db/flatFileStore/metrics.d.ts.map +0 -1
  117. package/lib/db/flatFileStore/metrics.js +0 -25
  118. package/lib/db/flatFileStore/metrics.js.map +0 -1
  119. package/lib/db/flatFileStore/path.d.ts +0 -9
  120. package/lib/db/flatFileStore/path.d.ts.map +0 -1
  121. package/lib/db/flatFileStore/path.js +0 -21
  122. package/lib/db/flatFileStore/path.js.map +0 -1
  123. package/lib/db/flatFileStore/slotIndex.d.ts +0 -14
  124. package/lib/db/flatFileStore/slotIndex.d.ts.map +0 -1
  125. package/lib/db/flatFileStore/slotIndex.js +0 -46
  126. package/lib/db/flatFileStore/slotIndex.js.map +0 -1
  127. package/lib/db/flatFileStore/snappy.d.ts +0 -3
  128. package/lib/db/flatFileStore/snappy.d.ts.map +0 -1
  129. package/lib/db/flatFileStore/snappy.js +0 -12
  130. package/lib/db/flatFileStore/snappy.js.map +0 -1
  131. package/src/db/dataColumnStore.ts +0 -113
  132. package/src/db/flatFileStore/atomicWrite.ts +0 -65
  133. package/src/db/flatFileStore/dcolFormat.ts +0 -389
  134. package/src/db/flatFileStore/errors.ts +0 -53
  135. package/src/db/flatFileStore/flatFileStore.ts +0 -310
  136. package/src/db/flatFileStore/index.ts +0 -3
  137. package/src/db/flatFileStore/interface.ts +0 -21
  138. package/src/db/flatFileStore/metrics.ts +0 -48
  139. package/src/db/flatFileStore/path.ts +0 -32
  140. package/src/db/flatFileStore/slotIndex.ts +0 -56
  141. package/src/db/flatFileStore/snappy.ts +0 -14
@@ -134,8 +134,6 @@ export async function archiveBlocks(
134
134
  logger.verbose("Migrated blobSidecars from hot DB to cold DB", {...logCtx, migratedEntries});
135
135
  }
136
136
 
137
- // Keep the normal LevelDB hot-to-cold flow for legacy columns. New columns are written
138
- // directly to flat files, so this only moves fallback data left by older versions.
139
137
  if (finalizedPostFulu) {
140
138
  const migratedSlots = await migrateDataColumnSidecarsFromHotToColdDb(
141
139
  config,
@@ -144,7 +142,7 @@ export async function archiveBlocks(
144
142
  finalizedCanonicalBlocks,
145
143
  currentEpoch
146
144
  );
147
- logger.verbose("Migrated legacy dataColumnSidecars from hot DB to cold DB", {
145
+ logger.verbose("Migrated dataColumnSidecars from hot DB to cold DB", {
148
146
  ...logCtx,
149
147
  migratedEntries: migratedSlots.length,
150
148
  slotRange: prettyPrintIndices(migratedSlots),
@@ -197,23 +195,6 @@ export async function archiveBlocks(
197
195
  slotRange: prettyPrintIndices(nonCanonicalSlots),
198
196
  };
199
197
 
200
- const columnItems = finalizedNonCanonicalBlocks
201
- // Gloas EMPTY and FULL variants share a block root. EMPTY has no sidecars, so deleting by its root could
202
- // remove the canonical FULL variant's columns. Pre-Gloas blocks are always FULL.
203
- .filter(
204
- (summary) => config.getForkSeq(summary.slot) >= ForkSeq.fulu && summary.payloadStatus === PayloadStatus.FULL
205
- )
206
- .map((summary) => ({slot: summary.slot, blockRoot: summary.blockRoot}));
207
- if (columnItems.length > 0) {
208
- // Delete sidecars first so their block roots remain available to retry cleanup after a failure or crash.
209
- await db.dataColumns.deleteMany(columnItems);
210
- logger.verbose("Deleted non canonical data columns of blocks", {
211
- ...logCtx,
212
- blocks: columnItems.length,
213
- slotRange: prettyPrintIndices(columnItems.map(({slot}) => slot).sort((a, b) => a - b)),
214
- });
215
- }
216
-
217
198
  await db.block.batchDelete(nonCanonicalBlockRoots);
218
199
  logger.verbose("Deleted non canonical blocks from hot DB", nonCanonicalLogCtx);
219
200
 
@@ -222,6 +203,11 @@ export async function archiveBlocks(
222
203
  logger.verbose("Deleted non canonical blobSidecars from hot DB", nonCanonicalLogCtx);
223
204
  }
224
205
 
206
+ if (finalizedPostFulu) {
207
+ await db.dataColumnSidecar.deleteMany(nonCanonicalBlockRoots);
208
+ logger.verbose("Deleted non canonical dataColumnSidecars from hot DB", nonCanonicalLogCtx);
209
+ }
210
+
225
211
  if (finalizedPostGloas) {
226
212
  await db.executionPayloadEnvelope.batchDelete(nonCanonicalBlockRoots);
227
213
  logger.verbose("Deleted non canonical executionPayloadEnvelopes from hot DB", nonCanonicalLogCtx);
@@ -259,16 +245,23 @@ export async function archiveBlocks(
259
245
  );
260
246
  const dataColumnSidecarsMinEpoch = currentEpoch - dataColumnSidecarsArchiveWindow;
261
247
  if (dataColumnSidecarsMinEpoch >= config.FULU_FORK_EPOCH) {
262
- const columnsPruneSlot = computeStartSlotAtEpoch(dataColumnSidecarsMinEpoch);
263
- const prunedColumnSlots = await db.dataColumns.pruneBefore(columnsPruneSlot);
264
- if (prunedColumnSlots.length > 0) {
248
+ const prefixedKeys = await db.dataColumnSidecarArchive.keys({
249
+ // The `id` value `0` refers to the column index. So we want to fetch all sidecars less than zero column of `dataColumnSidecarsMinEpoch`
250
+ lt: {prefix: computeStartSlotAtEpoch(dataColumnSidecarsMinEpoch), id: 0},
251
+ });
252
+ // for each slot there could be multiple dataColumnSidecar, so we need to deduplicate it
253
+ const slotsToDelete = [...new Set(prefixedKeys.map(({prefix}) => prefix))].sort((a, b) => a - b);
254
+
255
+ if (slotsToDelete.length > 0) {
256
+ await db.dataColumnSidecarArchive.deleteMany(slotsToDelete);
265
257
  logger.verbose("dataColumnSidecars prune", {
266
258
  ...logCtx,
267
- slotRange: prettyPrintIndices(prunedColumnSlots),
268
- numOfSlots: prunedColumnSlots.length,
259
+ slotRange: prettyPrintIndices(slotsToDelete),
260
+ numOfSlots: slotsToDelete.length,
261
+ totalNumOfSidecars: prefixedKeys.length,
269
262
  });
270
263
  } else {
271
- logger.verbose("dataColumnSidecars prune: no entries before slot", {...logCtx, slot: columnsPruneSlot});
264
+ logger.verbose(`dataColumnSidecars prune: no entries before epoch ${dataColumnSidecarsMinEpoch}`, logCtx);
272
265
  }
273
266
  } else {
274
267
  logger.verbose(
@@ -396,6 +389,7 @@ async function migrateBlobSidecarsFromHotToColdDb(
396
389
  )
397
390
  ).filter((e): e is KeyValue<Slot, Uint8Array> => e !== null);
398
391
 
392
+ // put to blockArchive db and delete block db
399
393
  await Promise.all([
400
394
  db.blobSidecarsArchive.batchPutBinary(canonicalBlobSidecarsEntries),
401
395
  db.blobSidecars.batchDelete(canonicalBlocks.map((block) => block.root)),
@@ -408,9 +402,11 @@ async function migrateBlobSidecarsFromHotToColdDb(
408
402
 
409
403
  // TODO: This function can be simplified further by reducing layers of promises in a loop
410
404
  /**
411
- * Post-gloas the data columns of a Gloas block are tied to its execution payload envelope.
412
- * Columns only exist once the FULL variant of the block is in the proto-array. Pre-Gloas
413
- * blocks only have a FULL variant, so the payload-status filter passes them all.
405
+ * Post-gloas the data columns of a Gloas block are tied to its execution payload envelope —
406
+ * columns only exist once the FULL variant of the block is in the proto-array. Pre-Gloas (Fulu)
407
+ * blocks only have a FULL variant, so the `payloadStatus === FULL` filter passes them all.
408
+ * Blocks whose canonical variant is PENDING/EMPTY are skipped here — their columns will be picked
409
+ * up on a later run once the FULL variant appears in the ancestor walk.
414
410
  */
415
411
  async function migrateDataColumnSidecarsFromHotToColdDb(
416
412
  config: ChainForkConfig,
@@ -432,24 +428,32 @@ async function migrateDataColumnSidecarsFromHotToColdDb(
432
428
  if (batch.length === 0) break;
433
429
 
434
430
  const promises: Promise<void>[] = [];
431
+
432
+ // load Buffer instead of ssz deserialized to improve performance
435
433
  for (const block of batch) {
436
- const blockEpoch = computeEpochAtSlot(block.slot);
434
+ const blockSlot = block.slot;
435
+ const blockEpoch = computeEpochAtSlot(blockSlot);
436
+
437
437
  if (
438
- config.getForkSeq(block.slot) < ForkSeq.fulu ||
438
+ config.getForkSeq(blockSlot) < ForkSeq.fulu ||
439
+ // if block is out of ${config.MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS}, skip this step
439
440
  blockEpoch < currentEpoch - config.MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS
440
441
  ) {
441
442
  continue;
442
443
  }
443
444
 
445
+ // Here we assume the data column sidecars are already in the hot db
444
446
  const dataColumnSidecarBytes = await fromAsync(db.dataColumnSidecar.valuesStreamBinary(block.root));
445
447
  if (dataColumnSidecarBytes.length === 0) {
448
+ // Empty stream: either the block has no blobs, or columns were already archived on a
449
+ // previous run (boundary block). Nothing to migrate.
446
450
  logger.debug("DataColumnSidecars in forkchoice but missing in hot db, could be already archived", {
447
451
  slot: block.slot,
448
452
  root: toRootHex(block.root),
449
453
  });
450
454
  continue;
451
455
  }
452
- logger.verbose("Migrated legacy dataColumnSidecars for block", {
456
+ logger.verbose("Migrated dataColumnSidecars for block", {
453
457
  currentEpoch,
454
458
  slot: block.slot,
455
459
  root: toRootHex(block.root),
@@ -458,14 +462,15 @@ async function migrateDataColumnSidecarsFromHotToColdDb(
458
462
  promises.push(
459
463
  db.dataColumnSidecarArchive.putManyBinary(
460
464
  block.slot,
461
- dataColumnSidecarBytes.map((entry) => ({key: entry.id, value: entry.value}))
465
+ dataColumnSidecarBytes.map((p) => ({key: p.id, value: p.value}))
462
466
  )
463
467
  );
464
468
  migratedSlots.push(block.slot);
465
469
  }
466
470
 
471
+ promises.push(db.dataColumnSidecar.deleteMany(batch.map((block) => block.root)));
472
+
467
473
  await Promise.all(promises);
468
- await db.dataColumnSidecar.deleteMany(batch.map((block) => block.root));
469
474
  }
470
475
 
471
476
  // Ancestor walk is newest to oldest; sort ascending so `prettyPrintIndices` renders cleanly.
@@ -1,6 +1,6 @@
1
- import {ForkPostDeneb, ForkPostFulu, isForkPostDeneb} from "@lodestar/params";
1
+ import {ForkPostDeneb, isForkPostDeneb} from "@lodestar/params";
2
2
  import {SignedBeaconBlock} from "@lodestar/types";
3
- import {toRootHex} from "@lodestar/utils";
3
+ import {fromHex, toRootHex} from "@lodestar/utils";
4
4
  import {getBlobKzgCommitments} from "../../util/dataColumns.js";
5
5
  import {BeaconChain} from "../chain.js";
6
6
  import {IBlockInput, IDataColumnsInput, isBlockInputBlobs, isBlockInputColumns} from "./blockInput/index.js";
@@ -76,6 +76,7 @@ async function writeBlockAndBlobsToDb(this: BeaconChain, blockInput: IBlockInput
76
76
  */
77
77
  export async function writeDataColumnsToDb(this: BeaconChain, blockInput: IDataColumnsInput): Promise<void> {
78
78
  const {slot, blockRootHex} = blockInput;
79
+ const blockRoot = fromHex(blockRootHex);
79
80
 
80
81
  if (!blockInput.hasComputedAllData()) {
81
82
  // Supernodes may only have a subset of the data columns by the time the block begins to be imported
@@ -88,19 +89,25 @@ export async function writeDataColumnsToDb(this: BeaconChain, blockInput: IDataC
88
89
 
89
90
  const {custodyColumns} = this.custodyConfig;
90
91
  const dataColumnSidecars = blockInput.getCustodyColumns();
91
- const dataColumnSidecarType = this.config.getForkTypes<ForkPostFulu>(slot).DataColumnSidecar;
92
92
 
93
- const binaryColumns: {index: number; data: Uint8Array}[] = [];
93
+ const binaryPuts: {key: number; value: Uint8Array}[] = [];
94
+ const nonbinaryPuts = [];
94
95
  for (const dataColumnSidecar of dataColumnSidecars) {
96
+ // skip reserializing column if we already have it
95
97
  const serialized = this.serializedCache.get(dataColumnSidecar);
96
- binaryColumns.push({
97
- index: dataColumnSidecar.index,
98
- data: serialized ?? dataColumnSidecarType.serialize(dataColumnSidecar),
99
- });
98
+ if (serialized) {
99
+ binaryPuts.push({key: dataColumnSidecar.index, value: serialized});
100
+ } else {
101
+ nonbinaryPuts.push(dataColumnSidecar);
102
+ }
100
103
  }
101
- await this.db.dataColumns.putManyBinary({slot, blockRoot: blockRootHex}, binaryColumns);
102
104
 
103
- this.logger.debug("Persisted dataColumnSidecars", {
105
+ await Promise.all([
106
+ this.db.dataColumnSidecar.putManyBinary(blockRoot, binaryPuts),
107
+ this.db.dataColumnSidecar.putMany(blockRoot, nonbinaryPuts),
108
+ ]);
109
+
110
+ this.logger.debug("Persisted dataColumnSidecars to hot DB", {
104
111
  slot,
105
112
  root: blockRootHex,
106
113
  dataColumnSidecars: dataColumnSidecars.length,
@@ -268,7 +268,6 @@ export class BeaconChain implements IBeaconChain {
268
268
  db,
269
269
  dbName,
270
270
  dataDir,
271
- dataColumnDir,
272
271
  logger,
273
272
  processShutdownCallback,
274
273
  clock,
@@ -286,7 +285,6 @@ export class BeaconChain implements IBeaconChain {
286
285
  db: IBeaconDb;
287
286
  dbName: string;
288
287
  dataDir: string;
289
- dataColumnDir?: string;
290
288
  logger: Logger;
291
289
  processShutdownCallback: ProcessShutdownCallback;
292
290
  /** Used for testing to supply fake clock */
@@ -507,12 +505,7 @@ export class BeaconChain implements IBeaconChain {
507
505
 
508
506
  this.archiveStore = new ArchiveStore(
509
507
  {db, chain: this, logger: logger as LoggerNode, metrics},
510
- {
511
- ...opts,
512
- dbName,
513
- dataColumnDir: dataColumnDir ?? path.join(dataDir, "data_columns"),
514
- anchorState: {finalizedCheckpoint: anchorState.finalizedCheckpoint},
515
- },
508
+ {...opts, dbName, anchorState: {finalizedCheckpoint: anchorState.finalizedCheckpoint}},
516
509
  signal
517
510
  );
518
511
 
@@ -976,15 +969,12 @@ export class BeaconChain implements IBeaconChain {
976
969
 
977
970
  async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<DataColumnSidecar[]> {
978
971
  const fork = this.config.getForkName(blockSlot);
979
- const sidecarsByIndex = new Map<number, DataColumnSidecar>();
980
972
 
981
973
  if (isForkPostGloas(fork)) {
982
974
  // After gloas, columns are tracked in PayloadEnvelopeInput
983
975
  const payloadInput = this.seenPayloadEnvelopeInputCache.get(blockRootHex);
984
976
  if (payloadInput) {
985
- for (const sidecar of payloadInput.getAllColumns()) {
986
- sidecarsByIndex.set(sidecar.index, sidecar);
987
- }
977
+ return payloadInput.getAllColumns();
988
978
  }
989
979
  } else {
990
980
  // Before gloas, columns are tracked in BlockInput
@@ -993,17 +983,16 @@ export class BeaconChain implements IBeaconChain {
993
983
  if (!isBlockInputColumns(blockInput)) {
994
984
  throw new Error(`Expected block input to have columns: slot=${blockSlot} root=${blockRootHex}`);
995
985
  }
996
- for (const sidecar of blockInput.getAllColumns()) {
997
- sidecarsByIndex.set(sidecar.index, sidecar);
998
- }
986
+ return blockInput.getAllColumns();
999
987
  }
1000
988
  }
1001
989
 
1002
- for (const sidecar of await this.db.dataColumns.getAll({slot: blockSlot, blockRoot: blockRootHex})) {
1003
- if (!sidecarsByIndex.has(sidecar.index)) sidecarsByIndex.set(sidecar.index, sidecar);
990
+ const sidecarsUnfinalized = await this.db.dataColumnSidecar.values(fromHex(blockRootHex));
991
+ if (sidecarsUnfinalized.length > 0) {
992
+ return sidecarsUnfinalized;
1004
993
  }
1005
-
1006
- return [...sidecarsByIndex.values()].sort((a, b) => a.index - b.index);
994
+ const sidecarsFinalized = await this.db.dataColumnSidecarArchive.values(blockSlot);
995
+ return sidecarsFinalized;
1007
996
  }
1008
997
 
1009
998
  async getSerializedDataColumnSidecars(
@@ -1012,25 +1001,22 @@ export class BeaconChain implements IBeaconChain {
1012
1001
  indices: number[]
1013
1002
  ): Promise<(Uint8Array | undefined)[]> {
1014
1003
  const fork = this.config.getForkName(blockSlot);
1015
- const dataColumnSidecars: (Uint8Array | undefined)[] = indices.map(() => undefined);
1016
1004
 
1017
1005
  if (isForkPostGloas(fork)) {
1018
1006
  // After gloas, columns are tracked in PayloadEnvelopeInput
1019
1007
  const payloadInput = this.seenPayloadEnvelopeInputCache.get(blockRootHex);
1020
1008
  if (payloadInput) {
1021
- for (let i = 0; i < indices.length; i++) {
1022
- const index = indices[i];
1009
+ return indices.map((index) => {
1023
1010
  const sidecar = payloadInput.getColumn(index);
1024
1011
  if (!sidecar) {
1025
- continue;
1012
+ return undefined;
1026
1013
  }
1027
1014
  const serialized = this.serializedCache.get(sidecar);
1028
1015
  if (serialized) {
1029
- dataColumnSidecars[i] = serialized;
1030
- } else {
1031
- dataColumnSidecars[i] = sszTypesFor(fork as ForkPostGloas).DataColumnSidecar.serialize(sidecar);
1016
+ return serialized;
1032
1017
  }
1033
- }
1018
+ return sszTypesFor(fork as ForkPostGloas).DataColumnSidecar.serialize(sidecar);
1019
+ });
1034
1020
  }
1035
1021
  } else {
1036
1022
  // Before gloas, columns are tracked in BlockInput
@@ -1039,42 +1025,26 @@ export class BeaconChain implements IBeaconChain {
1039
1025
  if (!isBlockInputColumns(blockInput)) {
1040
1026
  throw new Error(`Expected block input to have columns: slot=${blockSlot} root=${blockRootHex}`);
1041
1027
  }
1042
- for (let i = 0; i < indices.length; i++) {
1043
- const index = indices[i];
1028
+ return indices.map((index) => {
1044
1029
  const sidecar = blockInput.getColumn(index);
1045
1030
  if (!sidecar) {
1046
- continue;
1031
+ return undefined;
1047
1032
  }
1048
1033
  const serialized = this.serializedCache.get(sidecar);
1049
1034
  if (serialized) {
1050
- dataColumnSidecars[i] = serialized;
1051
- } else {
1052
- dataColumnSidecars[i] = sszTypesFor(blockInput.forkName as ForkPostFulu).DataColumnSidecar.serialize(
1053
- sidecar
1054
- );
1035
+ return serialized;
1055
1036
  }
1056
- }
1037
+ return sszTypesFor(blockInput.forkName as ForkPostFulu).DataColumnSidecar.serialize(sidecar);
1038
+ });
1057
1039
  }
1058
1040
  }
1059
1041
 
1060
- const missingPositions = dataColumnSidecars
1061
- .map((sidecar, position) => (sidecar === undefined ? position : -1))
1062
- .filter((position) => position !== -1);
1063
-
1064
- if (missingPositions.length > 0) {
1065
- const persistedSidecars = await this.db.dataColumns.getManyBinary(
1066
- {slot: blockSlot, blockRoot: blockRootHex},
1067
- missingPositions.map((position) => indices[position])
1068
- );
1069
- for (let i = 0; i < missingPositions.length; i++) {
1070
- const sidecar = persistedSidecars[i];
1071
- if (sidecar !== undefined) {
1072
- dataColumnSidecars[missingPositions[i]] = sidecar;
1073
- }
1074
- }
1042
+ const sidecarsUnfinalized = await this.db.dataColumnSidecar.getManyBinary(fromHex(blockRootHex), indices);
1043
+ if (sidecarsUnfinalized.some((sidecar) => sidecar != null)) {
1044
+ return sidecarsUnfinalized;
1075
1045
  }
1076
-
1077
- return dataColumnSidecars;
1046
+ const sidecarsFinalized = await this.db.dataColumnSidecarArchive.getManyBinary(blockSlot, indices);
1047
+ return sidecarsFinalized;
1078
1048
  }
1079
1049
 
1080
1050
  async produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody> {
package/src/db/beacon.ts CHANGED
@@ -1,11 +1,6 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
2
  import {Db, LevelDbControllerMetrics, encodeKey} from "@lodestar/db";
3
- import {Slot} from "@lodestar/types";
4
- import {Logger} from "@lodestar/utils";
5
3
  import {Bucket} from "./buckets.js";
6
- import {type IDataColumnStore, LegacyDataColumnStore} from "./dataColumnStore.js";
7
- import {FlatFileStore} from "./flatFileStore/flatFileStore.js";
8
- import type {FlatFileStoreMetrics} from "./flatFileStore/metrics.js";
9
4
  import {IBeaconDb} from "./interface.js";
10
5
  import {CheckpointStateRepository} from "./repositories/checkpointState.js";
11
6
  import {
@@ -29,10 +24,9 @@ import {
29
24
  VoluntaryExitRepository,
30
25
  } from "./repositories/index.js";
31
26
 
32
- export type BeaconDbOpts = {
33
- dataColumnDir: string;
34
- logger: Logger;
35
- metrics?: FlatFileStoreMetrics | null;
27
+ export type BeaconDbModules = {
28
+ config: ChainForkConfig;
29
+ db: Db;
36
30
  };
37
31
 
38
32
  export class BeaconDb implements IBeaconDb {
@@ -63,14 +57,9 @@ export class BeaconDb implements IBeaconDb {
63
57
 
64
58
  backfilledRanges: BackfilledRanges;
65
59
 
66
- readonly dataColumns: IDataColumnStore;
67
- lastLegacyArchiveSlot: Slot | null = null;
68
- private readonly flatFileStore: FlatFileStore;
69
-
70
60
  constructor(
71
61
  config: ChainForkConfig,
72
- protected readonly db: Db,
73
- opts: BeaconDbOpts
62
+ protected readonly db: Db
74
63
  ) {
75
64
  // Warning: If code is ever run in the constructor, must change this stub to not extend 'packages/beacon-node/test/utils/stub/beaconDb.ts' -
76
65
  this.block = new BlockRepository(config, db);
@@ -98,30 +87,14 @@ export class BeaconDb implements IBeaconDb {
98
87
  this.syncCommitteeWitness = new SyncCommitteeWitnessRepository(config, db);
99
88
 
100
89
  this.backfilledRanges = new BackfilledRanges(config, db);
101
-
102
- this.flatFileStore = new FlatFileStore(opts.dataColumnDir, config, opts.logger, opts.metrics);
103
- this.dataColumns = new LegacyDataColumnStore(
104
- this.flatFileStore,
105
- this.dataColumnSidecar,
106
- this.dataColumnSidecarArchive,
107
- this.blockArchive
108
- );
109
- }
110
-
111
- async init(): Promise<void> {
112
- await this.flatFileStore.init();
113
- const [lastLegacyKey] = await this.dataColumnSidecarArchive.keys({reverse: true, limit: 1});
114
- this.lastLegacyArchiveSlot = lastLegacyKey?.prefix ?? null;
115
90
  }
116
91
 
117
- async close(): Promise<void> {
118
- await this.flatFileStore.close();
92
+ close(): Promise<void> {
119
93
  return this.db.close();
120
94
  }
121
95
 
122
- setMetrics(metrics: LevelDbControllerMetrics, flatFileStoreMetrics: FlatFileStoreMetrics | null = null): void {
96
+ setMetrics(metrics: LevelDbControllerMetrics): void {
123
97
  this.db.setMetrics(metrics);
124
- this.flatFileStore.setMetrics(flatFileStoreMetrics);
125
98
  }
126
99
 
127
100
  async pruneHotDb(): Promise<void> {
package/src/db/index.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export {BeaconDb} from "./beacon.js";
2
2
  export {Bucket} from "./buckets.js";
3
- export type {DataColumnKey, IDataColumnStore, IndexedDataColumnBytes} from "./dataColumnStore.js";
4
3
  export type {IBeaconDb} from "./interface.js";
@@ -1,7 +1,4 @@
1
1
  import {LevelDbControllerMetrics} from "@lodestar/db";
2
- import {Slot} from "@lodestar/types";
3
- import type {IDataColumnStore} from "./dataColumnStore.js";
4
- import type {FlatFileStoreMetrics} from "./flatFileStore/metrics.js";
5
2
  import {CheckpointStateRepository} from "./repositories/checkpointState.js";
6
3
  import {
7
4
  AttesterSlashingRepository,
@@ -62,11 +59,6 @@ export interface IBeaconDb {
62
59
 
63
60
  backfilledRanges: BackfilledRanges;
64
61
 
65
- dataColumns: IDataColumnStore;
66
- /** Highest legacy column slot at startup; later archival writes the block-root index first. */
67
- readonly lastLegacyArchiveSlot: Slot | null;
68
- init(): Promise<void>;
69
-
70
62
  pruneHotDb(): Promise<void>;
71
63
 
72
64
  deleteDeprecatedEth1Data(): Promise<void>;
@@ -74,5 +66,5 @@ export interface IBeaconDb {
74
66
  /** Close the connection to the db instance and close the db store. */
75
67
  close(): Promise<void>;
76
68
  /** To inject metrics after CLI initialization */
77
- setMetrics(metrics: LevelDbControllerMetrics, flatFileStoreMetrics?: FlatFileStoreMetrics | null): void;
69
+ setMetrics(metrics: LevelDbControllerMetrics): void;
78
70
  }
@@ -1,17 +1,16 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {Db, DbBatch, FilterOptions, KeyValue, Repository} from "@lodestar/db";
2
+ import {Db, FilterOptions, KeyValue, Repository} from "@lodestar/db";
3
3
  import {Root, SignedBeaconBlock, Slot, ssz} from "@lodestar/types";
4
- import {bytesToInt, intToBytes} from "@lodestar/utils";
4
+ import {bytesToInt} from "@lodestar/utils";
5
5
  import {getSignedBlockTypeFromBytes} from "../../util/multifork.js";
6
6
  import {Bucket, getBucketNameByValue} from "../buckets.js";
7
7
  import {
8
8
  deleteParentRootIndex,
9
9
  deleteRootIndex,
10
10
  getParentRootIndex,
11
- getParentRootIndexKey,
12
11
  getRootIndex,
13
- getRootIndexKey,
14
- getSlotIndexKey,
12
+ storeParentRootIndex,
13
+ storeRootIndex,
15
14
  } from "./blockArchiveIndex.js";
16
15
 
17
16
  export interface BlockFilterOptions extends FilterOptions<Slot> {
@@ -57,91 +56,37 @@ export class BlockArchiveRepository extends Repository<Slot, SignedBeaconBlock>
57
56
  // Overrides to index
58
57
 
59
58
  async put(key: Slot, value: SignedBeaconBlock): Promise<void> {
60
- await this.batchPut([{key, value}]);
61
- }
62
-
63
- async putBinary(key: Slot, value: Uint8Array): Promise<void> {
64
- const block = this.decodeValue(value);
65
- await this.batchPutBinary([
66
- {
67
- key,
68
- value,
69
- slot: block.message.slot,
70
- blockRoot: this.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message),
71
- parentRoot: block.message.parentRoot,
72
- },
59
+ const blockRoot = this.config.getForkTypes(value.message.slot).BeaconBlock.hashTreeRoot(value.message);
60
+ const slot = value.message.slot;
61
+ await Promise.all([
62
+ super.put(key, value),
63
+ storeRootIndex(this.db, slot, blockRoot),
64
+ storeParentRootIndex(this.db, slot, value.message.parentRoot),
73
65
  ]);
74
66
  }
75
67
 
76
68
  async batchPut(items: KeyValue<Slot, SignedBeaconBlock>[]): Promise<void> {
77
- await this.batchPutBinary(
78
- items.map(({key, value}) => ({
79
- key,
80
- value: this.encodeValue(value),
81
- slot: value.message.slot,
82
- blockRoot: this.config.getForkTypes(value.message.slot).BeaconBlock.hashTreeRoot(value.message),
83
- parentRoot: value.message.parentRoot,
84
- }))
85
- );
69
+ await Promise.all([
70
+ super.batchPut(items),
71
+ Array.from(items).map((item) => {
72
+ const slot = item.value.message.slot;
73
+ const blockRoot = this.config.getForkTypes(slot).BeaconBlock.hashTreeRoot(item.value.message);
74
+ return storeRootIndex(this.db, slot, blockRoot);
75
+ }),
76
+ Array.from(items).map((item) => {
77
+ const slot = item.value.message.slot;
78
+ const parentRoot = item.value.message.parentRoot;
79
+ return storeParentRootIndex(this.db, slot, parentRoot);
80
+ }),
81
+ ]);
86
82
  }
87
83
 
88
84
  async batchPutBinary(items: BlockArchiveBatchPutBinaryItem[]): Promise<void> {
89
- await this.db.batchPut(
90
- items.flatMap((item) => this.indexedBlockEntries(item)),
91
- this.dbReqOpts
92
- );
93
- }
94
-
95
- async delete(slot: Slot): Promise<void> {
96
- await this.batchDelete([slot]);
97
- }
98
-
99
- async batchDelete(slots: Slot[]): Promise<void> {
100
- await this.db.batchDelete(
101
- slots.flatMap((slot) => [this.encodeKey(slot), getSlotIndexKey(slot)]),
102
- this.dbReqOpts
103
- );
104
- }
105
-
106
- async batch(operations: DbBatch<Slot, SignedBeaconBlock>): Promise<void> {
107
- await this.batchBinary(
108
- operations.map((operation) =>
109
- operation.type === "put" ? {...operation, value: this.encodeValue(operation.value)} : operation
110
- )
111
- );
112
- }
113
-
114
- async batchBinary(operations: DbBatch<Slot, Uint8Array>): Promise<void> {
115
- const batch: DbBatch<Uint8Array, Uint8Array> = [];
116
- for (const operation of operations) {
117
- if (operation.type === "del") {
118
- batch.push(
119
- {type: "del", key: this.encodeKey(operation.key)},
120
- {type: "del", key: getSlotIndexKey(operation.key)}
121
- );
122
- } else {
123
- const block = this.decodeValue(operation.value);
124
- const entries = this.indexedBlockEntries({
125
- key: operation.key,
126
- value: operation.value,
127
- slot: block.message.slot,
128
- blockRoot: this.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message),
129
- parentRoot: block.message.parentRoot,
130
- });
131
- batch.push(...entries.map((entry) => ({type: "put" as const, ...entry})));
132
- }
133
- }
134
- await this.db.batch(batch, this.dbReqOpts);
135
- }
136
-
137
- private indexedBlockEntries(item: BlockArchiveBatchPutBinaryItem): KeyValue<Uint8Array, Uint8Array>[] {
138
- const slotBytes = intToBytes(item.slot, 8, "be");
139
- return [
140
- {key: this.encodeKey(item.key), value: item.value},
141
- {key: getRootIndexKey(item.blockRoot), value: slotBytes},
142
- {key: getParentRootIndexKey(item.parentRoot), value: slotBytes},
143
- {key: getSlotIndexKey(item.key), value: item.blockRoot},
144
- ];
85
+ await Promise.all([
86
+ super.batchPutBinary(items),
87
+ Array.from(items).map((item) => storeRootIndex(this.db, item.slot, item.blockRoot)),
88
+ Array.from(items).map((item) => storeParentRootIndex(this.db, item.slot, item.parentRoot)),
89
+ ]);
145
90
  }
146
91
 
147
92
  async remove(value: SignedBeaconBlock): Promise<void> {
@@ -155,10 +100,10 @@ export class BlockArchiveRepository extends Repository<Slot, SignedBeaconBlock>
155
100
  async batchRemove(values: SignedBeaconBlock[]): Promise<void> {
156
101
  await Promise.all([
157
102
  super.batchRemove(values),
158
- ...values.map((value) =>
103
+ Array.from(values).map((value) =>
159
104
  deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).SignedBeaconBlock, value)
160
105
  ),
161
- ...values.map((value) => deleteParentRootIndex(this.db, value)),
106
+ Array.from(values).map((value) => deleteParentRootIndex(this.db, value)),
162
107
  ]);
163
108
  }
164
109
 
@@ -195,10 +140,6 @@ export class BlockArchiveRepository extends Repository<Slot, SignedBeaconBlock>
195
140
  return slot !== null ? this.get(slot) : null;
196
141
  }
197
142
 
198
- async getRootBySlot(slot: Slot): Promise<Root | null> {
199
- return this.db.get(getSlotIndexKey(slot), {bucketId: getBucketNameByValue(Bucket.index_mainChain)});
200
- }
201
-
202
143
  async getSlotByRoot(root: Root): Promise<Slot | null> {
203
144
  return this.parseSlot(await getRootIndex(this.db, root));
204
145
  }
@@ -1,6 +1,7 @@
1
1
  import {Db, encodeKey} from "@lodestar/db";
2
2
  import {ForkAll} from "@lodestar/params";
3
3
  import {Root, SSZTypesFor, SignedBeaconBlock, Slot, ssz} from "@lodestar/types";
4
+ import {intToBytes} from "@lodestar/utils";
4
5
  import {Bucket, getBucketNameByValue} from "../buckets.js";
5
6
 
6
7
  export const rootIndexBucketId = getBucketNameByValue(Bucket.index_blockArchiveRootIndex);
@@ -14,6 +15,14 @@ export async function getParentRootIndex(db: Db, parentRoot: Root): Promise<Uint
14
15
  return db.get(getParentRootIndexKey(parentRoot), {bucketId: parentRootIndexBucketId});
15
16
  }
16
17
 
18
+ export async function storeRootIndex(db: Db, slot: Slot, blockRoot: Root): Promise<void> {
19
+ return db.put(getRootIndexKey(blockRoot), intToBytes(slot, 8, "be"), {bucketId: rootIndexBucketId});
20
+ }
21
+
22
+ export async function storeParentRootIndex(db: Db, slot: Slot, parentRoot: Root): Promise<void> {
23
+ return db.put(getParentRootIndexKey(parentRoot), intToBytes(slot, 8, "be"), {bucketId: parentRootIndexBucketId});
24
+ }
25
+
17
26
  export async function deleteRootIndex(
18
27
  db: Db,
19
28
  signedBeaconBlockType: SSZTypesFor<ForkAll, "SignedBeaconBlock">,
@@ -34,7 +43,3 @@ export function getParentRootIndexKey(parentRoot: Root): Uint8Array {
34
43
  export function getRootIndexKey(root: Root): Uint8Array {
35
44
  return encodeKey(Bucket.index_blockArchiveRootIndex, root);
36
45
  }
37
-
38
- export function getSlotIndexKey(slot: Slot): Uint8Array {
39
- return encodeKey(Bucket.index_mainChain, slot);
40
- }