@aztec/archiver 0.56.0 → 0.58.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/README.md +1 -1
- package/dest/archiver/archiver.d.ts +27 -22
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +421 -115
- package/dest/archiver/archiver_store.d.ts +39 -10
- 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 +84 -31
- package/dest/archiver/config.js +6 -6
- package/dest/archiver/data_retrieval.d.ts +2 -3
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/data_retrieval.js +23 -22
- package/dest/archiver/epoch_helpers.d.ts +15 -0
- package/dest/archiver/epoch_helpers.d.ts.map +1 -0
- package/dest/archiver/epoch_helpers.js +23 -0
- package/dest/archiver/kv_archiver_store/block_store.d.ts +20 -1
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +62 -5
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +3 -3
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +12 -5
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +2 -2
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +5 -2
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +29 -10
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +57 -17
- package/dest/archiver/kv_archiver_store/log_store.d.ts +4 -5
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +18 -14
- package/dest/archiver/kv_archiver_store/message_store.d.ts +2 -1
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +17 -13
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +3 -2
- 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 +12 -14
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +23 -23
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +130 -70
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +17 -1
- package/dest/index.js +2 -1
- package/dest/test/index.d.ts +4 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +4 -0
- package/dest/test/mock_archiver.d.ts +22 -0
- package/dest/test/mock_archiver.d.ts.map +1 -0
- package/dest/test/mock_archiver.js +44 -0
- package/dest/test/mock_l1_to_l2_message_source.d.ts +16 -0
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -0
- package/dest/test/mock_l1_to_l2_message_source.js +25 -0
- package/dest/test/mock_l2_block_source.d.ts +75 -0
- package/dest/test/mock_l2_block_source.d.ts.map +1 -0
- package/dest/test/mock_l2_block_source.js +154 -0
- package/package.json +15 -11
- package/src/archiver/archiver.ts +553 -170
- package/src/archiver/archiver_store.ts +48 -19
- package/src/archiver/archiver_store_test_suite.ts +111 -73
- package/src/archiver/config.ts +5 -5
- package/src/archiver/data_retrieval.ts +25 -26
- package/src/archiver/epoch_helpers.ts +26 -0
- package/src/archiver/kv_archiver_store/block_store.ts +70 -2
- package/src/archiver/kv_archiver_store/contract_class_store.ts +24 -9
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +5 -2
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +71 -29
- package/src/archiver/kv_archiver_store/log_store.ts +18 -18
- package/src/archiver/kv_archiver_store/message_store.ts +16 -18
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +13 -19
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +154 -83
- package/src/factory.ts +18 -0
- package/src/index.ts +1 -0
- package/src/test/index.ts +3 -0
- package/src/test/mock_archiver.ts +55 -0
- package/src/test/mock_l1_to_l2_message_source.ts +31 -0
- package/src/test/mock_l2_block_source.ts +190 -0
- package/dest/archiver/kv_archiver_store/proven_store.d.ts +0 -14
- package/dest/archiver/kv_archiver_store/proven_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/proven_store.js +0 -30
- package/src/archiver/kv_archiver_store/proven_store.ts +0 -34
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type EncryptedL2BlockL2Logs,
|
|
3
|
-
type EncryptedNoteL2BlockL2Logs,
|
|
4
2
|
type FromLogType,
|
|
5
3
|
type GetUnencryptedLogsResponse,
|
|
6
4
|
type InboxLeaf,
|
|
@@ -11,19 +9,19 @@ import {
|
|
|
11
9
|
type TxEffect,
|
|
12
10
|
type TxHash,
|
|
13
11
|
type TxReceipt,
|
|
14
|
-
type UnencryptedL2BlockL2Logs,
|
|
15
12
|
} from '@aztec/circuit-types';
|
|
16
|
-
import { type Fr } from '@aztec/circuits.js';
|
|
17
|
-
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
18
|
-
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
19
13
|
import {
|
|
20
14
|
type ContractClassPublic,
|
|
21
15
|
type ContractInstanceWithAddress,
|
|
22
16
|
type ExecutablePrivateFunctionWithMembershipProof,
|
|
17
|
+
type Fr,
|
|
18
|
+
type Header,
|
|
23
19
|
type UnconstrainedFunctionWithMembershipProof,
|
|
24
|
-
} from '@aztec/
|
|
20
|
+
} from '@aztec/circuits.js';
|
|
21
|
+
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
22
|
+
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
25
23
|
|
|
26
|
-
import { type DataRetrieval
|
|
24
|
+
import { type DataRetrieval } from './structs/data_retrieval.js';
|
|
27
25
|
import { type L1Published } from './structs/published.js';
|
|
28
26
|
|
|
29
27
|
/**
|
|
@@ -50,6 +48,15 @@ export interface ArchiverDataStore {
|
|
|
50
48
|
*/
|
|
51
49
|
addBlocks(blocks: L1Published<L2Block>[]): Promise<boolean>;
|
|
52
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Unwinds blocks from the database
|
|
53
|
+
* @param from - The tip of the chain, passed for verification purposes,
|
|
54
|
+
* ensuring that we don't end up deleting something we did not intend
|
|
55
|
+
* @param blocksToUnwind - The number of blocks we are to unwind
|
|
56
|
+
* @returns True if the operation is successful
|
|
57
|
+
*/
|
|
58
|
+
unwindBlocks(from: number, blocksToUnwind: number): Promise<boolean>;
|
|
59
|
+
|
|
53
60
|
/**
|
|
54
61
|
* Gets up to `limit` amount of L2 blocks starting from `from`.
|
|
55
62
|
* @param from - Number of the first block to return (inclusive).
|
|
@@ -58,6 +65,14 @@ export interface ArchiverDataStore {
|
|
|
58
65
|
*/
|
|
59
66
|
getBlocks(from: number, limit: number): Promise<L1Published<L2Block>[]>;
|
|
60
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Gets up to `limit` amount of L2 block headers starting from `from`.
|
|
70
|
+
* @param from - Number of the first block to return (inclusive).
|
|
71
|
+
* @param limit - The number of blocks to return.
|
|
72
|
+
* @returns The requested L2 block headers.
|
|
73
|
+
*/
|
|
74
|
+
getBlockHeaders(from: number, limit: number): Promise<Header[]>;
|
|
75
|
+
|
|
61
76
|
/**
|
|
62
77
|
* Gets a tx effect.
|
|
63
78
|
* @param txHash - The txHash of the tx corresponding to the tx effect.
|
|
@@ -74,18 +89,11 @@ export interface ArchiverDataStore {
|
|
|
74
89
|
|
|
75
90
|
/**
|
|
76
91
|
* Append new logs to the store's list.
|
|
77
|
-
* @param
|
|
78
|
-
* @param encryptedLogs - The encrypted logs to be added to the store.
|
|
79
|
-
* @param unencryptedLogs - The unencrypted logs to be added to the store.
|
|
80
|
-
* @param blockNumber - The block for which to add the logs.
|
|
92
|
+
* @param blocks - The blocks for which to add the logs.
|
|
81
93
|
* @returns True if the operation is successful.
|
|
82
94
|
*/
|
|
83
|
-
addLogs(
|
|
84
|
-
|
|
85
|
-
encryptedLogs: EncryptedL2BlockL2Logs | undefined,
|
|
86
|
-
unencryptedLogs: UnencryptedL2BlockL2Logs | undefined,
|
|
87
|
-
blockNumber: number,
|
|
88
|
-
): Promise<boolean>;
|
|
95
|
+
addLogs(blocks: L2Block[]): Promise<boolean>;
|
|
96
|
+
deleteLogs(blocks: L2Block[]): Promise<boolean>;
|
|
89
97
|
|
|
90
98
|
/**
|
|
91
99
|
* Append L1 to L2 messages to the store.
|
|
@@ -109,6 +117,12 @@ export interface ArchiverDataStore {
|
|
|
109
117
|
*/
|
|
110
118
|
getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise<bigint | undefined>;
|
|
111
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Get the total number of L1 to L2 messages
|
|
122
|
+
* @returns The number of L1 to L2 messages in the store
|
|
123
|
+
*/
|
|
124
|
+
getTotalL1ToL2MessageCount(): Promise<bigint>;
|
|
125
|
+
|
|
112
126
|
/**
|
|
113
127
|
* Gets up to `limit` amount of logs starting from `from`.
|
|
114
128
|
* @param from - Number of the L2 block to which corresponds the first logs to be returned.
|
|
@@ -141,11 +155,23 @@ export interface ArchiverDataStore {
|
|
|
141
155
|
*/
|
|
142
156
|
getProvenL2BlockNumber(): Promise<number>;
|
|
143
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Gets the number of the latest proven L2 epoch.
|
|
160
|
+
* @returns The number of the latest proven L2 epoch.
|
|
161
|
+
*/
|
|
162
|
+
getProvenL2EpochNumber(): Promise<number | undefined>;
|
|
163
|
+
|
|
144
164
|
/**
|
|
145
165
|
* Stores the number of the latest proven L2 block processed.
|
|
146
166
|
* @param l2BlockNumber - The number of the latest proven L2 block processed.
|
|
147
167
|
*/
|
|
148
|
-
setProvenL2BlockNumber(l2BlockNumber:
|
|
168
|
+
setProvenL2BlockNumber(l2BlockNumber: number): Promise<void>;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Stores the number of the latest proven L2 epoch.
|
|
172
|
+
* @param l2EpochNumber - The number of the latest proven L2 epoch.
|
|
173
|
+
*/
|
|
174
|
+
setProvenL2EpochNumber(l2EpochNumber: number): Promise<void>;
|
|
149
175
|
|
|
150
176
|
/**
|
|
151
177
|
* Stores the l1 block number that blocks have been synched until
|
|
@@ -172,6 +198,8 @@ export interface ArchiverDataStore {
|
|
|
172
198
|
*/
|
|
173
199
|
addContractClasses(data: ContractClassPublic[], blockNumber: number): Promise<boolean>;
|
|
174
200
|
|
|
201
|
+
deleteContractClasses(data: ContractClassPublic[], blockNumber: number): Promise<boolean>;
|
|
202
|
+
|
|
175
203
|
/**
|
|
176
204
|
* Returns a contract class given its id, or undefined if not exists.
|
|
177
205
|
* @param id - Id of the contract class.
|
|
@@ -185,6 +213,7 @@ export interface ArchiverDataStore {
|
|
|
185
213
|
* @returns True if the operation is successful.
|
|
186
214
|
*/
|
|
187
215
|
addContractInstances(data: ContractInstanceWithAddress[], blockNumber: number): Promise<boolean>;
|
|
216
|
+
deleteContractInstances(data: ContractInstanceWithAddress[], blockNumber: number): Promise<boolean>;
|
|
188
217
|
|
|
189
218
|
/**
|
|
190
219
|
* Adds private functions to a contract class.
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { InboxLeaf, L2Block, LogId, LogType, TxHash } from '@aztec/circuit-types';
|
|
2
2
|
import '@aztec/circuit-types/jest';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AztecAddress,
|
|
5
|
+
type ContractClassPublic,
|
|
6
|
+
type ContractInstanceWithAddress,
|
|
7
|
+
Fr,
|
|
8
|
+
INITIAL_L2_BLOCK_NUM,
|
|
9
|
+
L1_TO_L2_MSG_SUBTREE_HEIGHT,
|
|
10
|
+
SerializableContractInstance,
|
|
11
|
+
} from '@aztec/circuits.js';
|
|
4
12
|
import {
|
|
5
13
|
makeContractClassPublic,
|
|
6
14
|
makeExecutablePrivateFunctionWithMembershipProof,
|
|
@@ -8,11 +16,6 @@ import {
|
|
|
8
16
|
} from '@aztec/circuits.js/testing';
|
|
9
17
|
import { times } from '@aztec/foundation/collection';
|
|
10
18
|
import { randomBytes, randomInt } from '@aztec/foundation/crypto';
|
|
11
|
-
import {
|
|
12
|
-
type ContractClassPublic,
|
|
13
|
-
type ContractInstanceWithAddress,
|
|
14
|
-
SerializableContractInstance,
|
|
15
|
-
} from '@aztec/types/contracts';
|
|
16
19
|
|
|
17
20
|
import { type ArchiverDataStore, type ArchiverL1SynchPoint } from './archiver_store.js';
|
|
18
21
|
import { type L1Published } from './structs/published.js';
|
|
@@ -52,6 +55,20 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
52
55
|
});
|
|
53
56
|
});
|
|
54
57
|
|
|
58
|
+
describe('unwindBlocks', () => {
|
|
59
|
+
it('unwinding blocks will remove blocks from the chain', async () => {
|
|
60
|
+
await store.addBlocks(blocks);
|
|
61
|
+
const blockNumber = await store.getSynchedL2BlockNumber();
|
|
62
|
+
|
|
63
|
+
expect(await store.getBlocks(blockNumber, 1)).toEqual([blocks[blocks.length - 1]]);
|
|
64
|
+
|
|
65
|
+
await store.unwindBlocks(blockNumber, 1);
|
|
66
|
+
|
|
67
|
+
expect(await store.getSynchedL2BlockNumber()).toBe(blockNumber - 1);
|
|
68
|
+
expect(await store.getBlocks(blockNumber, 1)).toEqual([]);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
55
72
|
describe('getBlocks', () => {
|
|
56
73
|
beforeEach(async () => {
|
|
57
74
|
await store.addBlocks(blocks);
|
|
@@ -69,8 +86,8 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
69
86
|
await expect(store.getBlocks(1, 0)).rejects.toThrow('Invalid limit: 0');
|
|
70
87
|
});
|
|
71
88
|
|
|
72
|
-
it('
|
|
73
|
-
await expect(store.getBlocks(INITIAL_L2_BLOCK_NUM - 100, 1)).
|
|
89
|
+
it('throws an error if `from` it is out of range', async () => {
|
|
90
|
+
await expect(store.getBlocks(INITIAL_L2_BLOCK_NUM - 100, 1)).rejects.toThrow('Invalid start: -99');
|
|
74
91
|
});
|
|
75
92
|
});
|
|
76
93
|
|
|
@@ -90,7 +107,6 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
90
107
|
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
91
108
|
blocksSynchedTo: undefined,
|
|
92
109
|
messagesSynchedTo: undefined,
|
|
93
|
-
provenLogsSynchedTo: undefined,
|
|
94
110
|
} satisfies ArchiverL1SynchPoint);
|
|
95
111
|
});
|
|
96
112
|
|
|
@@ -99,28 +115,17 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
99
115
|
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
100
116
|
blocksSynchedTo: 19n,
|
|
101
117
|
messagesSynchedTo: undefined,
|
|
102
|
-
provenLogsSynchedTo: undefined,
|
|
103
118
|
} satisfies ArchiverL1SynchPoint);
|
|
104
119
|
});
|
|
105
120
|
|
|
106
121
|
it('returns the L1 block number that most recently added messages from inbox', async () => {
|
|
107
122
|
await store.addL1ToL2Messages({
|
|
108
123
|
lastProcessedL1BlockNumber: 1n,
|
|
109
|
-
retrievedData: [new InboxLeaf(
|
|
124
|
+
retrievedData: [new InboxLeaf(1n, Fr.ZERO)],
|
|
110
125
|
});
|
|
111
126
|
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
112
127
|
blocksSynchedTo: undefined,
|
|
113
128
|
messagesSynchedTo: 1n,
|
|
114
|
-
provenLogsSynchedTo: undefined,
|
|
115
|
-
} satisfies ArchiverL1SynchPoint);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('returns the L1 block number that most recently logged a proven block', async () => {
|
|
119
|
-
await store.setProvenL2BlockNumber({ lastProcessedL1BlockNumber: 3n, retrievedData: 5 });
|
|
120
|
-
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
121
|
-
blocksSynchedTo: undefined,
|
|
122
|
-
messagesSynchedTo: undefined,
|
|
123
|
-
provenLogsSynchedTo: 3n,
|
|
124
129
|
} satisfies ArchiverL1SynchPoint);
|
|
125
130
|
});
|
|
126
131
|
});
|
|
@@ -128,14 +133,26 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
128
133
|
describe('addLogs', () => {
|
|
129
134
|
it('adds encrypted & unencrypted logs', async () => {
|
|
130
135
|
const block = blocks[0].data;
|
|
131
|
-
await expect(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
await expect(store.addLogs([block])).resolves.toEqual(true);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('deleteLogs', () => {
|
|
141
|
+
it('deletes encrypted & unencrypted logs', async () => {
|
|
142
|
+
const block = blocks[0].data;
|
|
143
|
+
await store.addBlocks([blocks[0]]);
|
|
144
|
+
await expect(store.addLogs([block])).resolves.toEqual(true);
|
|
145
|
+
|
|
146
|
+
expect((await store.getLogs(1, 1, LogType.NOTEENCRYPTED))[0]).toEqual(block.body.noteEncryptedLogs);
|
|
147
|
+
expect((await store.getLogs(1, 1, LogType.ENCRYPTED))[0]).toEqual(block.body.encryptedLogs);
|
|
148
|
+
expect((await store.getLogs(1, 1, LogType.UNENCRYPTED))[0]).toEqual(block.body.unencryptedLogs);
|
|
149
|
+
|
|
150
|
+
// This one is a pain for memory as we would never want to just delete memory in the middle.
|
|
151
|
+
await store.deleteLogs([block]);
|
|
152
|
+
|
|
153
|
+
expect((await store.getLogs(1, 1, LogType.NOTEENCRYPTED))[0]).toEqual(undefined);
|
|
154
|
+
expect((await store.getLogs(1, 1, LogType.ENCRYPTED))[0]).toEqual(undefined);
|
|
155
|
+
expect((await store.getLogs(1, 1, LogType.UNENCRYPTED))[0]).toEqual(undefined);
|
|
139
156
|
});
|
|
140
157
|
});
|
|
141
158
|
|
|
@@ -145,16 +162,8 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
145
162
|
['unencrypted', LogType.UNENCRYPTED],
|
|
146
163
|
])('getLogs (%s)', (_, logType) => {
|
|
147
164
|
beforeEach(async () => {
|
|
148
|
-
await
|
|
149
|
-
|
|
150
|
-
store.addLogs(
|
|
151
|
-
block.data.body.noteEncryptedLogs,
|
|
152
|
-
block.data.body.encryptedLogs,
|
|
153
|
-
block.data.body.unencryptedLogs,
|
|
154
|
-
block.data.number,
|
|
155
|
-
),
|
|
156
|
-
),
|
|
157
|
-
);
|
|
165
|
+
await store.addBlocks(blocks);
|
|
166
|
+
await store.addLogs(blocks.map(b => b.data));
|
|
158
167
|
});
|
|
159
168
|
|
|
160
169
|
it.each(blockTests)('retrieves previously stored logs', async (from, limit, getExpectedBlocks) => {
|
|
@@ -176,16 +185,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
176
185
|
|
|
177
186
|
describe('getTxEffect', () => {
|
|
178
187
|
beforeEach(async () => {
|
|
179
|
-
await
|
|
180
|
-
blocks.map(block =>
|
|
181
|
-
store.addLogs(
|
|
182
|
-
block.data.body.noteEncryptedLogs,
|
|
183
|
-
block.data.body.encryptedLogs,
|
|
184
|
-
block.data.body.unencryptedLogs,
|
|
185
|
-
block.data.number,
|
|
186
|
-
),
|
|
187
|
-
),
|
|
188
|
-
);
|
|
188
|
+
await store.addLogs(blocks.map(b => b.data));
|
|
189
189
|
await store.addBlocks(blocks);
|
|
190
190
|
});
|
|
191
191
|
|
|
@@ -204,6 +204,24 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
204
204
|
it('returns undefined if tx is not found', async () => {
|
|
205
205
|
await expect(store.getTxEffect(new TxHash(Fr.random().toBuffer()))).resolves.toBeUndefined();
|
|
206
206
|
});
|
|
207
|
+
|
|
208
|
+
it.each([
|
|
209
|
+
() => blocks[0].data.body.txEffects[0],
|
|
210
|
+
() => blocks[9].data.body.txEffects[3],
|
|
211
|
+
() => blocks[3].data.body.txEffects[1],
|
|
212
|
+
() => blocks[5].data.body.txEffects[2],
|
|
213
|
+
() => blocks[1].data.body.txEffects[0],
|
|
214
|
+
])('tries to retrieves a previously stored transaction after deleted', async getExpectedTx => {
|
|
215
|
+
await store.unwindBlocks(blocks.length, blocks.length);
|
|
216
|
+
|
|
217
|
+
const expectedTx = getExpectedTx();
|
|
218
|
+
const actualTx = await store.getTxEffect(expectedTx.txHash);
|
|
219
|
+
expect(actualTx).toEqual(undefined);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('returns undefined if tx is not found', async () => {
|
|
223
|
+
await expect(store.getTxEffect(new TxHash(Fr.random().toBuffer()))).resolves.toBeUndefined();
|
|
224
|
+
});
|
|
207
225
|
});
|
|
208
226
|
|
|
209
227
|
describe('L1 to L2 Messages', () => {
|
|
@@ -211,7 +229,10 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
211
229
|
const l1ToL2MessageSubtreeSize = 2 ** L1_TO_L2_MSG_SUBTREE_HEIGHT;
|
|
212
230
|
|
|
213
231
|
const generateBlockMessages = (blockNumber: bigint, numMessages: number) =>
|
|
214
|
-
Array.from(
|
|
232
|
+
Array.from(
|
|
233
|
+
{ length: numMessages },
|
|
234
|
+
(_, i) => new InboxLeaf(InboxLeaf.smallestIndexFromL2Block(blockNumber) + BigInt(i), Fr.random()),
|
|
235
|
+
);
|
|
215
236
|
|
|
216
237
|
it('returns messages in correct order', async () => {
|
|
217
238
|
const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize);
|
|
@@ -226,8 +247,9 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
226
247
|
it('throws if it is impossible to sequence messages correctly', async () => {
|
|
227
248
|
const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize - 1);
|
|
228
249
|
// We replace a message with index 4 with a message with index at the end of the tree
|
|
229
|
-
// --> with that there will be a gap and it will be impossible to sequence the
|
|
230
|
-
|
|
250
|
+
// --> with that there will be a gap and it will be impossible to sequence the
|
|
251
|
+
// end of tree = start of next tree/block - 1
|
|
252
|
+
msgs[4] = new InboxLeaf(InboxLeaf.smallestIndexFromL2Block(l2BlockNumber + 1n) - 1n, Fr.random());
|
|
231
253
|
|
|
232
254
|
await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
|
|
233
255
|
await expect(async () => {
|
|
@@ -235,26 +257,18 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
235
257
|
}).rejects.toThrow(`L1 to L2 message gap found in block ${l2BlockNumber}`);
|
|
236
258
|
});
|
|
237
259
|
|
|
238
|
-
it('throws if adding more messages than fits into a block', async () => {
|
|
239
|
-
const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize + 1);
|
|
240
|
-
|
|
241
|
-
await expect(async () => {
|
|
242
|
-
await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
|
|
243
|
-
}).rejects.toThrow(`Message index ${l1ToL2MessageSubtreeSize} out of subtree range`);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
260
|
it('correctly handles duplicate messages', async () => {
|
|
247
261
|
const messageHash = Fr.random();
|
|
248
|
-
|
|
249
|
-
const msgs = [new InboxLeaf(1n, 0n, messageHash), new InboxLeaf(2n, 0n, messageHash)];
|
|
262
|
+
const msgs = [new InboxLeaf(0n, messageHash), new InboxLeaf(16n, messageHash)];
|
|
250
263
|
|
|
251
264
|
await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
|
|
252
265
|
|
|
253
266
|
const index1 = (await store.getL1ToL2MessageIndex(messageHash, 0n))!;
|
|
267
|
+
expect(index1).toBe(0n);
|
|
254
268
|
const index2 = await store.getL1ToL2MessageIndex(messageHash, index1 + 1n);
|
|
255
|
-
|
|
256
269
|
expect(index2).toBeDefined();
|
|
257
270
|
expect(index2).toBeGreaterThan(index1);
|
|
271
|
+
expect(index2).toBe(16n);
|
|
258
272
|
});
|
|
259
273
|
});
|
|
260
274
|
|
|
@@ -274,6 +288,11 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
274
288
|
it('returns undefined if contract instance is not found', async () => {
|
|
275
289
|
await expect(store.getContractInstance(AztecAddress.random())).resolves.toBeUndefined();
|
|
276
290
|
});
|
|
291
|
+
|
|
292
|
+
it('returns undefined if previously stored contract instances was deleted', async () => {
|
|
293
|
+
await store.deleteContractInstances([contractInstance], blockNum);
|
|
294
|
+
await expect(store.getContractInstance(contractInstance.address)).resolves.toBeUndefined();
|
|
295
|
+
});
|
|
277
296
|
});
|
|
278
297
|
|
|
279
298
|
describe('contractClasses', () => {
|
|
@@ -289,6 +308,17 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
289
308
|
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
290
309
|
});
|
|
291
310
|
|
|
311
|
+
it('returns undefined if the initial deployed contract class was deleted', async () => {
|
|
312
|
+
await store.deleteContractClasses([contractClass], blockNum);
|
|
313
|
+
await expect(store.getContractClass(contractClass.id)).resolves.toBeUndefined();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('returns contract class if later "deployment" class was deleted', async () => {
|
|
317
|
+
await store.addContractClasses([contractClass], blockNum + 1);
|
|
318
|
+
await store.deleteContractClasses([contractClass], blockNum + 1);
|
|
319
|
+
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
320
|
+
});
|
|
321
|
+
|
|
292
322
|
it('returns undefined if contract class is not found', async () => {
|
|
293
323
|
await expect(store.getContractClass(Fr.random())).resolves.toBeUndefined();
|
|
294
324
|
});
|
|
@@ -338,17 +368,25 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
338
368
|
}));
|
|
339
369
|
|
|
340
370
|
await store.addBlocks(blocks);
|
|
371
|
+
await store.addLogs(blocks.map(b => b.data));
|
|
372
|
+
});
|
|
341
373
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
),
|
|
351
|
-
);
|
|
374
|
+
it('no logs returned if deleted ("txHash" filter param is respected variant)', async () => {
|
|
375
|
+
// get random tx
|
|
376
|
+
const targetBlockIndex = randomInt(numBlocks);
|
|
377
|
+
const targetTxIndex = randomInt(txsPerBlock);
|
|
378
|
+
const targetTxHash = blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash;
|
|
379
|
+
|
|
380
|
+
await Promise.all([
|
|
381
|
+
store.unwindBlocks(blocks.length, blocks.length),
|
|
382
|
+
store.deleteLogs(blocks.map(b => b.data)),
|
|
383
|
+
]);
|
|
384
|
+
|
|
385
|
+
const response = await store.getUnencryptedLogs({ txHash: targetTxHash });
|
|
386
|
+
const logs = response.logs;
|
|
387
|
+
|
|
388
|
+
expect(response.maxLogsHit).toBeFalsy();
|
|
389
|
+
expect(logs.length).toEqual(0);
|
|
352
390
|
});
|
|
353
391
|
|
|
354
392
|
it('"txHash" filter param is respected', async () => {
|
package/src/archiver/config.ts
CHANGED
|
@@ -52,11 +52,6 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
|
52
52
|
description: 'The polling interval in ms for retrieving new L2 blocks and encrypted logs.',
|
|
53
53
|
...numberConfigHelper(1000),
|
|
54
54
|
},
|
|
55
|
-
viemPollingIntervalMS: {
|
|
56
|
-
env: 'ARCHIVER_VIEM_POLLING_INTERVAL_MS',
|
|
57
|
-
description: 'The polling interval viem uses in ms',
|
|
58
|
-
...numberConfigHelper(1000),
|
|
59
|
-
},
|
|
60
55
|
dataDirectory: {
|
|
61
56
|
env: 'DATA_DIRECTORY',
|
|
62
57
|
description: 'Optional dir to store data. If omitted will store in memory.',
|
|
@@ -67,6 +62,11 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
|
67
62
|
...numberConfigHelper(1_000),
|
|
68
63
|
},
|
|
69
64
|
...l1ReaderConfigMappings,
|
|
65
|
+
viemPollingIntervalMS: {
|
|
66
|
+
env: 'ARCHIVER_VIEM_POLLING_INTERVAL_MS',
|
|
67
|
+
description: 'The polling interval viem uses in ms',
|
|
68
|
+
...numberConfigHelper(1000),
|
|
69
|
+
},
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -134,7 +134,9 @@ async function getBlockFromRollupTx(
|
|
|
134
134
|
data,
|
|
135
135
|
});
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
const allowedMethods = ['propose', 'proposeAndClaim'];
|
|
138
|
+
|
|
139
|
+
if (!allowedMethods.includes(functionName)) {
|
|
138
140
|
throw new Error(`Unexpected method called ${functionName}`);
|
|
139
141
|
}
|
|
140
142
|
const [headerHex, archiveRootHex, , , , bodyHex] = args! as readonly [Hex, Hex, Hex, Hex[], ViemSignature[], Hex];
|
|
@@ -192,8 +194,8 @@ export async function retrieveL1ToL2Messages(
|
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
for (const log of messageSentLogs) {
|
|
195
|
-
const {
|
|
196
|
-
retrievedL1ToL2Messages.push(new InboxLeaf(
|
|
197
|
+
const { index, hash } = log.args;
|
|
198
|
+
retrievedL1ToL2Messages.push(new InboxLeaf(index!, Fr.fromString(hash!)));
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
// handles the case when there are no new messages:
|
|
@@ -237,7 +239,7 @@ export async function retrieveL2ProofsFromRollup(
|
|
|
237
239
|
const lastProcessedL1BlockNumber = logs.length > 0 ? logs.at(-1)!.l1BlockNumber : searchStartBlock - 1n;
|
|
238
240
|
|
|
239
241
|
for (const { txHash, proverId, l2BlockNumber } of logs) {
|
|
240
|
-
const proofData = await
|
|
242
|
+
const proofData = await getProofFromSubmitProofTx(publicClient, txHash, proverId);
|
|
241
243
|
retrievedData.push({ proof: proofData.proof, proverId: proofData.proverId, l2BlockNumber, txHash });
|
|
242
244
|
}
|
|
243
245
|
return {
|
|
@@ -247,7 +249,6 @@ export async function retrieveL2ProofsFromRollup(
|
|
|
247
249
|
}
|
|
248
250
|
|
|
249
251
|
export type SubmitBlockProof = {
|
|
250
|
-
header: Header;
|
|
251
252
|
archiveRoot: Fr;
|
|
252
253
|
proverId: Fr;
|
|
253
254
|
aggregationObject: Buffer;
|
|
@@ -263,39 +264,37 @@ export type SubmitBlockProof = {
|
|
|
263
264
|
* @param l2BlockNum - L2 block number.
|
|
264
265
|
* @returns L2 block metadata (header and archive) from the calldata, deserialized
|
|
265
266
|
*/
|
|
266
|
-
export async function
|
|
267
|
+
export async function getProofFromSubmitProofTx(
|
|
267
268
|
publicClient: PublicClient,
|
|
268
269
|
txHash: `0x${string}`,
|
|
269
|
-
l2BlockNum: bigint,
|
|
270
270
|
expectedProverId: Fr,
|
|
271
271
|
): Promise<SubmitBlockProof> {
|
|
272
272
|
const { input: data } = await publicClient.getTransaction({ hash: txHash });
|
|
273
|
-
const { functionName, args } = decodeFunctionData({
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
273
|
+
const { functionName, args } = decodeFunctionData({ abi: RollupAbi, data });
|
|
274
|
+
|
|
275
|
+
let proverId: Fr;
|
|
276
|
+
let archiveRoot: Fr;
|
|
277
|
+
let aggregationObject: Buffer;
|
|
278
|
+
let proof: Proof;
|
|
279
|
+
|
|
280
|
+
if (functionName === 'submitEpochRootProof') {
|
|
281
|
+
const [_epochSize, nestedArgs, _fees, aggregationObjectHex, proofHex] = args!;
|
|
282
|
+
aggregationObject = Buffer.from(hexToBytes(aggregationObjectHex));
|
|
283
|
+
proverId = Fr.fromString(nestedArgs[6]);
|
|
284
|
+
archiveRoot = Fr.fromString(nestedArgs[1]);
|
|
285
|
+
proof = Proof.fromBuffer(Buffer.from(hexToBytes(proofHex)));
|
|
286
|
+
} else {
|
|
287
|
+
throw new Error(`Unexpected proof method called ${functionName}`);
|
|
280
288
|
}
|
|
281
|
-
const [headerHex, archiveHex, proverIdHex, aggregationObjectHex, proofHex] = args!;
|
|
282
|
-
|
|
283
|
-
const header = Header.fromBuffer(Buffer.from(hexToBytes(headerHex)));
|
|
284
|
-
const proverId = Fr.fromString(proverIdHex);
|
|
285
289
|
|
|
286
|
-
const blockNumberFromHeader = header.globalVariables.blockNumber.toBigInt();
|
|
287
|
-
if (blockNumberFromHeader !== l2BlockNum) {
|
|
288
|
-
throw new Error(`Block number mismatch: expected ${l2BlockNum} but got ${blockNumberFromHeader}`);
|
|
289
|
-
}
|
|
290
290
|
if (!proverId.equals(expectedProverId)) {
|
|
291
291
|
throw new Error(`Prover ID mismatch: expected ${expectedProverId} but got ${proverId}`);
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
return {
|
|
295
|
-
header,
|
|
296
295
|
proverId,
|
|
297
|
-
aggregationObject
|
|
298
|
-
archiveRoot
|
|
299
|
-
proof
|
|
296
|
+
aggregationObject,
|
|
297
|
+
archiveRoot,
|
|
298
|
+
proof,
|
|
300
299
|
};
|
|
301
300
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AZTEC_EPOCH_DURATION, AZTEC_SLOT_DURATION } from '@aztec/circuits.js';
|
|
2
|
+
|
|
3
|
+
/** Returns the slot number for a given timestamp. */
|
|
4
|
+
export function getSlotAtTimestamp(ts: bigint, constants: { l1GenesisTime: bigint }) {
|
|
5
|
+
return ts < constants.l1GenesisTime ? 0n : (ts - constants.l1GenesisTime) / BigInt(AZTEC_SLOT_DURATION);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Returns the epoch number for a given timestamp. */
|
|
9
|
+
export function getEpochNumberAtTimestamp(ts: bigint, constants: { l1GenesisTime: bigint }) {
|
|
10
|
+
return getSlotAtTimestamp(ts, constants) / BigInt(AZTEC_EPOCH_DURATION);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Returns the range of slots (inclusive) for a given epoch number. */
|
|
14
|
+
export function getSlotRangeForEpoch(epochNumber: bigint) {
|
|
15
|
+
const startSlot = epochNumber * BigInt(AZTEC_EPOCH_DURATION);
|
|
16
|
+
return [startSlot, startSlot + BigInt(AZTEC_EPOCH_DURATION) - 1n];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Returns the range of L1 timestamps (inclusive) for a given epoch number. */
|
|
20
|
+
export function getTimestampRangeForEpoch(epochNumber: bigint, constants: { l1GenesisTime: bigint }) {
|
|
21
|
+
const [startSlot, endSlot] = getSlotRangeForEpoch(epochNumber);
|
|
22
|
+
return [
|
|
23
|
+
constants.l1GenesisTime + startSlot * BigInt(AZTEC_SLOT_DURATION),
|
|
24
|
+
constants.l1GenesisTime + endSlot * BigInt(AZTEC_SLOT_DURATION),
|
|
25
|
+
];
|
|
26
|
+
}
|