@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.
- package/dest/archiver/archiver.d.ts +7 -9
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +51 -22
- package/dest/archiver/archiver_store.d.ts +16 -11
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.js +51 -15
- package/dest/archiver/data_retrieval.js +6 -6
- package/dest/archiver/eth_log_handlers.d.ts +7 -7
- package/dest/archiver/eth_log_handlers.d.ts.map +1 -1
- package/dest/archiver/eth_log_handlers.js +11 -11
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +3 -2
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +3 -4
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +64 -13
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +9 -7
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +11 -9
- package/dest/archiver/kv_archiver_store/log_store.d.ts +3 -3
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +7 -5
- package/dest/archiver/kv_archiver_store/message_store.d.ts +3 -2
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +10 -6
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +4 -4
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +11 -8
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +11 -7
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +34 -11
- package/dest/rpc/archiver_client.d.ts.map +1 -1
- package/dest/rpc/archiver_client.js +4 -3
- package/dest/rpc/archiver_server.d.ts.map +1 -1
- package/dest/rpc/archiver_server.js +4 -3
- package/package.json +11 -9
- package/src/archiver/archiver.ts +80 -23
- package/src/archiver/archiver_store.ts +33 -11
- package/src/archiver/archiver_store_test_suite.ts +65 -18
- package/src/archiver/data_retrieval.ts +6 -6
- package/src/archiver/eth_log_handlers.ts +13 -13
- package/src/archiver/kv_archiver_store/block_store.ts +9 -1
- package/src/archiver/kv_archiver_store/contract_class_store.ts +106 -18
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +31 -12
- package/src/archiver/kv_archiver_store/log_store.ts +23 -10
- package/src/archiver/kv_archiver_store/message_store.ts +10 -5
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +10 -7
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +66 -15
- package/src/rpc/archiver_client.ts +5 -3
- package/src/rpc/archiver_server.ts +4 -2
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { InboxLeaf, L2Block, L2BlockContext, LogId, LogType, TxHash
|
|
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 {
|
|
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('
|
|
89
|
+
describe('getSynchPoint', () => {
|
|
85
90
|
it('returns 0n if no blocks have been added', async () => {
|
|
86
|
-
await expect(store.
|
|
87
|
-
|
|
88
|
-
|
|
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.
|
|
95
|
-
|
|
96
|
-
|
|
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.
|
|
106
|
-
|
|
107
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
9
|
+
getMessageSentLogs,
|
|
10
10
|
getTxsPublishedLogs,
|
|
11
11
|
processL2BlockProcessedLogs,
|
|
12
|
-
|
|
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
|
|
136
|
-
if (
|
|
135
|
+
const messageSentLogs = await getMessageSentLogs(publicClient, inboxAddress, searchStartBlock, searchEndBlock);
|
|
136
|
+
if (messageSentLogs.length === 0) {
|
|
137
137
|
break;
|
|
138
138
|
}
|
|
139
|
-
const l1ToL2Messages =
|
|
139
|
+
const l1ToL2Messages = processMessageSentLogs(messageSentLogs);
|
|
140
140
|
retrievedL1ToL2Messages.push(...l1ToL2Messages);
|
|
141
141
|
// handles the case when there are no new messages:
|
|
142
|
-
searchStartBlock = (
|
|
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
|
|
12
|
-
* @param logs -
|
|
13
|
-
* @returns Array of all processed
|
|
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
|
|
16
|
-
logs: Log<bigint, number, false, undefined, true, typeof InboxAbi, '
|
|
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 {
|
|
21
|
-
leaves.push(new InboxLeaf(
|
|
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
|
|
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 `
|
|
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 `
|
|
199
|
+
* @returns An array of `MessageSent` logs.
|
|
200
200
|
*/
|
|
201
|
-
export function
|
|
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, '
|
|
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: '
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
141
|
-
unencryptedLogs:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
218
|
-
const blocks = this.#blockStore.getSynchedL1BlockNumber();
|
|
219
|
-
const messages = this.#messageStore.getSynchedL1BlockNumber();
|
|
238
|
+
getSynchPoint(): Promise<ArchiverL1SynchPoint> {
|
|
220
239
|
return Promise.resolve({
|
|
221
|
-
|
|
222
|
-
|
|
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:
|
|
41
|
-
unencryptedLogs:
|
|
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
|
|
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()
|
|
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 =
|
|
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()
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
76
|
-
const
|
|
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
|
|