@aztec/archiver 0.56.0 → 0.57.0

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 (68) hide show
  1. package/README.md +1 -1
  2. package/dest/archiver/archiver.d.ts +23 -20
  3. package/dest/archiver/archiver.d.ts.map +1 -1
  4. package/dest/archiver/archiver.js +353 -103
  5. package/dest/archiver/archiver_store.d.ts +39 -9
  6. package/dest/archiver/archiver_store.d.ts.map +1 -1
  7. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  8. package/dest/archiver/archiver_store_test_suite.js +75 -18
  9. package/dest/archiver/config.js +6 -6
  10. package/dest/archiver/data_retrieval.d.ts +2 -3
  11. package/dest/archiver/data_retrieval.d.ts.map +1 -1
  12. package/dest/archiver/data_retrieval.js +21 -20
  13. package/dest/archiver/epoch_helpers.d.ts +15 -0
  14. package/dest/archiver/epoch_helpers.d.ts.map +1 -0
  15. package/dest/archiver/epoch_helpers.js +23 -0
  16. package/dest/archiver/kv_archiver_store/block_store.d.ts +20 -1
  17. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  18. package/dest/archiver/kv_archiver_store/block_store.js +62 -5
  19. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +2 -1
  20. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  21. package/dest/archiver/kv_archiver_store/contract_class_store.js +11 -4
  22. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +1 -0
  23. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
  24. package/dest/archiver/kv_archiver_store/contract_instance_store.js +4 -1
  25. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +29 -9
  26. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  27. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +57 -17
  28. package/dest/archiver/kv_archiver_store/log_store.d.ts +4 -5
  29. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  30. package/dest/archiver/kv_archiver_store/log_store.js +18 -14
  31. package/dest/archiver/kv_archiver_store/message_store.d.ts +1 -0
  32. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  33. package/dest/archiver/kv_archiver_store/message_store.js +10 -3
  34. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +1 -0
  35. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +1 -1
  36. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +4 -1
  37. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +23 -22
  38. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
  39. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +129 -69
  40. package/dest/index.js +2 -1
  41. package/dest/test/index.d.ts +2 -0
  42. package/dest/test/index.d.ts.map +1 -0
  43. package/dest/test/index.js +2 -0
  44. package/dest/test/mock_l2_block_source.d.ts +73 -0
  45. package/dest/test/mock_l2_block_source.d.ts.map +1 -0
  46. package/dest/test/mock_l2_block_source.js +134 -0
  47. package/package.json +15 -11
  48. package/src/archiver/archiver.ts +457 -149
  49. package/src/archiver/archiver_store.ts +44 -16
  50. package/src/archiver/archiver_store_test_suite.ts +91 -52
  51. package/src/archiver/config.ts +5 -5
  52. package/src/archiver/data_retrieval.ts +23 -24
  53. package/src/archiver/epoch_helpers.ts +26 -0
  54. package/src/archiver/kv_archiver_store/block_store.ts +70 -2
  55. package/src/archiver/kv_archiver_store/contract_class_store.ts +18 -5
  56. package/src/archiver/kv_archiver_store/contract_instance_store.ts +4 -0
  57. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +65 -24
  58. package/src/archiver/kv_archiver_store/log_store.ts +18 -18
  59. package/src/archiver/kv_archiver_store/message_store.ts +9 -0
  60. package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +4 -0
  61. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +149 -80
  62. package/src/index.ts +1 -0
  63. package/src/test/index.ts +1 -0
  64. package/src/test/mock_l2_block_source.ts +165 -0
  65. package/dest/archiver/kv_archiver_store/proven_store.d.ts +0 -14
  66. package/dest/archiver/kv_archiver_store/proven_store.d.ts.map +0 -1
  67. package/dest/archiver/kv_archiver_store/proven_store.js +0 -30
  68. package/src/archiver/kv_archiver_store/proven_store.ts +0 -34
@@ -19,6 +19,10 @@ export class ContractInstanceStore {
19
19
  );
20
20
  }
21
21
 
22
+ deleteContractInstance(contractInstance: ContractInstanceWithAddress): Promise<void> {
23
+ return this.#contractInstances.delete(contractInstance.address.toString());
24
+ }
25
+
22
26
  getContractInstance(address: AztecAddress): ContractInstanceWithAddress | undefined {
23
27
  const contractInstance = this.#contractInstances.get(address.toString());
24
28
  return contractInstance && SerializableContractInstance.fromBuffer(contractInstance).withAddress(address);
@@ -1,6 +1,4 @@
1
1
  import {
2
- type EncryptedL2BlockL2Logs,
3
- type EncryptedNoteL2BlockL2Logs,
4
2
  type FromLogType,
5
3
  type GetUnencryptedLogsResponse,
6
4
  type InboxLeaf,
@@ -11,9 +9,8 @@ import {
11
9
  type TxEffect,
12
10
  type TxHash,
13
11
  type TxReceipt,
14
- type UnencryptedL2BlockL2Logs,
15
12
  } from '@aztec/circuit-types';
16
- import { type Fr } from '@aztec/circuits.js';
13
+ import { type Fr, type Header } from '@aztec/circuits.js';
17
14
  import { type ContractArtifact } from '@aztec/foundation/abi';
18
15
  import { type AztecAddress } from '@aztec/foundation/aztec-address';
19
16
  import { createDebugLogger } from '@aztec/foundation/log';
@@ -26,7 +23,7 @@ import {
26
23
  } from '@aztec/types/contracts';
27
24
 
28
25
  import { type ArchiverDataStore, type ArchiverL1SynchPoint } from '../archiver_store.js';
29
- import { type DataRetrieval, type SingletonDataRetrieval } from '../structs/data_retrieval.js';
26
+ import { type DataRetrieval } from '../structs/data_retrieval.js';
30
27
  import { type L1Published } from '../structs/published.js';
31
28
  import { BlockStore } from './block_store.js';
32
29
  import { ContractArtifactsStore } from './contract_artifacts_store.js';
@@ -34,14 +31,12 @@ import { ContractClassStore } from './contract_class_store.js';
34
31
  import { ContractInstanceStore } from './contract_instance_store.js';
35
32
  import { LogStore } from './log_store.js';
36
33
  import { MessageStore } from './message_store.js';
37
- import { ProvenStore } from './proven_store.js';
38
34
 
39
35
  /**
40
36
  * LMDB implementation of the ArchiverDataStore interface.
41
37
  */
42
38
  export class KVArchiverDataStore implements ArchiverDataStore {
43
39
  #blockStore: BlockStore;
44
- #provenStore: ProvenStore;
45
40
  #logStore: LogStore;
46
41
  #messageStore: MessageStore;
47
42
  #contractClassStore: ContractClassStore;
@@ -52,7 +47,6 @@ export class KVArchiverDataStore implements ArchiverDataStore {
52
47
 
53
48
  constructor(db: AztecKVStore, logsMaxPageSize: number = 1000) {
54
49
  this.#blockStore = new BlockStore(db);
55
- this.#provenStore = new ProvenStore(db);
56
50
  this.#logStore = new LogStore(db, this.#blockStore, logsMaxPageSize);
57
51
  this.#messageStore = new MessageStore(db);
58
52
  this.#contractClassStore = new ContractClassStore(db);
@@ -80,8 +74,14 @@ export class KVArchiverDataStore implements ArchiverDataStore {
80
74
  return Promise.resolve(this.#contractInstanceStore.getContractInstance(address));
81
75
  }
82
76
 
83
- async addContractClasses(data: ContractClassPublic[], _blockNumber: number): Promise<boolean> {
84
- return (await Promise.all(data.map(c => this.#contractClassStore.addContractClass(c)))).every(Boolean);
77
+ async addContractClasses(data: ContractClassPublic[], blockNumber: number): Promise<boolean> {
78
+ return (await Promise.all(data.map(c => this.#contractClassStore.addContractClass(c, blockNumber)))).every(Boolean);
79
+ }
80
+
81
+ async deleteContractClasses(data: ContractClassPublic[], blockNumber: number): Promise<boolean> {
82
+ return (await Promise.all(data.map(c => this.#contractClassStore.deleteContractClasses(c, blockNumber)))).every(
83
+ Boolean,
84
+ );
85
85
  }
86
86
 
87
87
  addFunctions(
@@ -96,6 +96,10 @@ export class KVArchiverDataStore implements ArchiverDataStore {
96
96
  return (await Promise.all(data.map(c => this.#contractInstanceStore.addContractInstance(c)))).every(Boolean);
97
97
  }
98
98
 
99
+ async deleteContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise<boolean> {
100
+ return (await Promise.all(data.map(c => this.#contractInstanceStore.deleteContractInstance(c)))).every(Boolean);
101
+ }
102
+
99
103
  /**
100
104
  * Append new blocks to the store's list.
101
105
  * @param blocks - The L2 blocks to be added to the store and the last processed L1 block.
@@ -105,6 +109,17 @@ export class KVArchiverDataStore implements ArchiverDataStore {
105
109
  return this.#blockStore.addBlocks(blocks);
106
110
  }
107
111
 
112
+ /**
113
+ * Unwinds blocks from the database
114
+ * @param from - The tip of the chain, passed for verification purposes,
115
+ * ensuring that we don't end up deleting something we did not intend
116
+ * @param blocksToUnwind - The number of blocks we are to unwind
117
+ * @returns True if the operation is successful
118
+ */
119
+ unwindBlocks(from: number, blocksToUnwind: number): Promise<boolean> {
120
+ return this.#blockStore.unwindBlocks(from, blocksToUnwind);
121
+ }
122
+
108
123
  /**
109
124
  * Gets up to `limit` amount of L2 blocks starting from `from`.
110
125
  *
@@ -121,6 +136,22 @@ export class KVArchiverDataStore implements ArchiverDataStore {
121
136
  }
122
137
  }
123
138
 
139
+ /**
140
+ * Gets up to `limit` amount of L2 blocks headers starting from `from`.
141
+ *
142
+ * @param start - Number of the first block to return (inclusive).
143
+ * @param limit - The number of blocks to return.
144
+ * @returns The requested L2 blocks
145
+ */
146
+ getBlockHeaders(start: number, limit: number): Promise<Header[]> {
147
+ try {
148
+ return Promise.resolve(Array.from(this.#blockStore.getBlockHeaders(start, limit)));
149
+ } catch (err) {
150
+ // this function is sync so if any errors are thrown we need to make sure they're passed on as rejected Promises
151
+ return Promise.reject(err);
152
+ }
153
+ }
154
+
124
155
  /**
125
156
  * Gets a tx effect.
126
157
  * @param txHash - The txHash of the tx corresponding to the tx effect.
@@ -141,18 +172,19 @@ export class KVArchiverDataStore implements ArchiverDataStore {
141
172
 
142
173
  /**
143
174
  * Append new logs to the store's list.
144
- * @param encryptedLogs - The logs to be added to the store.
145
- * @param unencryptedLogs - The type of the logs to be added to the store.
146
- * @param blockNumber - The block for which to add the logs.
175
+ * @param blocks - The blocks for which to add the logs.
147
176
  * @returns True if the operation is successful.
148
177
  */
149
- addLogs(
150
- noteEncryptedLogs: EncryptedNoteL2BlockL2Logs | undefined,
151
- encryptedLogs: EncryptedL2BlockL2Logs | undefined,
152
- unencryptedLogs: UnencryptedL2BlockL2Logs | undefined,
153
- blockNumber: number,
154
- ): Promise<boolean> {
155
- return this.#logStore.addLogs(noteEncryptedLogs, encryptedLogs, unencryptedLogs, blockNumber);
178
+ addLogs(blocks: L2Block[]): Promise<boolean> {
179
+ return this.#logStore.addLogs(blocks);
180
+ }
181
+
182
+ deleteLogs(blocks: L2Block[]): Promise<boolean> {
183
+ return this.#logStore.deleteLogs(blocks);
184
+ }
185
+
186
+ getTotalL1ToL2MessageCount(): Promise<bigint> {
187
+ return Promise.resolve(this.#messageStore.getTotalL1ToL2MessageCount());
156
188
  }
157
189
 
158
190
  /**
@@ -228,11 +260,21 @@ export class KVArchiverDataStore implements ArchiverDataStore {
228
260
  }
229
261
 
230
262
  getProvenL2BlockNumber(): Promise<number> {
231
- return Promise.resolve(this.#provenStore.getProvenL2BlockNumber());
263
+ return Promise.resolve(this.#blockStore.getProvenL2BlockNumber());
264
+ }
265
+
266
+ getProvenL2EpochNumber(): Promise<number | undefined> {
267
+ return Promise.resolve(this.#blockStore.getProvenL2EpochNumber());
232
268
  }
233
269
 
234
- async setProvenL2BlockNumber(blockNumber: SingletonDataRetrieval<number>) {
235
- await this.#provenStore.setProvenL2BlockNumber(blockNumber);
270
+ setProvenL2BlockNumber(blockNumber: number) {
271
+ this.#blockStore.setProvenL2BlockNumber(blockNumber);
272
+ return Promise.resolve();
273
+ }
274
+
275
+ setProvenL2EpochNumber(epochNumber: number) {
276
+ this.#blockStore.setProvenL2EpochNumber(epochNumber);
277
+ return Promise.resolve();
236
278
  }
237
279
 
238
280
  setBlockSynchedL1BlockNumber(l1BlockNumber: bigint) {
@@ -252,7 +294,6 @@ export class KVArchiverDataStore implements ArchiverDataStore {
252
294
  return Promise.resolve({
253
295
  blocksSynchedTo: this.#blockStore.getSynchedL1BlockNumber(),
254
296
  messagesSynchedTo: this.#messageStore.getSynchedL1BlockNumber(),
255
- provenLogsSynchedTo: this.#provenStore.getSynchedL1BlockNumber(),
256
297
  });
257
298
  }
258
299
  }
@@ -4,6 +4,7 @@ import {
4
4
  ExtendedUnencryptedL2Log,
5
5
  type FromLogType,
6
6
  type GetUnencryptedLogsResponse,
7
+ type L2Block,
7
8
  type L2BlockL2Logs,
8
9
  type LogFilter,
9
10
  LogId,
@@ -37,29 +38,28 @@ export class LogStore {
37
38
 
38
39
  /**
39
40
  * Append new logs to the store's list.
40
- * @param encryptedLogs - The logs to be added to the store.
41
- * @param unencryptedLogs - The type of the logs to be added to the store.
42
- * @param blockNumber - The block for which to add the logs.
41
+ * @param blocks - The blocks for which to add the logs.
43
42
  * @returns True if the operation is successful.
44
43
  */
45
- addLogs(
46
- noteEncryptedLogs: EncryptedNoteL2BlockL2Logs | undefined,
47
- encryptedLogs: EncryptedL2BlockL2Logs | undefined,
48
- unencryptedLogs: UnencryptedL2BlockL2Logs | undefined,
49
- blockNumber: number,
50
- ): Promise<boolean> {
44
+ addLogs(blocks: L2Block[]): Promise<boolean> {
51
45
  return this.db.transaction(() => {
52
- if (noteEncryptedLogs) {
53
- void this.#noteEncryptedLogs.set(blockNumber, noteEncryptedLogs.toBuffer());
54
- }
46
+ blocks.forEach(block => {
47
+ void this.#noteEncryptedLogs.set(block.number, block.body.noteEncryptedLogs.toBuffer());
48
+ void this.#encryptedLogs.set(block.number, block.body.encryptedLogs.toBuffer());
49
+ void this.#unencryptedLogs.set(block.number, block.body.unencryptedLogs.toBuffer());
50
+ });
55
51
 
56
- if (encryptedLogs) {
57
- void this.#encryptedLogs.set(blockNumber, encryptedLogs.toBuffer());
58
- }
52
+ return true;
53
+ });
54
+ }
59
55
 
60
- if (unencryptedLogs) {
61
- void this.#unencryptedLogs.set(blockNumber, unencryptedLogs.toBuffer());
62
- }
56
+ deleteLogs(blocks: L2Block[]): Promise<boolean> {
57
+ return this.db.transaction(() => {
58
+ blocks.forEach(block => {
59
+ void this.#noteEncryptedLogs.delete(block.number);
60
+ void this.#encryptedLogs.delete(block.number);
61
+ void this.#unencryptedLogs.delete(block.number);
62
+ });
63
63
 
64
64
  return true;
65
65
  });
@@ -17,6 +17,7 @@ export class MessageStore {
17
17
  #l1ToL2Messages: AztecMap<string, Buffer>;
18
18
  #l1ToL2MessageIndices: AztecMap<string, bigint[]>; // We store array of bigints here because there can be duplicate messages
19
19
  #lastSynchedL1Block: AztecSingleton<bigint>;
20
+ #totalMessageCount: AztecSingleton<bigint>;
20
21
 
21
22
  #log = createDebugLogger('aztec:archiver:message_store');
22
23
 
@@ -26,6 +27,11 @@ export class MessageStore {
26
27
  this.#l1ToL2Messages = db.openMap('archiver_l1_to_l2_messages');
27
28
  this.#l1ToL2MessageIndices = db.openMap('archiver_l1_to_l2_message_indices');
28
29
  this.#lastSynchedL1Block = db.openSingleton('archiver_last_l1_block_new_messages');
30
+ this.#totalMessageCount = db.openSingleton('archiver_l1_to_l2_message_count');
31
+ }
32
+
33
+ getTotalL1ToL2MessageCount(): bigint {
34
+ return this.#totalMessageCount.get() ?? 0n;
29
35
  }
30
36
 
31
37
  /**
@@ -70,6 +76,9 @@ export class MessageStore {
70
76
  void this.#l1ToL2MessageIndices.set(message.leaf.toString(), indices);
71
77
  }
72
78
 
79
+ const lastTotalMessageCount = this.getTotalL1ToL2MessageCount();
80
+ void this.#totalMessageCount.set(lastTotalMessageCount + BigInt(messages.retrievedData.length));
81
+
73
82
  return true;
74
83
  });
75
84
  }
@@ -19,6 +19,10 @@ export class L1ToL2MessageStore {
19
19
 
20
20
  constructor() {}
21
21
 
22
+ getTotalL1ToL2MessageCount(): bigint {
23
+ return BigInt(this.store.size);
24
+ }
25
+
22
26
  addMessage(message: InboxLeaf) {
23
27
  if (message.index >= this.#l1ToL2MessagesSubtreeSize) {
24
28
  throw new Error(`Message index ${message.index} out of subtree range`);
@@ -15,18 +15,19 @@ import {
15
15
  TxReceipt,
16
16
  type UnencryptedL2BlockL2Logs,
17
17
  } from '@aztec/circuit-types';
18
- import { Fr, INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js';
18
+ import { Fr, type Header, INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js';
19
19
  import { type ContractArtifact } from '@aztec/foundation/abi';
20
20
  import { type AztecAddress } from '@aztec/foundation/aztec-address';
21
21
  import {
22
22
  type ContractClassPublic,
23
+ type ContractClassPublicWithBlockNumber,
23
24
  type ContractInstanceWithAddress,
24
25
  type ExecutablePrivateFunctionWithMembershipProof,
25
26
  type UnconstrainedFunctionWithMembershipProof,
26
27
  } from '@aztec/types/contracts';
27
28
 
28
29
  import { type ArchiverDataStore, type ArchiverL1SynchPoint } from '../archiver_store.js';
29
- import { type DataRetrieval, type SingletonDataRetrieval } from '../structs/data_retrieval.js';
30
+ import { type DataRetrieval } from '../structs/data_retrieval.js';
30
31
  import { type L1Published } from '../structs/published.js';
31
32
  import { L1ToL2MessageStore } from './l1_to_l2_message_store.js';
32
33
 
@@ -44,23 +45,11 @@ export class MemoryArchiverStore implements ArchiverDataStore {
44
45
  */
45
46
  private txEffects: TxEffect[] = [];
46
47
 
47
- /**
48
- * An array containing all the encrypted logs that have been fetched so far.
49
- * Note: Index in the "outer" array equals to (corresponding L2 block's number - INITIAL_L2_BLOCK_NUM).
50
- */
51
- private noteEncryptedLogsPerBlock: EncryptedNoteL2BlockL2Logs[] = [];
48
+ private noteEncryptedLogsPerBlock: Map<number, EncryptedNoteL2BlockL2Logs> = new Map();
52
49
 
53
- /**
54
- * An array containing all the encrypted logs that have been fetched so far.
55
- * Note: Index in the "outer" array equals to (corresponding L2 block's number - INITIAL_L2_BLOCK_NUM).
56
- */
57
- private encryptedLogsPerBlock: EncryptedL2BlockL2Logs[] = [];
50
+ private encryptedLogsPerBlock: Map<number, EncryptedL2BlockL2Logs> = new Map();
58
51
 
59
- /**
60
- * An array containing all the unencrypted logs that have been fetched so far.
61
- * Note: Index in the "outer" array equals to (corresponding L2 block's number - INITIAL_L2_BLOCK_NUM).
62
- */
63
- private unencryptedLogsPerBlock: UnencryptedL2BlockL2Logs[] = [];
52
+ private unencryptedLogsPerBlock: Map<number, UnencryptedL2BlockL2Logs> = new Map();
64
53
 
65
54
  /**
66
55
  * Contains all L1 to L2 messages.
@@ -69,7 +58,7 @@ export class MemoryArchiverStore implements ArchiverDataStore {
69
58
 
70
59
  private contractArtifacts: Map<string, ContractArtifact> = new Map();
71
60
 
72
- private contractClasses: Map<string, ContractClassPublic> = new Map();
61
+ private contractClasses: Map<string, ContractClassPublicWithBlockNumber> = new Map();
73
62
 
74
63
  private privateFunctions: Map<string, ExecutablePrivateFunctionWithMembershipProof[]> = new Map();
75
64
 
@@ -79,9 +68,9 @@ export class MemoryArchiverStore implements ArchiverDataStore {
79
68
 
80
69
  private lastL1BlockNewBlocks: bigint | undefined = undefined;
81
70
  private lastL1BlockNewMessages: bigint | undefined = undefined;
82
- private lastL1BlockNewProvenLogs: bigint | undefined = undefined;
83
71
 
84
72
  private lastProvenL2BlockNumber: number = 0;
73
+ private lastProvenL2EpochNumber: number = 0;
85
74
 
86
75
  constructor(
87
76
  /** The max number of logs that can be obtained in 1 "getUnencryptedLogs" call. */
@@ -129,9 +118,24 @@ export class MemoryArchiverStore implements ArchiverDataStore {
129
118
  return Promise.resolve(true);
130
119
  }
131
120
 
132
- public addContractClasses(data: ContractClassPublic[], _blockNumber: number): Promise<boolean> {
121
+ public addContractClasses(data: ContractClassPublic[], blockNumber: number): Promise<boolean> {
133
122
  for (const contractClass of data) {
134
- this.contractClasses.set(contractClass.id.toString(), contractClass);
123
+ if (!this.contractClasses.has(contractClass.id.toString())) {
124
+ this.contractClasses.set(contractClass.id.toString(), {
125
+ ...contractClass,
126
+ l2BlockNumber: blockNumber,
127
+ });
128
+ }
129
+ }
130
+ return Promise.resolve(true);
131
+ }
132
+
133
+ public deleteContractClasses(data: ContractClassPublic[], blockNumber: number): Promise<boolean> {
134
+ for (const contractClass of data) {
135
+ const restored = this.contractClasses.get(contractClass.id.toString());
136
+ if (restored && restored.l2BlockNumber >= blockNumber) {
137
+ this.contractClasses.delete(contractClass.id.toString());
138
+ }
135
139
  }
136
140
  return Promise.resolve(true);
137
141
  }
@@ -143,6 +147,13 @@ export class MemoryArchiverStore implements ArchiverDataStore {
143
147
  return Promise.resolve(true);
144
148
  }
145
149
 
150
+ public deleteContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise<boolean> {
151
+ for (const contractInstance of data) {
152
+ this.contractInstances.delete(contractInstance.address.toString());
153
+ }
154
+ return Promise.resolve(true);
155
+ }
156
+
146
157
  /**
147
158
  * Append new blocks to the store's list.
148
159
  * @param blocks - The L2 blocks to be added to the store and the last processed L1 block.
@@ -161,33 +172,58 @@ export class MemoryArchiverStore implements ArchiverDataStore {
161
172
  }
162
173
 
163
174
  /**
164
- * Append new logs to the store's list.
165
- * @param encryptedLogs - The encrypted logs to be added to the store.
166
- * @param unencryptedLogs - The unencrypted logs to be added to the store.
167
- * @param blockNumber - The block for which to add the logs.
168
- * @returns True if the operation is successful.
175
+ * Unwinds blocks from the database
176
+ * @param from - The tip of the chain, passed for verification purposes,
177
+ * ensuring that we don't end up deleting something we did not intend
178
+ * @param blocksToUnwind - The number of blocks we are to unwind
179
+ * @returns True if the operation is successful
169
180
  */
170
- addLogs(
171
- noteEncryptedLogs: EncryptedNoteL2BlockL2Logs,
172
- encryptedLogs: EncryptedL2BlockL2Logs,
173
- unencryptedLogs: UnencryptedL2BlockL2Logs,
174
- blockNumber: number,
175
- ): Promise<boolean> {
176
- if (noteEncryptedLogs) {
177
- this.noteEncryptedLogsPerBlock[blockNumber - INITIAL_L2_BLOCK_NUM] = noteEncryptedLogs;
181
+ public async unwindBlocks(from: number, blocksToUnwind: number): Promise<boolean> {
182
+ const last = await this.getSynchedL2BlockNumber();
183
+ if (from != last) {
184
+ throw new Error(`Can only remove the tip`);
178
185
  }
179
186
 
180
- if (encryptedLogs) {
181
- this.encryptedLogsPerBlock[blockNumber - INITIAL_L2_BLOCK_NUM] = encryptedLogs;
187
+ const stopAt = from - blocksToUnwind;
188
+ while ((await this.getSynchedL2BlockNumber()) > stopAt) {
189
+ const block = this.l2Blocks.pop();
190
+ if (block == undefined) {
191
+ break;
192
+ }
193
+ block.data.body.txEffects.forEach(() => this.txEffects.pop());
182
194
  }
183
195
 
184
- if (unencryptedLogs) {
185
- this.unencryptedLogsPerBlock[blockNumber - INITIAL_L2_BLOCK_NUM] = unencryptedLogs;
186
- }
196
+ return Promise.resolve(true);
197
+ }
198
+
199
+ /**
200
+ * Append new logs to the store's list.
201
+ * @param block - The block for which to add the logs.
202
+ * @returns True if the operation is successful.
203
+ */
204
+ addLogs(blocks: L2Block[]): Promise<boolean> {
205
+ blocks.forEach(block => {
206
+ this.noteEncryptedLogsPerBlock.set(block.number, block.body.noteEncryptedLogs);
207
+ this.encryptedLogsPerBlock.set(block.number, block.body.encryptedLogs);
208
+ this.unencryptedLogsPerBlock.set(block.number, block.body.unencryptedLogs);
209
+ });
210
+ return Promise.resolve(true);
211
+ }
212
+
213
+ deleteLogs(blocks: L2Block[]): Promise<boolean> {
214
+ blocks.forEach(block => {
215
+ this.encryptedLogsPerBlock.delete(block.number);
216
+ this.noteEncryptedLogsPerBlock.delete(block.number);
217
+ this.unencryptedLogsPerBlock.delete(block.number);
218
+ });
187
219
 
188
220
  return Promise.resolve(true);
189
221
  }
190
222
 
223
+ getTotalL1ToL2MessageCount(): Promise<bigint> {
224
+ return Promise.resolve(this.l1ToL2Messages.getTotalL1ToL2MessageCount());
225
+ }
226
+
191
227
  /**
192
228
  * Append L1 to L2 messages to the store.
193
229
  * @param messages - The L1 to L2 messages to be added to the store and the last processed L1 block.
@@ -226,12 +262,15 @@ export class MemoryArchiverStore implements ArchiverDataStore {
226
262
  * @remarks When "from" is smaller than genesis block number, blocks from the beginning are returned.
227
263
  */
228
264
  public getBlocks(from: number, limit: number): Promise<L1Published<L2Block>[]> {
229
- // Return an empty array if we are outside of range
230
265
  if (limit < 1) {
231
266
  return Promise.reject(new Error(`Invalid limit: ${limit}`));
232
267
  }
233
268
 
234
- const fromIndex = Math.max(from - INITIAL_L2_BLOCK_NUM, 0);
269
+ if (from < INITIAL_L2_BLOCK_NUM) {
270
+ return Promise.reject(new Error(`Invalid start: ${from}`));
271
+ }
272
+
273
+ const fromIndex = from - INITIAL_L2_BLOCK_NUM;
235
274
  if (fromIndex >= this.l2Blocks.length) {
236
275
  return Promise.resolve([]);
237
276
  }
@@ -240,6 +279,11 @@ export class MemoryArchiverStore implements ArchiverDataStore {
240
279
  return Promise.resolve(this.l2Blocks.slice(fromIndex, toIndex));
241
280
  }
242
281
 
282
+ public async getBlockHeaders(from: number, limit: number): Promise<Header[]> {
283
+ const blocks = await this.getBlocks(from, limit);
284
+ return blocks.map(block => block.data.header);
285
+ }
286
+
243
287
  /**
244
288
  * Gets a tx effect.
245
289
  * @param txHash - The txHash of the tx effect.
@@ -297,9 +341,14 @@ export class MemoryArchiverStore implements ArchiverDataStore {
297
341
  logType: TLogType,
298
342
  ): Promise<L2BlockL2Logs<FromLogType<TLogType>>[]> {
299
343
  if (from < INITIAL_L2_BLOCK_NUM || limit < 1) {
300
- throw new Error(`Invalid limit: ${limit}`);
344
+ return Promise.resolve([]);
301
345
  }
302
- const logs = (() => {
346
+
347
+ if (from > this.l2Blocks.length) {
348
+ return Promise.resolve([]);
349
+ }
350
+
351
+ const logMap = (() => {
303
352
  switch (logType) {
304
353
  case LogType.ENCRYPTED:
305
354
  return this.encryptedLogsPerBlock;
@@ -309,14 +358,24 @@ export class MemoryArchiverStore implements ArchiverDataStore {
309
358
  default:
310
359
  return this.unencryptedLogsPerBlock;
311
360
  }
312
- })() as L2BlockL2Logs<FromLogType<TLogType>>[];
361
+ })() as Map<number, L2BlockL2Logs<FromLogType<TLogType>>>;
313
362
 
314
- if (from > logs.length) {
315
- return Promise.resolve([]);
316
- }
317
- const startIndex = from - INITIAL_L2_BLOCK_NUM;
363
+ const startIndex = from;
318
364
  const endIndex = startIndex + limit;
319
- return Promise.resolve(logs.slice(startIndex, endIndex));
365
+ const upper = Math.min(endIndex, this.l2Blocks.length + INITIAL_L2_BLOCK_NUM);
366
+
367
+ const l = [];
368
+ for (let i = startIndex; i < upper; i++) {
369
+ const log = logMap.get(i);
370
+ if (log) {
371
+ l.push(log);
372
+ } else {
373
+ // I hate typescript sometimes
374
+ l.push(undefined as unknown as L2BlockL2Logs<FromLogType<TLogType>>);
375
+ }
376
+ }
377
+
378
+ return Promise.resolve(l);
320
379
  }
321
380
 
322
381
  /**
@@ -327,37 +386,37 @@ export class MemoryArchiverStore implements ArchiverDataStore {
327
386
  */
328
387
  getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
329
388
  let txHash: TxHash | undefined;
330
- let fromBlockIndex = 0;
331
- let toBlockIndex = this.unencryptedLogsPerBlock.length;
389
+ let fromBlock = 0;
390
+ let toBlock = this.l2Blocks.length + INITIAL_L2_BLOCK_NUM;
332
391
  let txIndexInBlock = 0;
333
392
  let logIndexInTx = 0;
334
393
 
335
394
  if (filter.afterLog) {
336
395
  // Continuation parameter is set --> tx hash is ignored
337
396
  if (filter.fromBlock == undefined || filter.fromBlock <= filter.afterLog.blockNumber) {
338
- fromBlockIndex = filter.afterLog.blockNumber - INITIAL_L2_BLOCK_NUM;
397
+ fromBlock = filter.afterLog.blockNumber;
339
398
  txIndexInBlock = filter.afterLog.txIndex;
340
399
  logIndexInTx = filter.afterLog.logIndex + 1; // We want to start from the next log
341
400
  } else {
342
- fromBlockIndex = filter.fromBlock - INITIAL_L2_BLOCK_NUM;
401
+ fromBlock = filter.fromBlock;
343
402
  }
344
403
  } else {
345
404
  txHash = filter.txHash;
346
405
 
347
406
  if (filter.fromBlock !== undefined) {
348
- fromBlockIndex = filter.fromBlock - INITIAL_L2_BLOCK_NUM;
407
+ fromBlock = filter.fromBlock;
349
408
  }
350
409
  }
351
410
 
352
411
  if (filter.toBlock !== undefined) {
353
- toBlockIndex = filter.toBlock - INITIAL_L2_BLOCK_NUM;
412
+ toBlock = filter.toBlock;
354
413
  }
355
414
 
356
415
  // Ensure the indices are within block array bounds
357
- fromBlockIndex = Math.max(fromBlockIndex, 0);
358
- toBlockIndex = Math.min(toBlockIndex, this.unencryptedLogsPerBlock.length);
416
+ fromBlock = Math.max(fromBlock, INITIAL_L2_BLOCK_NUM);
417
+ toBlock = Math.min(toBlock, this.l2Blocks.length + INITIAL_L2_BLOCK_NUM);
359
418
 
360
- if (fromBlockIndex > this.unencryptedLogsPerBlock.length || toBlockIndex < fromBlockIndex || toBlockIndex <= 0) {
419
+ if (fromBlock > this.l2Blocks.length || toBlock < fromBlock || toBlock <= 0) {
361
420
  return Promise.resolve({
362
421
  logs: [],
363
422
  maxLogsHit: false,
@@ -368,27 +427,30 @@ export class MemoryArchiverStore implements ArchiverDataStore {
368
427
 
369
428
  const logs: ExtendedUnencryptedL2Log[] = [];
370
429
 
371
- for (; fromBlockIndex < toBlockIndex; fromBlockIndex++) {
372
- const block = this.l2Blocks[fromBlockIndex];
373
- const blockLogs = this.unencryptedLogsPerBlock[fromBlockIndex];
374
- for (; txIndexInBlock < blockLogs.txLogs.length; txIndexInBlock++) {
375
- const txLogs = blockLogs.txLogs[txIndexInBlock].unrollLogs();
376
- for (; logIndexInTx < txLogs.length; logIndexInTx++) {
377
- const log = txLogs[logIndexInTx];
378
- if (
379
- (!txHash || block.data.body.txEffects[txIndexInBlock].txHash.equals(txHash)) &&
380
- (!contractAddress || log.contractAddress.equals(contractAddress))
381
- ) {
382
- logs.push(new ExtendedUnencryptedL2Log(new LogId(block.data.number, txIndexInBlock, logIndexInTx), log));
383
- if (logs.length === this.maxLogs) {
384
- return Promise.resolve({
385
- logs,
386
- maxLogsHit: true,
387
- });
430
+ for (; fromBlock < toBlock; fromBlock++) {
431
+ const block = this.l2Blocks[fromBlock - INITIAL_L2_BLOCK_NUM];
432
+ const blockLogs = this.unencryptedLogsPerBlock.get(fromBlock);
433
+
434
+ if (blockLogs) {
435
+ for (; txIndexInBlock < blockLogs.txLogs.length; txIndexInBlock++) {
436
+ const txLogs = blockLogs.txLogs[txIndexInBlock].unrollLogs();
437
+ for (; logIndexInTx < txLogs.length; logIndexInTx++) {
438
+ const log = txLogs[logIndexInTx];
439
+ if (
440
+ (!txHash || block.data.body.txEffects[txIndexInBlock].txHash.equals(txHash)) &&
441
+ (!contractAddress || log.contractAddress.equals(contractAddress))
442
+ ) {
443
+ logs.push(new ExtendedUnencryptedL2Log(new LogId(block.data.number, txIndexInBlock, logIndexInTx), log));
444
+ if (logs.length === this.maxLogs) {
445
+ return Promise.resolve({
446
+ logs,
447
+ maxLogsHit: true,
448
+ });
449
+ }
388
450
  }
389
451
  }
452
+ logIndexInTx = 0;
390
453
  }
391
- logIndexInTx = 0;
392
454
  }
393
455
  txIndexInBlock = 0;
394
456
  }
@@ -414,9 +476,17 @@ export class MemoryArchiverStore implements ArchiverDataStore {
414
476
  return Promise.resolve(this.lastProvenL2BlockNumber);
415
477
  }
416
478
 
417
- public setProvenL2BlockNumber(l2BlockNumber: SingletonDataRetrieval<number>): Promise<void> {
418
- this.lastProvenL2BlockNumber = l2BlockNumber.retrievedData;
419
- this.lastL1BlockNewProvenLogs = l2BlockNumber.lastProcessedL1BlockNumber;
479
+ public getProvenL2EpochNumber(): Promise<number | undefined> {
480
+ return Promise.resolve(this.lastProvenL2EpochNumber);
481
+ }
482
+
483
+ public setProvenL2BlockNumber(l2BlockNumber: number): Promise<void> {
484
+ this.lastProvenL2BlockNumber = l2BlockNumber;
485
+ return Promise.resolve();
486
+ }
487
+
488
+ public setProvenL2EpochNumber(l2EpochNumber: number): Promise<void> {
489
+ this.lastProvenL2EpochNumber = l2EpochNumber;
420
490
  return Promise.resolve();
421
491
  }
422
492
 
@@ -434,7 +504,6 @@ export class MemoryArchiverStore implements ArchiverDataStore {
434
504
  return Promise.resolve({
435
505
  blocksSynchedTo: this.lastL1BlockNewBlocks,
436
506
  messagesSynchedTo: this.lastL1BlockNewMessages,
437
- provenLogsSynchedTo: this.lastL1BlockNewProvenLogs,
438
507
  });
439
508
  }
440
509