@aztec/archiver 0.0.1-commit.7d4e6cd → 0.0.1-commit.87a0206

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 (191) hide show
  1. package/README.md +156 -22
  2. package/dest/archiver.d.ts +136 -0
  3. package/dest/archiver.d.ts.map +1 -0
  4. package/dest/archiver.js +781 -0
  5. package/dest/{archiver/config.d.ts → config.d.ts} +9 -1
  6. package/dest/config.d.ts.map +1 -0
  7. package/dest/{archiver/config.js → config.js} +9 -0
  8. package/dest/errors.d.ts +41 -0
  9. package/dest/errors.d.ts.map +1 -0
  10. package/dest/{archiver/errors.js → errors.js} +8 -0
  11. package/dest/factory.d.ts +9 -7
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +89 -11
  14. package/dest/index.d.ts +10 -4
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +8 -3
  17. package/dest/interfaces.d.ts +9 -0
  18. package/dest/interfaces.d.ts.map +1 -0
  19. package/dest/interfaces.js +3 -0
  20. package/dest/{archiver/l1 → l1}/bin/retrieve-calldata.d.ts +1 -1
  21. package/dest/l1/bin/retrieve-calldata.d.ts.map +1 -0
  22. package/dest/{archiver/l1 → l1}/bin/retrieve-calldata.js +17 -18
  23. package/dest/{archiver/l1 → l1}/calldata_retriever.d.ts +2 -2
  24. package/dest/l1/calldata_retriever.d.ts.map +1 -0
  25. package/dest/l1/data_retrieval.d.ts +88 -0
  26. package/dest/l1/data_retrieval.d.ts.map +1 -0
  27. package/dest/{archiver/l1 → l1}/data_retrieval.js +36 -55
  28. package/dest/{archiver/l1 → l1}/debug_tx.d.ts +1 -1
  29. package/dest/l1/debug_tx.d.ts.map +1 -0
  30. package/dest/{archiver/l1 → l1}/spire_proposer.d.ts +1 -1
  31. package/dest/l1/spire_proposer.d.ts.map +1 -0
  32. package/dest/{archiver/l1 → l1}/trace_tx.d.ts +1 -1
  33. package/dest/l1/trace_tx.d.ts.map +1 -0
  34. package/dest/l1/types.d.ts +12 -0
  35. package/dest/l1/types.d.ts.map +1 -0
  36. package/dest/{archiver/l1 → l1}/validate_trace.d.ts +6 -3
  37. package/dest/l1/validate_trace.d.ts.map +1 -0
  38. package/dest/{archiver/l1 → l1}/validate_trace.js +13 -9
  39. package/dest/modules/data_source_base.d.ts +84 -0
  40. package/dest/modules/data_source_base.d.ts.map +1 -0
  41. package/dest/modules/data_source_base.js +260 -0
  42. package/dest/modules/data_store_updater.d.ts +73 -0
  43. package/dest/modules/data_store_updater.d.ts.map +1 -0
  44. package/dest/modules/data_store_updater.js +302 -0
  45. package/dest/modules/instrumentation.d.ts +37 -0
  46. package/dest/modules/instrumentation.d.ts.map +1 -0
  47. package/dest/{archiver → modules}/instrumentation.js +17 -10
  48. package/dest/modules/l1_synchronizer.d.ts +75 -0
  49. package/dest/modules/l1_synchronizer.d.ts.map +1 -0
  50. package/dest/modules/l1_synchronizer.js +1112 -0
  51. package/dest/{archiver → modules}/validation.d.ts +1 -1
  52. package/dest/modules/validation.d.ts.map +1 -0
  53. package/dest/{archiver → modules}/validation.js +6 -0
  54. package/dest/store/block_store.d.ts +192 -0
  55. package/dest/store/block_store.d.ts.map +1 -0
  56. package/dest/{archiver/kv_archiver_store → store}/block_store.js +142 -47
  57. package/dest/store/contract_class_store.d.ts +18 -0
  58. package/dest/store/contract_class_store.d.ts.map +1 -0
  59. package/dest/{archiver/kv_archiver_store → store}/contract_class_store.js +12 -8
  60. package/dest/store/contract_instance_store.d.ts +24 -0
  61. package/dest/store/contract_instance_store.d.ts.map +1 -0
  62. package/dest/{archiver/kv_archiver_store → store}/contract_instance_store.js +1 -1
  63. package/dest/store/kv_archiver_store.d.ts +340 -0
  64. package/dest/store/kv_archiver_store.d.ts.map +1 -0
  65. package/dest/store/kv_archiver_store.js +446 -0
  66. package/dest/store/log_store.d.ts +54 -0
  67. package/dest/store/log_store.d.ts.map +1 -0
  68. package/dest/{archiver/kv_archiver_store → store}/log_store.js +146 -91
  69. package/dest/{archiver/kv_archiver_store → store}/message_store.d.ts +1 -1
  70. package/dest/store/message_store.d.ts.map +1 -0
  71. package/dest/{archiver/structs → structs}/data_retrieval.d.ts +1 -1
  72. package/dest/structs/data_retrieval.d.ts.map +1 -0
  73. package/dest/structs/inbox_message.d.ts +15 -0
  74. package/dest/structs/inbox_message.d.ts.map +1 -0
  75. package/dest/{archiver/structs → structs}/published.d.ts +1 -1
  76. package/dest/structs/published.d.ts.map +1 -0
  77. package/dest/test/fake_l1_state.d.ts +190 -0
  78. package/dest/test/fake_l1_state.d.ts.map +1 -0
  79. package/dest/test/fake_l1_state.js +383 -0
  80. package/dest/test/index.d.ts +2 -1
  81. package/dest/test/index.d.ts.map +1 -1
  82. package/dest/test/index.js +4 -1
  83. package/dest/test/mock_archiver.d.ts +2 -2
  84. package/dest/test/mock_archiver.d.ts.map +1 -1
  85. package/dest/test/mock_archiver.js +1 -2
  86. package/dest/test/mock_l2_block_source.d.ts +20 -17
  87. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  88. package/dest/test/mock_l2_block_source.js +108 -48
  89. package/dest/test/mock_structs.d.ts +78 -3
  90. package/dest/test/mock_structs.d.ts.map +1 -1
  91. package/dest/test/mock_structs.js +140 -7
  92. package/dest/test/noop_l1_archiver.d.ts +23 -0
  93. package/dest/test/noop_l1_archiver.d.ts.map +1 -0
  94. package/dest/test/noop_l1_archiver.js +68 -0
  95. package/package.json +16 -17
  96. package/src/archiver.ts +543 -0
  97. package/src/{archiver/config.ts → config.ts} +11 -0
  98. package/src/{archiver/errors.ts → errors.ts} +12 -0
  99. package/src/factory.ts +125 -11
  100. package/src/index.ts +10 -3
  101. package/src/interfaces.ts +9 -0
  102. package/src/{archiver/l1 → l1}/bin/retrieve-calldata.ts +16 -17
  103. package/src/{archiver/l1 → l1}/calldata_retriever.ts +1 -1
  104. package/src/{archiver/l1 → l1}/data_retrieval.ts +57 -74
  105. package/src/{archiver/l1 → l1}/validate_trace.ts +24 -6
  106. package/src/modules/data_source_base.ts +367 -0
  107. package/src/modules/data_store_updater.ts +423 -0
  108. package/src/{archiver → modules}/instrumentation.ts +17 -12
  109. package/src/modules/l1_synchronizer.ts +930 -0
  110. package/src/{archiver → modules}/validation.ts +5 -0
  111. package/src/{archiver/kv_archiver_store → store}/block_store.ts +179 -63
  112. package/src/{archiver/kv_archiver_store → store}/contract_class_store.ts +12 -8
  113. package/src/{archiver/kv_archiver_store → store}/contract_instance_store.ts +1 -1
  114. package/src/{archiver/kv_archiver_store → store}/kv_archiver_store.ts +234 -37
  115. package/src/{archiver/kv_archiver_store → store}/log_store.ts +242 -121
  116. package/src/test/fake_l1_state.ts +599 -0
  117. package/src/test/index.ts +4 -0
  118. package/src/test/mock_archiver.ts +2 -2
  119. package/src/test/mock_l2_block_source.ts +114 -66
  120. package/src/test/mock_structs.ts +269 -8
  121. package/src/test/noop_l1_archiver.ts +109 -0
  122. package/dest/archiver/archiver.d.ts +0 -307
  123. package/dest/archiver/archiver.d.ts.map +0 -1
  124. package/dest/archiver/archiver.js +0 -2102
  125. package/dest/archiver/archiver_store.d.ts +0 -315
  126. package/dest/archiver/archiver_store.d.ts.map +0 -1
  127. package/dest/archiver/archiver_store.js +0 -4
  128. package/dest/archiver/archiver_store_test_suite.d.ts +0 -8
  129. package/dest/archiver/archiver_store_test_suite.d.ts.map +0 -1
  130. package/dest/archiver/archiver_store_test_suite.js +0 -2770
  131. package/dest/archiver/config.d.ts.map +0 -1
  132. package/dest/archiver/errors.d.ts +0 -36
  133. package/dest/archiver/errors.d.ts.map +0 -1
  134. package/dest/archiver/index.d.ts +0 -7
  135. package/dest/archiver/index.d.ts.map +0 -1
  136. package/dest/archiver/index.js +0 -4
  137. package/dest/archiver/instrumentation.d.ts +0 -37
  138. package/dest/archiver/instrumentation.d.ts.map +0 -1
  139. package/dest/archiver/kv_archiver_store/block_store.d.ts +0 -164
  140. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +0 -1
  141. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +0 -18
  142. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +0 -1
  143. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +0 -24
  144. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +0 -1
  145. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +0 -159
  146. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +0 -1
  147. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +0 -316
  148. package/dest/archiver/kv_archiver_store/log_store.d.ts +0 -45
  149. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +0 -1
  150. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +0 -1
  151. package/dest/archiver/l1/bin/retrieve-calldata.d.ts.map +0 -1
  152. package/dest/archiver/l1/calldata_retriever.d.ts.map +0 -1
  153. package/dest/archiver/l1/data_retrieval.d.ts +0 -90
  154. package/dest/archiver/l1/data_retrieval.d.ts.map +0 -1
  155. package/dest/archiver/l1/debug_tx.d.ts.map +0 -1
  156. package/dest/archiver/l1/spire_proposer.d.ts.map +0 -1
  157. package/dest/archiver/l1/trace_tx.d.ts.map +0 -1
  158. package/dest/archiver/l1/types.d.ts +0 -12
  159. package/dest/archiver/l1/types.d.ts.map +0 -1
  160. package/dest/archiver/l1/validate_trace.d.ts.map +0 -1
  161. package/dest/archiver/structs/data_retrieval.d.ts.map +0 -1
  162. package/dest/archiver/structs/inbox_message.d.ts +0 -15
  163. package/dest/archiver/structs/inbox_message.d.ts.map +0 -1
  164. package/dest/archiver/structs/published.d.ts.map +0 -1
  165. package/dest/archiver/validation.d.ts.map +0 -1
  166. package/dest/rpc/index.d.ts +0 -9
  167. package/dest/rpc/index.d.ts.map +0 -1
  168. package/dest/rpc/index.js +0 -15
  169. package/src/archiver/archiver.ts +0 -2265
  170. package/src/archiver/archiver_store.ts +0 -380
  171. package/src/archiver/archiver_store_test_suite.ts +0 -2842
  172. package/src/archiver/index.ts +0 -6
  173. package/src/rpc/index.ts +0 -16
  174. /package/dest/{archiver/l1 → l1}/calldata_retriever.js +0 -0
  175. /package/dest/{archiver/l1 → l1}/debug_tx.js +0 -0
  176. /package/dest/{archiver/l1 → l1}/spire_proposer.js +0 -0
  177. /package/dest/{archiver/l1 → l1}/trace_tx.js +0 -0
  178. /package/dest/{archiver/l1 → l1}/types.js +0 -0
  179. /package/dest/{archiver/kv_archiver_store → store}/message_store.js +0 -0
  180. /package/dest/{archiver/structs → structs}/data_retrieval.js +0 -0
  181. /package/dest/{archiver/structs → structs}/inbox_message.js +0 -0
  182. /package/dest/{archiver/structs → structs}/published.js +0 -0
  183. /package/src/{archiver/l1 → l1}/README.md +0 -0
  184. /package/src/{archiver/l1 → l1}/debug_tx.ts +0 -0
  185. /package/src/{archiver/l1 → l1}/spire_proposer.ts +0 -0
  186. /package/src/{archiver/l1 → l1}/trace_tx.ts +0 -0
  187. /package/src/{archiver/l1 → l1}/types.ts +0 -0
  188. /package/src/{archiver/kv_archiver_store → store}/message_store.ts +0 -0
  189. /package/src/{archiver/structs → structs}/data_retrieval.ts +0 -0
  190. /package/src/{archiver/structs → structs}/inbox_message.ts +0 -0
  191. /package/src/{archiver/structs → structs}/published.ts +0 -0
@@ -61,6 +61,11 @@ export async function validateCheckpointAttestations(
61
61
  return { valid: true };
62
62
  }
63
63
 
64
+ if (await epochCache.isEscapeHatchOpen(epoch)) {
65
+ logger?.warn(`Escape hatch open for epoch ${epoch} at slot ${slot}, skipping checkpoint validation`);
66
+ return { valid: true };
67
+ }
68
+
64
69
  const requiredAttestationCount = Math.floor((committee.length * 2) / 3) + 1;
65
70
 
66
71
  const failedValidationResult = <TReason extends ValidateCheckpointNegativeResult['reason']>(reason: TReason) => ({
@@ -1,5 +1,5 @@
1
1
  import { INITIAL_CHECKPOINT_NUMBER, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
- import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
+ import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
3
3
  import { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { toArray } from '@aztec/foundation/iterable';
5
5
  import { createLogger } from '@aztec/foundation/log';
@@ -9,16 +9,17 @@ 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
+ BlockHash,
12
13
  Body,
13
14
  CheckpointedL2Block,
14
15
  CommitteeAttestation,
15
- L2BlockHash,
16
- L2BlockNew,
16
+ L2Block,
17
17
  type ValidateCheckpointResult,
18
18
  deserializeValidateCheckpointResult,
19
19
  serializeValidateCheckpointResult,
20
20
  } from '@aztec/stdlib/block';
21
21
  import { L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
22
+ import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
22
23
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
23
24
  import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
24
25
  import {
@@ -27,6 +28,7 @@ import {
27
28
  TxEffect,
28
29
  TxHash,
29
30
  TxReceipt,
31
+ TxStatus,
30
32
  deserializeIndexedTxEffect,
31
33
  serializeIndexedTxEffect,
32
34
  } from '@aztec/stdlib/tx';
@@ -36,6 +38,7 @@ import {
36
38
  BlockIndexNotSequentialError,
37
39
  BlockNotFoundError,
38
40
  BlockNumberNotSequentialError,
41
+ CannotOverwriteCheckpointedBlockError,
39
42
  CheckpointNotFoundError,
40
43
  CheckpointNumberNotConsistentError,
41
44
  CheckpointNumberNotSequentialError,
@@ -75,8 +78,10 @@ export type CheckpointData = {
75
78
  attestations: Buffer[];
76
79
  };
77
80
 
81
+ export type RemoveCheckpointsResult = { blocksRemoved: L2Block[] | undefined };
82
+
78
83
  /**
79
- * LMDB implementation of the ArchiverDataStore interface.
84
+ * LMDB-based block storage for the archiver.
80
85
  */
81
86
  export class BlockStore {
82
87
  /** Map block number to block data */
@@ -111,7 +116,10 @@ export class BlockStore {
111
116
 
112
117
  #log = createLogger('archiver:block_store');
113
118
 
114
- constructor(private db: AztecAsyncKVStore) {
119
+ constructor(
120
+ private db: AztecAsyncKVStore,
121
+ private l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
122
+ ) {
115
123
  this.#blocks = db.openMap('archiver_blocks');
116
124
  this.#blockTxs = db.openMap('archiver_block_txs');
117
125
  this.#txEffects = db.openMap('archiver_tx_effects');
@@ -125,11 +133,25 @@ export class BlockStore {
125
133
  }
126
134
 
127
135
  /**
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.
136
+ * Computes the finalized block number based on the proven block number.
137
+ * A block is considered finalized when it's 2 epochs behind the proven block.
138
+ * TODO(#13569): Compute proper finalized block number based on L1 finalized block.
139
+ * TODO(palla/mbps): Even the provisional computation is wrong, since it should subtract checkpoints, not blocks
140
+ * @returns The finalized block number.
141
+ */
142
+ async getFinalizedL2BlockNumber(): Promise<BlockNumber> {
143
+ const provenBlockNumber = await this.getProvenBlockNumber();
144
+ return BlockNumber(Math.max(provenBlockNumber - this.l1Constants.epochDuration * 2, 0));
145
+ }
146
+
147
+ /**
148
+ * Append new proposed blocks to the store's list. All blocks must be for the 'current' checkpoint.
149
+ * These are uncheckpointed blocks that have been proposed by the sequencer but not yet included in a checkpoint on L1.
150
+ * For checkpointed blocks (already published to L1), use addCheckpoints() instead.
151
+ * @param blocks - The proposed L2 blocks to be added to the store.
130
152
  * @returns True if the operation is successful.
131
153
  */
132
- async addBlocks(blocks: L2BlockNew[], opts: { force?: boolean } = {}): Promise<boolean> {
154
+ async addProposedBlocks(blocks: L2Block[], opts: { force?: boolean } = {}): Promise<boolean> {
133
155
  if (blocks.length === 0) {
134
156
  return true;
135
157
  }
@@ -145,6 +167,12 @@ export class BlockStore {
145
167
  const previousBlockNumber = await this.getLatestBlockNumber();
146
168
  const previousCheckpointNumber = await this.getLatestCheckpointNumber();
147
169
 
170
+ // Verify we're not overwriting checkpointed blocks
171
+ const lastCheckpointedBlockNumber = await this.getCheckpointedL2BlockNumber();
172
+ if (!opts.force && firstBlockNumber <= lastCheckpointedBlockNumber) {
173
+ throw new CannotOverwriteCheckpointedBlockError(firstBlockNumber, lastCheckpointedBlockNumber);
174
+ }
175
+
148
176
  // Check that the first block number is the expected one
149
177
  if (!opts.force && previousBlockNumber !== firstBlockNumber - 1) {
150
178
  throw new InitialBlockNumberNotSequentialError(firstBlockNumber, previousBlockNumber);
@@ -182,7 +210,7 @@ export class BlockStore {
182
210
  }
183
211
 
184
212
  // 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;
213
+ let previousBlock: L2Block | undefined = undefined;
186
214
  for (const block of blocks) {
187
215
  if (!opts.force && previousBlock) {
188
216
  if (previousBlock.number + 1 !== block.number) {
@@ -241,7 +269,7 @@ export class BlockStore {
241
269
  }
242
270
 
243
271
  let previousBlockNumber: BlockNumber | undefined = undefined;
244
- let previousBlock: L2BlockNew | undefined = undefined;
272
+ let previousBlock: L2Block | undefined = undefined;
245
273
 
246
274
  // If we have a previous checkpoint then we need to get the previous block number
247
275
  if (previousCheckpointData !== undefined) {
@@ -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(),
@@ -350,57 +378,66 @@ export class BlockStore {
350
378
  await this.#blockArchiveIndex.set(block.archive.root.toString(), block.number);
351
379
  }
352
380
 
381
+ /** Deletes a block and all associated data (tx effects, indices). */
382
+ private async deleteBlock(block: L2Block): Promise<void> {
383
+ // Delete the block from the main blocks map
384
+ await this.#blocks.delete(block.number);
385
+
386
+ // Delete all tx effects for this block
387
+ await Promise.all(block.body.txEffects.map(tx => this.#txEffects.delete(tx.txHash.toString())));
388
+
389
+ // Delete block txs mapping
390
+ const blockHash = (await block.hash()).toString();
391
+ await this.#blockTxs.delete(blockHash);
392
+
393
+ // Clean up indices
394
+ await this.#blockHashIndex.delete(blockHash);
395
+ await this.#blockArchiveIndex.delete(block.archive.root.toString());
396
+ }
397
+
353
398
  /**
354
- * Unwinds checkpoints from the database
355
- * @param from - The tip of the chain, passed for verification purposes,
356
- * ensuring that we don't end up deleting something we did not intend
357
- * @param checkpointsToUnwind - The number of checkpoints we are to unwind
358
- * @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.
359
402
  */
360
- async unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number) {
403
+ async removeCheckpointsAfter(checkpointNumber: CheckpointNumber): Promise<RemoveCheckpointsResult> {
361
404
  return await this.db.transactionAsync(async () => {
362
- const last = await this.getLatestCheckpointNumber();
363
- if (from !== last) {
364
- 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 };
365
410
  }
366
411
 
412
+ // If the proven checkpoint is beyond the target, update it
367
413
  const proven = await this.getProvenCheckpointNumber();
368
- if (from - checkpointsToUnwind < proven) {
369
- 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);
370
417
  }
371
418
 
372
- for (let i = 0; i < checkpointsToUnwind; i++) {
373
- const checkpointNumber = from - i;
374
- const checkpoint = await this.#checkpoints.getAsync(checkpointNumber);
375
-
376
- if (checkpoint === undefined) {
377
- this.#log.warn(`Cannot remove checkpoint ${checkpointNumber} from the store since we don't have it`);
378
- 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`);
379
427
  }
380
- await this.#checkpoints.delete(checkpointNumber);
381
- const maxBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
382
-
383
- for (let blockNumber = checkpoint.startBlock; blockNumber <= maxBlock; blockNumber++) {
384
- const block = await this.getBlock(BlockNumber(blockNumber));
385
-
386
- if (block === undefined) {
387
- this.#log.warn(`Cannot remove block ${blockNumber} from the store since we don't have it`);
388
- continue;
389
- }
390
- await this.#blocks.delete(block.number);
391
- await Promise.all(block.body.txEffects.map(tx => this.#txEffects.delete(tx.txHash.toString())));
392
- const blockHash = (await block.hash()).toString();
393
- await this.#blockTxs.delete(blockHash);
428
+ lastBlockToKeep = BlockNumber(targetCheckpoint.startBlock + targetCheckpoint.numBlocks - 1);
429
+ }
394
430
 
395
- // Clean up indices
396
- await this.#blockHashIndex.delete(blockHash);
397
- await this.#blockArchiveIndex.delete(block.archive.root.toString());
431
+ // Remove all blocks after lastBlockToKeep (both checkpointed and uncheckpointed)
432
+ const blocksRemoved = await this.removeBlocksAfter(lastBlockToKeep);
398
433
 
399
- this.#log.debug(`Unwound block ${blockNumber} ${blockHash} for checkpoint ${checkpointNumber}`);
400
- }
434
+ // Remove all checkpoints after the target
435
+ for (let c = latestCheckpointNumber; c > checkpointNumber; c = CheckpointNumber(c - 1)) {
436
+ await this.#checkpoints.delete(c);
437
+ this.#log.debug(`Removed checkpoint ${c}`);
401
438
  }
402
439
 
403
- return true;
440
+ return { blocksRemoved };
404
441
  });
405
442
  }
406
443
 
@@ -437,7 +474,7 @@ export class BlockStore {
437
474
  return data;
438
475
  }
439
476
 
440
- async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2BlockNew[] | undefined> {
477
+ async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2Block[] | undefined> {
441
478
  const checkpoint = await this.#checkpoints.getAsync(checkpointNumber);
442
479
  if (!checkpoint) {
443
480
  return undefined;
@@ -454,6 +491,62 @@ export class BlockStore {
454
491
  return converted.filter(isDefined);
455
492
  }
456
493
 
494
+ /**
495
+ * Gets all blocks that have the given slot number.
496
+ * Iterates backwards through blocks for efficiency since we usually query for the last slot.
497
+ * @param slotNumber - The slot number to search for.
498
+ * @returns All blocks with the given slot number, in ascending block number order.
499
+ */
500
+ async getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
501
+ const blocks: L2Block[] = [];
502
+
503
+ // Iterate backwards through all blocks and filter by slot number
504
+ // This is more efficient since we usually query for the most recent slot
505
+ for await (const [blockNumber, blockStorage] of this.#blocks.entriesAsync({ reverse: true })) {
506
+ const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
507
+ const blockSlot = block?.header.globalVariables.slotNumber;
508
+ if (block && blockSlot === slotNumber) {
509
+ blocks.push(block);
510
+ } else if (blockSlot && blockSlot < slotNumber) {
511
+ break; // Blocks are stored in slot ascending order, so we can stop searching
512
+ }
513
+ }
514
+
515
+ // Reverse to return blocks in ascending order (block number order)
516
+ return blocks.reverse();
517
+ }
518
+
519
+ /**
520
+ * Removes all blocks with block number > blockNumber.
521
+ * Does not remove any associated checkpoints.
522
+ * @param blockNumber - The block number to remove after.
523
+ * @returns The removed blocks (for event emission).
524
+ */
525
+ async removeBlocksAfter(blockNumber: BlockNumber): Promise<L2Block[]> {
526
+ return await this.db.transactionAsync(async () => {
527
+ const removedBlocks: L2Block[] = [];
528
+
529
+ // Get the latest block number to determine the range
530
+ const latestBlockNumber = await this.getLatestBlockNumber();
531
+
532
+ // Iterate from blockNumber + 1 to latestBlockNumber
533
+ for (let bn = blockNumber + 1; bn <= latestBlockNumber; bn++) {
534
+ const block = await this.getBlock(BlockNumber(bn));
535
+
536
+ if (block === undefined) {
537
+ this.#log.warn(`Cannot remove block ${bn} from the store since we don't have it`);
538
+ continue;
539
+ }
540
+
541
+ removedBlocks.push(block);
542
+ await this.deleteBlock(block);
543
+ this.#log.debug(`Removed block ${bn} ${(await block.hash()).toString()}`);
544
+ }
545
+
546
+ return removedBlocks;
547
+ });
548
+ }
549
+
457
550
  async getProvenBlockNumber(): Promise<BlockNumber> {
458
551
  const provenCheckpointNumber = await this.getProvenCheckpointNumber();
459
552
  if (provenCheckpointNumber === INITIAL_CHECKPOINT_NUMBER - 1) {
@@ -531,13 +624,14 @@ export class BlockStore {
531
624
  }
532
625
  }
533
626
 
534
- async getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
627
+ async getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
535
628
  const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
536
629
  if (blockNumber === undefined) {
537
630
  return undefined;
538
631
  }
539
632
  return this.getCheckpointedBlock(BlockNumber(blockNumber));
540
633
  }
634
+
541
635
  async getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
542
636
  const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
543
637
  if (blockNumber === undefined) {
@@ -552,7 +646,7 @@ export class BlockStore {
552
646
  * @param limit - The number of blocks to return.
553
647
  * @returns The requested L2 blocks
554
648
  */
555
- async *getBlocks(start: BlockNumber, limit: number): AsyncIterableIterator<L2BlockNew> {
649
+ async *getBlocks(start: BlockNumber, limit: number): AsyncIterableIterator<L2Block> {
556
650
  for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
557
651
  const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
558
652
  if (block) {
@@ -566,7 +660,7 @@ export class BlockStore {
566
660
  * @param blockNumber - The number of the block to return.
567
661
  * @returns The requested L2 block.
568
662
  */
569
- async getBlock(blockNumber: BlockNumber): Promise<L2BlockNew | undefined> {
663
+ async getBlock(blockNumber: BlockNumber): Promise<L2Block | undefined> {
570
664
  const blockStorage = await this.#blocks.getAsync(blockNumber);
571
665
  if (!blockStorage || !blockStorage.header) {
572
666
  return Promise.resolve(undefined);
@@ -579,7 +673,7 @@ export class BlockStore {
579
673
  * @param blockHash - The hash of the block to return.
580
674
  * @returns The requested L2 block.
581
675
  */
582
- async getBlockByHash(blockHash: L2BlockHash): Promise<L2BlockNew | undefined> {
676
+ async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
583
677
  const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
584
678
  if (blockNumber === undefined) {
585
679
  return undefined;
@@ -592,7 +686,7 @@ export class BlockStore {
592
686
  * @param archive - The archive root of the block to return.
593
687
  * @returns The requested L2 block.
594
688
  */
595
- async getBlockByArchive(archive: Fr): Promise<L2BlockNew | undefined> {
689
+ async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
596
690
  const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
597
691
  if (blockNumber === undefined) {
598
692
  return undefined;
@@ -605,7 +699,7 @@ export class BlockStore {
605
699
  * @param blockHash - The hash of the block to return.
606
700
  * @returns The requested block header.
607
701
  */
608
- async getBlockHeaderByHash(blockHash: L2BlockHash): Promise<BlockHeader | undefined> {
702
+ async getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
609
703
  const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
610
704
  if (blockNumber === undefined) {
611
705
  return undefined;
@@ -668,10 +762,11 @@ export class BlockStore {
668
762
  private async getBlockFromBlockStorage(
669
763
  blockNumber: number,
670
764
  blockStorage: BlockStorage,
671
- ): Promise<L2BlockNew | undefined> {
765
+ ): Promise<L2Block | undefined> {
672
766
  const header = BlockHeader.fromBuffer(blockStorage.header);
673
767
  const archive = AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive);
674
768
  const blockHash = blockStorage.blockHash;
769
+ header.setHash(Fr.fromBuffer(blockHash));
675
770
  const blockHashString = bufferToHex(blockHash);
676
771
  const blockTxsBuffer = await this.#blockTxs.getAsync(blockHashString);
677
772
  if (blockTxsBuffer === undefined) {
@@ -691,12 +786,12 @@ export class BlockStore {
691
786
  txEffects.push(deserializeIndexedTxEffect(txEffect).data);
692
787
  }
693
788
  const body = new Body(txEffects);
694
- const block = new L2BlockNew(
789
+ const block = new L2Block(
695
790
  archive,
696
791
  header,
697
792
  body,
698
793
  CheckpointNumber(blockStorage.checkpointNumber!),
699
- blockStorage.indexWithinCheckpoint,
794
+ IndexWithinCheckpoint(blockStorage.indexWithinCheckpoint),
700
795
  );
701
796
 
702
797
  if (block.number !== blockNumber) {
@@ -733,13 +828,34 @@ export class BlockStore {
733
828
  return undefined;
734
829
  }
735
830
 
831
+ const blockNumber = BlockNumber(txEffect.l2BlockNumber);
832
+
833
+ // Use existing archiver methods to determine finalization level
834
+ const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([
835
+ this.getProvenBlockNumber(),
836
+ this.getCheckpointedL2BlockNumber(),
837
+ this.getFinalizedL2BlockNumber(),
838
+ ]);
839
+
840
+ let status: TxStatus;
841
+ if (blockNumber <= finalizedBlockNumber) {
842
+ status = TxStatus.FINALIZED;
843
+ } else if (blockNumber <= provenBlockNumber) {
844
+ status = TxStatus.PROVEN;
845
+ } else if (blockNumber <= checkpointedBlockNumber) {
846
+ status = TxStatus.CHECKPOINTED;
847
+ } else {
848
+ status = TxStatus.PROPOSED;
849
+ }
850
+
736
851
  return new TxReceipt(
737
852
  txHash,
738
- TxReceipt.statusFromRevertCode(txEffect.data.revertCode),
739
- '',
853
+ status,
854
+ TxReceipt.executionResultFromRevertCode(txEffect.data.revertCode),
855
+ undefined,
740
856
  txEffect.data.transactionFee.toBigInt(),
741
857
  txEffect.l2BlockHash,
742
- BlockNumber(txEffect.l2BlockNumber),
858
+ blockNumber,
743
859
  );
744
860
  }
745
861
 
@@ -12,7 +12,7 @@ import type {
12
12
  import { Vector } from '@aztec/stdlib/types';
13
13
 
14
14
  /**
15
- * LMDB implementation of the ArchiverDataStore interface.
15
+ * LMDB-based contract class storage for the archiver.
16
16
  */
17
17
  export class ContractClassStore {
18
18
  #contractClasses: AztecAsyncMap<string, Buffer>;
@@ -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
 
@@ -12,7 +12,7 @@ import type { UInt64 } from '@aztec/stdlib/types';
12
12
  type ContractInstanceUpdateKey = [string, string] | [string, string, number];
13
13
 
14
14
  /**
15
- * LMDB implementation of the ArchiverDataStore interface.
15
+ * LMDB-based contract instance storage for the archiver.
16
16
  */
17
17
  export class ContractInstanceStore {
18
18
  #contractInstances: AztecAsyncMap<string, Buffer>;