@aztec/archiver 0.30.1 → 0.32.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 (50) hide show
  1. package/dest/archiver/archiver.d.ts +7 -9
  2. package/dest/archiver/archiver.d.ts.map +1 -1
  3. package/dest/archiver/archiver.js +51 -22
  4. package/dest/archiver/archiver_store.d.ts +16 -11
  5. package/dest/archiver/archiver_store.d.ts.map +1 -1
  6. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  7. package/dest/archiver/archiver_store_test_suite.js +51 -15
  8. package/dest/archiver/data_retrieval.js +6 -6
  9. package/dest/archiver/eth_log_handlers.d.ts +7 -7
  10. package/dest/archiver/eth_log_handlers.d.ts.map +1 -1
  11. package/dest/archiver/eth_log_handlers.js +11 -11
  12. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  13. package/dest/archiver/kv_archiver_store/block_store.js +3 -2
  14. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +3 -4
  15. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  16. package/dest/archiver/kv_archiver_store/contract_class_store.js +64 -13
  17. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +9 -7
  18. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  19. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +11 -9
  20. package/dest/archiver/kv_archiver_store/log_store.d.ts +3 -3
  21. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  22. package/dest/archiver/kv_archiver_store/log_store.js +7 -5
  23. package/dest/archiver/kv_archiver_store/message_store.d.ts +3 -2
  24. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  25. package/dest/archiver/kv_archiver_store/message_store.js +10 -6
  26. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +4 -4
  27. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +1 -1
  28. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +11 -8
  29. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +11 -7
  30. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
  31. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +34 -11
  32. package/dest/rpc/archiver_client.d.ts.map +1 -1
  33. package/dest/rpc/archiver_client.js +4 -3
  34. package/dest/rpc/archiver_server.d.ts.map +1 -1
  35. package/dest/rpc/archiver_server.js +4 -3
  36. package/package.json +11 -9
  37. package/src/archiver/archiver.ts +80 -23
  38. package/src/archiver/archiver_store.ts +33 -11
  39. package/src/archiver/archiver_store_test_suite.ts +65 -18
  40. package/src/archiver/data_retrieval.ts +6 -6
  41. package/src/archiver/eth_log_handlers.ts +13 -13
  42. package/src/archiver/kv_archiver_store/block_store.ts +9 -1
  43. package/src/archiver/kv_archiver_store/contract_class_store.ts +106 -18
  44. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +31 -12
  45. package/src/archiver/kv_archiver_store/log_store.ts +23 -10
  46. package/src/archiver/kv_archiver_store/message_store.ts +10 -5
  47. package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +10 -7
  48. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +66 -15
  49. package/src/rpc/archiver_client.ts +5 -3
  50. package/src/rpc/archiver_server.ts +4 -2
@@ -1,7 +1,12 @@
1
- import { InboxLeaf, L2Block, L2BlockContext, LogId, LogType, TxHash, UnencryptedL2Log } from '@aztec/circuit-types';
1
+ import { InboxLeaf, L2Block, L2BlockContext, LogId, LogType, TxHash } from '@aztec/circuit-types';
2
2
  import '@aztec/circuit-types/jest';
3
3
  import { AztecAddress, Fr, INITIAL_L2_BLOCK_NUM, L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/circuits.js';
4
- import { makeContractClassPublic } from '@aztec/circuits.js/testing';
4
+ import {
5
+ makeContractClassPublic,
6
+ makeExecutablePrivateFunctionWithMembershipProof,
7
+ makeUnconstrainedFunctionWithMembershipProof,
8
+ } from '@aztec/circuits.js/testing';
9
+ import { times } from '@aztec/foundation/collection';
5
10
  import { randomBytes, randomInt } from '@aztec/foundation/crypto';
6
11
  import { ContractClassPublic, ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts';
7
12
 
@@ -81,19 +86,19 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
81
86
  });
82
87
  });
83
88
 
84
- describe('getSynchedL1BlockNumbers', () => {
89
+ describe('getSynchPoint', () => {
85
90
  it('returns 0n if no blocks have been added', async () => {
86
- await expect(store.getSynchedL1BlockNumbers()).resolves.toEqual({
87
- blocks: 0n,
88
- messages: 0n,
91
+ await expect(store.getSynchPoint()).resolves.toEqual({
92
+ blocksSynchedTo: 0n,
93
+ messagesSynchedTo: 0n,
89
94
  });
90
95
  });
91
96
 
92
97
  it('returns the L1 block number in which the most recent L2 block was published', async () => {
93
98
  await store.addBlocks(blocks);
94
- await expect(store.getSynchedL1BlockNumbers()).resolves.toEqual({
95
- blocks: blocks.lastProcessedL1BlockNumber,
96
- messages: 0n,
99
+ await expect(store.getSynchPoint()).resolves.toEqual({
100
+ blocksSynchedTo: blocks.lastProcessedL1BlockNumber,
101
+ messagesSynchedTo: 0n,
97
102
  });
98
103
  });
99
104
 
@@ -102,9 +107,9 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
102
107
  lastProcessedL1BlockNumber: 1n,
103
108
  retrievedData: [new InboxLeaf(0n, 0n, Fr.ZERO)],
104
109
  });
105
- await expect(store.getSynchedL1BlockNumbers()).resolves.toEqual({
106
- blocks: 0n,
107
- messages: 1n,
110
+ await expect(store.getSynchPoint()).resolves.toEqual({
111
+ blocksSynchedTo: 0n,
112
+ messagesSynchedTo: 1n,
108
113
  });
109
114
  });
110
115
  });
@@ -206,6 +211,20 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
206
211
  await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
207
212
  }).rejects.toThrow(`Message index ${l1ToL2MessageSubtreeSize} out of subtree range`);
208
213
  });
214
+
215
+ it('correctly handles duplicate messages', async () => {
216
+ const messageHash = Fr.random();
217
+
218
+ const msgs = [new InboxLeaf(1n, 0n, messageHash), new InboxLeaf(2n, 0n, messageHash)];
219
+
220
+ await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
221
+
222
+ const index1 = (await store.getL1ToL2MessageIndex(messageHash, 0n))!;
223
+ const index2 = await store.getL1ToL2MessageIndex(messageHash, index1 + 1n);
224
+
225
+ expect(index2).toBeDefined();
226
+ expect(index2).toBeGreaterThan(index1);
227
+ });
209
228
  });
210
229
 
211
230
  describe('contractInstances', () => {
@@ -242,6 +261,36 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
242
261
  it('returns undefined if contract class is not found', async () => {
243
262
  await expect(store.getContractClass(Fr.random())).resolves.toBeUndefined();
244
263
  });
264
+
265
+ it('adds new private functions', async () => {
266
+ const fns = times(3, makeExecutablePrivateFunctionWithMembershipProof);
267
+ await store.addFunctions(contractClass.id, fns, []);
268
+ const stored = await store.getContractClass(contractClass.id);
269
+ expect(stored?.privateFunctions).toEqual(fns);
270
+ });
271
+
272
+ it('does not duplicate private functions', async () => {
273
+ const fns = times(3, makeExecutablePrivateFunctionWithMembershipProof);
274
+ await store.addFunctions(contractClass.id, fns.slice(0, 1), []);
275
+ await store.addFunctions(contractClass.id, fns, []);
276
+ const stored = await store.getContractClass(contractClass.id);
277
+ expect(stored?.privateFunctions).toEqual(fns);
278
+ });
279
+
280
+ it('adds new unconstrained functions', async () => {
281
+ const fns = times(3, makeUnconstrainedFunctionWithMembershipProof);
282
+ await store.addFunctions(contractClass.id, [], fns);
283
+ const stored = await store.getContractClass(contractClass.id);
284
+ expect(stored?.unconstrainedFunctions).toEqual(fns);
285
+ });
286
+
287
+ it('does not duplicate unconstrained functions', async () => {
288
+ const fns = times(3, makeUnconstrainedFunctionWithMembershipProof);
289
+ await store.addFunctions(contractClass.id, [], fns.slice(0, 1));
290
+ await store.addFunctions(contractClass.id, [], fns);
291
+ const stored = await store.getContractClass(contractClass.id);
292
+ expect(stored?.unconstrainedFunctions).toEqual(fns);
293
+ });
245
294
  });
246
295
 
247
296
  describe('getUnencryptedLogs', () => {
@@ -318,11 +367,10 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
318
367
  const targetTxIndex = randomInt(txsPerBlock);
319
368
  const targetFunctionLogIndex = randomInt(numPublicFunctionCalls);
320
369
  const targetLogIndex = randomInt(numUnencryptedLogs);
321
- const targetContractAddress = UnencryptedL2Log.fromBuffer(
370
+ const targetContractAddress =
322
371
  blocks.retrievedData[targetBlockIndex].body.txEffects[targetTxIndex].unencryptedLogs.functionLogs[
323
372
  targetFunctionLogIndex
324
- ].logs[targetLogIndex],
325
- ).contractAddress;
373
+ ].logs[targetLogIndex].contractAddress;
326
374
 
327
375
  const response = await store.getUnencryptedLogs({ contractAddress: targetContractAddress });
328
376
 
@@ -339,11 +387,10 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
339
387
  const targetTxIndex = randomInt(txsPerBlock);
340
388
  const targetFunctionLogIndex = randomInt(numPublicFunctionCalls);
341
389
  const targetLogIndex = randomInt(numUnencryptedLogs);
342
- const targetSelector = UnencryptedL2Log.fromBuffer(
390
+ const targetSelector =
343
391
  blocks.retrievedData[targetBlockIndex].body.txEffects[targetTxIndex].unencryptedLogs.functionLogs[
344
392
  targetFunctionLogIndex
345
- ].logs[targetLogIndex],
346
- ).selector;
393
+ ].logs[targetLogIndex].selector;
347
394
 
348
395
  const response = await store.getUnencryptedLogs({ selector: targetSelector });
349
396
 
@@ -6,10 +6,10 @@ import { PublicClient } from 'viem';
6
6
 
7
7
  import {
8
8
  getL2BlockProcessedLogs,
9
- getLeafInsertedLogs,
9
+ getMessageSentLogs,
10
10
  getTxsPublishedLogs,
11
11
  processL2BlockProcessedLogs,
12
- processLeafInsertedLogs,
12
+ processMessageSentLogs,
13
13
  processTxsPublishedLogs,
14
14
  } from './eth_log_handlers.js';
15
15
 
@@ -132,14 +132,14 @@ export async function retrieveL1ToL2Messages(
132
132
  if (searchStartBlock > searchEndBlock) {
133
133
  break;
134
134
  }
135
- const leafInsertedLogs = await getLeafInsertedLogs(publicClient, inboxAddress, searchStartBlock, searchEndBlock);
136
- if (leafInsertedLogs.length === 0) {
135
+ const messageSentLogs = await getMessageSentLogs(publicClient, inboxAddress, searchStartBlock, searchEndBlock);
136
+ if (messageSentLogs.length === 0) {
137
137
  break;
138
138
  }
139
- const l1ToL2Messages = processLeafInsertedLogs(leafInsertedLogs);
139
+ const l1ToL2Messages = processMessageSentLogs(messageSentLogs);
140
140
  retrievedL1ToL2Messages.push(...l1ToL2Messages);
141
141
  // handles the case when there are no new messages:
142
- searchStartBlock = (leafInsertedLogs.findLast(msgLog => !!msgLog)?.blockNumber || searchStartBlock) + 1n;
142
+ searchStartBlock = (messageSentLogs.findLast(msgLog => !!msgLog)?.blockNumber || searchStartBlock) + 1n;
143
143
  } while (blockUntilSynced && searchStartBlock <= searchEndBlock);
144
144
  return { lastProcessedL1BlockNumber: searchStartBlock - 1n, retrievedData: retrievedL1ToL2Messages };
145
145
  }
@@ -8,17 +8,17 @@ import { AvailabilityOracleAbi, InboxAbi, RollupAbi } from '@aztec/l1-artifacts'
8
8
  import { Hex, Log, PublicClient, decodeFunctionData, getAbiItem, getAddress, hexToBytes } from 'viem';
9
9
 
10
10
  /**
11
- * Processes newly received LeafInserted (L1 to L2) logs.
12
- * @param logs - LeafInserted logs.
13
- * @returns Array of all processed LeafInserted logs
11
+ * Processes newly received MessageSent (L1 to L2) logs.
12
+ * @param logs - MessageSent logs.
13
+ * @returns Array of all processed MessageSent logs
14
14
  */
15
- export function processLeafInsertedLogs(
16
- logs: Log<bigint, number, false, undefined, true, typeof InboxAbi, 'LeafInserted'>[],
15
+ export function processMessageSentLogs(
16
+ logs: Log<bigint, number, false, undefined, true, typeof InboxAbi, 'MessageSent'>[],
17
17
  ): InboxLeaf[] {
18
18
  const leaves: InboxLeaf[] = [];
19
19
  for (const log of logs) {
20
- const { blockNumber, index, value } = log.args;
21
- leaves.push(new InboxLeaf(blockNumber, index, Fr.fromString(value)));
20
+ const { l2BlockNumber, index, hash } = log.args;
21
+ leaves.push(new InboxLeaf(l2BlockNumber, index, Fr.fromString(hash)));
22
22
  }
23
23
  return leaves;
24
24
  }
@@ -91,7 +91,7 @@ async function getBlockMetadataFromRollupTx(
91
91
  if (functionName !== 'process') {
92
92
  throw new Error(`Unexpected method called ${functionName}`);
93
93
  }
94
- const [headerHex, archiveRootHex] = args! as readonly [Hex, Hex, Hex, Hex];
94
+ const [headerHex, archiveRootHex] = args! as readonly [Hex, Hex, Hex];
95
95
 
96
96
  const header = Header.fromBuffer(Buffer.from(hexToBytes(headerHex)));
97
97
 
@@ -191,24 +191,24 @@ export function getTxsPublishedLogs(
191
191
  }
192
192
 
193
193
  /**
194
- * Get relevant `LeafInserted` logs emitted by Inbox on chain.
194
+ * Get relevant `MessageSent` logs emitted by Inbox on chain.
195
195
  * @param publicClient - The viem public client to use for transaction retrieval.
196
196
  * @param inboxAddress - The address of the inbox contract.
197
197
  * @param fromBlock - First block to get logs from (inclusive).
198
198
  * @param toBlock - Last block to get logs from (inclusive).
199
- * @returns An array of `LeafInserted` logs.
199
+ * @returns An array of `MessageSent` logs.
200
200
  */
201
- export function getLeafInsertedLogs(
201
+ export function getMessageSentLogs(
202
202
  publicClient: PublicClient,
203
203
  inboxAddress: EthAddress,
204
204
  fromBlock: bigint,
205
205
  toBlock: bigint,
206
- ): Promise<Log<bigint, number, false, undefined, true, typeof InboxAbi, 'LeafInserted'>[]> {
206
+ ): Promise<Log<bigint, number, false, undefined, true, typeof InboxAbi, 'MessageSent'>[]> {
207
207
  return publicClient.getLogs({
208
208
  address: getAddress(inboxAddress.toString()),
209
209
  event: getAbiItem({
210
210
  abi: InboxAbi,
211
- name: 'LeafInserted',
211
+ name: 'MessageSent',
212
212
  }),
213
213
  fromBlock,
214
214
  toBlock: toBlock + 1n, // the toBlock argument in getLogs is exclusive
@@ -134,7 +134,15 @@ export class BlockStore {
134
134
  }
135
135
 
136
136
  const block = this.getBlock(blockNumber)!;
137
- return new TxReceipt(txHash, TxStatus.MINED, '', block.hash().toBuffer(), block.number);
137
+ const tx = block.getTx(txIndex);
138
+
139
+ return new TxReceipt(
140
+ txHash,
141
+ tx.revertCode.isOK() ? TxStatus.MINED : TxStatus.REVERTED,
142
+ '',
143
+ block.hash().toBuffer(),
144
+ block.number,
145
+ );
138
146
  }
139
147
 
140
148
  /**
@@ -1,7 +1,11 @@
1
- import { Fr, FunctionSelector } from '@aztec/circuits.js';
1
+ import { Fr, FunctionSelector, Vector } from '@aztec/circuits.js';
2
2
  import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
3
3
  import { AztecKVStore, AztecMap } from '@aztec/kv-store';
4
- import { ContractClassPublic } from '@aztec/types/contracts';
4
+ import {
5
+ ContractClassPublic,
6
+ ExecutablePrivateFunctionWithMembershipProof,
7
+ UnconstrainedFunctionWithMembershipProof,
8
+ } from '@aztec/types/contracts';
5
9
 
6
10
  /**
7
11
  * LMDB implementation of the ArchiverDataStore interface.
@@ -9,7 +13,7 @@ import { ContractClassPublic } from '@aztec/types/contracts';
9
13
  export class ContractClassStore {
10
14
  #contractClasses: AztecMap<string, Buffer>;
11
15
 
12
- constructor(db: AztecKVStore) {
16
+ constructor(private db: AztecKVStore) {
13
17
  this.#contractClasses = db.openMap('archiver_contract_classes');
14
18
  }
15
19
 
@@ -25,44 +29,128 @@ export class ContractClassStore {
25
29
  getContractClassIds(): Fr[] {
26
30
  return Array.from(this.#contractClasses.keys()).map(key => Fr.fromString(key));
27
31
  }
32
+
33
+ async addFunctions(
34
+ contractClassId: Fr,
35
+ newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
36
+ newUnconstrainedFunctions: UnconstrainedFunctionWithMembershipProof[],
37
+ ): Promise<boolean> {
38
+ await this.db.transaction(() => {
39
+ const existingClassBuffer = this.#contractClasses.get(contractClassId.toString());
40
+ if (!existingClassBuffer) {
41
+ throw new Error(`Unknown contract class ${contractClassId} when adding private functions to store`);
42
+ }
43
+
44
+ const existingClass = deserializeContractClassPublic(existingClassBuffer);
45
+ const { privateFunctions: existingPrivateFns, unconstrainedFunctions: existingUnconstrainedFns } = existingClass;
46
+
47
+ const updatedClass: Omit<ContractClassPublic, 'id'> = {
48
+ ...existingClass,
49
+ privateFunctions: [
50
+ ...existingPrivateFns,
51
+ ...newPrivateFunctions.filter(newFn => !existingPrivateFns.some(f => f.selector.equals(newFn.selector))),
52
+ ],
53
+ unconstrainedFunctions: [
54
+ ...existingUnconstrainedFns,
55
+ ...newUnconstrainedFunctions.filter(
56
+ newFn => !existingUnconstrainedFns.some(f => f.selector.equals(newFn.selector)),
57
+ ),
58
+ ],
59
+ };
60
+ void this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass));
61
+ });
62
+ return Promise.resolve(true);
63
+ }
28
64
  }
29
65
 
30
- export function serializeContractClassPublic(contractClass: ContractClassPublic): Buffer {
66
+ function serializeContractClassPublic(contractClass: Omit<ContractClassPublic, 'id'>): Buffer {
31
67
  return serializeToBuffer(
32
68
  numToUInt8(contractClass.version),
33
69
  contractClass.artifactHash,
34
- contractClass.privateFunctions?.length ?? 0,
35
- contractClass.privateFunctions?.map(f => serializeToBuffer(f.selector, f.vkHash, f.isInternal)) ?? [],
36
70
  contractClass.publicFunctions.length,
37
- contractClass.publicFunctions?.map(f =>
38
- serializeToBuffer(f.selector, f.bytecode.length, f.bytecode, f.isInternal),
39
- ) ?? [],
71
+ contractClass.publicFunctions?.map(f => serializeToBuffer(f.selector, f.bytecode.length, f.bytecode)) ?? [],
72
+ contractClass.privateFunctions.length,
73
+ contractClass.privateFunctions.map(serializePrivateFunction),
74
+ contractClass.unconstrainedFunctions.length,
75
+ contractClass.unconstrainedFunctions.map(serializeUnconstrainedFunction),
40
76
  contractClass.packedBytecode.length,
41
77
  contractClass.packedBytecode,
42
78
  contractClass.privateFunctionsRoot,
43
79
  );
44
80
  }
45
81
 
46
- export function deserializeContractClassPublic(buffer: Buffer): Omit<ContractClassPublic, 'id'> {
82
+ function serializePrivateFunction(fn: ExecutablePrivateFunctionWithMembershipProof): Buffer {
83
+ return serializeToBuffer(
84
+ fn.selector,
85
+ fn.vkHash,
86
+ fn.bytecode.length,
87
+ fn.bytecode,
88
+ fn.functionMetadataHash,
89
+ fn.artifactMetadataHash,
90
+ fn.unconstrainedFunctionsArtifactTreeRoot,
91
+ new Vector(fn.privateFunctionTreeSiblingPath),
92
+ fn.privateFunctionTreeLeafIndex,
93
+ new Vector(fn.artifactTreeSiblingPath),
94
+ fn.artifactTreeLeafIndex,
95
+ );
96
+ }
97
+
98
+ function serializeUnconstrainedFunction(fn: UnconstrainedFunctionWithMembershipProof): Buffer {
99
+ return serializeToBuffer(
100
+ fn.selector,
101
+ fn.bytecode.length,
102
+ fn.bytecode,
103
+ fn.functionMetadataHash,
104
+ fn.artifactMetadataHash,
105
+ fn.privateFunctionsArtifactTreeRoot,
106
+ new Vector(fn.artifactTreeSiblingPath),
107
+ fn.artifactTreeLeafIndex,
108
+ );
109
+ }
110
+
111
+ function deserializeContractClassPublic(buffer: Buffer): Omit<ContractClassPublic, 'id'> {
47
112
  const reader = BufferReader.asReader(buffer);
48
113
  return {
49
114
  version: reader.readUInt8() as 1,
50
115
  artifactHash: reader.readObject(Fr),
51
- privateFunctions: reader.readVector({
52
- fromBuffer: reader => ({
53
- selector: reader.readObject(FunctionSelector),
54
- vkHash: reader.readObject(Fr),
55
- isInternal: reader.readBoolean(),
56
- }),
57
- }),
58
116
  publicFunctions: reader.readVector({
59
117
  fromBuffer: reader => ({
60
118
  selector: reader.readObject(FunctionSelector),
61
119
  bytecode: reader.readBuffer(),
62
- isInternal: reader.readBoolean(),
63
120
  }),
64
121
  }),
122
+ privateFunctions: reader.readVector({ fromBuffer: deserializePrivateFunction }),
123
+ unconstrainedFunctions: reader.readVector({ fromBuffer: deserializeUnconstrainedFunction }),
65
124
  packedBytecode: reader.readBuffer(),
66
125
  privateFunctionsRoot: reader.readObject(Fr),
67
126
  };
68
127
  }
128
+
129
+ function deserializePrivateFunction(buffer: Buffer | BufferReader): ExecutablePrivateFunctionWithMembershipProof {
130
+ const reader = BufferReader.asReader(buffer);
131
+ return {
132
+ selector: reader.readObject(FunctionSelector),
133
+ vkHash: reader.readObject(Fr),
134
+ bytecode: reader.readBuffer(),
135
+ functionMetadataHash: reader.readObject(Fr),
136
+ artifactMetadataHash: reader.readObject(Fr),
137
+ unconstrainedFunctionsArtifactTreeRoot: reader.readObject(Fr),
138
+ privateFunctionTreeSiblingPath: reader.readVector(Fr),
139
+ privateFunctionTreeLeafIndex: reader.readNumber(),
140
+ artifactTreeSiblingPath: reader.readVector(Fr),
141
+ artifactTreeLeafIndex: reader.readNumber(),
142
+ };
143
+ }
144
+
145
+ function deserializeUnconstrainedFunction(buffer: Buffer | BufferReader): UnconstrainedFunctionWithMembershipProof {
146
+ const reader = BufferReader.asReader(buffer);
147
+ return {
148
+ selector: reader.readObject(FunctionSelector),
149
+ bytecode: reader.readBuffer(),
150
+ functionMetadataHash: reader.readObject(Fr),
151
+ artifactMetadataHash: reader.readObject(Fr),
152
+ privateFunctionsArtifactTreeRoot: reader.readObject(Fr),
153
+ artifactTreeSiblingPath: reader.readVector(Fr),
154
+ artifactTreeLeafIndex: reader.readNumber(),
155
+ };
156
+ }
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  Body,
3
+ EncryptedL2BlockL2Logs,
4
+ FromLogType,
3
5
  GetUnencryptedLogsResponse,
4
6
  InboxLeaf,
5
7
  L2Block,
@@ -9,12 +11,18 @@ import {
9
11
  TxEffect,
10
12
  TxHash,
11
13
  TxReceipt,
14
+ UnencryptedL2BlockL2Logs,
12
15
  } from '@aztec/circuit-types';
13
16
  import { Fr } from '@aztec/circuits.js';
14
17
  import { AztecAddress } from '@aztec/foundation/aztec-address';
15
18
  import { createDebugLogger } from '@aztec/foundation/log';
16
19
  import { AztecKVStore } from '@aztec/kv-store';
17
- import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts';
20
+ import {
21
+ ContractClassPublic,
22
+ ContractInstanceWithAddress,
23
+ ExecutablePrivateFunctionWithMembershipProof,
24
+ UnconstrainedFunctionWithMembershipProof,
25
+ } from '@aztec/types/contracts';
18
26
 
19
27
  import { ArchiverDataStore, ArchiverL1SynchPoint } from '../archiver_store.js';
20
28
  import { DataRetrieval } from '../data_retrieval.js';
@@ -63,6 +71,14 @@ export class KVArchiverDataStore implements ArchiverDataStore {
63
71
  return (await Promise.all(data.map(c => this.#contractClassStore.addContractClass(c)))).every(Boolean);
64
72
  }
65
73
 
74
+ addFunctions(
75
+ contractClassId: Fr,
76
+ privateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
77
+ unconstrainedFunctions: UnconstrainedFunctionWithMembershipProof[],
78
+ ): Promise<boolean> {
79
+ return this.#contractClassStore.addFunctions(contractClassId, privateFunctions, unconstrainedFunctions);
80
+ }
81
+
66
82
  async addContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise<boolean> {
67
83
  return (await Promise.all(data.map(c => this.#contractInstanceStore.addContractInstance(c)))).every(Boolean);
68
84
  }
@@ -137,8 +153,8 @@ export class KVArchiverDataStore implements ArchiverDataStore {
137
153
  * @returns True if the operation is successful.
138
154
  */
139
155
  addLogs(
140
- encryptedLogs: L2BlockL2Logs | undefined,
141
- unencryptedLogs: L2BlockL2Logs | undefined,
156
+ encryptedLogs: EncryptedL2BlockL2Logs | undefined,
157
+ unencryptedLogs: UnencryptedL2BlockL2Logs | undefined,
142
158
  blockNumber: number,
143
159
  ): Promise<boolean> {
144
160
  return this.#logStore.addLogs(encryptedLogs, unencryptedLogs, blockNumber);
@@ -154,12 +170,13 @@ export class KVArchiverDataStore implements ArchiverDataStore {
154
170
  }
155
171
 
156
172
  /**
157
- * Gets the L1 to L2 message index in the L1 to L2 message tree.
173
+ * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`.
158
174
  * @param l1ToL2Message - The L1 to L2 message.
175
+ * @param startIndex - The index to start searching from.
159
176
  * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found).
160
177
  */
161
- public getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
162
- return Promise.resolve(this.#messageStore.getL1ToL2MessageIndex(l1ToL2Message));
178
+ getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise<bigint | undefined> {
179
+ return Promise.resolve(this.#messageStore.getL1ToL2MessageIndex(l1ToL2Message, startIndex));
163
180
  }
164
181
 
165
182
  /**
@@ -182,7 +199,11 @@ export class KVArchiverDataStore implements ArchiverDataStore {
182
199
  * @param logType - Specifies whether to return encrypted or unencrypted logs.
183
200
  * @returns The requested logs.
184
201
  */
185
- getLogs(start: number, limit: number, logType: LogType): Promise<L2BlockL2Logs[]> {
202
+ getLogs<TLogType extends LogType>(
203
+ start: number,
204
+ limit: number,
205
+ logType: TLogType,
206
+ ): Promise<L2BlockL2Logs<FromLogType<TLogType>>[]> {
186
207
  try {
187
208
  return Promise.resolve(Array.from(this.#logStore.getLogs(start, limit, logType)));
188
209
  } catch (err) {
@@ -214,12 +235,10 @@ export class KVArchiverDataStore implements ArchiverDataStore {
214
235
  /**
215
236
  * Gets the last L1 block number processed by the archiver
216
237
  */
217
- getSynchedL1BlockNumbers(): Promise<ArchiverL1SynchPoint> {
218
- const blocks = this.#blockStore.getSynchedL1BlockNumber();
219
- const messages = this.#messageStore.getSynchedL1BlockNumber();
238
+ getSynchPoint(): Promise<ArchiverL1SynchPoint> {
220
239
  return Promise.resolve({
221
- blocks,
222
- messages,
240
+ blocksSynchedTo: this.#blockStore.getSynchedL1BlockNumber(),
241
+ messagesSynchedTo: this.#messageStore.getSynchedL1BlockNumber(),
223
242
  });
224
243
  }
225
244
  }
@@ -1,10 +1,13 @@
1
1
  import {
2
+ EncryptedL2BlockL2Logs,
2
3
  ExtendedUnencryptedL2Log,
4
+ FromLogType,
3
5
  GetUnencryptedLogsResponse,
4
6
  L2BlockL2Logs,
5
7
  LogFilter,
6
8
  LogId,
7
9
  LogType,
10
+ UnencryptedL2BlockL2Logs,
8
11
  UnencryptedL2Log,
9
12
  } from '@aztec/circuit-types';
10
13
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js/constants';
@@ -37,8 +40,8 @@ export class LogStore {
37
40
  * @returns True if the operation is successful.
38
41
  */
39
42
  addLogs(
40
- encryptedLogs: L2BlockL2Logs | undefined,
41
- unencryptedLogs: L2BlockL2Logs | undefined,
43
+ encryptedLogs: EncryptedL2BlockL2Logs | undefined,
44
+ unencryptedLogs: UnencryptedL2BlockL2Logs | undefined,
42
45
  blockNumber: number,
43
46
  ): Promise<boolean> {
44
47
  return this.db.transaction(() => {
@@ -61,10 +64,15 @@ export class LogStore {
61
64
  * @param logType - Specifies whether to return encrypted or unencrypted logs.
62
65
  * @returns The requested logs.
63
66
  */
64
- *getLogs(start: number, limit: number, logType: LogType): IterableIterator<L2BlockL2Logs> {
67
+ *getLogs<TLogType extends LogType>(
68
+ start: number,
69
+ limit: number,
70
+ logType: TLogType,
71
+ ): IterableIterator<L2BlockL2Logs<FromLogType<TLogType>>> {
65
72
  const logMap = logType === LogType.ENCRYPTED ? this.#encryptedLogs : this.#unencryptedLogs;
73
+ const L2BlockL2Logs = logType === LogType.ENCRYPTED ? EncryptedL2BlockL2Logs : UnencryptedL2BlockL2Logs;
66
74
  for (const buffer of logMap.values({ start, limit })) {
67
- yield L2BlockL2Logs.fromBuffer(buffer);
75
+ yield L2BlockL2Logs.fromBuffer(buffer) as L2BlockL2Logs<FromLogType<TLogType>>;
68
76
  }
69
77
  }
70
78
 
@@ -94,7 +102,7 @@ export class LogStore {
94
102
  }
95
103
 
96
104
  const unencryptedLogsInBlock = this.#getBlockLogs(blockNumber, LogType.UNENCRYPTED);
97
- const txLogs = unencryptedLogsInBlock.txLogs[txIndex].unrollLogs().map(log => UnencryptedL2Log.fromBuffer(log));
105
+ const txLogs = unencryptedLogsInBlock.txLogs[txIndex].unrollLogs();
98
106
 
99
107
  const logs: ExtendedUnencryptedL2Log[] = [];
100
108
  const maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter);
@@ -118,9 +126,9 @@ export class LogStore {
118
126
 
119
127
  let maxLogsHit = false;
120
128
  loopOverBlocks: for (const [blockNumber, logBuffer] of this.#unencryptedLogs.entries({ start, end })) {
121
- const unencryptedLogsInBlock = L2BlockL2Logs.fromBuffer(logBuffer);
129
+ const unencryptedLogsInBlock = UnencryptedL2BlockL2Logs.fromBuffer(logBuffer);
122
130
  for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < unencryptedLogsInBlock.txLogs.length; txIndex++) {
123
- const txLogs = unencryptedLogsInBlock.txLogs[txIndex].unrollLogs().map(log => UnencryptedL2Log.fromBuffer(log));
131
+ const txLogs = unencryptedLogsInBlock.txLogs[txIndex].unrollLogs();
124
132
  maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter);
125
133
  if (maxLogsHit) {
126
134
  this.#log(`Max logs hit at block ${blockNumber}`);
@@ -161,14 +169,19 @@ export class LogStore {
161
169
  return maxLogsHit;
162
170
  }
163
171
 
164
- #getBlockLogs(blockNumber: number, logType: LogType): L2BlockL2Logs {
172
+ #getBlockLogs<TLogType extends LogType>(
173
+ blockNumber: number,
174
+ logType: TLogType,
175
+ ): L2BlockL2Logs<FromLogType<TLogType>> {
165
176
  const logMap = logType === LogType.ENCRYPTED ? this.#encryptedLogs : this.#unencryptedLogs;
177
+ const L2BlockL2Logs: typeof EncryptedL2BlockL2Logs | typeof UnencryptedL2BlockL2Logs =
178
+ logType === LogType.ENCRYPTED ? EncryptedL2BlockL2Logs : UnencryptedL2BlockL2Logs;
166
179
  const buffer = logMap.get(blockNumber);
167
180
 
168
181
  if (!buffer) {
169
- return new L2BlockL2Logs([]);
182
+ return new L2BlockL2Logs([]) as L2BlockL2Logs<FromLogType<TLogType>>;
170
183
  }
171
184
 
172
- return L2BlockL2Logs.fromBuffer(buffer);
185
+ return L2BlockL2Logs.fromBuffer(buffer) as L2BlockL2Logs<FromLogType<TLogType>>;
173
186
  }
174
187
  }
@@ -15,7 +15,7 @@ import { DataRetrieval } from '../data_retrieval.js';
15
15
  */
16
16
  export class MessageStore {
17
17
  #l1ToL2Messages: AztecMap<string, Buffer>;
18
- #l1ToL2MessageIndices: AztecMap<string, bigint>;
18
+ #l1ToL2MessageIndices: AztecMap<string, bigint[]>; // We store array of bigints here because there can be duplicate messages
19
19
  #lastL1BlockMessages: AztecSingleton<bigint>;
20
20
 
21
21
  #log = createDebugLogger('aztec:archiver:message_store');
@@ -60,7 +60,10 @@ export class MessageStore {
60
60
  const indexInTheWholeTree =
61
61
  (message.blockNumber - BigInt(INITIAL_L2_BLOCK_NUM)) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) +
62
62
  message.index;
63
- void this.#l1ToL2MessageIndices.setIfNotExists(message.leaf.toString(), indexInTheWholeTree);
63
+
64
+ const indices = this.#l1ToL2MessageIndices.get(message.leaf.toString()) ?? [];
65
+ indices.push(indexInTheWholeTree);
66
+ void this.#l1ToL2MessageIndices.set(message.leaf.toString(), indices);
64
67
  }
65
68
 
66
69
  return true;
@@ -68,12 +71,14 @@ export class MessageStore {
68
71
  }
69
72
 
70
73
  /**
71
- * Gets the L1 to L2 message index in the L1 to L2 message tree.
74
+ * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`.
72
75
  * @param l1ToL2Message - The L1 to L2 message.
76
+ * @param startIndex - The index to start searching from.
73
77
  * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found).
74
78
  */
75
- public getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
76
- const index = this.#l1ToL2MessageIndices.get(l1ToL2Message.toString());
79
+ getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise<bigint | undefined> {
80
+ const indices = this.#l1ToL2MessageIndices.get(l1ToL2Message.toString()) ?? [];
81
+ const index = indices.find(i => i >= startIndex);
77
82
  return Promise.resolve(index);
78
83
  }
79
84