@aztec/archiver 0.86.0 → 0.87.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 +62 -5
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +362 -91
- package/dest/archiver/archiver_store.d.ts +33 -17
- 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 +364 -62
- package/dest/archiver/data_retrieval.d.ts +23 -14
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/data_retrieval.js +86 -38
- package/dest/archiver/errors.d.ts +8 -0
- package/dest/archiver/errors.d.ts.map +1 -1
- package/dest/archiver/errors.js +12 -0
- package/dest/archiver/instrumentation.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.d.ts +4 -1
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +43 -6
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +3 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +17 -3
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +24 -14
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +37 -11
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +1 -1
- package/dest/archiver/kv_archiver_store/message_store.d.ts +21 -15
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +150 -48
- package/dest/archiver/structs/inbox_message.d.ts +14 -0
- package/dest/archiver/structs/inbox_message.d.ts.map +1 -0
- package/dest/archiver/structs/inbox_message.js +38 -0
- package/dest/rpc/index.d.ts +1 -1
- package/dest/test/mock_l2_block_source.d.ts +2 -0
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +8 -1
- package/dest/test/mock_structs.d.ts +9 -0
- package/dest/test/mock_structs.d.ts.map +1 -0
- package/dest/test/mock_structs.js +37 -0
- package/package.json +15 -15
- package/src/archiver/archiver.ts +431 -108
- package/src/archiver/archiver_store.ts +37 -18
- package/src/archiver/archiver_store_test_suite.ts +307 -52
- package/src/archiver/data_retrieval.ts +130 -53
- package/src/archiver/errors.ts +21 -0
- package/src/archiver/instrumentation.ts +4 -1
- package/src/archiver/kv_archiver_store/block_store.ts +46 -8
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +20 -7
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +61 -17
- package/src/archiver/kv_archiver_store/log_store.ts +6 -2
- package/src/archiver/kv_archiver_store/message_store.ts +209 -53
- package/src/archiver/structs/inbox_message.ts +40 -0
- package/src/test/mock_l2_block_source.ts +9 -1
- package/src/test/mock_structs.ts +49 -0
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +0 -23
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +0 -1
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +0 -49
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +0 -61
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { L1BlockId } from '@aztec/ethereum';
|
|
1
2
|
import type { Fr } from '@aztec/foundation/fields';
|
|
3
|
+
import type { CustomRange } from '@aztec/kv-store';
|
|
2
4
|
import type { FunctionSelector } from '@aztec/stdlib/abi';
|
|
3
5
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
4
6
|
import type { L2Block } from '@aztec/stdlib/block';
|
|
@@ -11,10 +13,9 @@ import type {
|
|
|
11
13
|
} from '@aztec/stdlib/contract';
|
|
12
14
|
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
13
15
|
import type { LogFilter, PrivateLog, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
14
|
-
import type { InboxLeaf } from '@aztec/stdlib/messaging';
|
|
15
16
|
import { BlockHeader, type IndexedTxEffect, type TxHash, type TxReceipt } from '@aztec/stdlib/tx';
|
|
16
17
|
|
|
17
|
-
import type {
|
|
18
|
+
import type { InboxMessage } from './structs/inbox_message.js';
|
|
18
19
|
import type { PublishedL2Block } from './structs/published.js';
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -23,10 +24,8 @@ import type { PublishedL2Block } from './structs/published.js';
|
|
|
23
24
|
export type ArchiverL1SynchPoint = {
|
|
24
25
|
/** Number of the last L1 block that added a new L2 block metadata. */
|
|
25
26
|
blocksSynchedTo?: bigint;
|
|
26
|
-
/**
|
|
27
|
-
messagesSynchedTo?:
|
|
28
|
-
/** Number of the last L1 block that added a new proven block. */
|
|
29
|
-
provenLogsSynchedTo?: bigint;
|
|
27
|
+
/** Last L1 block checked for L1 to L2 messages. */
|
|
28
|
+
messagesSynchedTo?: L1BlockId;
|
|
30
29
|
};
|
|
31
30
|
|
|
32
31
|
/**
|
|
@@ -34,12 +33,17 @@ export type ArchiverL1SynchPoint = {
|
|
|
34
33
|
* (blocks, encrypted logs, aztec contract data extended contract data).
|
|
35
34
|
*/
|
|
36
35
|
export interface ArchiverDataStore {
|
|
36
|
+
/** Opens a new transaction to the underlying store and runs all operations within it. */
|
|
37
|
+
transactionAsync<T>(callback: () => Promise<T>): Promise<T>;
|
|
38
|
+
|
|
37
39
|
/**
|
|
38
40
|
* Append new blocks to the store's list.
|
|
39
41
|
* @param blocks - The L2 blocks to be added to the store and the last processed L1 block.
|
|
42
|
+
* @param opts - Options for the operation.
|
|
43
|
+
* @param opts.force - If true, the blocks will be added even if they have gaps.
|
|
40
44
|
* @returns True if the operation is successful.
|
|
41
45
|
*/
|
|
42
|
-
addBlocks(blocks: PublishedL2Block[]): Promise<boolean>;
|
|
46
|
+
addBlocks(blocks: PublishedL2Block[], opts?: { force?: boolean }): Promise<boolean>;
|
|
43
47
|
|
|
44
48
|
/**
|
|
45
49
|
* Unwinds blocks from the database
|
|
@@ -51,12 +55,18 @@ export interface ArchiverDataStore {
|
|
|
51
55
|
unwindBlocks(from: number, blocksToUnwind: number): Promise<boolean>;
|
|
52
56
|
|
|
53
57
|
/**
|
|
54
|
-
*
|
|
58
|
+
* Returns the block for the given number, or undefined if not exists.
|
|
59
|
+
* @param number - The block number to return.
|
|
60
|
+
*/
|
|
61
|
+
getPublishedBlock(number: number): Promise<PublishedL2Block | undefined>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Gets up to `limit` amount of published L2 blocks starting from `from`.
|
|
55
65
|
* @param from - Number of the first block to return (inclusive).
|
|
56
66
|
* @param limit - The number of blocks to return.
|
|
57
67
|
* @returns The requested L2 blocks.
|
|
58
68
|
*/
|
|
59
|
-
|
|
69
|
+
getPublishedBlocks(from: number, limit: number): Promise<PublishedL2Block[]>;
|
|
60
70
|
|
|
61
71
|
/**
|
|
62
72
|
* Gets up to `limit` amount of L2 block headers starting from `from`.
|
|
@@ -90,10 +100,10 @@ export interface ArchiverDataStore {
|
|
|
90
100
|
|
|
91
101
|
/**
|
|
92
102
|
* Append L1 to L2 messages to the store.
|
|
93
|
-
* @param messages - The L1 to L2 messages to be added to the store
|
|
103
|
+
* @param messages - The L1 to L2 messages to be added to the store.
|
|
94
104
|
* @returns True if the operation is successful.
|
|
95
105
|
*/
|
|
96
|
-
addL1ToL2Messages(messages:
|
|
106
|
+
addL1ToL2Messages(messages: InboxMessage[]): Promise<void>;
|
|
97
107
|
|
|
98
108
|
/**
|
|
99
109
|
* Gets L1 to L2 message (to be) included in a given block.
|
|
@@ -170,10 +180,9 @@ export interface ArchiverDataStore {
|
|
|
170
180
|
setBlockSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void>;
|
|
171
181
|
|
|
172
182
|
/**
|
|
173
|
-
* Stores the l1 block
|
|
174
|
-
* @param l1BlockNumber - The l1 block number
|
|
183
|
+
* Stores the l1 block that messages have been synched until
|
|
175
184
|
*/
|
|
176
|
-
|
|
185
|
+
setMessageSynchedL1Block(l1Block: L1BlockId): Promise<void>;
|
|
177
186
|
|
|
178
187
|
/**
|
|
179
188
|
* Gets the synch point of the archiver
|
|
@@ -240,14 +249,24 @@ export interface ArchiverDataStore {
|
|
|
240
249
|
registerContractFunctionSignatures(address: AztecAddress, signatures: string[]): Promise<void>;
|
|
241
250
|
getDebugFunctionName(address: AztecAddress, selector: FunctionSelector): Promise<string | undefined>;
|
|
242
251
|
|
|
243
|
-
/**
|
|
244
|
-
|
|
245
|
-
*/
|
|
246
|
-
estimateSize(): Promise<{ mappingSize: number; actualSize: number; numItems: number }>;
|
|
252
|
+
/** Estimates the size of the store in bytes. */
|
|
253
|
+
estimateSize(): Promise<{ mappingSize: number; physicalFileSize: number; actualSize: number; numItems: number }>;
|
|
247
254
|
|
|
248
255
|
/** Backups the archiver db to the target folder. Returns the path to the db file. */
|
|
249
256
|
backupTo(path: string): Promise<string>;
|
|
250
257
|
|
|
251
258
|
/** Closes the underlying data store. */
|
|
252
259
|
close(): Promise<void>;
|
|
260
|
+
|
|
261
|
+
/** Deletes all L1 to L2 messages up until (excluding) the target L2 block number. */
|
|
262
|
+
rollbackL1ToL2MessagesToL2Block(targetBlockNumber: number | bigint): Promise<void>;
|
|
263
|
+
|
|
264
|
+
/** Returns an async iterator to all L1 to L2 messages on the range. */
|
|
265
|
+
iterateL1ToL2Messages(range?: CustomRange<bigint>): AsyncIterableIterator<InboxMessage>;
|
|
266
|
+
|
|
267
|
+
/** Removes all L1 to L2 messages starting from the given index (inclusive). */
|
|
268
|
+
removeL1ToL2Messages(startIndex: bigint): Promise<void>;
|
|
269
|
+
|
|
270
|
+
/** Returns the last L1 to L2 message stored. */
|
|
271
|
+
getLastL1ToL2Message(): Promise<InboxMessage | undefined>;
|
|
253
272
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
INITIAL_L2_BLOCK_NUM,
|
|
3
|
-
|
|
3
|
+
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
4
4
|
PRIVATE_LOG_SIZE_IN_FIELDS,
|
|
5
|
-
|
|
5
|
+
PUBLIC_LOG_SIZE_IN_FIELDS,
|
|
6
6
|
} from '@aztec/constants';
|
|
7
|
+
import { makeTuple } from '@aztec/foundation/array';
|
|
8
|
+
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
7
9
|
import { times, timesParallel } from '@aztec/foundation/collection';
|
|
8
10
|
import { randomInt } from '@aztec/foundation/crypto';
|
|
9
11
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
10
12
|
import { Fr } from '@aztec/foundation/fields';
|
|
13
|
+
import { toArray } from '@aztec/foundation/iterable';
|
|
11
14
|
import { sleep } from '@aztec/foundation/sleep';
|
|
12
15
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
13
16
|
import { L2Block, wrapInBlock } from '@aztec/stdlib/block';
|
|
@@ -27,7 +30,11 @@ import {
|
|
|
27
30
|
import '@aztec/stdlib/testing/jest';
|
|
28
31
|
import { TxEffect, TxHash } from '@aztec/stdlib/tx';
|
|
29
32
|
|
|
33
|
+
import { makeInboxMessage, makeInboxMessages } from '../test/mock_structs.js';
|
|
30
34
|
import type { ArchiverDataStore, ArchiverL1SynchPoint } from './archiver_store.js';
|
|
35
|
+
import { BlockNumberNotSequentialError, InitialBlockNumberNotSequentialError } from './errors.js';
|
|
36
|
+
import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
37
|
+
import type { InboxMessage } from './structs/inbox_message.js';
|
|
31
38
|
import type { PublishedL2Block } from './structs/published.js';
|
|
32
39
|
|
|
33
40
|
/**
|
|
@@ -85,6 +92,18 @@ export function describeArchiverDataStore(
|
|
|
85
92
|
await store.addBlocks(blocks);
|
|
86
93
|
await expect(store.addBlocks(blocks)).resolves.toBe(true);
|
|
87
94
|
});
|
|
95
|
+
|
|
96
|
+
it('throws an error if the previous block does not exist in the store', async () => {
|
|
97
|
+
const block = makePublished(await L2Block.random(2), 2);
|
|
98
|
+
await expect(store.addBlocks([block])).rejects.toThrow(InitialBlockNumberNotSequentialError);
|
|
99
|
+
await expect(store.getPublishedBlocks(1, 10)).resolves.toEqual([]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('throws an error if there is a gap in the blocks being added', async () => {
|
|
103
|
+
const blocks = [makePublished(await L2Block.random(1), 1), makePublished(await L2Block.random(3), 3)];
|
|
104
|
+
await expect(store.addBlocks(blocks)).rejects.toThrow(BlockNumberNotSequentialError);
|
|
105
|
+
await expect(store.getPublishedBlocks(1, 10)).resolves.toEqual([]);
|
|
106
|
+
});
|
|
88
107
|
});
|
|
89
108
|
|
|
90
109
|
describe('unwindBlocks', () => {
|
|
@@ -92,12 +111,12 @@ export function describeArchiverDataStore(
|
|
|
92
111
|
await store.addBlocks(blocks);
|
|
93
112
|
const blockNumber = await store.getSynchedL2BlockNumber();
|
|
94
113
|
|
|
95
|
-
expectBlocksEqual(await store.
|
|
114
|
+
expectBlocksEqual(await store.getPublishedBlocks(blockNumber, 1), [blocks[blocks.length - 1]]);
|
|
96
115
|
|
|
97
116
|
await store.unwindBlocks(blockNumber, 1);
|
|
98
117
|
|
|
99
118
|
expect(await store.getSynchedL2BlockNumber()).toBe(blockNumber - 1);
|
|
100
|
-
expect(await store.
|
|
119
|
+
expect(await store.getPublishedBlocks(blockNumber, 1)).toEqual([]);
|
|
101
120
|
});
|
|
102
121
|
|
|
103
122
|
it('can unwind multiple empty blocks', async () => {
|
|
@@ -107,7 +126,7 @@ export function describeArchiverDataStore(
|
|
|
107
126
|
|
|
108
127
|
await store.unwindBlocks(10, 3);
|
|
109
128
|
expect(await store.getSynchedL2BlockNumber()).toBe(7);
|
|
110
|
-
expect((await store.
|
|
129
|
+
expect((await store.getPublishedBlocks(1, 10)).map(b => b.block.number)).toEqual([1, 2, 3, 4, 5, 6, 7]);
|
|
111
130
|
});
|
|
112
131
|
|
|
113
132
|
it('refuses to unwind blocks if the tip is not the last block', async () => {
|
|
@@ -122,19 +141,32 @@ export function describeArchiverDataStore(
|
|
|
122
141
|
});
|
|
123
142
|
|
|
124
143
|
it.each(blockTests)('retrieves previously stored blocks', async (start, limit, getExpectedBlocks) => {
|
|
125
|
-
expectBlocksEqual(await store.
|
|
144
|
+
expectBlocksEqual(await store.getPublishedBlocks(start, limit), getExpectedBlocks());
|
|
126
145
|
});
|
|
127
146
|
|
|
128
147
|
it('returns an empty array if no blocks are found', async () => {
|
|
129
|
-
await expect(store.
|
|
148
|
+
await expect(store.getPublishedBlocks(12, 1)).resolves.toEqual([]);
|
|
130
149
|
});
|
|
131
150
|
|
|
132
151
|
it('throws an error if limit is invalid', async () => {
|
|
133
|
-
await expect(store.
|
|
152
|
+
await expect(store.getPublishedBlocks(1, 0)).rejects.toThrow('Invalid limit: 0');
|
|
134
153
|
});
|
|
135
154
|
|
|
136
155
|
it('throws an error if `from` it is out of range', async () => {
|
|
137
|
-
await expect(store.
|
|
156
|
+
await expect(store.getPublishedBlocks(INITIAL_L2_BLOCK_NUM - 100, 1)).rejects.toThrow('Invalid start: -99');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('throws an error if unexpected initial block number is found', async () => {
|
|
160
|
+
await store.addBlocks([makePublished(await L2Block.random(21), 31)], { force: true });
|
|
161
|
+
await expect(store.getPublishedBlocks(20, 1)).rejects.toThrow(`mismatch`);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('throws an error if a gap is found', async () => {
|
|
165
|
+
await store.addBlocks(
|
|
166
|
+
[makePublished(await L2Block.random(20), 30), makePublished(await L2Block.random(22), 32)],
|
|
167
|
+
{ force: true },
|
|
168
|
+
);
|
|
169
|
+
await expect(store.getPublishedBlocks(20, 2)).rejects.toThrow(`mismatch`);
|
|
138
170
|
});
|
|
139
171
|
});
|
|
140
172
|
|
|
@@ -166,13 +198,25 @@ export function describeArchiverDataStore(
|
|
|
166
198
|
});
|
|
167
199
|
|
|
168
200
|
it('returns the L1 block number that most recently added messages from inbox', async () => {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
});
|
|
201
|
+
const l1BlockHash = Buffer32.random();
|
|
202
|
+
const l1BlockNumber = 10n;
|
|
203
|
+
await store.setMessageSynchedL1Block({ l1BlockNumber: 5n, l1BlockHash: Buffer32.random() });
|
|
204
|
+
await store.addL1ToL2Messages([makeInboxMessage(Buffer16.ZERO, { l1BlockNumber, l1BlockHash })]);
|
|
173
205
|
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
174
206
|
blocksSynchedTo: undefined,
|
|
175
|
-
messagesSynchedTo:
|
|
207
|
+
messagesSynchedTo: { l1BlockHash, l1BlockNumber },
|
|
208
|
+
} satisfies ArchiverL1SynchPoint);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('returns the latest syncpoint if latest message is behind', async () => {
|
|
212
|
+
const l1BlockHash = Buffer32.random();
|
|
213
|
+
const l1BlockNumber = 10n;
|
|
214
|
+
await store.setMessageSynchedL1Block({ l1BlockNumber, l1BlockHash });
|
|
215
|
+
const msg = makeInboxMessage(Buffer16.ZERO, { l1BlockNumber: 5n, l1BlockHash: Buffer32.random() });
|
|
216
|
+
await store.addL1ToL2Messages([msg]);
|
|
217
|
+
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
218
|
+
blocksSynchedTo: undefined,
|
|
219
|
+
messagesSynchedTo: { l1BlockHash, l1BlockNumber },
|
|
176
220
|
} satisfies ArchiverL1SynchPoint);
|
|
177
221
|
});
|
|
178
222
|
});
|
|
@@ -278,36 +322,196 @@ export function describeArchiverDataStore(
|
|
|
278
322
|
});
|
|
279
323
|
|
|
280
324
|
describe('L1 to L2 Messages', () => {
|
|
281
|
-
const
|
|
282
|
-
|
|
325
|
+
const initialL2BlockNumber = 13n;
|
|
326
|
+
|
|
327
|
+
const checkMessages = async (msgs: InboxMessage[]) => {
|
|
328
|
+
expect(await store.getLastL1ToL2Message()).toEqual(msgs.at(-1));
|
|
329
|
+
expect(await toArray(store.iterateL1ToL2Messages())).toEqual(msgs);
|
|
330
|
+
expect(await store.getTotalL1ToL2MessageCount()).toEqual(BigInt(msgs.length));
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const makeInboxMessagesWithFullBlocks = (blockCount: number, opts: { initialL2BlockNumber?: bigint } = {}) =>
|
|
334
|
+
makeInboxMessages(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * blockCount, {
|
|
335
|
+
overrideFn: (msg, i) => {
|
|
336
|
+
const l2BlockNumber =
|
|
337
|
+
(opts.initialL2BlockNumber ?? initialL2BlockNumber) +
|
|
338
|
+
BigInt(Math.floor(i / NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP));
|
|
339
|
+
const index =
|
|
340
|
+
InboxLeaf.smallestIndexFromL2Block(l2BlockNumber) + BigInt(i % NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
341
|
+
return { ...msg, l2BlockNumber, index };
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('stores first message ever', async () => {
|
|
346
|
+
const msg = makeInboxMessage(Buffer16.ZERO, { index: 0n, l2BlockNumber: 1n });
|
|
347
|
+
await store.addL1ToL2Messages([msg]);
|
|
348
|
+
|
|
349
|
+
await checkMessages([msg]);
|
|
350
|
+
expect(await store.getL1ToL2Messages(1n)).toEqual([msg.leaf]);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('stores single message', async () => {
|
|
354
|
+
const msg = makeInboxMessage(Buffer16.ZERO, { l2BlockNumber: 2n });
|
|
355
|
+
await store.addL1ToL2Messages([msg]);
|
|
356
|
+
|
|
357
|
+
await checkMessages([msg]);
|
|
358
|
+
expect(await store.getL1ToL2Messages(2n)).toEqual([msg.leaf]);
|
|
359
|
+
});
|
|
283
360
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
361
|
+
it('stores and returns messages across different blocks', async () => {
|
|
362
|
+
const msgs = makeInboxMessages(5, { initialL2BlockNumber });
|
|
363
|
+
await store.addL1ToL2Messages(msgs);
|
|
364
|
+
|
|
365
|
+
await checkMessages(msgs);
|
|
366
|
+
expect(await store.getL1ToL2Messages(initialL2BlockNumber + 2n)).toEqual([msgs[2]].map(m => m.leaf));
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('stores the same messages again', async () => {
|
|
370
|
+
const msgs = makeInboxMessages(5, { initialL2BlockNumber });
|
|
371
|
+
await store.addL1ToL2Messages(msgs);
|
|
372
|
+
await store.addL1ToL2Messages(msgs.slice(2));
|
|
373
|
+
|
|
374
|
+
await checkMessages(msgs);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('stores and returns messages across different blocks with gaps', async () => {
|
|
378
|
+
const msgs1 = makeInboxMessages(3, { initialL2BlockNumber: 1n });
|
|
379
|
+
const msgs2 = makeInboxMessages(3, { initialL2BlockNumber: 20n, initialHash: msgs1.at(-1)!.rollingHash });
|
|
380
|
+
|
|
381
|
+
await store.addL1ToL2Messages(msgs1);
|
|
382
|
+
await store.addL1ToL2Messages(msgs2);
|
|
383
|
+
|
|
384
|
+
await checkMessages([...msgs1, ...msgs2]);
|
|
385
|
+
|
|
386
|
+
expect(await store.getL1ToL2Messages(1n)).toEqual([msgs1[0].leaf]);
|
|
387
|
+
expect(await store.getL1ToL2Messages(4n)).toEqual([]);
|
|
388
|
+
expect(await store.getL1ToL2Messages(20n)).toEqual([msgs2[0].leaf]);
|
|
389
|
+
expect(await store.getL1ToL2Messages(24n)).toEqual([]);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('stores and returns messages with block numbers larger than a byte', async () => {
|
|
393
|
+
const msgs = makeInboxMessages(5, { initialL2BlockNumber: 1000n });
|
|
394
|
+
await store.addL1ToL2Messages(msgs);
|
|
395
|
+
|
|
396
|
+
await checkMessages(msgs);
|
|
397
|
+
expect(await store.getL1ToL2Messages(1002n)).toEqual([msgs[2]].map(m => m.leaf));
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('stores and returns multiple messages per block', async () => {
|
|
401
|
+
const msgs = makeInboxMessagesWithFullBlocks(4);
|
|
402
|
+
await store.addL1ToL2Messages(msgs);
|
|
403
|
+
|
|
404
|
+
await checkMessages(msgs);
|
|
405
|
+
const blockMessages = await store.getL1ToL2Messages(initialL2BlockNumber + 1n);
|
|
406
|
+
expect(blockMessages).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
407
|
+
expect(blockMessages).toEqual(
|
|
408
|
+
msgs.slice(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2).map(m => m.leaf),
|
|
288
409
|
);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('stores messages in multiple operations', async () => {
|
|
413
|
+
const msgs = makeInboxMessages(20, { initialL2BlockNumber });
|
|
414
|
+
await store.addL1ToL2Messages(msgs.slice(0, 10));
|
|
415
|
+
await store.addL1ToL2Messages(msgs.slice(10, 20));
|
|
416
|
+
|
|
417
|
+
expect(await store.getL1ToL2Messages(initialL2BlockNumber + 2n)).toEqual([msgs[2]].map(m => m.leaf));
|
|
418
|
+
expect(await store.getL1ToL2Messages(initialL2BlockNumber + 12n)).toEqual([msgs[12]].map(m => m.leaf));
|
|
419
|
+
await checkMessages(msgs);
|
|
420
|
+
});
|
|
289
421
|
|
|
290
|
-
it('
|
|
291
|
-
const msgs =
|
|
292
|
-
|
|
293
|
-
await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: shuffledMessages });
|
|
294
|
-
const retrievedMessages = await store.getL1ToL2Messages(l2BlockNumber);
|
|
422
|
+
it('iterates over messages from start index', async () => {
|
|
423
|
+
const msgs = makeInboxMessages(10, { initialL2BlockNumber });
|
|
424
|
+
await store.addL1ToL2Messages(msgs);
|
|
295
425
|
|
|
296
|
-
const
|
|
297
|
-
expect(
|
|
426
|
+
const iterated = await toArray(store.iterateL1ToL2Messages({ start: msgs[3].index }));
|
|
427
|
+
expect(iterated).toEqual(msgs.slice(3));
|
|
298
428
|
});
|
|
299
429
|
|
|
300
|
-
it('
|
|
301
|
-
const msgs =
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
430
|
+
it('iterates over messages in reverse', async () => {
|
|
431
|
+
const msgs = makeInboxMessages(10, { initialL2BlockNumber });
|
|
432
|
+
await store.addL1ToL2Messages(msgs);
|
|
433
|
+
|
|
434
|
+
const iterated = await toArray(store.iterateL1ToL2Messages({ reverse: true, end: msgs[3].index }));
|
|
435
|
+
expect(iterated).toEqual(msgs.slice(0, 4).reverse());
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it('throws if messages are added out of order', async () => {
|
|
439
|
+
const msgs = makeInboxMessages(5, { overrideFn: (msg, i) => ({ ...msg, index: BigInt(10 - i) }) });
|
|
440
|
+
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('throws if block number for the first message is out of order', async () => {
|
|
444
|
+
const msgs = makeInboxMessages(4, { initialL2BlockNumber });
|
|
445
|
+
msgs[2].l2BlockNumber = initialL2BlockNumber - 1n;
|
|
446
|
+
await store.addL1ToL2Messages(msgs.slice(0, 2));
|
|
447
|
+
await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
|
|
448
|
+
});
|
|
306
449
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
450
|
+
it('throws if rolling hash is not correct', async () => {
|
|
451
|
+
const msgs = makeInboxMessages(5);
|
|
452
|
+
msgs[1].rollingHash = Buffer16.random();
|
|
453
|
+
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('throws if rolling hash for first message is not correct', async () => {
|
|
457
|
+
const msgs = makeInboxMessages(4);
|
|
458
|
+
msgs[2].rollingHash = Buffer16.random();
|
|
459
|
+
await store.addL1ToL2Messages(msgs.slice(0, 2));
|
|
460
|
+
await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('throws if index is not in the correct range', async () => {
|
|
464
|
+
const msgs = makeInboxMessages(5, { initialL2BlockNumber });
|
|
465
|
+
msgs.at(-1)!.index += 100n;
|
|
466
|
+
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('throws if first index in block has gaps', async () => {
|
|
470
|
+
const msgs = makeInboxMessages(4, { initialL2BlockNumber });
|
|
471
|
+
msgs[2].index++;
|
|
472
|
+
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('throws if index does not follow previous one', async () => {
|
|
476
|
+
const msgs = makeInboxMessages(2, {
|
|
477
|
+
initialL2BlockNumber,
|
|
478
|
+
overrideFn: (msg, i) => ({
|
|
479
|
+
...msg,
|
|
480
|
+
l2BlockNumber: 2n,
|
|
481
|
+
index: BigInt(i + NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2),
|
|
482
|
+
}),
|
|
483
|
+
});
|
|
484
|
+
msgs[1].index++;
|
|
485
|
+
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('removes messages up to the given block number', async () => {
|
|
489
|
+
const msgs = makeInboxMessagesWithFullBlocks(4, { initialL2BlockNumber: 1n });
|
|
490
|
+
|
|
491
|
+
await store.addL1ToL2Messages(msgs);
|
|
492
|
+
await checkMessages(msgs);
|
|
493
|
+
|
|
494
|
+
expect(await store.getL1ToL2Messages(1n)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
495
|
+
expect(await store.getL1ToL2Messages(2n)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
496
|
+
expect(await store.getL1ToL2Messages(3n)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
497
|
+
expect(await store.getL1ToL2Messages(4n)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
498
|
+
|
|
499
|
+
await store.rollbackL1ToL2MessagesToL2Block(2n);
|
|
500
|
+
|
|
501
|
+
expect(await store.getL1ToL2Messages(1n)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
502
|
+
expect(await store.getL1ToL2Messages(2n)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
503
|
+
expect(await store.getL1ToL2Messages(3n)).toHaveLength(0);
|
|
504
|
+
expect(await store.getL1ToL2Messages(4n)).toHaveLength(0);
|
|
505
|
+
|
|
506
|
+
await checkMessages(msgs.slice(0, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2));
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('removes messages starting with the given index', async () => {
|
|
510
|
+
const msgs = makeInboxMessagesWithFullBlocks(4, { initialL2BlockNumber: 1n });
|
|
511
|
+
await store.addL1ToL2Messages(msgs);
|
|
512
|
+
|
|
513
|
+
await store.removeL1ToL2Messages(msgs[13].index);
|
|
514
|
+
await checkMessages(msgs.slice(0, 13));
|
|
311
515
|
});
|
|
312
516
|
});
|
|
313
517
|
|
|
@@ -386,6 +590,52 @@ export function describeArchiverDataStore(
|
|
|
386
590
|
expect(fetchedInstance?.originalContractClassId).toEqual(classId);
|
|
387
591
|
expect(fetchedInstance?.currentContractClassId).toEqual(nextClassId);
|
|
388
592
|
});
|
|
593
|
+
|
|
594
|
+
it('ignores updates for the wrong contract', async () => {
|
|
595
|
+
const otherClassId = Fr.random();
|
|
596
|
+
const randomInstance = await SerializableContractInstance.random({
|
|
597
|
+
currentContractClassId: otherClassId,
|
|
598
|
+
originalContractClassId: otherClassId,
|
|
599
|
+
});
|
|
600
|
+
const otherContractInstance = {
|
|
601
|
+
...randomInstance,
|
|
602
|
+
address: await AztecAddress.random(),
|
|
603
|
+
};
|
|
604
|
+
await store.addContractInstances([otherContractInstance], 1);
|
|
605
|
+
|
|
606
|
+
const fetchedInstance = await store.getContractInstance(otherContractInstance.address, blockOfChange + 1);
|
|
607
|
+
expect(fetchedInstance?.originalContractClassId).toEqual(otherClassId);
|
|
608
|
+
expect(fetchedInstance?.currentContractClassId).toEqual(otherClassId);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it('bounds its search to the right contract if more than than one update exists', async () => {
|
|
612
|
+
const otherClassId = Fr.random();
|
|
613
|
+
const otherNextClassId = Fr.random();
|
|
614
|
+
const randomInstance = await SerializableContractInstance.random({
|
|
615
|
+
currentContractClassId: otherClassId,
|
|
616
|
+
originalContractClassId: otherNextClassId,
|
|
617
|
+
});
|
|
618
|
+
const otherContractInstance = {
|
|
619
|
+
...randomInstance,
|
|
620
|
+
address: await AztecAddress.random(),
|
|
621
|
+
};
|
|
622
|
+
await store.addContractInstances([otherContractInstance], 1);
|
|
623
|
+
await store.addContractInstanceUpdates(
|
|
624
|
+
[
|
|
625
|
+
{
|
|
626
|
+
prevContractClassId: otherClassId,
|
|
627
|
+
newContractClassId: otherNextClassId,
|
|
628
|
+
blockOfChange,
|
|
629
|
+
address: otherContractInstance.address,
|
|
630
|
+
},
|
|
631
|
+
],
|
|
632
|
+
blockOfChange - 1,
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
const fetchedInstance = await store.getContractInstance(contractInstance.address, blockOfChange + 1);
|
|
636
|
+
expect(fetchedInstance?.originalContractClassId).toEqual(classId);
|
|
637
|
+
expect(fetchedInstance?.currentContractClassId).toEqual(nextClassId);
|
|
638
|
+
});
|
|
389
639
|
});
|
|
390
640
|
|
|
391
641
|
describe('contractClasses', () => {
|
|
@@ -464,17 +714,22 @@ export function describeArchiverDataStore(
|
|
|
464
714
|
let blocks: PublishedL2Block[];
|
|
465
715
|
|
|
466
716
|
const makeTag = (blockNumber: number, txIndex: number, logIndex: number, isPublic = false) =>
|
|
467
|
-
|
|
717
|
+
blockNumber === 1 && txIndex === 0 && logIndex === 0
|
|
718
|
+
? Fr.ZERO // Shared tag
|
|
719
|
+
: new Fr((blockNumber * 100 + txIndex * 10 + logIndex) * (isPublic ? 123 : 1));
|
|
468
720
|
|
|
469
721
|
const makePrivateLog = (tag: Fr) =>
|
|
470
|
-
PrivateLog.
|
|
722
|
+
PrivateLog.from({
|
|
723
|
+
fields: makeTuple(PRIVATE_LOG_SIZE_IN_FIELDS, i => (!i ? tag : new Fr(tag.toNumber() + i))),
|
|
724
|
+
emittedLength: PRIVATE_LOG_SIZE_IN_FIELDS,
|
|
725
|
+
});
|
|
471
726
|
|
|
472
727
|
const makePublicLog = (tag: Fr) =>
|
|
473
|
-
PublicLog.
|
|
474
|
-
AztecAddress.fromNumber(1)
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
728
|
+
PublicLog.from({
|
|
729
|
+
contractAddress: AztecAddress.fromNumber(1),
|
|
730
|
+
fields: makeTuple(PUBLIC_LOG_SIZE_IN_FIELDS, i => (!i ? tag : new Fr(tag.toNumber() + i))),
|
|
731
|
+
emittedLength: PUBLIC_LOG_SIZE_IN_FIELDS,
|
|
732
|
+
});
|
|
478
733
|
|
|
479
734
|
const mockPrivateLogs = (blockNumber: number, txIndex: number) => {
|
|
480
735
|
return times(numPrivateLogsPerTx, (logIndex: number) => {
|
|
@@ -509,28 +764,28 @@ export function describeArchiverDataStore(
|
|
|
509
764
|
};
|
|
510
765
|
|
|
511
766
|
beforeEach(async () => {
|
|
512
|
-
blocks = await timesParallel(numBlocks, (index: number) => mockBlockWithLogs(index));
|
|
767
|
+
blocks = await timesParallel(numBlocks, (index: number) => mockBlockWithLogs(index + 1));
|
|
513
768
|
|
|
514
769
|
await store.addBlocks(blocks);
|
|
515
770
|
await store.addLogs(blocks.map(b => b.block));
|
|
516
771
|
});
|
|
517
772
|
|
|
518
773
|
it('is possible to batch request private logs via tags', async () => {
|
|
519
|
-
const tags = [makeTag(
|
|
774
|
+
const tags = [makeTag(2, 1, 2), makeTag(1, 2, 0)];
|
|
520
775
|
|
|
521
776
|
const logsByTags = await store.getLogsByTags(tags);
|
|
522
777
|
|
|
523
778
|
expect(logsByTags).toEqual([
|
|
524
779
|
[
|
|
525
780
|
expect.objectContaining({
|
|
526
|
-
blockNumber:
|
|
781
|
+
blockNumber: 2,
|
|
527
782
|
log: makePrivateLog(tags[0]),
|
|
528
783
|
isFromPublic: false,
|
|
529
784
|
}),
|
|
530
785
|
],
|
|
531
786
|
[
|
|
532
787
|
expect.objectContaining({
|
|
533
|
-
blockNumber:
|
|
788
|
+
blockNumber: 1,
|
|
534
789
|
log: makePrivateLog(tags[1]),
|
|
535
790
|
isFromPublic: false,
|
|
536
791
|
}),
|
|
@@ -539,20 +794,20 @@ export function describeArchiverDataStore(
|
|
|
539
794
|
});
|
|
540
795
|
|
|
541
796
|
it('is possible to batch request all logs (private and public) via tags', async () => {
|
|
542
|
-
// Tag(
|
|
543
|
-
const tags = [makeTag(
|
|
797
|
+
// Tag(1, 0, 0) is shared with the first private log and the first public log.
|
|
798
|
+
const tags = [makeTag(1, 0, 0)];
|
|
544
799
|
|
|
545
800
|
const logsByTags = await store.getLogsByTags(tags);
|
|
546
801
|
|
|
547
802
|
expect(logsByTags).toEqual([
|
|
548
803
|
[
|
|
549
804
|
expect.objectContaining({
|
|
550
|
-
blockNumber:
|
|
805
|
+
blockNumber: 1,
|
|
551
806
|
log: makePrivateLog(tags[0]),
|
|
552
807
|
isFromPublic: false,
|
|
553
808
|
}),
|
|
554
809
|
expect.objectContaining({
|
|
555
|
-
blockNumber:
|
|
810
|
+
blockNumber: 1,
|
|
556
811
|
log: makePublicLog(tags[0]),
|
|
557
812
|
isFromPublic: true,
|
|
558
813
|
}),
|