@aztec/archiver 0.56.0 → 0.57.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 +23 -20
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +353 -103
- package/dest/archiver/archiver_store.d.ts +39 -9
- 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 +75 -18
- 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 +21 -20
- 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 +2 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +11 -4
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +1 -0
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +4 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +29 -9
- 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 +1 -0
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +10 -3
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +1 -0
- 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 +4 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +23 -22
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +129 -69
- package/dest/index.js +2 -1
- package/dest/test/index.d.ts +2 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +2 -0
- package/dest/test/mock_l2_block_source.d.ts +73 -0
- package/dest/test/mock_l2_block_source.d.ts.map +1 -0
- package/dest/test/mock_l2_block_source.js +134 -0
- package/package.json +15 -11
- package/src/archiver/archiver.ts +457 -149
- package/src/archiver/archiver_store.ts +44 -16
- package/src/archiver/archiver_store_test_suite.ts +91 -52
- package/src/archiver/config.ts +5 -5
- package/src/archiver/data_retrieval.ts +23 -24
- 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 +18 -5
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +4 -0
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +65 -24
- package/src/archiver/kv_archiver_store/log_store.ts +18 -18
- package/src/archiver/kv_archiver_store/message_store.ts +9 -0
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +4 -0
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +149 -80
- package/src/index.ts +1 -0
- package/src/test/index.ts +1 -0
- package/src/test/mock_l2_block_source.ts +165 -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,9 +9,8 @@ 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';
|
|
13
|
+
import { type Fr, type Header } from '@aztec/circuits.js';
|
|
17
14
|
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
18
15
|
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
19
16
|
import {
|
|
@@ -23,7 +20,7 @@ import {
|
|
|
23
20
|
type UnconstrainedFunctionWithMembershipProof,
|
|
24
21
|
} from '@aztec/types/contracts';
|
|
25
22
|
|
|
26
|
-
import { type DataRetrieval
|
|
23
|
+
import { type DataRetrieval } from './structs/data_retrieval.js';
|
|
27
24
|
import { type L1Published } from './structs/published.js';
|
|
28
25
|
|
|
29
26
|
/**
|
|
@@ -50,6 +47,15 @@ export interface ArchiverDataStore {
|
|
|
50
47
|
*/
|
|
51
48
|
addBlocks(blocks: L1Published<L2Block>[]): Promise<boolean>;
|
|
52
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Unwinds blocks from the database
|
|
52
|
+
* @param from - The tip of the chain, passed for verification purposes,
|
|
53
|
+
* ensuring that we don't end up deleting something we did not intend
|
|
54
|
+
* @param blocksToUnwind - The number of blocks we are to unwind
|
|
55
|
+
* @returns True if the operation is successful
|
|
56
|
+
*/
|
|
57
|
+
unwindBlocks(from: number, blocksToUnwind: number): Promise<boolean>;
|
|
58
|
+
|
|
53
59
|
/**
|
|
54
60
|
* Gets up to `limit` amount of L2 blocks starting from `from`.
|
|
55
61
|
* @param from - Number of the first block to return (inclusive).
|
|
@@ -58,6 +64,14 @@ export interface ArchiverDataStore {
|
|
|
58
64
|
*/
|
|
59
65
|
getBlocks(from: number, limit: number): Promise<L1Published<L2Block>[]>;
|
|
60
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Gets up to `limit` amount of L2 block headers starting from `from`.
|
|
69
|
+
* @param from - Number of the first block to return (inclusive).
|
|
70
|
+
* @param limit - The number of blocks to return.
|
|
71
|
+
* @returns The requested L2 block headers.
|
|
72
|
+
*/
|
|
73
|
+
getBlockHeaders(from: number, limit: number): Promise<Header[]>;
|
|
74
|
+
|
|
61
75
|
/**
|
|
62
76
|
* Gets a tx effect.
|
|
63
77
|
* @param txHash - The txHash of the tx corresponding to the tx effect.
|
|
@@ -74,18 +88,11 @@ export interface ArchiverDataStore {
|
|
|
74
88
|
|
|
75
89
|
/**
|
|
76
90
|
* 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.
|
|
91
|
+
* @param blocks - The blocks for which to add the logs.
|
|
81
92
|
* @returns True if the operation is successful.
|
|
82
93
|
*/
|
|
83
|
-
addLogs(
|
|
84
|
-
|
|
85
|
-
encryptedLogs: EncryptedL2BlockL2Logs | undefined,
|
|
86
|
-
unencryptedLogs: UnencryptedL2BlockL2Logs | undefined,
|
|
87
|
-
blockNumber: number,
|
|
88
|
-
): Promise<boolean>;
|
|
94
|
+
addLogs(blocks: L2Block[]): Promise<boolean>;
|
|
95
|
+
deleteLogs(blocks: L2Block[]): Promise<boolean>;
|
|
89
96
|
|
|
90
97
|
/**
|
|
91
98
|
* Append L1 to L2 messages to the store.
|
|
@@ -109,6 +116,12 @@ export interface ArchiverDataStore {
|
|
|
109
116
|
*/
|
|
110
117
|
getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise<bigint | undefined>;
|
|
111
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Get the total number of L1 to L2 messages
|
|
121
|
+
* @returns The number of L1 to L2 messages in the store
|
|
122
|
+
*/
|
|
123
|
+
getTotalL1ToL2MessageCount(): Promise<bigint>;
|
|
124
|
+
|
|
112
125
|
/**
|
|
113
126
|
* Gets up to `limit` amount of logs starting from `from`.
|
|
114
127
|
* @param from - Number of the L2 block to which corresponds the first logs to be returned.
|
|
@@ -141,11 +154,23 @@ export interface ArchiverDataStore {
|
|
|
141
154
|
*/
|
|
142
155
|
getProvenL2BlockNumber(): Promise<number>;
|
|
143
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Gets the number of the latest proven L2 epoch.
|
|
159
|
+
* @returns The number of the latest proven L2 epoch.
|
|
160
|
+
*/
|
|
161
|
+
getProvenL2EpochNumber(): Promise<number | undefined>;
|
|
162
|
+
|
|
144
163
|
/**
|
|
145
164
|
* Stores the number of the latest proven L2 block processed.
|
|
146
165
|
* @param l2BlockNumber - The number of the latest proven L2 block processed.
|
|
147
166
|
*/
|
|
148
|
-
setProvenL2BlockNumber(l2BlockNumber:
|
|
167
|
+
setProvenL2BlockNumber(l2BlockNumber: number): Promise<void>;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Stores the number of the latest proven L2 epoch.
|
|
171
|
+
* @param l2EpochNumber - The number of the latest proven L2 epoch.
|
|
172
|
+
*/
|
|
173
|
+
setProvenL2EpochNumber(l2EpochNumber: number): Promise<void>;
|
|
149
174
|
|
|
150
175
|
/**
|
|
151
176
|
* Stores the l1 block number that blocks have been synched until
|
|
@@ -172,6 +197,8 @@ export interface ArchiverDataStore {
|
|
|
172
197
|
*/
|
|
173
198
|
addContractClasses(data: ContractClassPublic[], blockNumber: number): Promise<boolean>;
|
|
174
199
|
|
|
200
|
+
deleteContractClasses(data: ContractClassPublic[], blockNumber: number): Promise<boolean>;
|
|
201
|
+
|
|
175
202
|
/**
|
|
176
203
|
* Returns a contract class given its id, or undefined if not exists.
|
|
177
204
|
* @param id - Id of the contract class.
|
|
@@ -185,6 +212,7 @@ export interface ArchiverDataStore {
|
|
|
185
212
|
* @returns True if the operation is successful.
|
|
186
213
|
*/
|
|
187
214
|
addContractInstances(data: ContractInstanceWithAddress[], blockNumber: number): Promise<boolean>;
|
|
215
|
+
deleteContractInstances(data: ContractInstanceWithAddress[], blockNumber: number): Promise<boolean>;
|
|
188
216
|
|
|
189
217
|
/**
|
|
190
218
|
* Adds private functions to a contract class.
|
|
@@ -52,6 +52,20 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
describe('unwindBlocks', () => {
|
|
56
|
+
it('unwinding blocks will remove blocks from the chain', async () => {
|
|
57
|
+
await store.addBlocks(blocks);
|
|
58
|
+
const blockNumber = await store.getSynchedL2BlockNumber();
|
|
59
|
+
|
|
60
|
+
expect(await store.getBlocks(blockNumber, 1)).toEqual([blocks[blocks.length - 1]]);
|
|
61
|
+
|
|
62
|
+
await store.unwindBlocks(blockNumber, 1);
|
|
63
|
+
|
|
64
|
+
expect(await store.getSynchedL2BlockNumber()).toBe(blockNumber - 1);
|
|
65
|
+
expect(await store.getBlocks(blockNumber, 1)).toEqual([]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
55
69
|
describe('getBlocks', () => {
|
|
56
70
|
beforeEach(async () => {
|
|
57
71
|
await store.addBlocks(blocks);
|
|
@@ -69,8 +83,8 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
69
83
|
await expect(store.getBlocks(1, 0)).rejects.toThrow('Invalid limit: 0');
|
|
70
84
|
});
|
|
71
85
|
|
|
72
|
-
it('
|
|
73
|
-
await expect(store.getBlocks(INITIAL_L2_BLOCK_NUM - 100, 1)).
|
|
86
|
+
it('throws an error if `from` it is out of range', async () => {
|
|
87
|
+
await expect(store.getBlocks(INITIAL_L2_BLOCK_NUM - 100, 1)).rejects.toThrow('Invalid start: -99');
|
|
74
88
|
});
|
|
75
89
|
});
|
|
76
90
|
|
|
@@ -90,7 +104,6 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
90
104
|
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
91
105
|
blocksSynchedTo: undefined,
|
|
92
106
|
messagesSynchedTo: undefined,
|
|
93
|
-
provenLogsSynchedTo: undefined,
|
|
94
107
|
} satisfies ArchiverL1SynchPoint);
|
|
95
108
|
});
|
|
96
109
|
|
|
@@ -99,7 +112,6 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
99
112
|
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
100
113
|
blocksSynchedTo: 19n,
|
|
101
114
|
messagesSynchedTo: undefined,
|
|
102
|
-
provenLogsSynchedTo: undefined,
|
|
103
115
|
} satisfies ArchiverL1SynchPoint);
|
|
104
116
|
});
|
|
105
117
|
|
|
@@ -111,16 +123,6 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
111
123
|
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
112
124
|
blocksSynchedTo: undefined,
|
|
113
125
|
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
126
|
} satisfies ArchiverL1SynchPoint);
|
|
125
127
|
});
|
|
126
128
|
});
|
|
@@ -128,14 +130,26 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
128
130
|
describe('addLogs', () => {
|
|
129
131
|
it('adds encrypted & unencrypted logs', async () => {
|
|
130
132
|
const block = blocks[0].data;
|
|
131
|
-
await expect(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
133
|
+
await expect(store.addLogs([block])).resolves.toEqual(true);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('deleteLogs', () => {
|
|
138
|
+
it('deletes encrypted & unencrypted logs', async () => {
|
|
139
|
+
const block = blocks[0].data;
|
|
140
|
+
await store.addBlocks([blocks[0]]);
|
|
141
|
+
await expect(store.addLogs([block])).resolves.toEqual(true);
|
|
142
|
+
|
|
143
|
+
expect((await store.getLogs(1, 1, LogType.NOTEENCRYPTED))[0]).toEqual(block.body.noteEncryptedLogs);
|
|
144
|
+
expect((await store.getLogs(1, 1, LogType.ENCRYPTED))[0]).toEqual(block.body.encryptedLogs);
|
|
145
|
+
expect((await store.getLogs(1, 1, LogType.UNENCRYPTED))[0]).toEqual(block.body.unencryptedLogs);
|
|
146
|
+
|
|
147
|
+
// This one is a pain for memory as we would never want to just delete memory in the middle.
|
|
148
|
+
await store.deleteLogs([block]);
|
|
149
|
+
|
|
150
|
+
expect((await store.getLogs(1, 1, LogType.NOTEENCRYPTED))[0]).toEqual(undefined);
|
|
151
|
+
expect((await store.getLogs(1, 1, LogType.ENCRYPTED))[0]).toEqual(undefined);
|
|
152
|
+
expect((await store.getLogs(1, 1, LogType.UNENCRYPTED))[0]).toEqual(undefined);
|
|
139
153
|
});
|
|
140
154
|
});
|
|
141
155
|
|
|
@@ -145,16 +159,8 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
145
159
|
['unencrypted', LogType.UNENCRYPTED],
|
|
146
160
|
])('getLogs (%s)', (_, logType) => {
|
|
147
161
|
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
|
-
);
|
|
162
|
+
await store.addBlocks(blocks);
|
|
163
|
+
await store.addLogs(blocks.map(b => b.data));
|
|
158
164
|
});
|
|
159
165
|
|
|
160
166
|
it.each(blockTests)('retrieves previously stored logs', async (from, limit, getExpectedBlocks) => {
|
|
@@ -176,16 +182,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
176
182
|
|
|
177
183
|
describe('getTxEffect', () => {
|
|
178
184
|
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
|
-
);
|
|
185
|
+
await store.addLogs(blocks.map(b => b.data));
|
|
189
186
|
await store.addBlocks(blocks);
|
|
190
187
|
});
|
|
191
188
|
|
|
@@ -204,6 +201,24 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
204
201
|
it('returns undefined if tx is not found', async () => {
|
|
205
202
|
await expect(store.getTxEffect(new TxHash(Fr.random().toBuffer()))).resolves.toBeUndefined();
|
|
206
203
|
});
|
|
204
|
+
|
|
205
|
+
it.each([
|
|
206
|
+
() => blocks[0].data.body.txEffects[0],
|
|
207
|
+
() => blocks[9].data.body.txEffects[3],
|
|
208
|
+
() => blocks[3].data.body.txEffects[1],
|
|
209
|
+
() => blocks[5].data.body.txEffects[2],
|
|
210
|
+
() => blocks[1].data.body.txEffects[0],
|
|
211
|
+
])('tries to retrieves a previously stored transaction after deleted', async getExpectedTx => {
|
|
212
|
+
await store.unwindBlocks(blocks.length, blocks.length);
|
|
213
|
+
|
|
214
|
+
const expectedTx = getExpectedTx();
|
|
215
|
+
const actualTx = await store.getTxEffect(expectedTx.txHash);
|
|
216
|
+
expect(actualTx).toEqual(undefined);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('returns undefined if tx is not found', async () => {
|
|
220
|
+
await expect(store.getTxEffect(new TxHash(Fr.random().toBuffer()))).resolves.toBeUndefined();
|
|
221
|
+
});
|
|
207
222
|
});
|
|
208
223
|
|
|
209
224
|
describe('L1 to L2 Messages', () => {
|
|
@@ -274,6 +289,11 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
274
289
|
it('returns undefined if contract instance is not found', async () => {
|
|
275
290
|
await expect(store.getContractInstance(AztecAddress.random())).resolves.toBeUndefined();
|
|
276
291
|
});
|
|
292
|
+
|
|
293
|
+
it('returns undefined if previously stored contract instances was deleted', async () => {
|
|
294
|
+
await store.deleteContractInstances([contractInstance], blockNum);
|
|
295
|
+
await expect(store.getContractInstance(contractInstance.address)).resolves.toBeUndefined();
|
|
296
|
+
});
|
|
277
297
|
});
|
|
278
298
|
|
|
279
299
|
describe('contractClasses', () => {
|
|
@@ -289,6 +309,17 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
289
309
|
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
290
310
|
});
|
|
291
311
|
|
|
312
|
+
it('returns undefined if the initial deployed contract class was deleted', async () => {
|
|
313
|
+
await store.deleteContractClasses([contractClass], blockNum);
|
|
314
|
+
await expect(store.getContractClass(contractClass.id)).resolves.toBeUndefined();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('returns contract class if later "deployment" class was deleted', async () => {
|
|
318
|
+
await store.addContractClasses([contractClass], blockNum + 1);
|
|
319
|
+
await store.deleteContractClasses([contractClass], blockNum + 1);
|
|
320
|
+
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
321
|
+
});
|
|
322
|
+
|
|
292
323
|
it('returns undefined if contract class is not found', async () => {
|
|
293
324
|
await expect(store.getContractClass(Fr.random())).resolves.toBeUndefined();
|
|
294
325
|
});
|
|
@@ -338,17 +369,25 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
338
369
|
}));
|
|
339
370
|
|
|
340
371
|
await store.addBlocks(blocks);
|
|
372
|
+
await store.addLogs(blocks.map(b => b.data));
|
|
373
|
+
});
|
|
341
374
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
),
|
|
351
|
-
);
|
|
375
|
+
it('no logs returned if deleted ("txHash" filter param is respected variant)', async () => {
|
|
376
|
+
// get random tx
|
|
377
|
+
const targetBlockIndex = randomInt(numBlocks);
|
|
378
|
+
const targetTxIndex = randomInt(txsPerBlock);
|
|
379
|
+
const targetTxHash = blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash;
|
|
380
|
+
|
|
381
|
+
await Promise.all([
|
|
382
|
+
store.unwindBlocks(blocks.length, blocks.length),
|
|
383
|
+
store.deleteLogs(blocks.map(b => b.data)),
|
|
384
|
+
]);
|
|
385
|
+
|
|
386
|
+
const response = await store.getUnencryptedLogs({ txHash: targetTxHash });
|
|
387
|
+
const logs = response.logs;
|
|
388
|
+
|
|
389
|
+
expect(response.maxLogsHit).toBeFalsy();
|
|
390
|
+
expect(logs.length).toEqual(0);
|
|
352
391
|
});
|
|
353
392
|
|
|
354
393
|
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];
|
|
@@ -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
|
+
}
|
|
@@ -26,6 +26,12 @@ export class BlockStore {
|
|
|
26
26
|
/** Stores L1 block number in which the last processed L2 block was included */
|
|
27
27
|
#lastSynchedL1Block: AztecSingleton<bigint>;
|
|
28
28
|
|
|
29
|
+
/** Stores l2 block number of the last proven block */
|
|
30
|
+
#lastProvenL2Block: AztecSingleton<number>;
|
|
31
|
+
|
|
32
|
+
/** Stores l2 epoch number of the last proven epoch */
|
|
33
|
+
#lastProvenL2Epoch: AztecSingleton<number>;
|
|
34
|
+
|
|
29
35
|
/** Index mapping transaction hash (as a string) to its location in a block */
|
|
30
36
|
#txIndex: AztecMap<string, BlockIndexValue>;
|
|
31
37
|
|
|
@@ -40,6 +46,8 @@ export class BlockStore {
|
|
|
40
46
|
this.#txIndex = db.openMap('archiver_tx_index');
|
|
41
47
|
this.#contractIndex = db.openMap('archiver_contract_index');
|
|
42
48
|
this.#lastSynchedL1Block = db.openSingleton('archiver_last_synched_l1_block');
|
|
49
|
+
this.#lastProvenL2Block = db.openSingleton('archiver_last_proven_l2_block');
|
|
50
|
+
this.#lastProvenL2Epoch = db.openSingleton('archiver_last_proven_l2_epoch');
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
/**
|
|
@@ -73,6 +81,38 @@ export class BlockStore {
|
|
|
73
81
|
});
|
|
74
82
|
}
|
|
75
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Unwinds blocks from the database
|
|
86
|
+
* @param from - The tip of the chain, passed for verification purposes,
|
|
87
|
+
* ensuring that we don't end up deleting something we did not intend
|
|
88
|
+
* @param blocksToUnwind - The number of blocks we are to unwind
|
|
89
|
+
* @returns True if the operation is successful
|
|
90
|
+
*/
|
|
91
|
+
unwindBlocks(from: number, blocksToUnwind: number) {
|
|
92
|
+
return this.db.transaction(() => {
|
|
93
|
+
const last = this.getSynchedL2BlockNumber();
|
|
94
|
+
if (from != last) {
|
|
95
|
+
throw new Error(`Can only remove from the tip`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (let i = 0; i < blocksToUnwind; i++) {
|
|
99
|
+
const blockNumber = from - i;
|
|
100
|
+
const block = this.getBlock(blockNumber);
|
|
101
|
+
|
|
102
|
+
if (block === undefined) {
|
|
103
|
+
throw new Error(`Cannot remove block ${blockNumber} from the store, we don't have it`);
|
|
104
|
+
}
|
|
105
|
+
void this.#blocks.delete(block.data.number);
|
|
106
|
+
block.data.body.txEffects.forEach(tx => {
|
|
107
|
+
void this.#txIndex.delete(tx.txHash.toString());
|
|
108
|
+
});
|
|
109
|
+
void this.#blockBodies.delete(block.data.body.getTxsEffectsHash().toString('hex'));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return true;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
76
116
|
/**
|
|
77
117
|
* Gets up to `limit` amount of L2 blocks starting from `from`.
|
|
78
118
|
* @param start - Number of the first block to return (inclusive).
|
|
@@ -99,6 +139,18 @@ export class BlockStore {
|
|
|
99
139
|
return this.getBlockFromBlockStorage(blockStorage);
|
|
100
140
|
}
|
|
101
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Gets the headers for a sequence of L2 blocks.
|
|
144
|
+
* @param start - Number of the first block to return (inclusive).
|
|
145
|
+
* @param limit - The number of blocks to return.
|
|
146
|
+
* @returns The requested L2 block headers
|
|
147
|
+
*/
|
|
148
|
+
*getBlockHeaders(start: number, limit: number): IterableIterator<Header> {
|
|
149
|
+
for (const blockStorage of this.#blocks.values(this.#computeBlockRange(start, limit))) {
|
|
150
|
+
yield Header.fromBuffer(blockStorage.header);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
102
154
|
private getBlockFromBlockStorage(blockStorage: BlockStorage) {
|
|
103
155
|
const header = Header.fromBuffer(blockStorage.header);
|
|
104
156
|
const archive = AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive);
|
|
@@ -109,7 +161,7 @@ export class BlockStore {
|
|
|
109
161
|
}
|
|
110
162
|
const body = Body.fromBuffer(blockBodyBuffer);
|
|
111
163
|
|
|
112
|
-
const l2Block = L2Block
|
|
164
|
+
const l2Block = new L2Block(archive, header, body);
|
|
113
165
|
return { data: l2Block, l1: blockStorage.l1 };
|
|
114
166
|
}
|
|
115
167
|
|
|
@@ -191,13 +243,29 @@ export class BlockStore {
|
|
|
191
243
|
void this.#lastSynchedL1Block.set(l1BlockNumber);
|
|
192
244
|
}
|
|
193
245
|
|
|
246
|
+
getProvenL2BlockNumber(): number {
|
|
247
|
+
return this.#lastProvenL2Block.get() ?? 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
setProvenL2BlockNumber(blockNumber: number) {
|
|
251
|
+
void this.#lastProvenL2Block.set(blockNumber);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
getProvenL2EpochNumber(): number | undefined {
|
|
255
|
+
return this.#lastProvenL2Epoch.get();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
setProvenL2EpochNumber(epochNumber: number) {
|
|
259
|
+
void this.#lastProvenL2Epoch.set(epochNumber);
|
|
260
|
+
}
|
|
261
|
+
|
|
194
262
|
#computeBlockRange(start: number, limit: number): Required<Pick<Range<number>, 'start' | 'end'>> {
|
|
195
263
|
if (limit < 1) {
|
|
196
264
|
throw new Error(`Invalid limit: ${limit}`);
|
|
197
265
|
}
|
|
198
266
|
|
|
199
267
|
if (start < INITIAL_L2_BLOCK_NUM) {
|
|
200
|
-
start
|
|
268
|
+
throw new Error(`Invalid start: ${start}`);
|
|
201
269
|
}
|
|
202
270
|
|
|
203
271
|
const end = start + limit;
|
|
@@ -3,6 +3,7 @@ import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/s
|
|
|
3
3
|
import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
|
|
4
4
|
import {
|
|
5
5
|
type ContractClassPublic,
|
|
6
|
+
type ContractClassPublicWithBlockNumber,
|
|
6
7
|
type ExecutablePrivateFunctionWithMembershipProof,
|
|
7
8
|
type UnconstrainedFunctionWithMembershipProof,
|
|
8
9
|
} from '@aztec/types/contracts';
|
|
@@ -17,8 +18,18 @@ export class ContractClassStore {
|
|
|
17
18
|
this.#contractClasses = db.openMap('archiver_contract_classes');
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
addContractClass(contractClass: ContractClassPublic): Promise<void> {
|
|
21
|
-
|
|
21
|
+
async addContractClass(contractClass: ContractClassPublic, blockNumber: number): Promise<void> {
|
|
22
|
+
await this.#contractClasses.setIfNotExists(
|
|
23
|
+
contractClass.id.toString(),
|
|
24
|
+
serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async deleteContractClasses(contractClass: ContractClassPublic, blockNumber: number): Promise<void> {
|
|
29
|
+
const restoredContractClass = this.#contractClasses.get(contractClass.id.toString());
|
|
30
|
+
if (restoredContractClass && deserializeContractClassPublic(restoredContractClass).l2BlockNumber >= blockNumber) {
|
|
31
|
+
await this.#contractClasses.delete(contractClass.id.toString());
|
|
32
|
+
}
|
|
22
33
|
}
|
|
23
34
|
|
|
24
35
|
getContractClass(id: Fr): ContractClassPublic | undefined {
|
|
@@ -44,7 +55,7 @@ export class ContractClassStore {
|
|
|
44
55
|
const existingClass = deserializeContractClassPublic(existingClassBuffer);
|
|
45
56
|
const { privateFunctions: existingPrivateFns, unconstrainedFunctions: existingUnconstrainedFns } = existingClass;
|
|
46
57
|
|
|
47
|
-
const updatedClass: Omit<
|
|
58
|
+
const updatedClass: Omit<ContractClassPublicWithBlockNumber, 'id'> = {
|
|
48
59
|
...existingClass,
|
|
49
60
|
privateFunctions: [
|
|
50
61
|
...existingPrivateFns,
|
|
@@ -63,8 +74,9 @@ export class ContractClassStore {
|
|
|
63
74
|
}
|
|
64
75
|
}
|
|
65
76
|
|
|
66
|
-
function serializeContractClassPublic(contractClass: Omit<
|
|
77
|
+
function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWithBlockNumber, 'id'>): Buffer {
|
|
67
78
|
return serializeToBuffer(
|
|
79
|
+
contractClass.l2BlockNumber,
|
|
68
80
|
numToUInt8(contractClass.version),
|
|
69
81
|
contractClass.artifactHash,
|
|
70
82
|
contractClass.publicFunctions.length,
|
|
@@ -108,9 +120,10 @@ function serializeUnconstrainedFunction(fn: UnconstrainedFunctionWithMembershipP
|
|
|
108
120
|
);
|
|
109
121
|
}
|
|
110
122
|
|
|
111
|
-
function deserializeContractClassPublic(buffer: Buffer): Omit<
|
|
123
|
+
function deserializeContractClassPublic(buffer: Buffer): Omit<ContractClassPublicWithBlockNumber, 'id'> {
|
|
112
124
|
const reader = BufferReader.asReader(buffer);
|
|
113
125
|
return {
|
|
126
|
+
l2BlockNumber: reader.readNumber(),
|
|
114
127
|
version: reader.readUInt8() as 1,
|
|
115
128
|
artifactHash: reader.readObject(Fr),
|
|
116
129
|
publicFunctions: reader.readVector({
|