@aztec/archiver 0.71.0 → 0.73.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 +6 -6
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +35 -20
- package/dest/archiver/archiver_store.d.ts +6 -6
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.js +103 -65
- package/dest/archiver/config.d.ts +2 -2
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +5 -5
- package/dest/archiver/data_retrieval.d.ts +3 -2
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/data_retrieval.js +69 -16
- package/dest/archiver/errors.d.ts +4 -0
- package/dest/archiver/errors.d.ts.map +1 -0
- package/dest/archiver/errors.js +6 -0
- package/dest/archiver/kv_archiver_store/block_store.d.ts +16 -16
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +53 -53
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +5 -5
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +13 -12
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +3 -3
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +3 -3
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +7 -11
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +41 -63
- package/dest/archiver/kv_archiver_store/log_store.d.ts +8 -8
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +122 -89
- package/dest/archiver/kv_archiver_store/message_store.d.ts +6 -6
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +16 -16
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts +2 -2
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/nullifier_store.js +31 -22
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +9 -9
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +72 -71
- package/dest/factory.js +8 -8
- package/dest/rpc/index.d.ts +1 -1
- package/dest/rpc/index.d.ts.map +1 -1
- package/dest/rpc/index.js +5 -5
- package/dest/test/mock_archiver.d.ts +1 -1
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_archiver.js +2 -1
- package/dest/test/mock_l2_block_source.d.ts +3 -3
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +14 -11
- package/package.json +13 -13
- package/src/archiver/archiver.ts +46 -25
- package/src/archiver/archiver_store.ts +6 -5
- package/src/archiver/archiver_store_test_suite.ts +113 -77
- package/src/archiver/config.ts +6 -6
- package/src/archiver/data_retrieval.ts +94 -12
- package/src/archiver/errors.ts +5 -0
- package/src/archiver/kv_archiver_store/block_store.ts +66 -67
- package/src/archiver/kv_archiver_store/contract_class_store.ts +17 -15
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +5 -5
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +48 -66
- package/src/archiver/kv_archiver_store/log_store.ts +167 -112
- package/src/archiver/kv_archiver_store/message_store.ts +22 -22
- package/src/archiver/kv_archiver_store/nullifier_store.ts +48 -30
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +100 -96
- package/src/factory.ts +11 -9
- package/src/rpc/index.ts +4 -4
- package/src/test/mock_archiver.ts +1 -0
- package/src/test/mock_l2_block_source.ts +20 -18
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ContractClass2BlockL2Logs,
|
|
3
|
+
ExtendedPublicLog,
|
|
3
4
|
ExtendedUnencryptedL2Log,
|
|
4
|
-
type
|
|
5
|
+
type GetContractClassLogsResponse,
|
|
6
|
+
type GetPublicLogsResponse,
|
|
5
7
|
type L2Block,
|
|
6
8
|
type LogFilter,
|
|
7
9
|
LogId,
|
|
8
10
|
TxScopedL2Log,
|
|
9
|
-
|
|
10
|
-
type UnencryptedL2Log,
|
|
11
|
+
UnencryptedL2Log,
|
|
11
12
|
} from '@aztec/circuit-types';
|
|
12
|
-
import { Fr, PrivateLog } from '@aztec/circuits.js';
|
|
13
|
-
import {
|
|
13
|
+
import { type Fr, PrivateLog, PublicLog } from '@aztec/circuits.js';
|
|
14
|
+
import {
|
|
15
|
+
INITIAL_L2_BLOCK_NUM,
|
|
16
|
+
MAX_NOTE_HASHES_PER_TX,
|
|
17
|
+
PUBLIC_LOG_DATA_SIZE_IN_FIELDS,
|
|
18
|
+
} from '@aztec/circuits.js/constants';
|
|
14
19
|
import { createLogger } from '@aztec/foundation/log';
|
|
15
|
-
import { BufferReader } from '@aztec/foundation/serialize';
|
|
16
|
-
import {
|
|
20
|
+
import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
|
|
21
|
+
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
17
22
|
|
|
18
23
|
import { type BlockStore } from './block_store.js';
|
|
19
24
|
|
|
@@ -21,19 +26,19 @@ import { type BlockStore } from './block_store.js';
|
|
|
21
26
|
* A store for logs
|
|
22
27
|
*/
|
|
23
28
|
export class LogStore {
|
|
24
|
-
#logsByTag:
|
|
25
|
-
#logTagsByBlock:
|
|
26
|
-
#privateLogsByBlock:
|
|
27
|
-
#
|
|
28
|
-
#contractClassLogsByBlock:
|
|
29
|
+
#logsByTag: AztecAsyncMap<string, Buffer[]>;
|
|
30
|
+
#logTagsByBlock: AztecAsyncMap<number, string[]>;
|
|
31
|
+
#privateLogsByBlock: AztecAsyncMap<number, Buffer>;
|
|
32
|
+
#publicLogsByBlock: AztecAsyncMap<number, Buffer>;
|
|
33
|
+
#contractClassLogsByBlock: AztecAsyncMap<number, Buffer>;
|
|
29
34
|
#logsMaxPageSize: number;
|
|
30
35
|
#log = createLogger('archiver:log_store');
|
|
31
36
|
|
|
32
|
-
constructor(private db:
|
|
37
|
+
constructor(private db: AztecAsyncKVStore, private blockStore: BlockStore, logsMaxPageSize: number = 1000) {
|
|
33
38
|
this.#logsByTag = db.openMap('archiver_tagged_logs_by_tag');
|
|
34
39
|
this.#logTagsByBlock = db.openMap('archiver_log_tags_by_block');
|
|
35
40
|
this.#privateLogsByBlock = db.openMap('archiver_private_logs_by_block');
|
|
36
|
-
this.#
|
|
41
|
+
this.#publicLogsByBlock = db.openMap('archiver_public_logs_by_block');
|
|
37
42
|
this.#contractClassLogsByBlock = db.openMap('archiver_contract_class_logs_by_block');
|
|
38
43
|
|
|
39
44
|
this.#logsMaxPageSize = logsMaxPageSize;
|
|
@@ -70,39 +75,46 @@ export class LogStore {
|
|
|
70
75
|
const dataStartIndexForBlock =
|
|
71
76
|
block.header.state.partial.noteHashTree.nextAvailableLeafIndex -
|
|
72
77
|
block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX;
|
|
73
|
-
block.body.
|
|
74
|
-
const txHash =
|
|
78
|
+
block.body.txEffects.forEach((txEffect, txIndex) => {
|
|
79
|
+
const txHash = txEffect.txHash;
|
|
75
80
|
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
txEffect.publicLogs.forEach(log => {
|
|
82
|
+
// Check that each log stores 2 lengths in its first field. If not, it's not a tagged log:
|
|
83
|
+
const firstFieldBuf = log.log[0].toBuffer();
|
|
84
|
+
// See macros/note/mod/ and see how finalization_log[0] is constructed, to understand this monstrosity. (It wasn't me).
|
|
85
|
+
// Search the codebase for "disgusting encoding" to see other hardcoded instances of this encoding, that you might need to change if you ever find yourself here.
|
|
86
|
+
if (!firstFieldBuf.subarray(0, 27).equals(Buffer.alloc(27)) || firstFieldBuf[29] !== 0) {
|
|
87
|
+
// See parseLogFromPublic - the first field of a tagged log is 5 bytes structured:
|
|
88
|
+
// [ publicLen[0], publicLen[1], 0, privateLen[0], privateLen[1]]
|
|
89
|
+
this.#log.warn(`Skipping public log with invalid first field: ${log.log[0]}`);
|
|
81
90
|
return;
|
|
82
91
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
).toNumber();
|
|
93
|
-
correctedBuffer.writeUInt8(byte, i);
|
|
94
|
-
}
|
|
95
|
-
const tag = new Fr(correctedBuffer);
|
|
96
|
-
|
|
97
|
-
this.#log.debug(`Found tagged unencrypted log with tag ${tag.toString()} in block ${block.number}`);
|
|
98
|
-
const currentLogs = taggedLogs.get(tag.toString()) ?? [];
|
|
99
|
-
currentLogs.push(
|
|
100
|
-
new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, /* isFromPublic */ true, log.data).toBuffer(),
|
|
101
|
-
);
|
|
102
|
-
taggedLogs.set(tag.toString(), currentLogs);
|
|
103
|
-
} catch (err) {
|
|
104
|
-
this.#log.warn(`Failed to add tagged log to store: ${err}`);
|
|
92
|
+
// Check that the length values line up with the log contents
|
|
93
|
+
const publicValuesLength = firstFieldBuf.subarray(-5).readUint16BE();
|
|
94
|
+
const privateValuesLength = firstFieldBuf.subarray(-5).readUint16BE(3);
|
|
95
|
+
// Add 1 for the first field holding lengths
|
|
96
|
+
const totalLogLength = 1 + publicValuesLength + privateValuesLength;
|
|
97
|
+
// Note that zeroes can be valid log values, so we can only assert that we do not go over the given length
|
|
98
|
+
if (totalLogLength > PUBLIC_LOG_DATA_SIZE_IN_FIELDS || log.log.slice(totalLogLength).find(f => !f.isZero())) {
|
|
99
|
+
this.#log.warn(`Skipping invalid tagged public log with first field: ${log.log[0]}`);
|
|
100
|
+
return;
|
|
105
101
|
}
|
|
102
|
+
|
|
103
|
+
// The first elt stores lengths as above => tag is in fields[1]
|
|
104
|
+
const tag = log.log[1];
|
|
105
|
+
|
|
106
|
+
this.#log.debug(`Found tagged public log with tag ${tag.toString()} in block ${block.number}`);
|
|
107
|
+
const currentLogs = taggedLogs.get(tag.toString()) ?? [];
|
|
108
|
+
currentLogs.push(
|
|
109
|
+
new TxScopedL2Log(
|
|
110
|
+
txHash,
|
|
111
|
+
dataStartIndexForTx,
|
|
112
|
+
block.number,
|
|
113
|
+
/* isFromPublic */ true,
|
|
114
|
+
log.toBuffer(),
|
|
115
|
+
).toBuffer(),
|
|
116
|
+
);
|
|
117
|
+
taggedLogs.set(tag.toString(), currentLogs);
|
|
106
118
|
});
|
|
107
119
|
});
|
|
108
120
|
return taggedLogs;
|
|
@@ -113,7 +125,7 @@ export class LogStore {
|
|
|
113
125
|
* @param blocks - The blocks for which to add the logs.
|
|
114
126
|
* @returns True if the operation is successful.
|
|
115
127
|
*/
|
|
116
|
-
|
|
128
|
+
addLogs(blocks: L2Block[]): Promise<boolean> {
|
|
117
129
|
const taggedLogsToAdd = blocks
|
|
118
130
|
.flatMap(block => [this.#extractTaggedLogsFromPrivate(block), this.#extractTaggedLogsFromPublic(block)])
|
|
119
131
|
.reduce((acc, val) => {
|
|
@@ -124,55 +136,73 @@ export class LogStore {
|
|
|
124
136
|
return acc;
|
|
125
137
|
});
|
|
126
138
|
const tagsToUpdate = Array.from(taggedLogsToAdd.keys());
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
139
|
+
|
|
140
|
+
return this.db.transactionAsync(async () => {
|
|
141
|
+
const currentTaggedLogs = await Promise.all(
|
|
142
|
+
tagsToUpdate.map(async tag => ({ tag, logBuffers: await this.#logsByTag.getAsync(tag) })),
|
|
143
|
+
);
|
|
144
|
+
currentTaggedLogs.forEach(taggedLogBuffer => {
|
|
145
|
+
if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
|
|
146
|
+
taggedLogsToAdd.set(
|
|
147
|
+
taggedLogBuffer.tag,
|
|
148
|
+
taggedLogBuffer.logBuffers!.concat(taggedLogsToAdd.get(taggedLogBuffer.tag)!),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
for (const block of blocks) {
|
|
140
153
|
const tagsInBlock = [];
|
|
141
154
|
for (const [tag, logs] of taggedLogsToAdd.entries()) {
|
|
142
|
-
|
|
155
|
+
await this.#logsByTag.set(tag, logs);
|
|
143
156
|
tagsInBlock.push(tag);
|
|
144
157
|
}
|
|
145
|
-
|
|
158
|
+
await this.#logTagsByBlock.set(block.number, tagsInBlock);
|
|
146
159
|
|
|
147
160
|
const privateLogsInBlock = block.body.txEffects
|
|
148
161
|
.map(txEffect => txEffect.privateLogs)
|
|
149
162
|
.flat()
|
|
150
163
|
.map(log => log.toBuffer());
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
await this.#privateLogsByBlock.set(block.number, Buffer.concat(privateLogsInBlock));
|
|
165
|
+
|
|
166
|
+
const publicLogsInBlock = block.body.txEffects
|
|
167
|
+
.map((txEffect, txIndex) =>
|
|
168
|
+
[
|
|
169
|
+
numToUInt32BE(txIndex),
|
|
170
|
+
numToUInt32BE(txEffect.publicLogs.length),
|
|
171
|
+
txEffect.publicLogs.map(log => log.toBuffer()),
|
|
172
|
+
].flat(),
|
|
173
|
+
)
|
|
174
|
+
.flat();
|
|
175
|
+
|
|
176
|
+
await this.#publicLogsByBlock.set(block.number, Buffer.concat(publicLogsInBlock));
|
|
177
|
+
await this.#contractClassLogsByBlock.set(block.number, block.body.contractClassLogs.toBuffer());
|
|
178
|
+
}
|
|
156
179
|
|
|
157
180
|
return true;
|
|
158
181
|
});
|
|
159
182
|
}
|
|
160
183
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
184
|
+
deleteLogs(blocks: L2Block[]): Promise<boolean> {
|
|
185
|
+
return this.db.transactionAsync(async () => {
|
|
186
|
+
const tagsToDelete = (
|
|
187
|
+
await Promise.all(
|
|
188
|
+
blocks.map(async block => {
|
|
189
|
+
const tags = await this.#logTagsByBlock.getAsync(block.number);
|
|
190
|
+
return tags ?? [];
|
|
191
|
+
}),
|
|
192
|
+
)
|
|
193
|
+
).flat();
|
|
194
|
+
|
|
195
|
+
await Promise.all(
|
|
196
|
+
blocks.map(block =>
|
|
197
|
+
Promise.all([
|
|
198
|
+
this.#privateLogsByBlock.delete(block.number),
|
|
199
|
+
this.#publicLogsByBlock.delete(block.number),
|
|
200
|
+
this.#logTagsByBlock.delete(block.number),
|
|
201
|
+
]),
|
|
202
|
+
),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
await Promise.all(tagsToDelete.map(tag => this.#logsByTag.delete(tag.toString())));
|
|
176
206
|
return true;
|
|
177
207
|
});
|
|
178
208
|
}
|
|
@@ -183,9 +213,9 @@ export class LogStore {
|
|
|
183
213
|
* @param limit - The maximum number of blocks to retrieve logs from.
|
|
184
214
|
* @returns An array of private logs from the specified range of blocks.
|
|
185
215
|
*/
|
|
186
|
-
getPrivateLogs(start: number, limit: number) {
|
|
216
|
+
async getPrivateLogs(start: number, limit: number): Promise<PrivateLog[]> {
|
|
187
217
|
const logs = [];
|
|
188
|
-
for (const buffer of this.#privateLogsByBlock.
|
|
218
|
+
for await (const buffer of this.#privateLogsByBlock.valuesAsync({ start, limit })) {
|
|
189
219
|
const reader = new BufferReader(buffer);
|
|
190
220
|
while (reader.remainingBytes() > 0) {
|
|
191
221
|
logs.push(reader.readObject(PrivateLog));
|
|
@@ -200,51 +230,59 @@ export class LogStore {
|
|
|
200
230
|
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
201
231
|
* that tag.
|
|
202
232
|
*/
|
|
203
|
-
getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
.map(noteLogBuffers => noteLogBuffers?.map(noteLogBuffer => TxScopedL2Log.fromBuffer(noteLogBuffer)) ?? []),
|
|
233
|
+
async getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
|
|
234
|
+
const logs = await Promise.all(tags.map(tag => this.#logsByTag.getAsync(tag.toString())));
|
|
235
|
+
return logs.map(
|
|
236
|
+
noteLogBuffers => noteLogBuffers?.map(noteLogBuffer => TxScopedL2Log.fromBuffer(noteLogBuffer)) ?? [],
|
|
208
237
|
);
|
|
209
238
|
}
|
|
210
239
|
|
|
211
240
|
/**
|
|
212
|
-
* Gets
|
|
241
|
+
* Gets public logs based on the provided filter.
|
|
213
242
|
* @param filter - The filter to apply to the logs.
|
|
214
243
|
* @returns The requested logs.
|
|
215
244
|
*/
|
|
216
|
-
|
|
245
|
+
getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
217
246
|
if (filter.afterLog) {
|
|
218
|
-
return this.#
|
|
247
|
+
return this.#filterPublicLogsBetweenBlocks(filter);
|
|
219
248
|
} else if (filter.txHash) {
|
|
220
|
-
return this.#
|
|
249
|
+
return this.#filterPublicLogsOfTx(filter);
|
|
221
250
|
} else {
|
|
222
|
-
return this.#
|
|
251
|
+
return this.#filterPublicLogsBetweenBlocks(filter);
|
|
223
252
|
}
|
|
224
253
|
}
|
|
225
254
|
|
|
226
|
-
#
|
|
255
|
+
async #filterPublicLogsOfTx(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
227
256
|
if (!filter.txHash) {
|
|
228
257
|
throw new Error('Missing txHash');
|
|
229
258
|
}
|
|
230
259
|
|
|
231
|
-
const [blockNumber, txIndex] = this.blockStore.getTxLocation(filter.txHash) ?? [];
|
|
260
|
+
const [blockNumber, txIndex] = (await this.blockStore.getTxLocation(filter.txHash)) ?? [];
|
|
232
261
|
if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
|
|
233
262
|
return { logs: [], maxLogsHit: false };
|
|
234
263
|
}
|
|
235
264
|
|
|
236
|
-
const buffer = this.#
|
|
237
|
-
const
|
|
265
|
+
const buffer = (await this.#publicLogsByBlock.getAsync(blockNumber)) ?? Buffer.alloc(0);
|
|
266
|
+
const publicLogsInBlock: [PublicLog[]] = [[]];
|
|
267
|
+
const reader = new BufferReader(buffer);
|
|
268
|
+
while (reader.remainingBytes() > 0) {
|
|
269
|
+
const indexOfTx = reader.readNumber();
|
|
270
|
+
const numLogsInTx = reader.readNumber();
|
|
271
|
+
publicLogsInBlock[indexOfTx] = [];
|
|
272
|
+
for (let i = 0; i < numLogsInTx; i++) {
|
|
273
|
+
publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
238
276
|
|
|
239
|
-
const txLogs =
|
|
277
|
+
const txLogs = publicLogsInBlock[txIndex];
|
|
240
278
|
|
|
241
|
-
const logs:
|
|
279
|
+
const logs: ExtendedPublicLog[] = [];
|
|
242
280
|
const maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter);
|
|
243
281
|
|
|
244
282
|
return { logs, maxLogsHit };
|
|
245
283
|
}
|
|
246
284
|
|
|
247
|
-
#
|
|
285
|
+
async #filterPublicLogsBetweenBlocks(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
248
286
|
const start =
|
|
249
287
|
filter.afterLog?.blockNumber ?? Math.max(filter.fromBlock ?? INITIAL_L2_BLOCK_NUM, INITIAL_L2_BLOCK_NUM);
|
|
250
288
|
const end = filter.toBlock;
|
|
@@ -256,13 +294,22 @@ export class LogStore {
|
|
|
256
294
|
};
|
|
257
295
|
}
|
|
258
296
|
|
|
259
|
-
const logs:
|
|
297
|
+
const logs: ExtendedPublicLog[] = [];
|
|
260
298
|
|
|
261
299
|
let maxLogsHit = false;
|
|
262
|
-
loopOverBlocks: for (const [blockNumber, logBuffer] of this.#
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
300
|
+
loopOverBlocks: for await (const [blockNumber, logBuffer] of this.#publicLogsByBlock.entriesAsync({ start, end })) {
|
|
301
|
+
const publicLogsInBlock: [PublicLog[]] = [[]];
|
|
302
|
+
const reader = new BufferReader(logBuffer);
|
|
303
|
+
while (reader.remainingBytes() > 0) {
|
|
304
|
+
const indexOfTx = reader.readNumber();
|
|
305
|
+
const numLogsInTx = reader.readNumber();
|
|
306
|
+
publicLogsInBlock[indexOfTx] = [];
|
|
307
|
+
for (let i = 0; i < numLogsInTx; i++) {
|
|
308
|
+
publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < publicLogsInBlock.length; txIndex++) {
|
|
312
|
+
const txLogs = publicLogsInBlock[txIndex];
|
|
266
313
|
maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter);
|
|
267
314
|
if (maxLogsHit) {
|
|
268
315
|
this.#log.debug(`Max logs hit at block ${blockNumber}`);
|
|
@@ -279,7 +326,7 @@ export class LogStore {
|
|
|
279
326
|
* @param filter - The filter to apply to the logs.
|
|
280
327
|
* @returns The requested logs.
|
|
281
328
|
*/
|
|
282
|
-
getContractClassLogs(filter: LogFilter):
|
|
329
|
+
getContractClassLogs(filter: LogFilter): Promise<GetContractClassLogsResponse> {
|
|
283
330
|
if (filter.afterLog) {
|
|
284
331
|
return this.#filterContractClassLogsBetweenBlocks(filter);
|
|
285
332
|
} else if (filter.txHash) {
|
|
@@ -289,16 +336,16 @@ export class LogStore {
|
|
|
289
336
|
}
|
|
290
337
|
}
|
|
291
338
|
|
|
292
|
-
#filterContractClassLogsOfTx(filter: LogFilter):
|
|
339
|
+
async #filterContractClassLogsOfTx(filter: LogFilter): Promise<GetContractClassLogsResponse> {
|
|
293
340
|
if (!filter.txHash) {
|
|
294
341
|
throw new Error('Missing txHash');
|
|
295
342
|
}
|
|
296
343
|
|
|
297
|
-
const [blockNumber, txIndex] = this.blockStore.getTxLocation(filter.txHash) ?? [];
|
|
344
|
+
const [blockNumber, txIndex] = (await this.blockStore.getTxLocation(filter.txHash)) ?? [];
|
|
298
345
|
if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
|
|
299
346
|
return { logs: [], maxLogsHit: false };
|
|
300
347
|
}
|
|
301
|
-
const contractClassLogsBuffer = this.#contractClassLogsByBlock.
|
|
348
|
+
const contractClassLogsBuffer = await this.#contractClassLogsByBlock.getAsync(blockNumber);
|
|
302
349
|
const contractClassLogsInBlock = contractClassLogsBuffer
|
|
303
350
|
? ContractClass2BlockL2Logs.fromBuffer(contractClassLogsBuffer)
|
|
304
351
|
: new ContractClass2BlockL2Logs([]);
|
|
@@ -310,7 +357,7 @@ export class LogStore {
|
|
|
310
357
|
return { logs, maxLogsHit };
|
|
311
358
|
}
|
|
312
359
|
|
|
313
|
-
#filterContractClassLogsBetweenBlocks(filter: LogFilter):
|
|
360
|
+
async #filterContractClassLogsBetweenBlocks(filter: LogFilter): Promise<GetContractClassLogsResponse> {
|
|
314
361
|
const start =
|
|
315
362
|
filter.afterLog?.blockNumber ?? Math.max(filter.fromBlock ?? INITIAL_L2_BLOCK_NUM, INITIAL_L2_BLOCK_NUM);
|
|
316
363
|
const end = filter.toBlock;
|
|
@@ -325,7 +372,10 @@ export class LogStore {
|
|
|
325
372
|
const logs: ExtendedUnencryptedL2Log[] = [];
|
|
326
373
|
|
|
327
374
|
let maxLogsHit = false;
|
|
328
|
-
loopOverBlocks: for (const [blockNumber, logBuffer] of this.#contractClassLogsByBlock.
|
|
375
|
+
loopOverBlocks: for await (const [blockNumber, logBuffer] of this.#contractClassLogsByBlock.entriesAsync({
|
|
376
|
+
start,
|
|
377
|
+
end,
|
|
378
|
+
})) {
|
|
329
379
|
const contractClassLogsInBlock = ContractClass2BlockL2Logs.fromBuffer(logBuffer);
|
|
330
380
|
for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < contractClassLogsInBlock.txLogs.length; txIndex++) {
|
|
331
381
|
const txLogs = contractClassLogsInBlock.txLogs[txIndex].unrollLogs();
|
|
@@ -341,10 +391,10 @@ export class LogStore {
|
|
|
341
391
|
}
|
|
342
392
|
|
|
343
393
|
#accumulateLogs(
|
|
344
|
-
results: ExtendedUnencryptedL2Log[],
|
|
394
|
+
results: (ExtendedUnencryptedL2Log | ExtendedPublicLog)[],
|
|
345
395
|
blockNumber: number,
|
|
346
396
|
txIndex: number,
|
|
347
|
-
txLogs: UnencryptedL2Log[],
|
|
397
|
+
txLogs: (UnencryptedL2Log | PublicLog)[],
|
|
348
398
|
filter: LogFilter,
|
|
349
399
|
): boolean {
|
|
350
400
|
let maxLogsHit = false;
|
|
@@ -352,7 +402,12 @@ export class LogStore {
|
|
|
352
402
|
for (; logIndex < txLogs.length; logIndex++) {
|
|
353
403
|
const log = txLogs[logIndex];
|
|
354
404
|
if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) {
|
|
355
|
-
|
|
405
|
+
if (log instanceof UnencryptedL2Log) {
|
|
406
|
+
results.push(new ExtendedUnencryptedL2Log(new LogId(blockNumber, txIndex, logIndex), log));
|
|
407
|
+
} else {
|
|
408
|
+
results.push(new ExtendedPublicLog(new LogId(blockNumber, txIndex, logIndex), log));
|
|
409
|
+
}
|
|
410
|
+
|
|
356
411
|
if (results.length >= this.#logsMaxPageSize) {
|
|
357
412
|
maxLogsHit = true;
|
|
358
413
|
break;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { InboxLeaf } from '@aztec/circuit-types';
|
|
2
2
|
import { Fr, L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/circuits.js';
|
|
3
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
-
import {
|
|
4
|
+
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton } from '@aztec/kv-store';
|
|
5
5
|
|
|
6
6
|
import { type DataRetrieval } from '../structs/data_retrieval.js';
|
|
7
7
|
|
|
@@ -9,36 +9,36 @@ import { type DataRetrieval } from '../structs/data_retrieval.js';
|
|
|
9
9
|
* LMDB implementation of the ArchiverDataStore interface.
|
|
10
10
|
*/
|
|
11
11
|
export class MessageStore {
|
|
12
|
-
#l1ToL2Messages:
|
|
13
|
-
#l1ToL2MessageIndices:
|
|
14
|
-
#lastSynchedL1Block:
|
|
15
|
-
#totalMessageCount:
|
|
12
|
+
#l1ToL2Messages: AztecAsyncMap<string, Buffer>;
|
|
13
|
+
#l1ToL2MessageIndices: AztecAsyncMap<string, bigint>;
|
|
14
|
+
#lastSynchedL1Block: AztecAsyncSingleton<bigint>;
|
|
15
|
+
#totalMessageCount: AztecAsyncSingleton<bigint>;
|
|
16
16
|
|
|
17
17
|
#log = createLogger('archiver:message_store');
|
|
18
18
|
|
|
19
19
|
#l1ToL2MessagesSubtreeSize = 2 ** L1_TO_L2_MSG_SUBTREE_HEIGHT;
|
|
20
20
|
|
|
21
|
-
constructor(private db:
|
|
21
|
+
constructor(private db: AztecAsyncKVStore) {
|
|
22
22
|
this.#l1ToL2Messages = db.openMap('archiver_l1_to_l2_messages');
|
|
23
23
|
this.#l1ToL2MessageIndices = db.openMap('archiver_l1_to_l2_message_indices');
|
|
24
24
|
this.#lastSynchedL1Block = db.openSingleton('archiver_last_l1_block_new_messages');
|
|
25
25
|
this.#totalMessageCount = db.openSingleton('archiver_l1_to_l2_message_count');
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
getTotalL1ToL2MessageCount(): bigint {
|
|
29
|
-
return this.#totalMessageCount.
|
|
28
|
+
async getTotalL1ToL2MessageCount(): Promise<bigint> {
|
|
29
|
+
return (await this.#totalMessageCount.getAsync()) ?? 0n;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Gets the last L1 block number that emitted new messages.
|
|
34
34
|
* @returns The last L1 block number processed
|
|
35
35
|
*/
|
|
36
|
-
getSynchedL1BlockNumber(): bigint | undefined {
|
|
37
|
-
return this.#lastSynchedL1Block.
|
|
36
|
+
getSynchedL1BlockNumber(): Promise<bigint | undefined> {
|
|
37
|
+
return this.#lastSynchedL1Block.getAsync();
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
setSynchedL1BlockNumber(l1BlockNumber: bigint) {
|
|
41
|
-
|
|
40
|
+
async setSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
|
|
41
|
+
await this.#lastSynchedL1Block.set(l1BlockNumber);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -47,22 +47,22 @@ export class MessageStore {
|
|
|
47
47
|
* @returns True if the operation is successful.
|
|
48
48
|
*/
|
|
49
49
|
addL1ToL2Messages(messages: DataRetrieval<InboxLeaf>): Promise<boolean> {
|
|
50
|
-
return this.db.
|
|
51
|
-
const lastL1BlockNumber = this.#lastSynchedL1Block.
|
|
50
|
+
return this.db.transactionAsync(async () => {
|
|
51
|
+
const lastL1BlockNumber = (await this.#lastSynchedL1Block.getAsync()) ?? 0n;
|
|
52
52
|
if (lastL1BlockNumber >= messages.lastProcessedL1BlockNumber) {
|
|
53
53
|
return false;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
await this.#lastSynchedL1Block.set(messages.lastProcessedL1BlockNumber);
|
|
57
57
|
|
|
58
58
|
for (const message of messages.retrievedData) {
|
|
59
59
|
const key = `${message.index}`;
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
await this.#l1ToL2Messages.set(key, message.leaf.toBuffer());
|
|
61
|
+
await this.#l1ToL2MessageIndices.set(message.leaf.toString(), message.index);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
const lastTotalMessageCount = this.getTotalL1ToL2MessageCount();
|
|
65
|
-
|
|
64
|
+
const lastTotalMessageCount = await this.getTotalL1ToL2MessageCount();
|
|
65
|
+
await this.#totalMessageCount.set(lastTotalMessageCount + BigInt(messages.retrievedData.length));
|
|
66
66
|
|
|
67
67
|
return true;
|
|
68
68
|
});
|
|
@@ -74,17 +74,17 @@ export class MessageStore {
|
|
|
74
74
|
* @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found).
|
|
75
75
|
*/
|
|
76
76
|
getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
|
|
77
|
-
return
|
|
77
|
+
return this.#l1ToL2MessageIndices.getAsync(l1ToL2Message.toString());
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
getL1ToL2Messages(blockNumber: bigint): Fr[] {
|
|
80
|
+
async getL1ToL2Messages(blockNumber: bigint): Promise<Fr[]> {
|
|
81
81
|
const messages: Fr[] = [];
|
|
82
82
|
let undefinedMessageFound = false;
|
|
83
83
|
const startIndex = Number(InboxLeaf.smallestIndexFromL2Block(blockNumber));
|
|
84
84
|
for (let i = startIndex; i < startIndex + this.#l1ToL2MessagesSubtreeSize; i++) {
|
|
85
85
|
// This is inefficient but probably fine for now.
|
|
86
86
|
const key = `${i}`;
|
|
87
|
-
const message = this.#l1ToL2Messages.
|
|
87
|
+
const message = await this.#l1ToL2Messages.getAsync(key);
|
|
88
88
|
if (message) {
|
|
89
89
|
if (undefinedMessageFound) {
|
|
90
90
|
throw new Error(`L1 to L2 message gap found in block ${blockNumber}`);
|