@aztec/archiver 0.23.0 → 0.26.1
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 +15 -11
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +67 -72
- package/dest/archiver/archiver_store.d.ts +37 -15
- 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 +70 -54
- package/dest/archiver/data_retrieval.d.ts +18 -8
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/data_retrieval.js +39 -14
- package/dest/archiver/eth_log_handlers.d.ts +25 -23
- package/dest/archiver/eth_log_handlers.d.ts.map +1 -1
- package/dest/archiver/eth_log_handlers.js +98 -57
- package/dest/archiver/kv_archiver_store/block_body_store.d.ts +27 -0
- package/dest/archiver/kv_archiver_store/block_body_store.d.ts.map +1 -0
- package/dest/archiver/kv_archiver_store/block_body_store.js +47 -0
- package/dest/archiver/kv_archiver_store/block_store.d.ts +19 -11
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +58 -30
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +1 -0
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +4 -1
- package/dest/archiver/kv_archiver_store/contract_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_store.js +6 -3
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +36 -16
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +52 -22
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +4 -3
- package/dest/archiver/kv_archiver_store/message_store.d.ts +9 -9
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +34 -31
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +6 -6
- 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 +18 -18
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +41 -16
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +78 -29
- package/dest/index.js +2 -2
- package/dest/rpc/archiver_client.d.ts.map +1 -1
- package/dest/rpc/archiver_client.js +3 -3
- package/dest/rpc/archiver_server.d.ts.map +1 -1
- package/dest/rpc/archiver_server.js +4 -3
- package/package.json +10 -10
- package/src/archiver/archiver.ts +625 -0
- package/src/archiver/archiver_store.ts +226 -0
- package/src/archiver/archiver_store_test_suite.ts +676 -0
- package/src/archiver/config.ts +89 -0
- package/src/archiver/data_retrieval.ts +232 -0
- package/src/archiver/eth_log_handlers.ts +342 -0
- package/src/archiver/index.ts +5 -0
- package/src/archiver/kv_archiver_store/block_body_store.ts +54 -0
- package/src/archiver/kv_archiver_store/block_store.ts +199 -0
- package/src/archiver/kv_archiver_store/contract_class_store.ts +68 -0
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +26 -0
- package/src/archiver/kv_archiver_store/contract_store.ts +98 -0
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +300 -0
- package/src/archiver/kv_archiver_store/log_store.ts +174 -0
- package/src/archiver/kv_archiver_store/message_store.ts +174 -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 +506 -0
- package/src/index.ts +55 -0
- package/src/rpc/archiver_client.ts +35 -0
- package/src/rpc/archiver_server.ts +41 -0
- package/src/rpc/index.ts +2 -0
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ExtendedContractData,
|
|
3
|
+
L1ToL2Message,
|
|
4
|
+
L2Block,
|
|
5
|
+
L2BlockContext,
|
|
6
|
+
LogId,
|
|
7
|
+
LogType,
|
|
8
|
+
TxHash,
|
|
9
|
+
UnencryptedL2Log,
|
|
10
|
+
} from '@aztec/circuit-types';
|
|
11
|
+
import '@aztec/circuit-types/jest';
|
|
12
|
+
import { AztecAddress, Fr, INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js';
|
|
13
|
+
import { makeContractClassPublic } from '@aztec/circuits.js/testing';
|
|
14
|
+
import { randomBytes } from '@aztec/foundation/crypto';
|
|
15
|
+
import { ContractClassPublic, ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts';
|
|
16
|
+
|
|
17
|
+
import { ArchiverDataStore } from './archiver_store.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param testName - The name of the test suite.
|
|
21
|
+
* @param getStore - Returns an instance of a store that's already been initialized.
|
|
22
|
+
*/
|
|
23
|
+
export function describeArchiverDataStore(testName: string, getStore: () => ArchiverDataStore) {
|
|
24
|
+
describe(testName, () => {
|
|
25
|
+
let store: ArchiverDataStore;
|
|
26
|
+
let blocks: L2Block[];
|
|
27
|
+
const blockTests: [number, number, () => L2Block[]][] = [
|
|
28
|
+
[1, 1, () => blocks.slice(0, 1)],
|
|
29
|
+
[10, 1, () => blocks.slice(9, 10)],
|
|
30
|
+
[1, 10, () => blocks.slice(0, 10)],
|
|
31
|
+
[2, 5, () => blocks.slice(1, 6)],
|
|
32
|
+
[5, 2, () => blocks.slice(4, 6)],
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
store = getStore();
|
|
37
|
+
blocks = Array.from({ length: 10 }).map((_, i) => {
|
|
38
|
+
const block = L2Block.random(i + 1);
|
|
39
|
+
block.setL1BlockNumber(BigInt(i + 1));
|
|
40
|
+
return block;
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('addBlocks', () => {
|
|
45
|
+
it('returns success when adding block bodies', async () => {
|
|
46
|
+
await expect(store.addBlockBodies(blocks.map(block => block.body))).resolves.toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('returns success when adding blocks', async () => {
|
|
50
|
+
await expect(store.addBlocks(blocks)).resolves.toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('allows duplicate blocks', async () => {
|
|
54
|
+
await store.addBlocks(blocks);
|
|
55
|
+
await expect(store.addBlocks(blocks)).resolves.toBe(true);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('getBlocks', () => {
|
|
60
|
+
beforeEach(async () => {
|
|
61
|
+
await store.addBlocks(blocks);
|
|
62
|
+
await store.addBlockBodies(blocks.map(block => block.body));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it.each(blockTests)('retrieves previously stored blocks', async (start, limit, getExpectedBlocks) => {
|
|
66
|
+
await expect(store.getBlocks(start, limit)).resolves.toEqual(getExpectedBlocks());
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('returns an empty array if no blocks are found', async () => {
|
|
70
|
+
await expect(store.getBlocks(12, 1)).resolves.toEqual([]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('throws an error if limit is invalid', async () => {
|
|
74
|
+
await expect(store.getBlocks(1, 0)).rejects.toThrowError('Invalid limit: 0');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('resets `from` to the first block if it is out of range', async () => {
|
|
78
|
+
await expect(store.getBlocks(INITIAL_L2_BLOCK_NUM - 100, 1)).resolves.toEqual(blocks.slice(0, 1));
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('getBlockNumber', () => {
|
|
83
|
+
it('returns the block number before INITIAL_L2_BLOCK_NUM if no blocks have been added', async () => {
|
|
84
|
+
await expect(store.getBlockNumber()).resolves.toEqual(INITIAL_L2_BLOCK_NUM - 1);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("returns the most recently added block's number", async () => {
|
|
88
|
+
await store.addBlocks(blocks);
|
|
89
|
+
await expect(store.getBlockNumber()).resolves.toEqual(blocks.at(-1)!.number);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('getL1BlockNumber', () => {
|
|
94
|
+
it('returns 0n if no blocks have been added', async () => {
|
|
95
|
+
await expect(store.getL1BlockNumber()).resolves.toEqual({
|
|
96
|
+
addedBlock: 0n,
|
|
97
|
+
addedMessages: 0n,
|
|
98
|
+
cancelledMessages: 0n,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('returns the L1 block number in which the most recent L2 block was published', async () => {
|
|
103
|
+
await store.addBlocks(blocks);
|
|
104
|
+
await expect(store.getL1BlockNumber()).resolves.toEqual({
|
|
105
|
+
addedBlock: blocks.at(-1)!.getL1BlockNumber(),
|
|
106
|
+
addedMessages: 0n,
|
|
107
|
+
cancelledMessages: 0n,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('returns the L1 block number that most recently added pending messages', async () => {
|
|
112
|
+
await store.addPendingL1ToL2Messages([L1ToL2Message.random(Fr.random())], 1n);
|
|
113
|
+
await expect(store.getL1BlockNumber()).resolves.toEqual({
|
|
114
|
+
addedBlock: 0n,
|
|
115
|
+
addedMessages: 1n,
|
|
116
|
+
cancelledMessages: 0n,
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
it('returns the L1 block number that most recently cancelled pending messages', async () => {
|
|
120
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
121
|
+
await store.addPendingL1ToL2Messages([message], 1n);
|
|
122
|
+
await store.cancelPendingL1ToL2EntryKeys([message.entryKey!], 2n);
|
|
123
|
+
await expect(store.getL1BlockNumber()).resolves.toEqual({
|
|
124
|
+
addedBlock: 0n,
|
|
125
|
+
addedMessages: 1n,
|
|
126
|
+
cancelledMessages: 2n,
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('addLogs', () => {
|
|
132
|
+
it('adds encrypted & unencrypted logs', async () => {
|
|
133
|
+
await expect(
|
|
134
|
+
store.addLogs(blocks[0].body.encryptedLogs, blocks[0].body.unencryptedLogs, blocks[0].number),
|
|
135
|
+
).resolves.toEqual(true);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe.each([
|
|
140
|
+
['encrypted', LogType.ENCRYPTED],
|
|
141
|
+
['unencrypted', LogType.UNENCRYPTED],
|
|
142
|
+
])('getLogs (%s)', (_, logType) => {
|
|
143
|
+
beforeEach(async () => {
|
|
144
|
+
await Promise.all(
|
|
145
|
+
blocks.map(block => store.addLogs(block.body.encryptedLogs, block.body.unencryptedLogs, block.number)),
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it.each(blockTests)('retrieves previously stored logs', async (from, limit, getExpectedBlocks) => {
|
|
150
|
+
const expectedLogs = getExpectedBlocks().map(block =>
|
|
151
|
+
logType === LogType.ENCRYPTED ? block.body.encryptedLogs : block.body.unencryptedLogs,
|
|
152
|
+
);
|
|
153
|
+
const actualLogs = await store.getLogs(from, limit, logType);
|
|
154
|
+
expect(actualLogs).toEqual(expectedLogs);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('getTxEffect', () => {
|
|
159
|
+
beforeEach(async () => {
|
|
160
|
+
await Promise.all(
|
|
161
|
+
blocks.map(block => store.addLogs(block.body.encryptedLogs, block.body.unencryptedLogs, block.number)),
|
|
162
|
+
);
|
|
163
|
+
await store.addBlocks(blocks);
|
|
164
|
+
await store.addBlockBodies(blocks.map(block => block.body));
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it.each([
|
|
168
|
+
() => blocks[0].getTx(0),
|
|
169
|
+
() => blocks[9].getTx(3),
|
|
170
|
+
() => blocks[3].getTx(1),
|
|
171
|
+
() => blocks[5].getTx(2),
|
|
172
|
+
() => blocks[1].getTx(0),
|
|
173
|
+
])('retrieves a previously stored transaction', async getExpectedTx => {
|
|
174
|
+
const expectedTx = getExpectedTx();
|
|
175
|
+
const actualTx = await store.getTxEffect(expectedTx.txHash);
|
|
176
|
+
expect(actualTx).toEqual(expectedTx);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('returns undefined if tx is not found', async () => {
|
|
180
|
+
await expect(store.getTxEffect(new TxHash(Fr.random().toBuffer()))).resolves.toBeUndefined();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('addPendingL1ToL2Messages', () => {
|
|
185
|
+
it('stores pending L1 to L2 messages', async () => {
|
|
186
|
+
await expect(store.addPendingL1ToL2Messages([L1ToL2Message.random(Fr.random())], 1n)).resolves.toEqual(true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('allows duplicate pending messages in different positions in the same block', async () => {
|
|
190
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
191
|
+
await expect(store.addPendingL1ToL2Messages([message, message], 1n)).resolves.toEqual(true);
|
|
192
|
+
|
|
193
|
+
await expect(store.getPendingL1ToL2EntryKeys(2)).resolves.toEqual([message.entryKey!, message.entryKey!]);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('allows duplicate pending messages in different blocks', async () => {
|
|
197
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
198
|
+
await expect(store.addPendingL1ToL2Messages([message], 1n)).resolves.toEqual(true);
|
|
199
|
+
await expect(store.addPendingL1ToL2Messages([message], 2n)).resolves.toEqual(true);
|
|
200
|
+
|
|
201
|
+
await expect(store.getPendingL1ToL2EntryKeys(2)).resolves.toEqual([message.entryKey!, message.entryKey!]);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('is idempotent', async () => {
|
|
205
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
206
|
+
await expect(store.addPendingL1ToL2Messages([message], 1n)).resolves.toEqual(true);
|
|
207
|
+
await expect(store.addPendingL1ToL2Messages([message], 1n)).resolves.toEqual(false);
|
|
208
|
+
await expect(store.getPendingL1ToL2EntryKeys(2)).resolves.toEqual([message.entryKey!]);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('getPendingL1ToL2EntryKeys', () => {
|
|
213
|
+
it('returns previously stored pending L1 to L2 messages', async () => {
|
|
214
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
215
|
+
await store.addPendingL1ToL2Messages([message], 1n);
|
|
216
|
+
await expect(store.getPendingL1ToL2EntryKeys(1)).resolves.toEqual([message.entryKey!]);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// TODO(@spalladino): Fix and re-enable
|
|
220
|
+
it.skip('returns messages ordered by fee', async () => {
|
|
221
|
+
const messages = Array.from({ length: 3 }, () => L1ToL2Message.random(Fr.random()));
|
|
222
|
+
// add a duplicate message
|
|
223
|
+
messages.push(messages[0]);
|
|
224
|
+
|
|
225
|
+
await store.addPendingL1ToL2Messages(messages, 1n);
|
|
226
|
+
|
|
227
|
+
messages.sort((a, b) => b.fee - a.fee);
|
|
228
|
+
await expect(store.getPendingL1ToL2EntryKeys(messages.length)).resolves.toEqual(
|
|
229
|
+
messages.map(message => message.entryKey!),
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('returns an empty array if no messages are found', async () => {
|
|
234
|
+
await expect(store.getPendingL1ToL2EntryKeys(1)).resolves.toEqual([]);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe('confirmL1ToL2EntryKeys', () => {
|
|
239
|
+
it('updates a message from pending to confirmed', async () => {
|
|
240
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
241
|
+
await store.addPendingL1ToL2Messages([message], 1n);
|
|
242
|
+
await expect(store.confirmL1ToL2EntryKeys([message.entryKey!])).resolves.toEqual(true);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('once confirmed, a message is no longer pending', async () => {
|
|
246
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
247
|
+
await store.addPendingL1ToL2Messages([message], 1n);
|
|
248
|
+
await store.confirmL1ToL2EntryKeys([message.entryKey!]);
|
|
249
|
+
await expect(store.getPendingL1ToL2EntryKeys(1)).resolves.toEqual([]);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('once confirmed a message can also be pending if added again', async () => {
|
|
253
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
254
|
+
await store.addPendingL1ToL2Messages([message], 1n);
|
|
255
|
+
await store.confirmL1ToL2EntryKeys([message.entryKey!]);
|
|
256
|
+
await store.addPendingL1ToL2Messages([message], 2n);
|
|
257
|
+
await expect(store.getPendingL1ToL2EntryKeys(2)).resolves.toEqual([message.entryKey!]);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('once confirmed a message can remain pending if more of it were pending', async () => {
|
|
261
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
262
|
+
await store.addPendingL1ToL2Messages([message, message], 1n);
|
|
263
|
+
await store.confirmL1ToL2EntryKeys([message.entryKey!]);
|
|
264
|
+
await expect(store.getPendingL1ToL2EntryKeys(1)).resolves.toEqual([message.entryKey!]);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
describe('cancelL1ToL2Messages', () => {
|
|
269
|
+
it('cancels a pending message', async () => {
|
|
270
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
271
|
+
await store.addPendingL1ToL2Messages([message], 1n);
|
|
272
|
+
await store.cancelPendingL1ToL2EntryKeys([message.entryKey!], 1n);
|
|
273
|
+
await expect(store.getPendingL1ToL2EntryKeys(1)).resolves.toEqual([]);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('cancels only one of the pending messages if duplicates exist', async () => {
|
|
277
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
278
|
+
await store.addPendingL1ToL2Messages([message, message], 1n);
|
|
279
|
+
await store.cancelPendingL1ToL2EntryKeys([message.entryKey!], 1n);
|
|
280
|
+
await expect(store.getPendingL1ToL2EntryKeys(2)).resolves.toEqual([message.entryKey]);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('once canceled a message can also be pending if added again', async () => {
|
|
284
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
285
|
+
await store.addPendingL1ToL2Messages([message], 1n);
|
|
286
|
+
|
|
287
|
+
await store.cancelPendingL1ToL2EntryKeys([message.entryKey!], 1n);
|
|
288
|
+
await expect(store.getPendingL1ToL2EntryKeys(1)).resolves.toEqual([]);
|
|
289
|
+
|
|
290
|
+
await store.addPendingL1ToL2Messages([message], 2n);
|
|
291
|
+
await expect(store.getPendingL1ToL2EntryKeys(1)).resolves.toEqual([message.entryKey!]);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('allows adding and cancelling in the same block', async () => {
|
|
295
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
296
|
+
await store.addPendingL1ToL2Messages([message], 1n);
|
|
297
|
+
await store.cancelPendingL1ToL2EntryKeys([message.entryKey!], 1n);
|
|
298
|
+
await expect(store.getPendingL1ToL2EntryKeys(1)).resolves.toEqual([]);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('allows duplicates cancellations in different positions in the same block', async () => {
|
|
302
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
303
|
+
await store.addPendingL1ToL2Messages([message, message], 1n);
|
|
304
|
+
|
|
305
|
+
await store.cancelPendingL1ToL2EntryKeys([message.entryKey!, message.entryKey!], 1n);
|
|
306
|
+
|
|
307
|
+
await expect(store.getPendingL1ToL2EntryKeys(2)).resolves.toEqual([]);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('allows duplicates cancellations in different blocks', async () => {
|
|
311
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
312
|
+
await store.addPendingL1ToL2Messages([message, message], 1n);
|
|
313
|
+
|
|
314
|
+
await store.cancelPendingL1ToL2EntryKeys([message.entryKey!], 2n);
|
|
315
|
+
await store.cancelPendingL1ToL2EntryKeys([message.entryKey!], 3n);
|
|
316
|
+
|
|
317
|
+
await expect(store.getPendingL1ToL2EntryKeys(2)).resolves.toEqual([]);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('is idempotent', async () => {
|
|
321
|
+
const message = L1ToL2Message.random(Fr.random());
|
|
322
|
+
await store.addPendingL1ToL2Messages([message, message], 1n);
|
|
323
|
+
|
|
324
|
+
await store.cancelPendingL1ToL2EntryKeys([message.entryKey!], 2n);
|
|
325
|
+
await store.cancelPendingL1ToL2EntryKeys([message.entryKey!], 2n);
|
|
326
|
+
|
|
327
|
+
await expect(store.getPendingL1ToL2EntryKeys(2)).resolves.toEqual([message.entryKey!]);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
describe('contractInstances', () => {
|
|
332
|
+
let contractInstance: ContractInstanceWithAddress;
|
|
333
|
+
const blockNum = 10;
|
|
334
|
+
|
|
335
|
+
beforeEach(async () => {
|
|
336
|
+
contractInstance = { ...SerializableContractInstance.random(), address: AztecAddress.random() };
|
|
337
|
+
await store.addContractInstances([contractInstance], blockNum);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('returns previously stored contract instances', async () => {
|
|
341
|
+
await expect(store.getContractInstance(contractInstance.address)).resolves.toMatchObject(contractInstance);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('returns undefined if contract instance is not found', async () => {
|
|
345
|
+
await expect(store.getContractInstance(AztecAddress.random())).resolves.toBeUndefined();
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
describe('contractClasses', () => {
|
|
350
|
+
let contractClass: ContractClassPublic;
|
|
351
|
+
const blockNum = 10;
|
|
352
|
+
|
|
353
|
+
beforeEach(async () => {
|
|
354
|
+
contractClass = makeContractClassPublic();
|
|
355
|
+
await store.addContractClasses([contractClass], blockNum);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('returns previously stored contract class', async () => {
|
|
359
|
+
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('returns undefined if contract class is not found', async () => {
|
|
363
|
+
await expect(store.getContractClass(Fr.random())).resolves.toBeUndefined();
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
describe('getContractData', () => {
|
|
368
|
+
let block: L2Block;
|
|
369
|
+
beforeEach(async () => {
|
|
370
|
+
block = L2Block.random(1);
|
|
371
|
+
await store.addBlocks([block]);
|
|
372
|
+
await store.addBlockBodies([block.body]);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('returns previously stored contract data', async () => {
|
|
376
|
+
await expect(store.getContractData(block.body.txEffects[0].contractData[0].contractAddress)).resolves.toEqual(
|
|
377
|
+
block.body.txEffects[0].contractData[0],
|
|
378
|
+
);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('returns undefined if contract data is not found', async () => {
|
|
382
|
+
await expect(store.getContractData(AztecAddress.random())).resolves.toBeUndefined();
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
describe('getContractDataInBlock', () => {
|
|
387
|
+
let block: L2Block;
|
|
388
|
+
beforeEach(async () => {
|
|
389
|
+
block = L2Block.random(1);
|
|
390
|
+
await store.addBlocks([block]);
|
|
391
|
+
await store.addBlockBodies([block.body]);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('returns the contract data for a known block', async () => {
|
|
395
|
+
await expect(store.getContractDataInBlock(block.number)).resolves.toEqual(
|
|
396
|
+
block.body.txEffects.flatMap(txEffect => txEffect.contractData),
|
|
397
|
+
);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('returns an empty array if contract data is not found', async () => {
|
|
401
|
+
await expect(store.getContractDataInBlock(block.number + 1)).resolves.toEqual([]);
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
describe('addExtendedContractData', () => {
|
|
406
|
+
it('stores extended contract data', async () => {
|
|
407
|
+
const block = L2Block.random(1);
|
|
408
|
+
await store.addBlocks([block]);
|
|
409
|
+
await store.addBlockBodies([block.body]);
|
|
410
|
+
await expect(store.addExtendedContractData([ExtendedContractData.random()], block.number)).resolves.toEqual(
|
|
411
|
+
true,
|
|
412
|
+
);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('stores extended contract data for an unknown block', async () => {
|
|
416
|
+
await expect(store.addExtendedContractData([ExtendedContractData.random()], 1)).resolves.toEqual(true);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('"pushes" extended contract data and does not overwrite', async () => {
|
|
420
|
+
const block = L2Block.random(1);
|
|
421
|
+
await store.addBlocks([block]);
|
|
422
|
+
await store.addBlockBodies([block.body]);
|
|
423
|
+
|
|
424
|
+
// Assuming one contract per tx, and the first two txs
|
|
425
|
+
const firstContract = ExtendedContractData.random(block.body.txEffects[0].contractData[0]);
|
|
426
|
+
await store.addExtendedContractData([firstContract], block.number);
|
|
427
|
+
|
|
428
|
+
const secondContract = ExtendedContractData.random(block.body.txEffects[1].contractData[0]);
|
|
429
|
+
await store.addExtendedContractData([secondContract], block.number);
|
|
430
|
+
|
|
431
|
+
await expect(store.getExtendedContractDataInBlock(block.number)).resolves.toEqual([
|
|
432
|
+
firstContract,
|
|
433
|
+
secondContract,
|
|
434
|
+
]);
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
describe('getExtendedContractData', () => {
|
|
439
|
+
let block: L2Block;
|
|
440
|
+
let extendedContractData: ExtendedContractData;
|
|
441
|
+
beforeEach(async () => {
|
|
442
|
+
block = L2Block.random(1);
|
|
443
|
+
extendedContractData = ExtendedContractData.random(block.body.txEffects[0].contractData[0]);
|
|
444
|
+
await store.addBlocks([block]);
|
|
445
|
+
await store.addBlockBodies([block.body]);
|
|
446
|
+
await store.addExtendedContractData([extendedContractData], block.number);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('returns previously stored extended contract data', async () => {
|
|
450
|
+
await expect(store.getExtendedContractData(extendedContractData.contractData.contractAddress)).resolves.toEqual(
|
|
451
|
+
extendedContractData,
|
|
452
|
+
);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('returns undefined if extended contract data is not found', async () => {
|
|
456
|
+
await expect(store.getExtendedContractData(AztecAddress.random())).resolves.toBeUndefined();
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
describe('getExtendedContractDataInBlock', () => {
|
|
461
|
+
let block: L2Block;
|
|
462
|
+
let extendedContractData: ExtendedContractData;
|
|
463
|
+
beforeEach(async () => {
|
|
464
|
+
block = L2Block.random(1);
|
|
465
|
+
extendedContractData = ExtendedContractData.random(block.body.txEffects[0].contractData[0]);
|
|
466
|
+
await store.addBlocks([block]);
|
|
467
|
+
await store.addBlockBodies([block.body]);
|
|
468
|
+
await store.addExtendedContractData([extendedContractData], block.number);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('returns previously stored extended contract data', async () => {
|
|
472
|
+
await expect(store.getExtendedContractDataInBlock(block.number)).resolves.toEqual([extendedContractData]);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('returns an empty array if extended contract data is not found for the block', async () => {
|
|
476
|
+
await expect(store.getExtendedContractDataInBlock(block.number + 1)).resolves.toEqual([]);
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
describe('getUnencryptedLogs', () => {
|
|
481
|
+
const txsPerBlock = 4;
|
|
482
|
+
const numPublicFunctionCalls = 3;
|
|
483
|
+
const numUnencryptedLogs = 4;
|
|
484
|
+
const numBlocks = 10;
|
|
485
|
+
let blocks: L2Block[];
|
|
486
|
+
|
|
487
|
+
beforeEach(async () => {
|
|
488
|
+
blocks = Array(numBlocks)
|
|
489
|
+
.fill(0)
|
|
490
|
+
.map((_, index: number) =>
|
|
491
|
+
L2Block.random(index + 1, txsPerBlock, 2, numPublicFunctionCalls, 2, numUnencryptedLogs),
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
await store.addBlocks(blocks);
|
|
495
|
+
await store.addBlockBodies(blocks.map(block => block.body));
|
|
496
|
+
|
|
497
|
+
await Promise.all(
|
|
498
|
+
blocks.map(block => store.addLogs(block.body.encryptedLogs, block.body.unencryptedLogs, block.number)),
|
|
499
|
+
);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('"txHash" filter param is respected', async () => {
|
|
503
|
+
// get random tx
|
|
504
|
+
const targetBlockIndex = Math.floor(Math.random() * numBlocks);
|
|
505
|
+
const targetTxIndex = Math.floor(Math.random() * txsPerBlock);
|
|
506
|
+
const targetTxHash = new L2BlockContext(blocks[targetBlockIndex]).getTxHash(targetTxIndex);
|
|
507
|
+
|
|
508
|
+
const response = await store.getUnencryptedLogs({ txHash: targetTxHash });
|
|
509
|
+
const logs = response.logs;
|
|
510
|
+
|
|
511
|
+
expect(response.maxLogsHit).toBeFalsy();
|
|
512
|
+
|
|
513
|
+
const expectedNumLogs = numPublicFunctionCalls * numUnencryptedLogs;
|
|
514
|
+
expect(logs.length).toEqual(expectedNumLogs);
|
|
515
|
+
|
|
516
|
+
const targeBlockNumber = targetBlockIndex + INITIAL_L2_BLOCK_NUM;
|
|
517
|
+
for (const log of logs) {
|
|
518
|
+
expect(log.id.blockNumber).toEqual(targeBlockNumber);
|
|
519
|
+
expect(log.id.txIndex).toEqual(targetTxIndex);
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('"fromBlock" and "toBlock" filter params are respected', async () => {
|
|
524
|
+
// Set "fromBlock" and "toBlock"
|
|
525
|
+
const fromBlock = 3;
|
|
526
|
+
const toBlock = 7;
|
|
527
|
+
|
|
528
|
+
const response = await store.getUnencryptedLogs({ fromBlock, toBlock });
|
|
529
|
+
const logs = response.logs;
|
|
530
|
+
|
|
531
|
+
expect(response.maxLogsHit).toBeFalsy();
|
|
532
|
+
|
|
533
|
+
const expectedNumLogs = txsPerBlock * numPublicFunctionCalls * numUnencryptedLogs * (toBlock - fromBlock);
|
|
534
|
+
expect(logs.length).toEqual(expectedNumLogs);
|
|
535
|
+
|
|
536
|
+
for (const log of logs) {
|
|
537
|
+
const blockNumber = log.id.blockNumber;
|
|
538
|
+
expect(blockNumber).toBeGreaterThanOrEqual(fromBlock);
|
|
539
|
+
expect(blockNumber).toBeLessThan(toBlock);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it('"contractAddress" filter param is respected', async () => {
|
|
544
|
+
// Get a random contract address from the logs
|
|
545
|
+
const targetBlockIndex = Math.floor(Math.random() * numBlocks);
|
|
546
|
+
const targetTxIndex = Math.floor(Math.random() * txsPerBlock);
|
|
547
|
+
const targetFunctionLogIndex = Math.floor(Math.random() * numPublicFunctionCalls);
|
|
548
|
+
const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs);
|
|
549
|
+
const targetContractAddress = UnencryptedL2Log.fromBuffer(
|
|
550
|
+
blocks[targetBlockIndex].body.txEffects[targetTxIndex].unencryptedLogs.functionLogs[targetFunctionLogIndex]
|
|
551
|
+
.logs[targetLogIndex],
|
|
552
|
+
).contractAddress;
|
|
553
|
+
|
|
554
|
+
const response = await store.getUnencryptedLogs({ contractAddress: targetContractAddress });
|
|
555
|
+
|
|
556
|
+
expect(response.maxLogsHit).toBeFalsy();
|
|
557
|
+
|
|
558
|
+
for (const extendedLog of response.logs) {
|
|
559
|
+
expect(extendedLog.log.contractAddress.equals(targetContractAddress)).toBeTruthy();
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it('"selector" filter param is respected', async () => {
|
|
564
|
+
// Get a random selector from the logs
|
|
565
|
+
const targetBlockIndex = Math.floor(Math.random() * numBlocks);
|
|
566
|
+
const targetTxIndex = Math.floor(Math.random() * txsPerBlock);
|
|
567
|
+
const targetFunctionLogIndex = Math.floor(Math.random() * numPublicFunctionCalls);
|
|
568
|
+
const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs);
|
|
569
|
+
const targetSelector = UnencryptedL2Log.fromBuffer(
|
|
570
|
+
blocks[targetBlockIndex].body.txEffects[targetTxIndex].unencryptedLogs.functionLogs[targetFunctionLogIndex]
|
|
571
|
+
.logs[targetLogIndex],
|
|
572
|
+
).selector;
|
|
573
|
+
|
|
574
|
+
const response = await store.getUnencryptedLogs({ selector: targetSelector });
|
|
575
|
+
|
|
576
|
+
expect(response.maxLogsHit).toBeFalsy();
|
|
577
|
+
|
|
578
|
+
for (const extendedLog of response.logs) {
|
|
579
|
+
expect(extendedLog.log.selector.equals(targetSelector)).toBeTruthy();
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it('"afterLog" filter param is respected', async () => {
|
|
584
|
+
// Get a random log as reference
|
|
585
|
+
const targetBlockIndex = Math.floor(Math.random() * numBlocks);
|
|
586
|
+
const targetTxIndex = Math.floor(Math.random() * txsPerBlock);
|
|
587
|
+
const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs);
|
|
588
|
+
|
|
589
|
+
const afterLog = new LogId(targetBlockIndex + INITIAL_L2_BLOCK_NUM, targetTxIndex, targetLogIndex);
|
|
590
|
+
|
|
591
|
+
const response = await store.getUnencryptedLogs({ afterLog });
|
|
592
|
+
const logs = response.logs;
|
|
593
|
+
|
|
594
|
+
expect(response.maxLogsHit).toBeFalsy();
|
|
595
|
+
|
|
596
|
+
for (const log of logs) {
|
|
597
|
+
const logId = log.id;
|
|
598
|
+
expect(logId.blockNumber).toBeGreaterThanOrEqual(afterLog.blockNumber);
|
|
599
|
+
if (logId.blockNumber === afterLog.blockNumber) {
|
|
600
|
+
expect(logId.txIndex).toBeGreaterThanOrEqual(afterLog.txIndex);
|
|
601
|
+
if (logId.txIndex === afterLog.txIndex) {
|
|
602
|
+
expect(logId.logIndex).toBeGreaterThan(afterLog.logIndex);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it('"txHash" filter param is ignored when "afterLog" is set', async () => {
|
|
609
|
+
// Get random txHash
|
|
610
|
+
const txHash = new TxHash(randomBytes(TxHash.SIZE));
|
|
611
|
+
const afterLog = new LogId(1, 0, 0);
|
|
612
|
+
|
|
613
|
+
const response = await store.getUnencryptedLogs({ txHash, afterLog });
|
|
614
|
+
expect(response.logs.length).toBeGreaterThan(1);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
it('intersecting works', async () => {
|
|
618
|
+
let logs = (await store.getUnencryptedLogs({ fromBlock: -10, toBlock: -5 })).logs;
|
|
619
|
+
expect(logs.length).toBe(0);
|
|
620
|
+
|
|
621
|
+
// "fromBlock" gets correctly trimmed to range and "toBlock" is exclusive
|
|
622
|
+
logs = (await store.getUnencryptedLogs({ fromBlock: -10, toBlock: 5 })).logs;
|
|
623
|
+
let blockNumbers = new Set(logs.map(log => log.id.blockNumber));
|
|
624
|
+
expect(blockNumbers).toEqual(new Set([1, 2, 3, 4]));
|
|
625
|
+
|
|
626
|
+
// "toBlock" should be exclusive
|
|
627
|
+
logs = (await store.getUnencryptedLogs({ fromBlock: 1, toBlock: 1 })).logs;
|
|
628
|
+
expect(logs.length).toBe(0);
|
|
629
|
+
|
|
630
|
+
logs = (await store.getUnencryptedLogs({ fromBlock: 10, toBlock: 5 })).logs;
|
|
631
|
+
expect(logs.length).toBe(0);
|
|
632
|
+
|
|
633
|
+
// both "fromBlock" and "toBlock" get correctly capped to range and logs from all blocks are returned
|
|
634
|
+
logs = (await store.getUnencryptedLogs({ fromBlock: -100, toBlock: +100 })).logs;
|
|
635
|
+
blockNumbers = new Set(logs.map(log => log.id.blockNumber));
|
|
636
|
+
expect(blockNumbers.size).toBe(numBlocks);
|
|
637
|
+
|
|
638
|
+
// intersecting with "afterLog" works
|
|
639
|
+
logs = (await store.getUnencryptedLogs({ fromBlock: 2, toBlock: 5, afterLog: new LogId(4, 0, 0) })).logs;
|
|
640
|
+
blockNumbers = new Set(logs.map(log => log.id.blockNumber));
|
|
641
|
+
expect(blockNumbers).toEqual(new Set([4]));
|
|
642
|
+
|
|
643
|
+
logs = (await store.getUnencryptedLogs({ toBlock: 5, afterLog: new LogId(5, 1, 0) })).logs;
|
|
644
|
+
expect(logs.length).toBe(0);
|
|
645
|
+
|
|
646
|
+
logs = (await store.getUnencryptedLogs({ fromBlock: 2, toBlock: 5, afterLog: new LogId(100, 0, 0) })).logs;
|
|
647
|
+
expect(logs.length).toBe(0);
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
it('"txIndex" and "logIndex" are respected when "afterLog.blockNumber" is equal to "fromBlock"', async () => {
|
|
651
|
+
// Get a random log as reference
|
|
652
|
+
const targetBlockIndex = Math.floor(Math.random() * numBlocks);
|
|
653
|
+
const targetTxIndex = Math.floor(Math.random() * txsPerBlock);
|
|
654
|
+
const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs);
|
|
655
|
+
|
|
656
|
+
const afterLog = new LogId(targetBlockIndex + INITIAL_L2_BLOCK_NUM, targetTxIndex, targetLogIndex);
|
|
657
|
+
|
|
658
|
+
const response = await store.getUnencryptedLogs({ afterLog, fromBlock: afterLog.blockNumber });
|
|
659
|
+
const logs = response.logs;
|
|
660
|
+
|
|
661
|
+
expect(response.maxLogsHit).toBeFalsy();
|
|
662
|
+
|
|
663
|
+
for (const log of logs) {
|
|
664
|
+
const logId = log.id;
|
|
665
|
+
expect(logId.blockNumber).toBeGreaterThanOrEqual(afterLog.blockNumber);
|
|
666
|
+
if (logId.blockNumber === afterLog.blockNumber) {
|
|
667
|
+
expect(logId.txIndex).toBeGreaterThanOrEqual(afterLog.txIndex);
|
|
668
|
+
if (logId.txIndex === afterLog.txIndex) {
|
|
669
|
+
expect(logId.logIndex).toBeGreaterThan(afterLog.logIndex);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
}
|