@aztec/archiver 0.59.0 → 0.60.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 +8 -1
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +13 -1
- package/dest/archiver/archiver_store.d.ts +8 -1
- 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 +93 -1
- package/dest/archiver/data_retrieval.d.ts +1 -0
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +8 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +15 -1
- package/dest/archiver/kv_archiver_store/log_store.d.ts +9 -1
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +86 -24
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +11 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +44 -1
- package/package.json +10 -10
- package/src/archiver/archiver.ts +14 -0
- package/src/archiver/archiver_store.ts +9 -0
- package/src/archiver/archiver_store_test_suite.ts +118 -0
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +15 -0
- package/src/archiver/kv_archiver_store/log_store.ts +88 -21
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +45 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type EncryptedL2NoteLog,
|
|
2
3
|
type FromLogType,
|
|
3
4
|
type GetUnencryptedLogsResponse,
|
|
4
5
|
type InboxLeaf,
|
|
@@ -136,6 +137,14 @@ export interface ArchiverDataStore {
|
|
|
136
137
|
logType: TLogType,
|
|
137
138
|
): Promise<L2BlockL2Logs<FromLogType<TLogType>>[]>;
|
|
138
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag).
|
|
142
|
+
* @param tags - The tags to filter the logs by.
|
|
143
|
+
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
144
|
+
* that tag.
|
|
145
|
+
*/
|
|
146
|
+
getLogsByTags(tags: Fr[]): Promise<EncryptedL2NoteLog[][]>;
|
|
147
|
+
|
|
139
148
|
/**
|
|
140
149
|
* Gets unencrypted logs based on the provided filter.
|
|
141
150
|
* @param filter - The filter to apply to the logs.
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
makeExecutablePrivateFunctionWithMembershipProof,
|
|
15
15
|
makeUnconstrainedFunctionWithMembershipProof,
|
|
16
16
|
} from '@aztec/circuits.js/testing';
|
|
17
|
+
import { toBufferBE } from '@aztec/foundation/bigint-buffer';
|
|
17
18
|
import { times } from '@aztec/foundation/collection';
|
|
18
19
|
import { randomBytes, randomInt } from '@aztec/foundation/crypto';
|
|
19
20
|
|
|
@@ -354,6 +355,123 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
354
355
|
});
|
|
355
356
|
});
|
|
356
357
|
|
|
358
|
+
describe('getLogsByTags', () => {
|
|
359
|
+
const txsPerBlock = 4;
|
|
360
|
+
const numPrivateFunctionCalls = 3;
|
|
361
|
+
const numNoteEncryptedLogs = 2;
|
|
362
|
+
const numBlocks = 10;
|
|
363
|
+
let blocks: L1Published<L2Block>[];
|
|
364
|
+
let tags: { [i: number]: { [j: number]: Buffer[] } } = {};
|
|
365
|
+
|
|
366
|
+
beforeEach(async () => {
|
|
367
|
+
blocks = times(numBlocks, (index: number) => ({
|
|
368
|
+
data: L2Block.random(index + 1, txsPerBlock, numPrivateFunctionCalls, 2, numNoteEncryptedLogs, 2),
|
|
369
|
+
l1: { blockNumber: BigInt(index), blockHash: `0x${index}`, timestamp: BigInt(index) },
|
|
370
|
+
}));
|
|
371
|
+
// Last block has the note encrypted log tags of the first tx copied from the previous block
|
|
372
|
+
blocks[numBlocks - 1].data.body.noteEncryptedLogs.txLogs[0].functionLogs.forEach((fnLogs, fnIndex) => {
|
|
373
|
+
fnLogs.logs.forEach((log, logIndex) => {
|
|
374
|
+
const previousLogData =
|
|
375
|
+
blocks[numBlocks - 2].data.body.noteEncryptedLogs.txLogs[0].functionLogs[fnIndex].logs[logIndex].data;
|
|
376
|
+
previousLogData.copy(log.data, 0, 0, 32);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
// Last block has invalid tags in the second tx
|
|
380
|
+
const tooBig = toBufferBE(Fr.MODULUS, 32);
|
|
381
|
+
blocks[numBlocks - 1].data.body.noteEncryptedLogs.txLogs[1].functionLogs.forEach(fnLogs => {
|
|
382
|
+
fnLogs.logs.forEach(log => {
|
|
383
|
+
tooBig.copy(log.data, 0, 0, 32);
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
await store.addBlocks(blocks);
|
|
388
|
+
await store.addLogs(blocks.map(b => b.data));
|
|
389
|
+
|
|
390
|
+
tags = {};
|
|
391
|
+
blocks.forEach((b, blockIndex) => {
|
|
392
|
+
if (!tags[blockIndex]) {
|
|
393
|
+
tags[blockIndex] = {};
|
|
394
|
+
}
|
|
395
|
+
b.data.body.noteEncryptedLogs.txLogs.forEach((txLogs, txIndex) => {
|
|
396
|
+
if (!tags[blockIndex][txIndex]) {
|
|
397
|
+
tags[blockIndex][txIndex] = [];
|
|
398
|
+
}
|
|
399
|
+
tags[blockIndex][txIndex].push(...txLogs.unrollLogs().map(log => log.data.subarray(0, 32)));
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('is possible to batch request all logs of a tx via tags', async () => {
|
|
405
|
+
// get random tx from any block that's not the last one
|
|
406
|
+
const targetBlockIndex = randomInt(numBlocks - 2);
|
|
407
|
+
const targetTxIndex = randomInt(txsPerBlock);
|
|
408
|
+
|
|
409
|
+
const logsByTags = await store.getLogsByTags(
|
|
410
|
+
tags[targetBlockIndex][targetTxIndex].map(buffer => new Fr(buffer)),
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
const expectedResponseSize = numPrivateFunctionCalls * numNoteEncryptedLogs;
|
|
414
|
+
expect(logsByTags.length).toEqual(expectedResponseSize);
|
|
415
|
+
|
|
416
|
+
logsByTags.forEach((logsByTag, logIndex) => {
|
|
417
|
+
expect(logsByTag).toHaveLength(1);
|
|
418
|
+
const [log] = logsByTag;
|
|
419
|
+
expect(log).toEqual(
|
|
420
|
+
blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex],
|
|
421
|
+
);
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('is possible to batch request all logs of different blocks via tags', async () => {
|
|
426
|
+
// get first tx of first block and second tx of second block
|
|
427
|
+
const logsByTags = await store.getLogsByTags([...tags[0][0], ...tags[1][1]].map(buffer => new Fr(buffer)));
|
|
428
|
+
|
|
429
|
+
const expectedResponseSize = 2 * numPrivateFunctionCalls * numNoteEncryptedLogs;
|
|
430
|
+
expect(logsByTags.length).toEqual(expectedResponseSize);
|
|
431
|
+
|
|
432
|
+
logsByTags.forEach(logsByTag => expect(logsByTag).toHaveLength(1));
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('is possible to batch request logs that have the same tag but different content', async () => {
|
|
436
|
+
// get first tx of last block
|
|
437
|
+
const logsByTags = await store.getLogsByTags(tags[numBlocks - 1][0].map(buffer => new Fr(buffer)));
|
|
438
|
+
|
|
439
|
+
const expectedResponseSize = numPrivateFunctionCalls * numNoteEncryptedLogs;
|
|
440
|
+
expect(logsByTags.length).toEqual(expectedResponseSize);
|
|
441
|
+
|
|
442
|
+
logsByTags.forEach(logsByTag => {
|
|
443
|
+
expect(logsByTag).toHaveLength(2);
|
|
444
|
+
const [tag0, tag1] = logsByTag.map(log => new Fr(log.data.subarray(0, 32)));
|
|
445
|
+
expect(tag0).toEqual(tag1);
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('is possible to request logs for non-existing tags and determine their position', async () => {
|
|
450
|
+
// get random tx from any block that's not the last one
|
|
451
|
+
const targetBlockIndex = randomInt(numBlocks - 2);
|
|
452
|
+
const targetTxIndex = randomInt(txsPerBlock);
|
|
453
|
+
|
|
454
|
+
const logsByTags = await store.getLogsByTags([
|
|
455
|
+
Fr.random(),
|
|
456
|
+
...tags[targetBlockIndex][targetTxIndex].slice(1).map(buffer => new Fr(buffer)),
|
|
457
|
+
]);
|
|
458
|
+
|
|
459
|
+
const expectedResponseSize = numPrivateFunctionCalls * numNoteEncryptedLogs;
|
|
460
|
+
expect(logsByTags.length).toEqual(expectedResponseSize);
|
|
461
|
+
|
|
462
|
+
const [emptyLogsByTag, ...populatedLogsByTags] = logsByTags;
|
|
463
|
+
expect(emptyLogsByTag).toHaveLength(0);
|
|
464
|
+
|
|
465
|
+
populatedLogsByTags.forEach((logsByTag, logIndex) => {
|
|
466
|
+
expect(logsByTag).toHaveLength(1);
|
|
467
|
+
const [log] = logsByTag;
|
|
468
|
+
expect(log).toEqual(
|
|
469
|
+
blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex + 1],
|
|
470
|
+
);
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
357
475
|
describe('getUnencryptedLogs', () => {
|
|
358
476
|
const txsPerBlock = 4;
|
|
359
477
|
const numPublicFunctionCalls = 3;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type EncryptedL2NoteLog,
|
|
2
3
|
type FromLogType,
|
|
3
4
|
type GetUnencryptedLogsResponse,
|
|
4
5
|
type InboxLeaf,
|
|
@@ -239,6 +240,20 @@ export class KVArchiverDataStore implements ArchiverDataStore {
|
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag).
|
|
245
|
+
* @param tags - The tags to filter the logs by.
|
|
246
|
+
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
247
|
+
* that tag.
|
|
248
|
+
*/
|
|
249
|
+
getLogsByTags(tags: Fr[]): Promise<EncryptedL2NoteLog[][]> {
|
|
250
|
+
try {
|
|
251
|
+
return this.#logStore.getLogsByTags(tags);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
return Promise.reject(err);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
242
257
|
/**
|
|
243
258
|
* Gets unencrypted logs based on the provided filter.
|
|
244
259
|
* @param filter - The filter to apply to the logs.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EncryptedL2BlockL2Logs,
|
|
3
|
+
EncryptedL2NoteLog,
|
|
3
4
|
EncryptedNoteL2BlockL2Logs,
|
|
4
5
|
ExtendedUnencryptedL2Log,
|
|
5
6
|
type FromLogType,
|
|
@@ -12,9 +13,10 @@ import {
|
|
|
12
13
|
UnencryptedL2BlockL2Logs,
|
|
13
14
|
type UnencryptedL2Log,
|
|
14
15
|
} from '@aztec/circuit-types';
|
|
16
|
+
import { Fr } from '@aztec/circuits.js';
|
|
15
17
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js/constants';
|
|
16
18
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
17
|
-
import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
|
|
19
|
+
import { type AztecKVStore, type AztecMap, type AztecMultiMap } from '@aztec/kv-store';
|
|
18
20
|
|
|
19
21
|
import { type BlockStore } from './block_store.js';
|
|
20
22
|
|
|
@@ -22,16 +24,22 @@ import { type BlockStore } from './block_store.js';
|
|
|
22
24
|
* A store for logs
|
|
23
25
|
*/
|
|
24
26
|
export class LogStore {
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
27
|
+
#noteEncryptedLogsByBlock: AztecMap<number, Buffer>;
|
|
28
|
+
#noteEncryptedLogsByHash: AztecMap<string, Buffer>;
|
|
29
|
+
#noteEncryptedLogHashesByTag: AztecMultiMap<string, string>;
|
|
30
|
+
#noteEncryptedLogTagsByBlock: AztecMultiMap<number, string>;
|
|
31
|
+
#encryptedLogsByBlock: AztecMap<number, Buffer>;
|
|
32
|
+
#unencryptedLogsByBlock: AztecMap<number, Buffer>;
|
|
28
33
|
#logsMaxPageSize: number;
|
|
29
34
|
#log = createDebugLogger('aztec:archiver:log_store');
|
|
30
35
|
|
|
31
36
|
constructor(private db: AztecKVStore, private blockStore: BlockStore, logsMaxPageSize: number = 1000) {
|
|
32
|
-
this.#
|
|
33
|
-
this.#
|
|
34
|
-
this.#
|
|
37
|
+
this.#noteEncryptedLogsByBlock = db.openMap('archiver_note_encrypted_logs_by_block');
|
|
38
|
+
this.#noteEncryptedLogsByHash = db.openMap('archiver_note_encrypted_logs_by_hash');
|
|
39
|
+
this.#noteEncryptedLogHashesByTag = db.openMultiMap('archiver_tagged_note_encrypted_log_hashes_by_tag');
|
|
40
|
+
this.#noteEncryptedLogTagsByBlock = db.openMultiMap('archiver_note_encrypted_log_tags_by_block');
|
|
41
|
+
this.#encryptedLogsByBlock = db.openMap('archiver_encrypted_logs_by_block');
|
|
42
|
+
this.#unencryptedLogsByBlock = db.openMap('archiver_unencrypted_logs_by_block');
|
|
35
43
|
|
|
36
44
|
this.#logsMaxPageSize = logsMaxPageSize;
|
|
37
45
|
}
|
|
@@ -44,21 +52,58 @@ export class LogStore {
|
|
|
44
52
|
addLogs(blocks: L2Block[]): Promise<boolean> {
|
|
45
53
|
return this.db.transaction(() => {
|
|
46
54
|
blocks.forEach(block => {
|
|
47
|
-
void this.#
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
void this.#noteEncryptedLogsByBlock.set(block.number, block.body.noteEncryptedLogs.toBuffer());
|
|
56
|
+
block.body.noteEncryptedLogs.txLogs.forEach(txLogs => {
|
|
57
|
+
const noteLogs = txLogs.unrollLogs();
|
|
58
|
+
noteLogs.forEach(noteLog => {
|
|
59
|
+
if (noteLog.data.length < 32) {
|
|
60
|
+
this.#log.warn(`Skipping note log with invalid data length: ${noteLog.data.length}`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const tag = new Fr(noteLog.data.subarray(0, 32));
|
|
65
|
+
const hexHash = noteLog.hash().toString('hex');
|
|
66
|
+
// Ideally we'd store all of the logs for a matching tag in an AztecMultiMap, but this type doesn't doesn't
|
|
67
|
+
// handle storing buffers well. The 'ordered-binary' encoding returns an error trying to decode buffers
|
|
68
|
+
// ('the number <> cannot be converted to a BigInt because it is not an integer'). We therefore store
|
|
69
|
+
// instead the hashes of the logs.
|
|
70
|
+
void this.#noteEncryptedLogHashesByTag.set(tag.toString(), hexHash);
|
|
71
|
+
void this.#noteEncryptedLogsByHash.set(hexHash, noteLog.toBuffer());
|
|
72
|
+
void this.#noteEncryptedLogTagsByBlock.set(block.number, tag.toString());
|
|
73
|
+
} catch (err) {
|
|
74
|
+
this.#log.warn(`Failed to add tagged note log to store: ${err}`);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
void this.#encryptedLogsByBlock.set(block.number, block.body.encryptedLogs.toBuffer());
|
|
79
|
+
void this.#unencryptedLogsByBlock.set(block.number, block.body.unencryptedLogs.toBuffer());
|
|
50
80
|
});
|
|
51
81
|
|
|
52
82
|
return true;
|
|
53
83
|
});
|
|
54
84
|
}
|
|
55
85
|
|
|
56
|
-
deleteLogs(blocks: L2Block[]): Promise<boolean> {
|
|
86
|
+
async deleteLogs(blocks: L2Block[]): Promise<boolean> {
|
|
87
|
+
const noteTagsToDelete = await this.db.transaction(() => {
|
|
88
|
+
return blocks.flatMap(block => Array.from(this.#noteEncryptedLogTagsByBlock.getValues(block.number)));
|
|
89
|
+
});
|
|
90
|
+
const noteLogHashesToDelete = await this.db.transaction(() => {
|
|
91
|
+
return noteTagsToDelete.flatMap(tag => Array.from(this.#noteEncryptedLogHashesByTag.getValues(tag)));
|
|
92
|
+
});
|
|
57
93
|
return this.db.transaction(() => {
|
|
58
94
|
blocks.forEach(block => {
|
|
59
|
-
void this.#
|
|
60
|
-
void this.#
|
|
61
|
-
void this.#
|
|
95
|
+
void this.#noteEncryptedLogsByBlock.delete(block.number);
|
|
96
|
+
void this.#encryptedLogsByBlock.delete(block.number);
|
|
97
|
+
void this.#unencryptedLogsByBlock.delete(block.number);
|
|
98
|
+
void this.#noteEncryptedLogTagsByBlock.delete(block.number);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
noteTagsToDelete.forEach(tag => {
|
|
102
|
+
void this.#noteEncryptedLogHashesByTag.delete(tag.toString());
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
noteLogHashesToDelete.forEach(hash => {
|
|
106
|
+
void this.#noteEncryptedLogsByHash.delete(hash);
|
|
62
107
|
});
|
|
63
108
|
|
|
64
109
|
return true;
|
|
@@ -80,12 +125,12 @@ export class LogStore {
|
|
|
80
125
|
const logMap = (() => {
|
|
81
126
|
switch (logType) {
|
|
82
127
|
case LogType.ENCRYPTED:
|
|
83
|
-
return this.#
|
|
128
|
+
return this.#encryptedLogsByBlock;
|
|
84
129
|
case LogType.NOTEENCRYPTED:
|
|
85
|
-
return this.#
|
|
130
|
+
return this.#noteEncryptedLogsByBlock;
|
|
86
131
|
case LogType.UNENCRYPTED:
|
|
87
132
|
default:
|
|
88
|
-
return this.#
|
|
133
|
+
return this.#unencryptedLogsByBlock;
|
|
89
134
|
}
|
|
90
135
|
})();
|
|
91
136
|
const logTypeMap = (() => {
|
|
@@ -105,6 +150,28 @@ export class LogStore {
|
|
|
105
150
|
}
|
|
106
151
|
}
|
|
107
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag).
|
|
155
|
+
* @param tags - The tags to filter the logs by.
|
|
156
|
+
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
157
|
+
* that tag.
|
|
158
|
+
*/
|
|
159
|
+
getLogsByTags(tags: Fr[]): Promise<EncryptedL2NoteLog[][]> {
|
|
160
|
+
return this.db.transaction(() => {
|
|
161
|
+
return tags.map(tag => {
|
|
162
|
+
const logHashes = Array.from(this.#noteEncryptedLogHashesByTag.getValues(tag.toString()));
|
|
163
|
+
return (
|
|
164
|
+
logHashes
|
|
165
|
+
.map(hash => this.#noteEncryptedLogsByHash.get(hash))
|
|
166
|
+
// addLogs should ensure that we never have undefined logs, but we filter them out regardless to protect
|
|
167
|
+
// ourselves from database corruption
|
|
168
|
+
.filter(noteLogBuffer => noteLogBuffer != undefined)
|
|
169
|
+
.map(noteLogBuffer => EncryptedL2NoteLog.fromBuffer(noteLogBuffer!))
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
108
175
|
/**
|
|
109
176
|
* Gets unencrypted logs based on the provided filter.
|
|
110
177
|
* @param filter - The filter to apply to the logs.
|
|
@@ -154,7 +221,7 @@ export class LogStore {
|
|
|
154
221
|
const logs: ExtendedUnencryptedL2Log[] = [];
|
|
155
222
|
|
|
156
223
|
let maxLogsHit = false;
|
|
157
|
-
loopOverBlocks: for (const [blockNumber, logBuffer] of this.#
|
|
224
|
+
loopOverBlocks: for (const [blockNumber, logBuffer] of this.#unencryptedLogsByBlock.entries({ start, end })) {
|
|
158
225
|
const unencryptedLogsInBlock = UnencryptedL2BlockL2Logs.fromBuffer(logBuffer);
|
|
159
226
|
for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < unencryptedLogsInBlock.txLogs.length; txIndex++) {
|
|
160
227
|
const txLogs = unencryptedLogsInBlock.txLogs[txIndex].unrollLogs();
|
|
@@ -199,12 +266,12 @@ export class LogStore {
|
|
|
199
266
|
const logMap = (() => {
|
|
200
267
|
switch (logType) {
|
|
201
268
|
case LogType.ENCRYPTED:
|
|
202
|
-
return this.#
|
|
269
|
+
return this.#encryptedLogsByBlock;
|
|
203
270
|
case LogType.NOTEENCRYPTED:
|
|
204
|
-
return this.#
|
|
271
|
+
return this.#noteEncryptedLogsByBlock;
|
|
205
272
|
case LogType.UNENCRYPTED:
|
|
206
273
|
default:
|
|
207
|
-
return this.#
|
|
274
|
+
return this.#unencryptedLogsByBlock;
|
|
208
275
|
}
|
|
209
276
|
})();
|
|
210
277
|
const logTypeMap = (() => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type EncryptedL2BlockL2Logs,
|
|
3
|
+
type EncryptedL2NoteLog,
|
|
3
4
|
type EncryptedNoteL2BlockL2Logs,
|
|
4
5
|
ExtendedUnencryptedL2Log,
|
|
5
6
|
type FromLogType,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
} from '@aztec/circuits.js';
|
|
28
29
|
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
29
30
|
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
31
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
30
32
|
|
|
31
33
|
import { type ArchiverDataStore, type ArchiverL1SynchPoint } from '../archiver_store.js';
|
|
32
34
|
import { type DataRetrieval } from '../structs/data_retrieval.js';
|
|
@@ -49,6 +51,10 @@ export class MemoryArchiverStore implements ArchiverDataStore {
|
|
|
49
51
|
|
|
50
52
|
private noteEncryptedLogsPerBlock: Map<number, EncryptedNoteL2BlockL2Logs> = new Map();
|
|
51
53
|
|
|
54
|
+
private taggedNoteEncryptedLogs: Map<string, EncryptedL2NoteLog[]> = new Map();
|
|
55
|
+
|
|
56
|
+
private noteEncryptedLogTagsPerBlock: Map<number, Fr[]> = new Map();
|
|
57
|
+
|
|
52
58
|
private encryptedLogsPerBlock: Map<number, EncryptedL2BlockL2Logs> = new Map();
|
|
53
59
|
|
|
54
60
|
private unencryptedLogsPerBlock: Map<number, UnencryptedL2BlockL2Logs> = new Map();
|
|
@@ -74,6 +80,8 @@ export class MemoryArchiverStore implements ArchiverDataStore {
|
|
|
74
80
|
private lastProvenL2BlockNumber: number = 0;
|
|
75
81
|
private lastProvenL2EpochNumber: number = 0;
|
|
76
82
|
|
|
83
|
+
#log = createDebugLogger('aztec:archiver:data-store');
|
|
84
|
+
|
|
77
85
|
constructor(
|
|
78
86
|
/** The max number of logs that can be obtained in 1 "getUnencryptedLogs" call. */
|
|
79
87
|
public readonly maxLogs: number,
|
|
@@ -206,6 +214,24 @@ export class MemoryArchiverStore implements ArchiverDataStore {
|
|
|
206
214
|
addLogs(blocks: L2Block[]): Promise<boolean> {
|
|
207
215
|
blocks.forEach(block => {
|
|
208
216
|
this.noteEncryptedLogsPerBlock.set(block.number, block.body.noteEncryptedLogs);
|
|
217
|
+
block.body.noteEncryptedLogs.txLogs.forEach(txLogs => {
|
|
218
|
+
const noteLogs = txLogs.unrollLogs();
|
|
219
|
+
noteLogs.forEach(noteLog => {
|
|
220
|
+
if (noteLog.data.length < 32) {
|
|
221
|
+
this.#log.warn(`Skipping note log with invalid data length: ${noteLog.data.length}`);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
const tag = new Fr(noteLog.data.subarray(0, 32));
|
|
226
|
+
const currentNoteLogs = this.taggedNoteEncryptedLogs.get(tag.toString()) || [];
|
|
227
|
+
this.taggedNoteEncryptedLogs.set(tag.toString(), [...currentNoteLogs, noteLog]);
|
|
228
|
+
const currentTagsInBlock = this.noteEncryptedLogTagsPerBlock.get(block.number) || [];
|
|
229
|
+
this.noteEncryptedLogTagsPerBlock.set(block.number, [...currentTagsInBlock, tag]);
|
|
230
|
+
} catch (err) {
|
|
231
|
+
this.#log.warn(`Failed to add tagged note log to store: ${err}`);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
});
|
|
209
235
|
this.encryptedLogsPerBlock.set(block.number, block.body.encryptedLogs);
|
|
210
236
|
this.unencryptedLogsPerBlock.set(block.number, block.body.unencryptedLogs);
|
|
211
237
|
});
|
|
@@ -213,10 +239,18 @@ export class MemoryArchiverStore implements ArchiverDataStore {
|
|
|
213
239
|
}
|
|
214
240
|
|
|
215
241
|
deleteLogs(blocks: L2Block[]): Promise<boolean> {
|
|
242
|
+
const noteTagsToDelete = blocks.flatMap(block => this.noteEncryptedLogTagsPerBlock.get(block.number));
|
|
243
|
+
noteTagsToDelete
|
|
244
|
+
.filter(tag => tag != undefined)
|
|
245
|
+
.forEach(tag => {
|
|
246
|
+
this.taggedNoteEncryptedLogs.delete(tag!.toString());
|
|
247
|
+
});
|
|
248
|
+
|
|
216
249
|
blocks.forEach(block => {
|
|
217
250
|
this.encryptedLogsPerBlock.delete(block.number);
|
|
218
251
|
this.noteEncryptedLogsPerBlock.delete(block.number);
|
|
219
252
|
this.unencryptedLogsPerBlock.delete(block.number);
|
|
253
|
+
this.noteEncryptedLogTagsPerBlock.delete(block.number);
|
|
220
254
|
});
|
|
221
255
|
|
|
222
256
|
return Promise.resolve(true);
|
|
@@ -380,6 +414,17 @@ export class MemoryArchiverStore implements ArchiverDataStore {
|
|
|
380
414
|
return Promise.resolve(l);
|
|
381
415
|
}
|
|
382
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag).
|
|
419
|
+
* @param tags - The tags to filter the logs by.
|
|
420
|
+
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
421
|
+
* that tag.
|
|
422
|
+
*/
|
|
423
|
+
getLogsByTags(tags: Fr[]): Promise<EncryptedL2NoteLog[][]> {
|
|
424
|
+
const noteLogs = tags.map(tag => this.taggedNoteEncryptedLogs.get(tag.toString()) || []);
|
|
425
|
+
return Promise.resolve(noteLogs);
|
|
426
|
+
}
|
|
427
|
+
|
|
383
428
|
/**
|
|
384
429
|
* Gets unencrypted logs based on the provided filter.
|
|
385
430
|
* @param filter - The filter to apply to the logs.
|