@aztec/archiver 0.22.0 → 0.24.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 +14 -1
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +55 -11
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +2 -2
- package/package.json +9 -8
- package/src/archiver/archiver.ts +622 -0
- package/src/archiver/archiver_store.ts +199 -0
- package/src/archiver/archiver_store_test_suite.ts +661 -0
- package/src/archiver/config.ts +89 -0
- package/src/archiver/data_retrieval.ts +187 -0
- package/src/archiver/eth_log_handlers.ts +268 -0
- package/src/archiver/index.ts +5 -0
- package/src/archiver/kv_archiver_store/block_store.ts +164 -0
- package/src/archiver/kv_archiver_store/contract_class_store.ts +64 -0
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +26 -0
- package/src/archiver/kv_archiver_store/contract_store.ts +93 -0
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +264 -0
- package/src/archiver/kv_archiver_store/log_store.ts +174 -0
- package/src/archiver/kv_archiver_store/message_store.ts +170 -0
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +91 -0
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +444 -0
- package/src/index.ts +54 -0
- package/src/rpc/archiver_client.ts +33 -0
- package/src/rpc/archiver_server.ts +37 -0
- package/src/rpc/index.ts +2 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ContractData,
|
|
3
|
+
ExtendedContractData,
|
|
4
|
+
ExtendedUnencryptedL2Log,
|
|
5
|
+
GetUnencryptedLogsResponse,
|
|
6
|
+
INITIAL_L2_BLOCK_NUM,
|
|
7
|
+
L1ToL2Message,
|
|
8
|
+
L2Block,
|
|
9
|
+
L2BlockContext,
|
|
10
|
+
L2BlockL2Logs,
|
|
11
|
+
L2Tx,
|
|
12
|
+
LogFilter,
|
|
13
|
+
LogId,
|
|
14
|
+
LogType,
|
|
15
|
+
TxHash,
|
|
16
|
+
UnencryptedL2Log,
|
|
17
|
+
} from '@aztec/circuit-types';
|
|
18
|
+
import { Fr, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js';
|
|
19
|
+
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
20
|
+
import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts';
|
|
21
|
+
|
|
22
|
+
import { ArchiverDataStore } from '../archiver_store.js';
|
|
23
|
+
import { L1ToL2MessageStore, PendingL1ToL2MessageStore } from './l1_to_l2_message_store.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Simple, in-memory implementation of an archiver data store.
|
|
27
|
+
*/
|
|
28
|
+
export class MemoryArchiverStore implements ArchiverDataStore {
|
|
29
|
+
/**
|
|
30
|
+
* An array containing all the L2 blocks that have been fetched so far.
|
|
31
|
+
*/
|
|
32
|
+
private l2BlockContexts: L2BlockContext[] = [];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* An array containing all the L2 Txs in the L2 blocks that have been fetched so far.
|
|
36
|
+
*/
|
|
37
|
+
private l2Txs: L2Tx[] = [];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* An array containing all the encrypted logs that have been fetched so far.
|
|
41
|
+
* Note: Index in the "outer" array equals to (corresponding L2 block's number - INITIAL_L2_BLOCK_NUM).
|
|
42
|
+
*/
|
|
43
|
+
private encryptedLogsPerBlock: L2BlockL2Logs[] = [];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* An array containing all the unencrypted logs that have been fetched so far.
|
|
47
|
+
* Note: Index in the "outer" array equals to (corresponding L2 block's number - INITIAL_L2_BLOCK_NUM).
|
|
48
|
+
*/
|
|
49
|
+
private unencryptedLogsPerBlock: L2BlockL2Logs[] = [];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A sparse array containing all the extended contract data that have been fetched so far.
|
|
53
|
+
*/
|
|
54
|
+
private extendedContractDataByBlock: (ExtendedContractData[] | undefined)[] = [];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A mapping of contract address to extended contract data.
|
|
58
|
+
*/
|
|
59
|
+
private extendedContractData: Map<string, ExtendedContractData> = new Map();
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Contains all the confirmed L1 to L2 messages (i.e. messages that were consumed in an L2 block)
|
|
63
|
+
* It is a map of entryKey to the corresponding L1 to L2 message and the number of times it has appeared
|
|
64
|
+
*/
|
|
65
|
+
private confirmedL1ToL2Messages: L1ToL2MessageStore = new L1ToL2MessageStore();
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Contains all the pending L1 to L2 messages (accounts for duplication of messages)
|
|
69
|
+
*/
|
|
70
|
+
private pendingL1ToL2Messages: PendingL1ToL2MessageStore = new PendingL1ToL2MessageStore();
|
|
71
|
+
|
|
72
|
+
private contractClasses: Map<string, ContractClassPublic> = new Map();
|
|
73
|
+
|
|
74
|
+
private contractInstances: Map<string, ContractInstanceWithAddress> = new Map();
|
|
75
|
+
|
|
76
|
+
private lastL1BlockAddedMessages: bigint = 0n;
|
|
77
|
+
private lastL1BlockCancelledMessages: bigint = 0n;
|
|
78
|
+
|
|
79
|
+
constructor(
|
|
80
|
+
/** The max number of logs that can be obtained in 1 "getUnencryptedLogs" call. */
|
|
81
|
+
public readonly maxLogs: number,
|
|
82
|
+
) {}
|
|
83
|
+
|
|
84
|
+
public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
|
|
85
|
+
return Promise.resolve(this.contractClasses.get(id.toString()));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public getContractInstance(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
|
|
89
|
+
return Promise.resolve(this.contractInstances.get(address.toString()));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public addContractClasses(data: ContractClassPublic[], _blockNumber: number): Promise<boolean> {
|
|
93
|
+
for (const contractClass of data) {
|
|
94
|
+
this.contractClasses.set(contractClass.id.toString(), contractClass);
|
|
95
|
+
}
|
|
96
|
+
return Promise.resolve(true);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public addContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise<boolean> {
|
|
100
|
+
for (const contractInstance of data) {
|
|
101
|
+
this.contractInstances.set(contractInstance.address.toString(), contractInstance);
|
|
102
|
+
}
|
|
103
|
+
return Promise.resolve(true);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Append new blocks to the store's list.
|
|
108
|
+
* @param blocks - The L2 blocks to be added to the store.
|
|
109
|
+
* @returns True if the operation is successful (always in this implementation).
|
|
110
|
+
*/
|
|
111
|
+
public addBlocks(blocks: L2Block[]): Promise<boolean> {
|
|
112
|
+
this.l2BlockContexts.push(...blocks.map(block => new L2BlockContext(block)));
|
|
113
|
+
this.l2Txs.push(...blocks.flatMap(b => b.getTxs()));
|
|
114
|
+
return Promise.resolve(true);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Append new logs to the store's list.
|
|
119
|
+
* @param encryptedLogs - The encrypted logs to be added to the store.
|
|
120
|
+
* @param unencryptedLogs - The unencrypted logs to be added to the store.
|
|
121
|
+
* @param blockNumber - The block for which to add the logs.
|
|
122
|
+
* @returns True if the operation is successful.
|
|
123
|
+
*/
|
|
124
|
+
addLogs(encryptedLogs: L2BlockL2Logs, unencryptedLogs: L2BlockL2Logs, blockNumber: number): Promise<boolean> {
|
|
125
|
+
if (encryptedLogs) {
|
|
126
|
+
this.encryptedLogsPerBlock[blockNumber - INITIAL_L2_BLOCK_NUM] = encryptedLogs;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (unencryptedLogs) {
|
|
130
|
+
this.unencryptedLogsPerBlock[blockNumber - INITIAL_L2_BLOCK_NUM] = unencryptedLogs;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return Promise.resolve(true);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Append new pending L1 to L2 messages to the store.
|
|
138
|
+
* @param messages - The L1 to L2 messages to be added to the store.
|
|
139
|
+
* @param l1BlockNumber - The L1 block number for which to add the messages.
|
|
140
|
+
* @returns True if the operation is successful (always in this implementation).
|
|
141
|
+
*/
|
|
142
|
+
public addPendingL1ToL2Messages(messages: L1ToL2Message[], l1BlockNumber: bigint): Promise<boolean> {
|
|
143
|
+
if (l1BlockNumber <= this.lastL1BlockAddedMessages) {
|
|
144
|
+
return Promise.resolve(false);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.lastL1BlockAddedMessages = l1BlockNumber;
|
|
148
|
+
for (const message of messages) {
|
|
149
|
+
this.pendingL1ToL2Messages.addMessage(message.entryKey!, message);
|
|
150
|
+
}
|
|
151
|
+
return Promise.resolve(true);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Remove pending L1 to L2 messages from the store (if they were cancelled).
|
|
156
|
+
* @param messages - The message keys to be removed from the store.
|
|
157
|
+
* @param l1BlockNumber - The L1 block number for which to remove the messages.
|
|
158
|
+
* @returns True if the operation is successful (always in this implementation).
|
|
159
|
+
*/
|
|
160
|
+
public cancelPendingL1ToL2Messages(messages: Fr[], l1BlockNumber: bigint): Promise<boolean> {
|
|
161
|
+
if (l1BlockNumber <= this.lastL1BlockCancelledMessages) {
|
|
162
|
+
return Promise.resolve(false);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.lastL1BlockCancelledMessages = l1BlockNumber;
|
|
166
|
+
messages.forEach(entryKey => {
|
|
167
|
+
this.pendingL1ToL2Messages.removeMessage(entryKey);
|
|
168
|
+
});
|
|
169
|
+
return Promise.resolve(true);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Messages that have been published in an L2 block are confirmed.
|
|
174
|
+
* Add them to the confirmed store, also remove them from the pending store.
|
|
175
|
+
* @param messageKeys - The message keys to be removed from the store.
|
|
176
|
+
* @returns True if the operation is successful (always in this implementation).
|
|
177
|
+
*/
|
|
178
|
+
public confirmL1ToL2Messages(messageKeys: Fr[]): Promise<boolean> {
|
|
179
|
+
messageKeys.forEach(messageKey => {
|
|
180
|
+
this.confirmedL1ToL2Messages.addMessage(messageKey, this.pendingL1ToL2Messages.getMessage(messageKey)!);
|
|
181
|
+
this.pendingL1ToL2Messages.removeMessage(messageKey);
|
|
182
|
+
});
|
|
183
|
+
return Promise.resolve(true);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Store new extended contract data from an L2 block to the store's list.
|
|
188
|
+
* @param data - List of contracts' data to be added.
|
|
189
|
+
* @param blockNum - Number of the L2 block the contract data was deployed in.
|
|
190
|
+
* @returns True if the operation is successful (always in this implementation).
|
|
191
|
+
*/
|
|
192
|
+
public addExtendedContractData(data: ExtendedContractData[], blockNum: number): Promise<boolean> {
|
|
193
|
+
// Add to the contracts mapping
|
|
194
|
+
for (const contractData of data) {
|
|
195
|
+
const key = contractData.contractData.contractAddress.toString();
|
|
196
|
+
this.extendedContractData.set(key, contractData);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Add the index per block
|
|
200
|
+
if (this.extendedContractDataByBlock[blockNum]?.length) {
|
|
201
|
+
this.extendedContractDataByBlock[blockNum]?.push(...data);
|
|
202
|
+
} else {
|
|
203
|
+
this.extendedContractDataByBlock[blockNum] = [...data];
|
|
204
|
+
}
|
|
205
|
+
return Promise.resolve(true);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Gets up to `limit` amount of L2 blocks starting from `from`.
|
|
210
|
+
* @param from - Number of the first block to return (inclusive).
|
|
211
|
+
* @param limit - The number of blocks to return.
|
|
212
|
+
* @returns The requested L2 blocks.
|
|
213
|
+
* @remarks When "from" is smaller than genesis block number, blocks from the beginning are returned.
|
|
214
|
+
*/
|
|
215
|
+
public getBlocks(from: number, limit: number): Promise<L2Block[]> {
|
|
216
|
+
// Return an empty array if we are outside of range
|
|
217
|
+
if (limit < 1) {
|
|
218
|
+
return Promise.reject(new Error(`Invalid limit: ${limit}`));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const fromIndex = Math.max(from - INITIAL_L2_BLOCK_NUM, 0);
|
|
222
|
+
if (fromIndex >= this.l2BlockContexts.length) {
|
|
223
|
+
return Promise.resolve([]);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const toIndex = fromIndex + limit;
|
|
227
|
+
return Promise.resolve(this.l2BlockContexts.slice(fromIndex, toIndex).map(blockContext => blockContext.block));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Gets an l2 tx.
|
|
232
|
+
* @param txHash - The txHash of the l2 tx.
|
|
233
|
+
* @returns The requested L2 tx.
|
|
234
|
+
*/
|
|
235
|
+
public getL2Tx(txHash: TxHash): Promise<L2Tx | undefined> {
|
|
236
|
+
const l2Tx = this.l2Txs.find(tx => tx.txHash.equals(txHash));
|
|
237
|
+
return Promise.resolve(l2Tx);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Gets up to `limit` amount of pending L1 to L2 messages, sorted by fee
|
|
242
|
+
* @param limit - The number of messages to return (by default NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).
|
|
243
|
+
* @returns The requested L1 to L2 message keys.
|
|
244
|
+
*/
|
|
245
|
+
public getPendingL1ToL2MessageKeys(limit: number = NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP): Promise<Fr[]> {
|
|
246
|
+
return Promise.resolve(this.pendingL1ToL2Messages.getMessageKeys(limit));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Gets the confirmed L1 to L2 message corresponding to the given message key.
|
|
251
|
+
* @param messageKey - The message key to look up.
|
|
252
|
+
* @returns The requested L1 to L2 message or throws if not found.
|
|
253
|
+
*/
|
|
254
|
+
public getConfirmedL1ToL2Message(messageKey: Fr): Promise<L1ToL2Message> {
|
|
255
|
+
const message = this.confirmedL1ToL2Messages.getMessage(messageKey);
|
|
256
|
+
if (!message) {
|
|
257
|
+
throw new Error(`L1 to L2 Message with key ${messageKey.toString()} not found in the confirmed messages store`);
|
|
258
|
+
}
|
|
259
|
+
return Promise.resolve(message);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Gets up to `limit` amount of logs starting from `from`.
|
|
264
|
+
* @param from - Number of the L2 block to which corresponds the first logs to be returned.
|
|
265
|
+
* @param limit - The number of logs to return.
|
|
266
|
+
* @param logType - Specifies whether to return encrypted or unencrypted logs.
|
|
267
|
+
* @returns The requested logs.
|
|
268
|
+
*/
|
|
269
|
+
getLogs(from: number, limit: number, logType: LogType): Promise<L2BlockL2Logs[]> {
|
|
270
|
+
if (from < INITIAL_L2_BLOCK_NUM || limit < 1) {
|
|
271
|
+
throw new Error(`Invalid limit: ${limit}`);
|
|
272
|
+
}
|
|
273
|
+
const logs = logType === LogType.ENCRYPTED ? this.encryptedLogsPerBlock : this.unencryptedLogsPerBlock;
|
|
274
|
+
if (from > logs.length) {
|
|
275
|
+
return Promise.resolve([]);
|
|
276
|
+
}
|
|
277
|
+
const startIndex = from - INITIAL_L2_BLOCK_NUM;
|
|
278
|
+
const endIndex = startIndex + limit;
|
|
279
|
+
return Promise.resolve(logs.slice(startIndex, endIndex));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Gets unencrypted logs based on the provided filter.
|
|
284
|
+
* @param filter - The filter to apply to the logs.
|
|
285
|
+
* @returns The requested logs.
|
|
286
|
+
* @remarks Works by doing an intersection of all params in the filter.
|
|
287
|
+
*/
|
|
288
|
+
getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
|
|
289
|
+
let txHash: TxHash | undefined;
|
|
290
|
+
let fromBlockIndex = 0;
|
|
291
|
+
let toBlockIndex = this.unencryptedLogsPerBlock.length;
|
|
292
|
+
let txIndexInBlock = 0;
|
|
293
|
+
let logIndexInTx = 0;
|
|
294
|
+
|
|
295
|
+
if (filter.afterLog) {
|
|
296
|
+
// Continuation parameter is set --> tx hash is ignored
|
|
297
|
+
if (filter.fromBlock == undefined || filter.fromBlock <= filter.afterLog.blockNumber) {
|
|
298
|
+
fromBlockIndex = filter.afterLog.blockNumber - INITIAL_L2_BLOCK_NUM;
|
|
299
|
+
txIndexInBlock = filter.afterLog.txIndex;
|
|
300
|
+
logIndexInTx = filter.afterLog.logIndex + 1; // We want to start from the next log
|
|
301
|
+
} else {
|
|
302
|
+
fromBlockIndex = filter.fromBlock - INITIAL_L2_BLOCK_NUM;
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
txHash = filter.txHash;
|
|
306
|
+
|
|
307
|
+
if (filter.fromBlock !== undefined) {
|
|
308
|
+
fromBlockIndex = filter.fromBlock - INITIAL_L2_BLOCK_NUM;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (filter.toBlock !== undefined) {
|
|
313
|
+
toBlockIndex = filter.toBlock - INITIAL_L2_BLOCK_NUM;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Ensure the indices are within block array bounds
|
|
317
|
+
fromBlockIndex = Math.max(fromBlockIndex, 0);
|
|
318
|
+
toBlockIndex = Math.min(toBlockIndex, this.unencryptedLogsPerBlock.length);
|
|
319
|
+
|
|
320
|
+
if (fromBlockIndex > this.unencryptedLogsPerBlock.length || toBlockIndex < fromBlockIndex || toBlockIndex <= 0) {
|
|
321
|
+
return Promise.resolve({
|
|
322
|
+
logs: [],
|
|
323
|
+
maxLogsHit: false,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const contractAddress = filter.contractAddress;
|
|
328
|
+
const selector = filter.selector;
|
|
329
|
+
|
|
330
|
+
const logs: ExtendedUnencryptedL2Log[] = [];
|
|
331
|
+
|
|
332
|
+
for (; fromBlockIndex < toBlockIndex; fromBlockIndex++) {
|
|
333
|
+
const blockContext = this.l2BlockContexts[fromBlockIndex];
|
|
334
|
+
const blockLogs = this.unencryptedLogsPerBlock[fromBlockIndex];
|
|
335
|
+
for (; txIndexInBlock < blockLogs.txLogs.length; txIndexInBlock++) {
|
|
336
|
+
const txLogs = blockLogs.txLogs[txIndexInBlock].unrollLogs().map(log => UnencryptedL2Log.fromBuffer(log));
|
|
337
|
+
for (; logIndexInTx < txLogs.length; logIndexInTx++) {
|
|
338
|
+
const log = txLogs[logIndexInTx];
|
|
339
|
+
if (
|
|
340
|
+
(!txHash || blockContext.getTxHash(txIndexInBlock).equals(txHash)) &&
|
|
341
|
+
(!contractAddress || log.contractAddress.equals(contractAddress)) &&
|
|
342
|
+
(!selector || log.selector.equals(selector))
|
|
343
|
+
) {
|
|
344
|
+
logs.push(
|
|
345
|
+
new ExtendedUnencryptedL2Log(new LogId(blockContext.block.number, txIndexInBlock, logIndexInTx), log),
|
|
346
|
+
);
|
|
347
|
+
if (logs.length === this.maxLogs) {
|
|
348
|
+
return Promise.resolve({
|
|
349
|
+
logs,
|
|
350
|
+
maxLogsHit: true,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
logIndexInTx = 0;
|
|
356
|
+
}
|
|
357
|
+
txIndexInBlock = 0;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return Promise.resolve({
|
|
361
|
+
logs,
|
|
362
|
+
maxLogsHit: false,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Get the extended contract data for this contract.
|
|
368
|
+
* @param contractAddress - The contract data address.
|
|
369
|
+
* @returns The extended contract data or undefined if not found.
|
|
370
|
+
*/
|
|
371
|
+
getExtendedContractData(contractAddress: AztecAddress): Promise<ExtendedContractData | undefined> {
|
|
372
|
+
const result = this.extendedContractData.get(contractAddress.toString());
|
|
373
|
+
return Promise.resolve(result);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Lookup all contract data in an L2 block.
|
|
378
|
+
* @param blockNum - The block number to get all contract data from.
|
|
379
|
+
* @returns All extended contract data in the block (if found).
|
|
380
|
+
*/
|
|
381
|
+
public getExtendedContractDataInBlock(blockNum: number): Promise<ExtendedContractData[]> {
|
|
382
|
+
if (blockNum > this.l2BlockContexts.length) {
|
|
383
|
+
return Promise.resolve([]);
|
|
384
|
+
}
|
|
385
|
+
return Promise.resolve(this.extendedContractDataByBlock[blockNum] || []);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Get basic info for an L2 contract.
|
|
390
|
+
* Contains contract address & the ethereum portal address.
|
|
391
|
+
* @param contractAddress - The contract data address.
|
|
392
|
+
* @returns ContractData with the portal address (if we didn't throw an error).
|
|
393
|
+
*/
|
|
394
|
+
public getContractData(contractAddress: AztecAddress): Promise<ContractData | undefined> {
|
|
395
|
+
if (contractAddress.isZero()) {
|
|
396
|
+
return Promise.resolve(undefined);
|
|
397
|
+
}
|
|
398
|
+
for (const blockContext of this.l2BlockContexts) {
|
|
399
|
+
for (const contractData of blockContext.block.newContractData) {
|
|
400
|
+
if (contractData.contractAddress.equals(contractAddress)) {
|
|
401
|
+
return Promise.resolve(contractData);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return Promise.resolve(undefined);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Get basic info for an all L2 contracts deployed in a block.
|
|
410
|
+
* Contains contract address & the ethereum portal address.
|
|
411
|
+
* @param l2BlockNum - Number of the L2 block where contracts were deployed.
|
|
412
|
+
* @returns ContractData with the portal address (if we didn't throw an error).
|
|
413
|
+
*/
|
|
414
|
+
public getContractDataInBlock(l2BlockNum: number): Promise<ContractData[] | undefined> {
|
|
415
|
+
if (l2BlockNum > this.l2BlockContexts.length) {
|
|
416
|
+
return Promise.resolve([]);
|
|
417
|
+
}
|
|
418
|
+
const block: L2Block | undefined = this.l2BlockContexts[l2BlockNum - INITIAL_L2_BLOCK_NUM]?.block;
|
|
419
|
+
return Promise.resolve(block?.newContractData);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Gets the number of the latest L2 block processed.
|
|
424
|
+
* @returns The number of the latest L2 block processed.
|
|
425
|
+
*/
|
|
426
|
+
public getBlockNumber(): Promise<number> {
|
|
427
|
+
if (this.l2BlockContexts.length === 0) {
|
|
428
|
+
return Promise.resolve(INITIAL_L2_BLOCK_NUM - 1);
|
|
429
|
+
}
|
|
430
|
+
return Promise.resolve(this.l2BlockContexts[this.l2BlockContexts.length - 1].block.number);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
public getL1BlockNumber() {
|
|
434
|
+
const addedBlock = this.l2BlockContexts[this.l2BlockContexts.length - 1]?.block?.getL1BlockNumber() ?? 0n;
|
|
435
|
+
const addedMessages = this.lastL1BlockAddedMessages;
|
|
436
|
+
const cancelledMessages = this.lastL1BlockCancelledMessages;
|
|
437
|
+
|
|
438
|
+
return Promise.resolve({
|
|
439
|
+
addedBlock,
|
|
440
|
+
addedMessages,
|
|
441
|
+
cancelledMessages,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { fileURLToPath } from '@aztec/foundation/url';
|
|
3
|
+
|
|
4
|
+
import { createPublicClient, http } from 'viem';
|
|
5
|
+
import { localhost } from 'viem/chains';
|
|
6
|
+
|
|
7
|
+
import { Archiver, getConfigEnvVars } from './archiver/index.js';
|
|
8
|
+
import { MemoryArchiverStore } from './archiver/memory_archiver_store/memory_archiver_store.js';
|
|
9
|
+
|
|
10
|
+
export * from './archiver/index.js';
|
|
11
|
+
export * from './rpc/index.js';
|
|
12
|
+
|
|
13
|
+
const log = createDebugLogger('aztec:archiver');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A function which instantiates and starts Archiver.
|
|
17
|
+
*/
|
|
18
|
+
// eslint-disable-next-line require-await
|
|
19
|
+
async function main() {
|
|
20
|
+
const config = getConfigEnvVars();
|
|
21
|
+
const { rpcUrl, l1Contracts } = config;
|
|
22
|
+
|
|
23
|
+
const publicClient = createPublicClient({
|
|
24
|
+
chain: localhost,
|
|
25
|
+
transport: http(rpcUrl),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const archiverStore = new MemoryArchiverStore(1000);
|
|
29
|
+
|
|
30
|
+
const archiver = new Archiver(
|
|
31
|
+
publicClient,
|
|
32
|
+
l1Contracts.rollupAddress,
|
|
33
|
+
l1Contracts.inboxAddress,
|
|
34
|
+
l1Contracts.registryAddress,
|
|
35
|
+
l1Contracts.contractDeploymentEmitterAddress,
|
|
36
|
+
archiverStore,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const shutdown = async () => {
|
|
40
|
+
await archiver.stop();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
};
|
|
43
|
+
process.once('SIGINT', shutdown);
|
|
44
|
+
process.once('SIGTERM', shutdown);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// See https://twitter.com/Rich_Harris/status/1355289863130673153
|
|
48
|
+
if (process.argv[1] === fileURLToPath(import.meta.url).replace(/\/index\.js$/, '')) {
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
50
|
+
main().catch(err => {
|
|
51
|
+
log.error(err);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ContractData,
|
|
3
|
+
EncodedContractFunction,
|
|
4
|
+
ExtendedContractData,
|
|
5
|
+
ExtendedUnencryptedL2Log,
|
|
6
|
+
L1ToL2Message,
|
|
7
|
+
L2Block,
|
|
8
|
+
L2BlockL2Logs,
|
|
9
|
+
} from '@aztec/circuit-types';
|
|
10
|
+
import { EthAddress, Fr } from '@aztec/circuits.js';
|
|
11
|
+
import { createJsonRpcClient, makeFetch } from '@aztec/foundation/json-rpc/client';
|
|
12
|
+
|
|
13
|
+
import { ArchiveSource } from '../archiver/archiver.js';
|
|
14
|
+
|
|
15
|
+
export const createArchiverClient = (url: string, fetch = makeFetch([1, 2, 3], true)): ArchiveSource =>
|
|
16
|
+
createJsonRpcClient<ArchiveSource>(
|
|
17
|
+
url,
|
|
18
|
+
{
|
|
19
|
+
ContractData,
|
|
20
|
+
EncodedContractFunction,
|
|
21
|
+
EthAddress,
|
|
22
|
+
ExtendedContractData,
|
|
23
|
+
ExtendedUnencryptedL2Log,
|
|
24
|
+
Fr,
|
|
25
|
+
L1ToL2Message,
|
|
26
|
+
L2Block,
|
|
27
|
+
L2BlockL2Logs,
|
|
28
|
+
},
|
|
29
|
+
{},
|
|
30
|
+
false,
|
|
31
|
+
'archiver',
|
|
32
|
+
fetch,
|
|
33
|
+
);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ContractData,
|
|
3
|
+
EncodedContractFunction,
|
|
4
|
+
ExtendedContractData,
|
|
5
|
+
ExtendedUnencryptedL2Log,
|
|
6
|
+
L1ToL2Message,
|
|
7
|
+
L2Block,
|
|
8
|
+
L2BlockL2Logs,
|
|
9
|
+
} from '@aztec/circuit-types';
|
|
10
|
+
import { EthAddress, Fr } from '@aztec/circuits.js';
|
|
11
|
+
import { JsonRpcServer } from '@aztec/foundation/json-rpc/server';
|
|
12
|
+
|
|
13
|
+
import { Archiver } from '../archiver/archiver.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Wrap an Archiver instance with a JSON RPC HTTP server.
|
|
17
|
+
* @param archiverService - The Archiver instance
|
|
18
|
+
* @returns An JSON-RPC HTTP server
|
|
19
|
+
*/
|
|
20
|
+
export function createArchiverRpcServer(archiverService: Archiver): JsonRpcServer {
|
|
21
|
+
return new JsonRpcServer(
|
|
22
|
+
archiverService,
|
|
23
|
+
{
|
|
24
|
+
ContractData,
|
|
25
|
+
EncodedContractFunction,
|
|
26
|
+
EthAddress,
|
|
27
|
+
ExtendedContractData,
|
|
28
|
+
ExtendedUnencryptedL2Log,
|
|
29
|
+
Fr,
|
|
30
|
+
L1ToL2Message,
|
|
31
|
+
L2Block,
|
|
32
|
+
L2BlockL2Logs,
|
|
33
|
+
},
|
|
34
|
+
{},
|
|
35
|
+
['start', 'stop'],
|
|
36
|
+
);
|
|
37
|
+
}
|
package/src/rpc/index.ts
ADDED