@aztec/archiver 0.30.0 → 0.31.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 (40) hide show
  1. package/dest/archiver/archiver.d.ts +4 -6
  2. package/dest/archiver/archiver.d.ts.map +1 -1
  3. package/dest/archiver/archiver.js +48 -18
  4. package/dest/archiver/archiver_store.d.ts +13 -8
  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 +48 -12
  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 +6 -4
  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/message_store.d.ts +3 -2
  21. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  22. package/dest/archiver/kv_archiver_store/message_store.js +10 -6
  23. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +4 -4
  24. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +1 -1
  25. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +11 -8
  26. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +8 -4
  27. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
  28. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +31 -8
  29. package/package.json +11 -9
  30. package/src/archiver/archiver.ts +72 -19
  31. package/src/archiver/archiver_store.ts +23 -8
  32. package/src/archiver/archiver_store_test_suite.ts +60 -11
  33. package/src/archiver/data_retrieval.ts +6 -6
  34. package/src/archiver/eth_log_handlers.ts +13 -13
  35. package/src/archiver/kv_archiver_store/block_store.ts +9 -1
  36. package/src/archiver/kv_archiver_store/contract_class_store.ts +106 -18
  37. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +21 -9
  38. package/src/archiver/kv_archiver_store/message_store.ts +10 -5
  39. package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +10 -7
  40. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +47 -8
@@ -1,7 +1,12 @@
1
1
  import { InboxLeaf, L2Block, L2BlockContext, LogId, LogType, TxHash, UnencryptedL2Log } 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', () => {
@@ -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
+ }
@@ -14,7 +14,12 @@ import { Fr } from '@aztec/circuits.js';
14
14
  import { AztecAddress } from '@aztec/foundation/aztec-address';
15
15
  import { createDebugLogger } from '@aztec/foundation/log';
16
16
  import { AztecKVStore } from '@aztec/kv-store';
17
- import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts';
17
+ import {
18
+ ContractClassPublic,
19
+ ContractInstanceWithAddress,
20
+ ExecutablePrivateFunctionWithMembershipProof,
21
+ UnconstrainedFunctionWithMembershipProof,
22
+ } from '@aztec/types/contracts';
18
23
 
19
24
  import { ArchiverDataStore, ArchiverL1SynchPoint } from '../archiver_store.js';
20
25
  import { DataRetrieval } from '../data_retrieval.js';
@@ -63,6 +68,14 @@ export class KVArchiverDataStore implements ArchiverDataStore {
63
68
  return (await Promise.all(data.map(c => this.#contractClassStore.addContractClass(c)))).every(Boolean);
64
69
  }
65
70
 
71
+ addFunctions(
72
+ contractClassId: Fr,
73
+ privateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
74
+ unconstrainedFunctions: UnconstrainedFunctionWithMembershipProof[],
75
+ ): Promise<boolean> {
76
+ return this.#contractClassStore.addFunctions(contractClassId, privateFunctions, unconstrainedFunctions);
77
+ }
78
+
66
79
  async addContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise<boolean> {
67
80
  return (await Promise.all(data.map(c => this.#contractInstanceStore.addContractInstance(c)))).every(Boolean);
68
81
  }
@@ -154,12 +167,13 @@ export class KVArchiverDataStore implements ArchiverDataStore {
154
167
  }
155
168
 
156
169
  /**
157
- * Gets the L1 to L2 message index in the L1 to L2 message tree.
170
+ * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`.
158
171
  * @param l1ToL2Message - The L1 to L2 message.
172
+ * @param startIndex - The index to start searching from.
159
173
  * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found).
160
174
  */
161
- public getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
162
- return Promise.resolve(this.#messageStore.getL1ToL2MessageIndex(l1ToL2Message));
175
+ getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise<bigint | undefined> {
176
+ return Promise.resolve(this.#messageStore.getL1ToL2MessageIndex(l1ToL2Message, startIndex));
163
177
  }
164
178
 
165
179
  /**
@@ -214,12 +228,10 @@ export class KVArchiverDataStore implements ArchiverDataStore {
214
228
  /**
215
229
  * Gets the last L1 block number processed by the archiver
216
230
  */
217
- getSynchedL1BlockNumbers(): Promise<ArchiverL1SynchPoint> {
218
- const blocks = this.#blockStore.getSynchedL1BlockNumber();
219
- const messages = this.#messageStore.getSynchedL1BlockNumber();
231
+ getSynchPoint(): Promise<ArchiverL1SynchPoint> {
220
232
  return Promise.resolve({
221
- blocks,
222
- messages,
233
+ blocksSynchedTo: this.#blockStore.getSynchedL1BlockNumber(),
234
+ messagesSynchedTo: this.#messageStore.getSynchedL1BlockNumber(),
223
235
  });
224
236
  }
225
237
  }
@@ -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
 
@@ -11,8 +11,7 @@ import { Fr } from '@aztec/foundation/fields';
11
11
  */
12
12
  export class L1ToL2MessageStore {
13
13
  /**
14
- * A map containing the entry key to the corresponding L1 to L2
15
- * messages (and the number of times the message has been seen).
14
+ * A map pointing from a key in a "blockNum-messageIndex" format to the corresponding L1 to L2 message hash.
16
15
  */
17
16
  protected store: Map<string, Fr> = new Map();
18
17
 
@@ -50,17 +49,21 @@ export class L1ToL2MessageStore {
50
49
  }
51
50
 
52
51
  /**
53
- * Gets the L1 to L2 message index in the L1 to L2 message tree.
52
+ * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`.
54
53
  * @param l1ToL2Message - The L1 to L2 message.
54
+ * @param startIndex - The index to start searching from.
55
55
  * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found).
56
56
  */
57
- getMessageIndex(l1ToL2Message: Fr): bigint | undefined {
57
+ getMessageIndex(l1ToL2Message: Fr, startIndex: bigint): bigint | undefined {
58
58
  for (const [key, message] of this.store.entries()) {
59
59
  if (message.equals(l1ToL2Message)) {
60
- const [blockNumber, messageIndex] = key.split('-');
60
+ const keyParts = key.split('-');
61
+ const [blockNumber, messageIndex] = [BigInt(keyParts[0]), BigInt(keyParts[1])];
61
62
  const indexInTheWholeTree =
62
- (BigInt(blockNumber) - BigInt(INITIAL_L2_BLOCK_NUM)) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) +
63
- BigInt(messageIndex);
63
+ (blockNumber - BigInt(INITIAL_L2_BLOCK_NUM)) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) + messageIndex;
64
+ if (indexInTheWholeTree < startIndex) {
65
+ continue;
66
+ }
64
67
  return indexInTheWholeTree;
65
68
  }
66
69
  }
@@ -17,7 +17,12 @@ import {
17
17
  } from '@aztec/circuit-types';
18
18
  import { Fr, INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js';
19
19
  import { AztecAddress } from '@aztec/foundation/aztec-address';
20
- import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts';
20
+ import {
21
+ ContractClassPublic,
22
+ ContractInstanceWithAddress,
23
+ ExecutablePrivateFunctionWithMembershipProof,
24
+ UnconstrainedFunctionWithMembershipProof,
25
+ } from '@aztec/types/contracts';
21
26
 
22
27
  import { ArchiverDataStore, ArchiverL1SynchPoint } from '../archiver_store.js';
23
28
  import { DataRetrieval } from '../data_retrieval.js';
@@ -61,6 +66,10 @@ export class MemoryArchiverStore implements ArchiverDataStore {
61
66
 
62
67
  private contractClasses: Map<string, ContractClassPublic> = new Map();
63
68
 
69
+ private privateFunctions: Map<string, ExecutablePrivateFunctionWithMembershipProof[]> = new Map();
70
+
71
+ private unconstrainedFunctions: Map<string, UnconstrainedFunctionWithMembershipProof[]> = new Map();
72
+
64
73
  private contractInstances: Map<string, ContractInstanceWithAddress> = new Map();
65
74
 
66
75
  private lastL1BlockNewBlocks: bigint = 0n;
@@ -72,7 +81,14 @@ export class MemoryArchiverStore implements ArchiverDataStore {
72
81
  ) {}
73
82
 
74
83
  public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
75
- return Promise.resolve(this.contractClasses.get(id.toString()));
84
+ const contractClass = this.contractClasses.get(id.toString());
85
+ return Promise.resolve(
86
+ contractClass && {
87
+ ...contractClass,
88
+ privateFunctions: this.privateFunctions.get(id.toString()) ?? [],
89
+ unconstrainedFunctions: this.unconstrainedFunctions.get(id.toString()) ?? [],
90
+ },
91
+ );
76
92
  }
77
93
 
78
94
  public getContractClassIds(): Promise<Fr[]> {
@@ -83,6 +99,28 @@ export class MemoryArchiverStore implements ArchiverDataStore {
83
99
  return Promise.resolve(this.contractInstances.get(address.toString()));
84
100
  }
85
101
 
102
+ public addFunctions(
103
+ contractClassId: Fr,
104
+ newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
105
+ newUnconstrainedFunctions: UnconstrainedFunctionWithMembershipProof[],
106
+ ): Promise<boolean> {
107
+ const privateFunctions = this.privateFunctions.get(contractClassId.toString()) ?? [];
108
+ const unconstrainedFunctions = this.unconstrainedFunctions.get(contractClassId.toString()) ?? [];
109
+ const updatedPrivateFunctions = [
110
+ ...privateFunctions,
111
+ ...newPrivateFunctions.filter(newFn => !privateFunctions.find(f => f.selector.equals(newFn.selector))),
112
+ ];
113
+ const updatedUnconstrainedFunctions = [
114
+ ...unconstrainedFunctions,
115
+ ...newUnconstrainedFunctions.filter(
116
+ newFn => !unconstrainedFunctions.find(f => f.selector.equals(newFn.selector)),
117
+ ),
118
+ ];
119
+ this.privateFunctions.set(contractClassId.toString(), updatedPrivateFunctions);
120
+ this.unconstrainedFunctions.set(contractClassId.toString(), updatedUnconstrainedFunctions);
121
+ return Promise.resolve(true);
122
+ }
123
+
86
124
  public addContractClasses(data: ContractClassPublic[], _blockNumber: number): Promise<boolean> {
87
125
  for (const contractClass of data) {
88
126
  this.contractClasses.set(contractClass.id.toString(), contractClass);
@@ -175,12 +213,13 @@ export class MemoryArchiverStore implements ArchiverDataStore {
175
213
  }
176
214
 
177
215
  /**
178
- * Gets the L1 to L2 message index in the L1 to L2 message tree.
216
+ * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`.
179
217
  * @param l1ToL2Message - The L1 to L2 message.
218
+ * @param startIndex - The index to start searching from.
180
219
  * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found).
181
220
  */
182
- public getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
183
- return Promise.resolve(this.l1ToL2Messages.getMessageIndex(l1ToL2Message));
221
+ getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise<bigint | undefined> {
222
+ return Promise.resolve(this.l1ToL2Messages.getMessageIndex(l1ToL2Message, startIndex));
184
223
  }
185
224
 
186
225
  /**
@@ -357,10 +396,10 @@ export class MemoryArchiverStore implements ArchiverDataStore {
357
396
  return Promise.resolve(this.l2BlockContexts[this.l2BlockContexts.length - 1].block.number);
358
397
  }
359
398
 
360
- public getSynchedL1BlockNumbers(): Promise<ArchiverL1SynchPoint> {
399
+ public getSynchPoint(): Promise<ArchiverL1SynchPoint> {
361
400
  return Promise.resolve({
362
- blocks: this.lastL1BlockNewBlocks,
363
- messages: this.lastL1BlockNewMessages,
401
+ blocksSynchedTo: this.lastL1BlockNewBlocks,
402
+ messagesSynchedTo: this.lastL1BlockNewMessages,
364
403
  });
365
404
  }
366
405
  }