@aztec/archiver 0.0.1-commit.d431d1c → 0.0.1-commit.db765a8

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 (95) hide show
  1. package/README.md +9 -0
  2. package/dest/archiver.d.ts +10 -6
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +50 -111
  5. package/dest/errors.d.ts +6 -1
  6. package/dest/errors.d.ts.map +1 -1
  7. package/dest/errors.js +8 -0
  8. package/dest/factory.d.ts +5 -2
  9. package/dest/factory.d.ts.map +1 -1
  10. package/dest/factory.js +16 -13
  11. package/dest/index.d.ts +2 -1
  12. package/dest/index.d.ts.map +1 -1
  13. package/dest/index.js +1 -0
  14. package/dest/l1/bin/retrieve-calldata.js +35 -32
  15. package/dest/l1/calldata_retriever.d.ts +73 -50
  16. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  17. package/dest/l1/calldata_retriever.js +190 -259
  18. package/dest/l1/data_retrieval.d.ts +9 -9
  19. package/dest/l1/data_retrieval.d.ts.map +1 -1
  20. package/dest/l1/data_retrieval.js +24 -22
  21. package/dest/l1/spire_proposer.d.ts +5 -5
  22. package/dest/l1/spire_proposer.d.ts.map +1 -1
  23. package/dest/l1/spire_proposer.js +9 -17
  24. package/dest/l1/validate_trace.d.ts +6 -3
  25. package/dest/l1/validate_trace.d.ts.map +1 -1
  26. package/dest/l1/validate_trace.js +13 -9
  27. package/dest/modules/data_source_base.d.ts +23 -19
  28. package/dest/modules/data_source_base.d.ts.map +1 -1
  29. package/dest/modules/data_source_base.js +44 -119
  30. package/dest/modules/data_store_updater.d.ts +31 -20
  31. package/dest/modules/data_store_updater.d.ts.map +1 -1
  32. package/dest/modules/data_store_updater.js +79 -60
  33. package/dest/modules/instrumentation.d.ts +17 -4
  34. package/dest/modules/instrumentation.d.ts.map +1 -1
  35. package/dest/modules/instrumentation.js +36 -12
  36. package/dest/modules/l1_synchronizer.d.ts +4 -8
  37. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  38. package/dest/modules/l1_synchronizer.js +23 -19
  39. package/dest/store/block_store.d.ts +50 -32
  40. package/dest/store/block_store.d.ts.map +1 -1
  41. package/dest/store/block_store.js +147 -54
  42. package/dest/store/contract_class_store.d.ts +1 -1
  43. package/dest/store/contract_class_store.d.ts.map +1 -1
  44. package/dest/store/contract_class_store.js +11 -7
  45. package/dest/store/kv_archiver_store.d.ts +43 -25
  46. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  47. package/dest/store/kv_archiver_store.js +38 -17
  48. package/dest/store/l2_tips_cache.d.ts +19 -0
  49. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  50. package/dest/store/l2_tips_cache.js +89 -0
  51. package/dest/store/log_store.d.ts +4 -4
  52. package/dest/store/log_store.d.ts.map +1 -1
  53. package/dest/store/log_store.js +57 -37
  54. package/dest/test/fake_l1_state.d.ts +9 -4
  55. package/dest/test/fake_l1_state.d.ts.map +1 -1
  56. package/dest/test/fake_l1_state.js +56 -18
  57. package/dest/test/index.js +3 -1
  58. package/dest/test/mock_archiver.d.ts +1 -1
  59. package/dest/test/mock_archiver.d.ts.map +1 -1
  60. package/dest/test/mock_archiver.js +3 -2
  61. package/dest/test/mock_l2_block_source.d.ts +36 -21
  62. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  63. package/dest/test/mock_l2_block_source.js +151 -109
  64. package/dest/test/mock_structs.d.ts +3 -2
  65. package/dest/test/mock_structs.d.ts.map +1 -1
  66. package/dest/test/mock_structs.js +11 -9
  67. package/dest/test/noop_l1_archiver.d.ts +23 -0
  68. package/dest/test/noop_l1_archiver.d.ts.map +1 -0
  69. package/dest/test/noop_l1_archiver.js +68 -0
  70. package/package.json +14 -13
  71. package/src/archiver.ts +71 -136
  72. package/src/errors.ts +12 -0
  73. package/src/factory.ts +30 -14
  74. package/src/index.ts +1 -0
  75. package/src/l1/README.md +25 -68
  76. package/src/l1/bin/retrieve-calldata.ts +45 -33
  77. package/src/l1/calldata_retriever.ts +249 -379
  78. package/src/l1/data_retrieval.ts +27 -29
  79. package/src/l1/spire_proposer.ts +7 -15
  80. package/src/l1/validate_trace.ts +24 -6
  81. package/src/modules/data_source_base.ts +73 -163
  82. package/src/modules/data_store_updater.ts +92 -63
  83. package/src/modules/instrumentation.ts +46 -14
  84. package/src/modules/l1_synchronizer.ts +26 -24
  85. package/src/store/block_store.ts +188 -92
  86. package/src/store/contract_class_store.ts +11 -7
  87. package/src/store/kv_archiver_store.ts +69 -29
  88. package/src/store/l2_tips_cache.ts +89 -0
  89. package/src/store/log_store.ts +105 -43
  90. package/src/test/fake_l1_state.ts +77 -19
  91. package/src/test/index.ts +3 -0
  92. package/src/test/mock_archiver.ts +3 -2
  93. package/src/test/mock_l2_block_source.ts +196 -126
  94. package/src/test/mock_structs.ts +26 -10
  95. package/src/test/noop_l1_archiver.ts +109 -0
@@ -9,16 +9,18 @@ import { isDefined } from '@aztec/foundation/types';
9
9
  import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton, Range } from '@aztec/kv-store';
10
10
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
11
11
  import {
12
+ type BlockData,
13
+ BlockHash,
12
14
  Body,
13
15
  CheckpointedL2Block,
14
16
  CommitteeAttestation,
15
- L2BlockHash,
16
- L2BlockNew,
17
+ L2Block,
17
18
  type ValidateCheckpointResult,
18
19
  deserializeValidateCheckpointResult,
19
20
  serializeValidateCheckpointResult,
20
21
  } from '@aztec/stdlib/block';
21
- import { L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
22
+ import { type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
23
+ import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
22
24
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
23
25
  import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
24
26
  import {
@@ -27,6 +29,7 @@ import {
27
29
  TxEffect,
28
30
  TxHash,
29
31
  TxReceipt,
32
+ TxStatus,
30
33
  deserializeIndexedTxEffect,
31
34
  serializeIndexedTxEffect,
32
35
  } from '@aztec/stdlib/tx';
@@ -36,6 +39,7 @@ import {
36
39
  BlockIndexNotSequentialError,
37
40
  BlockNotFoundError,
38
41
  BlockNumberNotSequentialError,
42
+ CannotOverwriteCheckpointedBlockError,
39
43
  CheckpointNotFoundError,
40
44
  CheckpointNumberNotConsistentError,
41
45
  CheckpointNumberNotSequentialError,
@@ -58,22 +62,15 @@ type BlockStorage = {
58
62
  type CheckpointStorage = {
59
63
  header: Buffer;
60
64
  archive: Buffer;
65
+ checkpointOutHash: Buffer;
61
66
  checkpointNumber: number;
62
67
  startBlock: number;
63
- numBlocks: number;
68
+ blockCount: number;
64
69
  l1: Buffer;
65
70
  attestations: Buffer[];
66
71
  };
67
72
 
68
- export type CheckpointData = {
69
- checkpointNumber: CheckpointNumber;
70
- header: CheckpointHeader;
71
- archive: AppendOnlyTreeSnapshot;
72
- startBlock: number;
73
- numBlocks: number;
74
- l1: L1PublishedData;
75
- attestations: Buffer[];
76
- };
73
+ export type RemoveCheckpointsResult = { blocksRemoved: L2Block[] | undefined };
77
74
 
78
75
  /**
79
76
  * LMDB-based block storage for the archiver.
@@ -85,6 +82,9 @@ export class BlockStore {
85
82
  /** Map checkpoint number to checkpoint data */
86
83
  #checkpoints: AztecAsyncMap<number, CheckpointStorage>;
87
84
 
85
+ /** Map slot number to checkpoint number, for looking up checkpoints by slot range. */
86
+ #slotToCheckpoint: AztecAsyncMap<number, number>;
87
+
88
88
  /** Map block hash to list of tx hashes */
89
89
  #blockTxs: AztecAsyncMap<string, Buffer>;
90
90
 
@@ -111,7 +111,10 @@ export class BlockStore {
111
111
 
112
112
  #log = createLogger('archiver:block_store');
113
113
 
114
- constructor(private db: AztecAsyncKVStore) {
114
+ constructor(
115
+ private db: AztecAsyncKVStore,
116
+ private l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
117
+ ) {
115
118
  this.#blocks = db.openMap('archiver_blocks');
116
119
  this.#blockTxs = db.openMap('archiver_block_txs');
117
120
  this.#txEffects = db.openMap('archiver_tx_effects');
@@ -122,14 +125,29 @@ export class BlockStore {
122
125
  this.#lastProvenCheckpoint = db.openSingleton('archiver_last_proven_l2_checkpoint');
123
126
  this.#pendingChainValidationStatus = db.openSingleton('archiver_pending_chain_validation_status');
124
127
  this.#checkpoints = db.openMap('archiver_checkpoints');
128
+ this.#slotToCheckpoint = db.openMap('archiver_slot_to_checkpoint');
125
129
  }
126
130
 
127
131
  /**
128
- * Append new blocks to the store's list. All blocks must be for the 'current' checkpoint
129
- * @param blocks - The L2 blocks to be added to the store.
132
+ * Computes the finalized block number based on the proven block number.
133
+ * A block is considered finalized when it's 2 epochs behind the proven block.
134
+ * TODO(#13569): Compute proper finalized block number based on L1 finalized block.
135
+ * TODO(palla/mbps): Even the provisional computation is wrong, since it should subtract checkpoints, not blocks
136
+ * @returns The finalized block number.
137
+ */
138
+ async getFinalizedL2BlockNumber(): Promise<BlockNumber> {
139
+ const provenBlockNumber = await this.getProvenBlockNumber();
140
+ return BlockNumber(Math.max(provenBlockNumber - this.l1Constants.epochDuration * 2, 0));
141
+ }
142
+
143
+ /**
144
+ * Append new proposed blocks to the store's list. All blocks must be for the 'current' checkpoint.
145
+ * These are uncheckpointed blocks that have been proposed by the sequencer but not yet included in a checkpoint on L1.
146
+ * For checkpointed blocks (already published to L1), use addCheckpoints() instead.
147
+ * @param blocks - The proposed L2 blocks to be added to the store.
130
148
  * @returns True if the operation is successful.
131
149
  */
132
- async addBlocks(blocks: L2BlockNew[], opts: { force?: boolean } = {}): Promise<boolean> {
150
+ async addProposedBlocks(blocks: L2Block[], opts: { force?: boolean } = {}): Promise<boolean> {
133
151
  if (blocks.length === 0) {
134
152
  return true;
135
153
  }
@@ -145,6 +163,12 @@ export class BlockStore {
145
163
  const previousBlockNumber = await this.getLatestBlockNumber();
146
164
  const previousCheckpointNumber = await this.getLatestCheckpointNumber();
147
165
 
166
+ // Verify we're not overwriting checkpointed blocks
167
+ const lastCheckpointedBlockNumber = await this.getCheckpointedL2BlockNumber();
168
+ if (!opts.force && firstBlockNumber <= lastCheckpointedBlockNumber) {
169
+ throw new CannotOverwriteCheckpointedBlockError(firstBlockNumber, lastCheckpointedBlockNumber);
170
+ }
171
+
148
172
  // Check that the first block number is the expected one
149
173
  if (!opts.force && previousBlockNumber !== firstBlockNumber - 1) {
150
174
  throw new InitialBlockNumberNotSequentialError(firstBlockNumber, previousBlockNumber);
@@ -182,7 +206,7 @@ export class BlockStore {
182
206
  }
183
207
 
184
208
  // Iterate over blocks array and insert them, checking that the block numbers and indexes are sequential. Also check they are for the correct checkpoint.
185
- let previousBlock: L2BlockNew | undefined = undefined;
209
+ let previousBlock: L2Block | undefined = undefined;
186
210
  for (const block of blocks) {
187
211
  if (!opts.force && previousBlock) {
188
212
  if (previousBlock.number + 1 !== block.number) {
@@ -241,11 +265,11 @@ export class BlockStore {
241
265
  }
242
266
 
243
267
  let previousBlockNumber: BlockNumber | undefined = undefined;
244
- let previousBlock: L2BlockNew | undefined = undefined;
268
+ let previousBlock: L2Block | undefined = undefined;
245
269
 
246
270
  // If we have a previous checkpoint then we need to get the previous block number
247
271
  if (previousCheckpointData !== undefined) {
248
- previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.numBlocks - 1);
272
+ previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.blockCount - 1);
249
273
  previousBlock = await this.getBlock(previousBlockNumber);
250
274
  if (previousBlock === undefined) {
251
275
  // We should be able to get the required previous block
@@ -309,12 +333,16 @@ export class BlockStore {
309
333
  await this.#checkpoints.set(checkpoint.checkpoint.number, {
310
334
  header: checkpoint.checkpoint.header.toBuffer(),
311
335
  archive: checkpoint.checkpoint.archive.toBuffer(),
336
+ checkpointOutHash: checkpoint.checkpoint.getCheckpointOutHash().toBuffer(),
312
337
  l1: checkpoint.l1.toBuffer(),
313
338
  attestations: checkpoint.attestations.map(attestation => attestation.toBuffer()),
314
339
  checkpointNumber: checkpoint.checkpoint.number,
315
340
  startBlock: checkpoint.checkpoint.blocks[0].number,
316
- numBlocks: checkpoint.checkpoint.blocks.length,
341
+ blockCount: checkpoint.checkpoint.blocks.length,
317
342
  });
343
+
344
+ // Update slot-to-checkpoint index
345
+ await this.#slotToCheckpoint.set(checkpoint.checkpoint.header.slotNumber, checkpoint.checkpoint.number);
318
346
  }
319
347
 
320
348
  await this.#lastSynchedL1Block.set(checkpoints[checkpoints.length - 1].l1.blockNumber);
@@ -322,8 +350,8 @@ export class BlockStore {
322
350
  });
323
351
  }
324
352
 
325
- private async addBlockToDatabase(block: L2BlockNew, checkpointNumber: number, indexWithinCheckpoint: number) {
326
- const blockHash = L2BlockHash.fromField(await block.hash());
353
+ private async addBlockToDatabase(block: L2Block, checkpointNumber: number, indexWithinCheckpoint: number) {
354
+ const blockHash = await block.hash();
327
355
 
328
356
  await this.#blocks.set(block.number, {
329
357
  header: block.header.toBuffer(),
@@ -351,7 +379,7 @@ export class BlockStore {
351
379
  }
352
380
 
353
381
  /** Deletes a block and all associated data (tx effects, indices). */
354
- private async deleteBlock(block: L2BlockNew): Promise<void> {
382
+ private async deleteBlock(block: L2Block): Promise<void> {
355
383
  // Delete the block from the main blocks map
356
384
  await this.#blocks.delete(block.number);
357
385
 
@@ -368,51 +396,53 @@ export class BlockStore {
368
396
  }
369
397
 
370
398
  /**
371
- * Unwinds checkpoints from the database
372
- * @param from - The tip of the chain, passed for verification purposes,
373
- * ensuring that we don't end up deleting something we did not intend
374
- * @param checkpointsToUnwind - The number of checkpoints we are to unwind
375
- * @returns True if the operation is successful
399
+ * Removes all checkpoints with checkpoint number > checkpointNumber.
400
+ * Also removes ALL blocks (both checkpointed and uncheckpointed) after the last block of the given checkpoint.
401
+ * @param checkpointNumber - Remove all checkpoints strictly after this one.
376
402
  */
377
- async unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number) {
403
+ async removeCheckpointsAfter(checkpointNumber: CheckpointNumber): Promise<RemoveCheckpointsResult> {
378
404
  return await this.db.transactionAsync(async () => {
379
- const last = await this.getLatestCheckpointNumber();
380
- if (from !== last) {
381
- throw new Error(`Can only unwind checkpoints from the tip (requested ${from} but current tip is ${last})`);
405
+ const latestCheckpointNumber = await this.getLatestCheckpointNumber();
406
+
407
+ if (checkpointNumber >= latestCheckpointNumber) {
408
+ this.#log.warn(`No checkpoints to remove after ${checkpointNumber} (latest is ${latestCheckpointNumber})`);
409
+ return { blocksRemoved: undefined };
382
410
  }
383
411
 
412
+ // If the proven checkpoint is beyond the target, update it
384
413
  const proven = await this.getProvenCheckpointNumber();
385
- if (from - checkpointsToUnwind < proven) {
386
- await this.setProvenCheckpointNumber(CheckpointNumber(from - checkpointsToUnwind));
414
+ if (proven > checkpointNumber) {
415
+ this.#log.warn(`Updating proven checkpoint ${proven} to last valid checkpoint ${checkpointNumber}`);
416
+ await this.setProvenCheckpointNumber(checkpointNumber);
387
417
  }
388
418
 
389
- for (let i = 0; i < checkpointsToUnwind; i++) {
390
- const checkpointNumber = from - i;
391
- const checkpoint = await this.#checkpoints.getAsync(checkpointNumber);
392
-
393
- if (checkpoint === undefined) {
394
- this.#log.warn(`Cannot remove checkpoint ${checkpointNumber} from the store since we don't have it`);
395
- continue;
419
+ // Find the last block number to keep (last block of the given checkpoint, or 0 if no checkpoint)
420
+ let lastBlockToKeep: BlockNumber;
421
+ if (checkpointNumber <= 0) {
422
+ lastBlockToKeep = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
423
+ } else {
424
+ const targetCheckpoint = await this.#checkpoints.getAsync(checkpointNumber);
425
+ if (!targetCheckpoint) {
426
+ throw new Error(`Target checkpoint ${checkpointNumber} not found in store`);
396
427
  }
397
- await this.#checkpoints.delete(checkpointNumber);
398
- const maxBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
399
-
400
- for (let blockNumber = checkpoint.startBlock; blockNumber <= maxBlock; blockNumber++) {
401
- const block = await this.getBlock(BlockNumber(blockNumber));
428
+ lastBlockToKeep = BlockNumber(targetCheckpoint.startBlock + targetCheckpoint.blockCount - 1);
429
+ }
402
430
 
403
- if (block === undefined) {
404
- this.#log.warn(`Cannot remove block ${blockNumber} from the store since we don't have it`);
405
- continue;
406
- }
431
+ // Remove all blocks after lastBlockToKeep (both checkpointed and uncheckpointed)
432
+ const blocksRemoved = await this.removeBlocksAfter(lastBlockToKeep);
407
433
 
408
- await this.deleteBlock(block);
409
- this.#log.debug(
410
- `Unwound block ${blockNumber} ${(await block.hash()).toString()} for checkpoint ${checkpointNumber}`,
411
- );
434
+ // Remove all checkpoints after the target
435
+ for (let c = latestCheckpointNumber; c > checkpointNumber; c = CheckpointNumber(c - 1)) {
436
+ const checkpointStorage = await this.#checkpoints.getAsync(c);
437
+ if (checkpointStorage) {
438
+ const slotNumber = CheckpointHeader.fromBuffer(checkpointStorage.header).slotNumber;
439
+ await this.#slotToCheckpoint.delete(slotNumber);
412
440
  }
441
+ await this.#checkpoints.delete(c);
442
+ this.#log.debug(`Removed checkpoint ${c}`);
413
443
  }
414
444
 
415
- return true;
445
+ return { blocksRemoved };
416
446
  });
417
447
  }
418
448
 
@@ -436,20 +466,35 @@ export class BlockStore {
436
466
  return checkpoints;
437
467
  }
438
468
 
439
- private checkpointDataFromCheckpointStorage(checkpointStorage: CheckpointStorage) {
440
- const data: CheckpointData = {
469
+ /** Returns checkpoint data for all checkpoints whose slot falls within the given range (inclusive). */
470
+ async getCheckpointDataForSlotRange(startSlot: SlotNumber, endSlot: SlotNumber): Promise<CheckpointData[]> {
471
+ const result: CheckpointData[] = [];
472
+ for await (const [, checkpointNumber] of this.#slotToCheckpoint.entriesAsync({
473
+ start: startSlot,
474
+ end: endSlot + 1,
475
+ })) {
476
+ const checkpointStorage = await this.#checkpoints.getAsync(checkpointNumber);
477
+ if (checkpointStorage) {
478
+ result.push(this.checkpointDataFromCheckpointStorage(checkpointStorage));
479
+ }
480
+ }
481
+ return result;
482
+ }
483
+
484
+ private checkpointDataFromCheckpointStorage(checkpointStorage: CheckpointStorage): CheckpointData {
485
+ return {
441
486
  header: CheckpointHeader.fromBuffer(checkpointStorage.header),
442
487
  archive: AppendOnlyTreeSnapshot.fromBuffer(checkpointStorage.archive),
488
+ checkpointOutHash: Fr.fromBuffer(checkpointStorage.checkpointOutHash),
443
489
  checkpointNumber: CheckpointNumber(checkpointStorage.checkpointNumber),
444
- startBlock: checkpointStorage.startBlock,
445
- numBlocks: checkpointStorage.numBlocks,
490
+ startBlock: BlockNumber(checkpointStorage.startBlock),
491
+ blockCount: checkpointStorage.blockCount,
446
492
  l1: L1PublishedData.fromBuffer(checkpointStorage.l1),
447
- attestations: checkpointStorage.attestations,
493
+ attestations: checkpointStorage.attestations.map(buf => CommitteeAttestation.fromBuffer(buf)),
448
494
  };
449
- return data;
450
495
  }
451
496
 
452
- async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2BlockNew[] | undefined> {
497
+ async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2Block[] | undefined> {
453
498
  const checkpoint = await this.#checkpoints.getAsync(checkpointNumber);
454
499
  if (!checkpoint) {
455
500
  return undefined;
@@ -458,7 +503,7 @@ export class BlockStore {
458
503
  const blocksForCheckpoint = await toArray(
459
504
  this.#blocks.entriesAsync({
460
505
  start: checkpoint.startBlock,
461
- end: checkpoint.startBlock + checkpoint.numBlocks,
506
+ end: checkpoint.startBlock + checkpoint.blockCount,
462
507
  }),
463
508
  );
464
509
 
@@ -472,8 +517,8 @@ export class BlockStore {
472
517
  * @param slotNumber - The slot number to search for.
473
518
  * @returns All blocks with the given slot number, in ascending block number order.
474
519
  */
475
- async getBlocksForSlot(slotNumber: SlotNumber): Promise<L2BlockNew[]> {
476
- const blocks: L2BlockNew[] = [];
520
+ async getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
521
+ const blocks: L2Block[] = [];
477
522
 
478
523
  // Iterate backwards through all blocks and filter by slot number
479
524
  // This is more efficient since we usually query for the most recent slot
@@ -493,12 +538,13 @@ export class BlockStore {
493
538
 
494
539
  /**
495
540
  * Removes all blocks with block number > blockNumber.
541
+ * Does not remove any associated checkpoints.
496
542
  * @param blockNumber - The block number to remove after.
497
543
  * @returns The removed blocks (for event emission).
498
544
  */
499
- async unwindBlocksAfter(blockNumber: BlockNumber): Promise<L2BlockNew[]> {
545
+ async removeBlocksAfter(blockNumber: BlockNumber): Promise<L2Block[]> {
500
546
  return await this.db.transactionAsync(async () => {
501
- const removedBlocks: L2BlockNew[] = [];
547
+ const removedBlocks: L2Block[] = [];
502
548
 
503
549
  // Get the latest block number to determine the range
504
550
  const latestBlockNumber = await this.getLatestBlockNumber();
@@ -530,7 +576,7 @@ export class BlockStore {
530
576
  if (!checkpointStorage) {
531
577
  throw new CheckpointNotFoundError(provenCheckpointNumber);
532
578
  } else {
533
- return BlockNumber(checkpointStorage.startBlock + checkpointStorage.numBlocks - 1);
579
+ return BlockNumber(checkpointStorage.startBlock + checkpointStorage.blockCount - 1);
534
580
  }
535
581
  }
536
582
 
@@ -598,7 +644,7 @@ export class BlockStore {
598
644
  }
599
645
  }
600
646
 
601
- async getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
647
+ async getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
602
648
  const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
603
649
  if (blockNumber === undefined) {
604
650
  return undefined;
@@ -620,7 +666,7 @@ export class BlockStore {
620
666
  * @param limit - The number of blocks to return.
621
667
  * @returns The requested L2 blocks
622
668
  */
623
- async *getBlocks(start: BlockNumber, limit: number): AsyncIterableIterator<L2BlockNew> {
669
+ async *getBlocks(start: BlockNumber, limit: number): AsyncIterableIterator<L2Block> {
624
670
  for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
625
671
  const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
626
672
  if (block) {
@@ -629,12 +675,38 @@ export class BlockStore {
629
675
  }
630
676
  }
631
677
 
678
+ /**
679
+ * Gets block metadata (without tx data) by block number.
680
+ * @param blockNumber - The number of the block to return.
681
+ * @returns The requested block data.
682
+ */
683
+ async getBlockData(blockNumber: BlockNumber): Promise<BlockData | undefined> {
684
+ const blockStorage = await this.#blocks.getAsync(blockNumber);
685
+ if (!blockStorage || !blockStorage.header) {
686
+ return undefined;
687
+ }
688
+ return this.getBlockDataFromBlockStorage(blockStorage);
689
+ }
690
+
691
+ /**
692
+ * Gets block metadata (without tx data) by archive root.
693
+ * @param archive - The archive root of the block to return.
694
+ * @returns The requested block data.
695
+ */
696
+ async getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
697
+ const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
698
+ if (blockNumber === undefined) {
699
+ return undefined;
700
+ }
701
+ return this.getBlockData(BlockNumber(blockNumber));
702
+ }
703
+
632
704
  /**
633
705
  * Gets an L2 block.
634
706
  * @param blockNumber - The number of the block to return.
635
707
  * @returns The requested L2 block.
636
708
  */
637
- async getBlock(blockNumber: BlockNumber): Promise<L2BlockNew | undefined> {
709
+ async getBlock(blockNumber: BlockNumber): Promise<L2Block | undefined> {
638
710
  const blockStorage = await this.#blocks.getAsync(blockNumber);
639
711
  if (!blockStorage || !blockStorage.header) {
640
712
  return Promise.resolve(undefined);
@@ -647,7 +719,7 @@ export class BlockStore {
647
719
  * @param blockHash - The hash of the block to return.
648
720
  * @returns The requested L2 block.
649
721
  */
650
- async getBlockByHash(blockHash: L2BlockHash): Promise<L2BlockNew | undefined> {
722
+ async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
651
723
  const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
652
724
  if (blockNumber === undefined) {
653
725
  return undefined;
@@ -660,7 +732,7 @@ export class BlockStore {
660
732
  * @param archive - The archive root of the block to return.
661
733
  * @returns The requested L2 block.
662
734
  */
663
- async getBlockByArchive(archive: Fr): Promise<L2BlockNew | undefined> {
735
+ async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
664
736
  const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
665
737
  if (blockNumber === undefined) {
666
738
  return undefined;
@@ -673,7 +745,7 @@ export class BlockStore {
673
745
  * @param blockHash - The hash of the block to return.
674
746
  * @returns The requested block header.
675
747
  */
676
- async getBlockHeaderByHash(blockHash: L2BlockHash): Promise<BlockHeader | undefined> {
748
+ async getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
677
749
  const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
678
750
  if (blockNumber === undefined) {
679
751
  return undefined;
@@ -733,15 +805,24 @@ export class BlockStore {
733
805
  }
734
806
  }
735
807
 
808
+ private getBlockDataFromBlockStorage(blockStorage: BlockStorage): BlockData {
809
+ return {
810
+ header: BlockHeader.fromBuffer(blockStorage.header),
811
+ archive: AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive),
812
+ blockHash: Fr.fromBuffer(blockStorage.blockHash),
813
+ checkpointNumber: CheckpointNumber(blockStorage.checkpointNumber),
814
+ indexWithinCheckpoint: IndexWithinCheckpoint(blockStorage.indexWithinCheckpoint),
815
+ };
816
+ }
817
+
736
818
  private async getBlockFromBlockStorage(
737
819
  blockNumber: number,
738
820
  blockStorage: BlockStorage,
739
- ): Promise<L2BlockNew | undefined> {
740
- const header = BlockHeader.fromBuffer(blockStorage.header);
741
- const archive = AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive);
742
- const blockHash = blockStorage.blockHash;
743
- header.setHash(Fr.fromBuffer(blockHash));
744
- const blockHashString = bufferToHex(blockHash);
821
+ ): Promise<L2Block | undefined> {
822
+ const { header, archive, blockHash, checkpointNumber, indexWithinCheckpoint } =
823
+ this.getBlockDataFromBlockStorage(blockStorage);
824
+ header.setHash(blockHash);
825
+ const blockHashString = bufferToHex(blockStorage.blockHash);
745
826
  const blockTxsBuffer = await this.#blockTxs.getAsync(blockHashString);
746
827
  if (blockTxsBuffer === undefined) {
747
828
  this.#log.warn(`Could not find body for block ${header.globalVariables.blockNumber} ${blockHash}`);
@@ -760,13 +841,7 @@ export class BlockStore {
760
841
  txEffects.push(deserializeIndexedTxEffect(txEffect).data);
761
842
  }
762
843
  const body = new Body(txEffects);
763
- const block = new L2BlockNew(
764
- archive,
765
- header,
766
- body,
767
- CheckpointNumber(blockStorage.checkpointNumber!),
768
- IndexWithinCheckpoint(blockStorage.indexWithinCheckpoint),
769
- );
844
+ const block = new L2Block(archive, header, body, checkpointNumber, indexWithinCheckpoint);
770
845
 
771
846
  if (block.number !== blockNumber) {
772
847
  throw new Error(
@@ -802,13 +877,34 @@ export class BlockStore {
802
877
  return undefined;
803
878
  }
804
879
 
880
+ const blockNumber = BlockNumber(txEffect.l2BlockNumber);
881
+
882
+ // Use existing archiver methods to determine finalization level
883
+ const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([
884
+ this.getProvenBlockNumber(),
885
+ this.getCheckpointedL2BlockNumber(),
886
+ this.getFinalizedL2BlockNumber(),
887
+ ]);
888
+
889
+ let status: TxStatus;
890
+ if (blockNumber <= finalizedBlockNumber) {
891
+ status = TxStatus.FINALIZED;
892
+ } else if (blockNumber <= provenBlockNumber) {
893
+ status = TxStatus.PROVEN;
894
+ } else if (blockNumber <= checkpointedBlockNumber) {
895
+ status = TxStatus.CHECKPOINTED;
896
+ } else {
897
+ status = TxStatus.PROPOSED;
898
+ }
899
+
805
900
  return new TxReceipt(
806
901
  txHash,
807
- TxReceipt.statusFromRevertCode(txEffect.data.revertCode),
808
- '',
902
+ status,
903
+ TxReceipt.executionResultFromRevertCode(txEffect.data.revertCode),
904
+ undefined,
809
905
  txEffect.data.transactionFee.toBigInt(),
810
906
  txEffect.l2BlockHash,
811
- BlockNumber(txEffect.l2BlockNumber),
907
+ blockNumber,
812
908
  );
813
909
  }
814
910
 
@@ -845,7 +941,7 @@ export class BlockStore {
845
941
  if (!checkpoint) {
846
942
  return BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
847
943
  }
848
- return BlockNumber(checkpoint.startBlock + checkpoint.numBlocks - 1);
944
+ return BlockNumber(checkpoint.startBlock + checkpoint.blockCount - 1);
849
945
  }
850
946
 
851
947
  async getLatestL2BlockNumber(): Promise<BlockNumber> {
@@ -28,18 +28,22 @@ export class ContractClassStore {
28
28
  bytecodeCommitment: Fr,
29
29
  blockNumber: number,
30
30
  ): Promise<void> {
31
- await this.#contractClasses.setIfNotExists(
32
- contractClass.id.toString(),
33
- serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
34
- );
35
- await this.#bytecodeCommitments.setIfNotExists(contractClass.id.toString(), bytecodeCommitment.toBuffer());
31
+ await this.db.transactionAsync(async () => {
32
+ await this.#contractClasses.setIfNotExists(
33
+ contractClass.id.toString(),
34
+ serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
35
+ );
36
+ await this.#bytecodeCommitments.setIfNotExists(contractClass.id.toString(), bytecodeCommitment.toBuffer());
37
+ });
36
38
  }
37
39
 
38
40
  async deleteContractClasses(contractClass: ContractClassPublic, blockNumber: number): Promise<void> {
39
41
  const restoredContractClass = await this.#contractClasses.getAsync(contractClass.id.toString());
40
42
  if (restoredContractClass && deserializeContractClassPublic(restoredContractClass).l2BlockNumber >= blockNumber) {
41
- await this.#contractClasses.delete(contractClass.id.toString());
42
- await this.#bytecodeCommitments.delete(contractClass.id.toString());
43
+ await this.db.transactionAsync(async () => {
44
+ await this.#contractClasses.delete(contractClass.id.toString());
45
+ await this.#bytecodeCommitments.delete(contractClass.id.toString());
46
+ });
43
47
  }
44
48
  }
45
49