@aztec/archiver 3.0.0-nightly.20251223 → 3.0.0-nightly.20251224
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 -10
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +10 -9
- package/dest/archiver/archiver_store.d.ts +11 -8
- 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 +134 -39
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +4 -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 +9 -8
- package/dest/archiver/kv_archiver_store/log_store.d.ts +11 -8
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +112 -46
- package/dest/archiver/l1/data_retrieval.js +2 -2
- package/package.json +13 -13
- package/src/archiver/archiver.ts +20 -11
- package/src/archiver/archiver_store.ts +11 -7
- package/src/archiver/archiver_store_test_suite.ts +157 -43
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +11 -10
- package/src/archiver/kv_archiver_store/log_store.ts +140 -51
- package/src/archiver/l1/data_retrieval.ts +2 -2
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
SerializableContractInstance,
|
|
31
31
|
computePublicBytecodeCommitment,
|
|
32
32
|
} from '@aztec/stdlib/contract';
|
|
33
|
-
import { ContractClassLog, LogId, PrivateLog, PublicLog } from '@aztec/stdlib/logs';
|
|
33
|
+
import { ContractClassLog, LogId, PrivateLog, PublicLog, SiloedTag, Tag } from '@aztec/stdlib/logs';
|
|
34
34
|
import { InboxLeaf } from '@aztec/stdlib/messaging';
|
|
35
35
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
36
36
|
import {
|
|
@@ -2198,46 +2198,35 @@ export function describeArchiverDataStore(
|
|
|
2198
2198
|
});
|
|
2199
2199
|
});
|
|
2200
2200
|
|
|
2201
|
-
describe('
|
|
2201
|
+
describe('getPrivateLogsByTags', () => {
|
|
2202
2202
|
const numBlocksForLogs = 3;
|
|
2203
2203
|
const numTxsPerBlock = 4;
|
|
2204
2204
|
const numPrivateLogsPerTx = 3;
|
|
2205
|
-
const numPublicLogsPerTx = 2;
|
|
2206
2205
|
|
|
2207
2206
|
let logsCheckpoints: PublishedCheckpoint[];
|
|
2208
2207
|
|
|
2209
|
-
const
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2208
|
+
const makePrivateLogTag = (blockNumber: number, txIndex: number, logIndex: number): SiloedTag =>
|
|
2209
|
+
new SiloedTag(
|
|
2210
|
+
blockNumber === 1 && txIndex === 0 && logIndex === 0
|
|
2211
|
+
? Fr.ZERO // Shared tag
|
|
2212
|
+
: new Fr(blockNumber * 100 + txIndex * 10 + logIndex),
|
|
2213
|
+
);
|
|
2213
2214
|
|
|
2214
|
-
const makePrivateLog = (tag:
|
|
2215
|
+
const makePrivateLog = (tag: SiloedTag) =>
|
|
2215
2216
|
PrivateLog.from({
|
|
2216
|
-
fields: makeTuple(PRIVATE_LOG_SIZE_IN_FIELDS, i =>
|
|
2217
|
+
fields: makeTuple(PRIVATE_LOG_SIZE_IN_FIELDS, i =>
|
|
2218
|
+
!i ? tag.value : new Fr(tag.value.toBigInt() + BigInt(i)),
|
|
2219
|
+
),
|
|
2217
2220
|
emittedLength: PRIVATE_LOG_SIZE_IN_FIELDS,
|
|
2218
2221
|
});
|
|
2219
2222
|
|
|
2220
|
-
const makePublicLog = (tag: Fr) =>
|
|
2221
|
-
PublicLog.from({
|
|
2222
|
-
contractAddress: AztecAddress.fromNumber(1),
|
|
2223
|
-
// Arbitrary length
|
|
2224
|
-
fields: new Array(10).fill(null).map((_, i) => (!i ? tag : new Fr(tag.toNumber() + i))),
|
|
2225
|
-
});
|
|
2226
|
-
|
|
2227
2223
|
const mockPrivateLogs = (blockNumber: number, txIndex: number) => {
|
|
2228
2224
|
return times(numPrivateLogsPerTx, (logIndex: number) => {
|
|
2229
|
-
const tag =
|
|
2225
|
+
const tag = makePrivateLogTag(blockNumber, txIndex, logIndex);
|
|
2230
2226
|
return makePrivateLog(tag);
|
|
2231
2227
|
});
|
|
2232
2228
|
};
|
|
2233
2229
|
|
|
2234
|
-
const mockPublicLogs = (blockNumber: number, txIndex: number) => {
|
|
2235
|
-
return times(numPublicLogsPerTx, (logIndex: number) => {
|
|
2236
|
-
const tag = makeTag(blockNumber, txIndex, logIndex, /* isPublic */ true);
|
|
2237
|
-
return makePublicLog(tag);
|
|
2238
|
-
});
|
|
2239
|
-
};
|
|
2240
|
-
|
|
2241
2230
|
const mockCheckpointWithLogs = async (
|
|
2242
2231
|
blockNumber: number,
|
|
2243
2232
|
previousArchive?: AppendOnlyTreeSnapshot,
|
|
@@ -2253,7 +2242,7 @@ export function describeArchiverDataStore(
|
|
|
2253
2242
|
block.body.txEffects = await timesParallel(numTxsPerBlock, async (txIndex: number) => {
|
|
2254
2243
|
const txEffect = await TxEffect.random();
|
|
2255
2244
|
txEffect.privateLogs = mockPrivateLogs(blockNumber, txIndex);
|
|
2256
|
-
txEffect.publicLogs =
|
|
2245
|
+
txEffect.publicLogs = []; // No public logs needed for private log tests
|
|
2257
2246
|
return txEffect;
|
|
2258
2247
|
});
|
|
2259
2248
|
|
|
@@ -2279,9 +2268,9 @@ export function describeArchiverDataStore(
|
|
|
2279
2268
|
});
|
|
2280
2269
|
|
|
2281
2270
|
it('is possible to batch request private logs via tags', async () => {
|
|
2282
|
-
const tags = [
|
|
2271
|
+
const tags = [makePrivateLogTag(2, 1, 2), makePrivateLogTag(1, 2, 0)];
|
|
2283
2272
|
|
|
2284
|
-
const logsByTags = await store.
|
|
2273
|
+
const logsByTags = await store.getPrivateLogsByTags(tags);
|
|
2285
2274
|
|
|
2286
2275
|
expect(logsByTags).toEqual([
|
|
2287
2276
|
[
|
|
@@ -2303,11 +2292,21 @@ export function describeArchiverDataStore(
|
|
|
2303
2292
|
]);
|
|
2304
2293
|
});
|
|
2305
2294
|
|
|
2306
|
-
it('is possible to batch request
|
|
2307
|
-
|
|
2308
|
-
const tags = [makeTag(1, 0, 0)];
|
|
2295
|
+
it('is possible to batch request logs that have the same tag but different content', async () => {
|
|
2296
|
+
const tags = [makePrivateLogTag(1, 2, 1)];
|
|
2309
2297
|
|
|
2310
|
-
|
|
2298
|
+
// Create a checkpoint containing logs that have the same tag as the checkpoints before.
|
|
2299
|
+
// Chain from the last checkpoint's archive
|
|
2300
|
+
const newBlockNumber = numBlocksForLogs + 1;
|
|
2301
|
+
const previousArchive = logsCheckpoints[logsCheckpoints.length - 1].checkpoint.blocks[0].archive;
|
|
2302
|
+
const newCheckpoint = await mockCheckpointWithLogs(newBlockNumber, previousArchive);
|
|
2303
|
+
const newLog = newCheckpoint.checkpoint.blocks[0].body.txEffects[1].privateLogs[1];
|
|
2304
|
+
newLog.fields[0] = tags[0].value;
|
|
2305
|
+
newCheckpoint.checkpoint.blocks[0].body.txEffects[1].privateLogs[1] = newLog;
|
|
2306
|
+
await store.addCheckpoints([newCheckpoint]);
|
|
2307
|
+
await store.addLogs([newCheckpoint.checkpoint.blocks[0]]);
|
|
2308
|
+
|
|
2309
|
+
const logsByTags = await store.getPrivateLogsByTags(tags);
|
|
2311
2310
|
|
|
2312
2311
|
expect(logsByTags).toEqual([
|
|
2313
2312
|
[
|
|
@@ -2317,54 +2316,169 @@ export function describeArchiverDataStore(
|
|
|
2317
2316
|
log: makePrivateLog(tags[0]),
|
|
2318
2317
|
isFromPublic: false,
|
|
2319
2318
|
}),
|
|
2319
|
+
expect.objectContaining({
|
|
2320
|
+
blockNumber: newBlockNumber,
|
|
2321
|
+
blockHash: L2BlockHash.fromField(await newCheckpoint.checkpoint.blocks[0].header.hash()),
|
|
2322
|
+
log: newLog,
|
|
2323
|
+
isFromPublic: false,
|
|
2324
|
+
}),
|
|
2325
|
+
],
|
|
2326
|
+
]);
|
|
2327
|
+
});
|
|
2328
|
+
|
|
2329
|
+
it('is possible to request logs for non-existing tags and determine their position', async () => {
|
|
2330
|
+
const tags = [makePrivateLogTag(99, 88, 77), makePrivateLogTag(1, 1, 1)];
|
|
2331
|
+
|
|
2332
|
+
const logsByTags = await store.getPrivateLogsByTags(tags);
|
|
2333
|
+
|
|
2334
|
+
expect(logsByTags).toEqual([
|
|
2335
|
+
[
|
|
2336
|
+
// No logs for the first tag.
|
|
2337
|
+
],
|
|
2338
|
+
[
|
|
2320
2339
|
expect.objectContaining({
|
|
2321
2340
|
blockNumber: 1,
|
|
2322
2341
|
blockHash: L2BlockHash.fromField(await logsCheckpoints[1 - 1].checkpoint.blocks[0].header.hash()),
|
|
2342
|
+
log: makePrivateLog(tags[1]),
|
|
2343
|
+
isFromPublic: false,
|
|
2344
|
+
}),
|
|
2345
|
+
],
|
|
2346
|
+
]);
|
|
2347
|
+
});
|
|
2348
|
+
});
|
|
2349
|
+
|
|
2350
|
+
describe('getPublicLogsByTagsFromContract', () => {
|
|
2351
|
+
const numBlocksForLogs = 3;
|
|
2352
|
+
const numTxsPerBlock = 4;
|
|
2353
|
+
const numPublicLogsPerTx = 2;
|
|
2354
|
+
const contractAddress = AztecAddress.fromNumber(543254);
|
|
2355
|
+
|
|
2356
|
+
let logsCheckpoints: PublishedCheckpoint[];
|
|
2357
|
+
|
|
2358
|
+
const makePublicLogTag = (blockNumber: number, txIndex: number, logIndex: number): Tag =>
|
|
2359
|
+
new Tag(
|
|
2360
|
+
blockNumber === 1 && txIndex === 0 && logIndex === 0
|
|
2361
|
+
? Fr.ZERO // Shared tag
|
|
2362
|
+
: new Fr((blockNumber * 100 + txIndex * 10 + logIndex) * 123),
|
|
2363
|
+
);
|
|
2364
|
+
|
|
2365
|
+
const makePublicLog = (tag: Tag) =>
|
|
2366
|
+
PublicLog.from({
|
|
2367
|
+
contractAddress: contractAddress,
|
|
2368
|
+
// Arbitrary length
|
|
2369
|
+
fields: new Array(10).fill(null).map((_, i) => (!i ? tag.value : new Fr(tag.value.toBigInt() + BigInt(i)))),
|
|
2370
|
+
});
|
|
2371
|
+
|
|
2372
|
+
const mockPublicLogs = (blockNumber: number, txIndex: number) => {
|
|
2373
|
+
return times(numPublicLogsPerTx, (logIndex: number) => {
|
|
2374
|
+
const tag = makePublicLogTag(blockNumber, txIndex, logIndex);
|
|
2375
|
+
return makePublicLog(tag);
|
|
2376
|
+
});
|
|
2377
|
+
};
|
|
2378
|
+
|
|
2379
|
+
const mockCheckpointWithLogs = async (
|
|
2380
|
+
blockNumber: number,
|
|
2381
|
+
previousArchive?: AppendOnlyTreeSnapshot,
|
|
2382
|
+
): Promise<PublishedCheckpoint> => {
|
|
2383
|
+
const block = await L2BlockNew.random(BlockNumber(blockNumber), {
|
|
2384
|
+
checkpointNumber: CheckpointNumber(blockNumber),
|
|
2385
|
+
indexWithinCheckpoint: 0,
|
|
2386
|
+
state: makeStateForBlock(blockNumber, numTxsPerBlock),
|
|
2387
|
+
...(previousArchive ? { lastArchive: previousArchive } : {}),
|
|
2388
|
+
});
|
|
2389
|
+
block.header.globalVariables.blockNumber = BlockNumber(blockNumber);
|
|
2390
|
+
|
|
2391
|
+
block.body.txEffects = await timesParallel(numTxsPerBlock, async (txIndex: number) => {
|
|
2392
|
+
const txEffect = await TxEffect.random();
|
|
2393
|
+
txEffect.privateLogs = []; // No private logs needed for public log tests
|
|
2394
|
+
txEffect.publicLogs = mockPublicLogs(blockNumber, txIndex);
|
|
2395
|
+
return txEffect;
|
|
2396
|
+
});
|
|
2397
|
+
|
|
2398
|
+
const checkpoint = new Checkpoint(
|
|
2399
|
+
AppendOnlyTreeSnapshot.random(),
|
|
2400
|
+
CheckpointHeader.random(),
|
|
2401
|
+
[block],
|
|
2402
|
+
CheckpointNumber(blockNumber),
|
|
2403
|
+
);
|
|
2404
|
+
return makePublishedCheckpoint(checkpoint, blockNumber);
|
|
2405
|
+
};
|
|
2406
|
+
|
|
2407
|
+
beforeEach(async () => {
|
|
2408
|
+
// Create checkpoints sequentially to chain archive roots
|
|
2409
|
+
logsCheckpoints = [];
|
|
2410
|
+
for (let i = 0; i < numBlocksForLogs; i++) {
|
|
2411
|
+
const previousArchive = i > 0 ? logsCheckpoints[i - 1].checkpoint.blocks[0].archive : undefined;
|
|
2412
|
+
logsCheckpoints.push(await mockCheckpointWithLogs(i + 1, previousArchive));
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
await store.addCheckpoints(logsCheckpoints);
|
|
2416
|
+
await store.addLogs(logsCheckpoints.flatMap(p => p.checkpoint.blocks));
|
|
2417
|
+
});
|
|
2418
|
+
|
|
2419
|
+
it('is possible to batch request public logs via tags', async () => {
|
|
2420
|
+
const tags = [makePublicLogTag(2, 1, 1), makePublicLogTag(1, 2, 0)];
|
|
2421
|
+
|
|
2422
|
+
const logsByTags = await store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
2423
|
+
|
|
2424
|
+
expect(logsByTags).toEqual([
|
|
2425
|
+
[
|
|
2426
|
+
expect.objectContaining({
|
|
2427
|
+
blockNumber: 2,
|
|
2428
|
+
blockHash: L2BlockHash.fromField(await logsCheckpoints[2 - 1].checkpoint.blocks[0].header.hash()),
|
|
2323
2429
|
log: makePublicLog(tags[0]),
|
|
2324
2430
|
isFromPublic: true,
|
|
2325
2431
|
}),
|
|
2326
2432
|
],
|
|
2433
|
+
[
|
|
2434
|
+
expect.objectContaining({
|
|
2435
|
+
blockNumber: 1,
|
|
2436
|
+
blockHash: L2BlockHash.fromField(await logsCheckpoints[1 - 1].checkpoint.blocks[0].header.hash()),
|
|
2437
|
+
log: makePublicLog(tags[1]),
|
|
2438
|
+
isFromPublic: true,
|
|
2439
|
+
}),
|
|
2440
|
+
],
|
|
2327
2441
|
]);
|
|
2328
2442
|
});
|
|
2329
2443
|
|
|
2330
2444
|
it('is possible to batch request logs that have the same tag but different content', async () => {
|
|
2331
|
-
const tags = [
|
|
2445
|
+
const tags = [makePublicLogTag(1, 2, 1)];
|
|
2332
2446
|
|
|
2333
2447
|
// Create a checkpoint containing logs that have the same tag as the checkpoints before.
|
|
2334
2448
|
// Chain from the last checkpoint's archive
|
|
2335
2449
|
const newBlockNumber = numBlocksForLogs + 1;
|
|
2336
2450
|
const previousArchive = logsCheckpoints[logsCheckpoints.length - 1].checkpoint.blocks[0].archive;
|
|
2337
2451
|
const newCheckpoint = await mockCheckpointWithLogs(newBlockNumber, previousArchive);
|
|
2338
|
-
const newLog = newCheckpoint.checkpoint.blocks[0].body.txEffects[1].
|
|
2339
|
-
newLog.fields[0] = tags[0];
|
|
2340
|
-
newCheckpoint.checkpoint.blocks[0].body.txEffects[1].
|
|
2452
|
+
const newLog = newCheckpoint.checkpoint.blocks[0].body.txEffects[1].publicLogs[1];
|
|
2453
|
+
newLog.fields[0] = tags[0].value;
|
|
2454
|
+
newCheckpoint.checkpoint.blocks[0].body.txEffects[1].publicLogs[1] = newLog;
|
|
2341
2455
|
await store.addCheckpoints([newCheckpoint]);
|
|
2342
2456
|
await store.addLogs([newCheckpoint.checkpoint.blocks[0]]);
|
|
2343
2457
|
|
|
2344
|
-
const logsByTags = await store.
|
|
2458
|
+
const logsByTags = await store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
2345
2459
|
|
|
2346
2460
|
expect(logsByTags).toEqual([
|
|
2347
2461
|
[
|
|
2348
2462
|
expect.objectContaining({
|
|
2349
2463
|
blockNumber: 1,
|
|
2350
2464
|
blockHash: L2BlockHash.fromField(await logsCheckpoints[1 - 1].checkpoint.blocks[0].header.hash()),
|
|
2351
|
-
log:
|
|
2352
|
-
isFromPublic:
|
|
2465
|
+
log: makePublicLog(tags[0]),
|
|
2466
|
+
isFromPublic: true,
|
|
2353
2467
|
}),
|
|
2354
2468
|
expect.objectContaining({
|
|
2355
2469
|
blockNumber: newBlockNumber,
|
|
2356
2470
|
blockHash: L2BlockHash.fromField(await newCheckpoint.checkpoint.blocks[0].header.hash()),
|
|
2357
2471
|
log: newLog,
|
|
2358
|
-
isFromPublic:
|
|
2472
|
+
isFromPublic: true,
|
|
2359
2473
|
}),
|
|
2360
2474
|
],
|
|
2361
2475
|
]);
|
|
2362
2476
|
});
|
|
2363
2477
|
|
|
2364
2478
|
it('is possible to request logs for non-existing tags and determine their position', async () => {
|
|
2365
|
-
const tags = [
|
|
2479
|
+
const tags = [makePublicLogTag(99, 88, 77), makePublicLogTag(1, 1, 0)];
|
|
2366
2480
|
|
|
2367
|
-
const logsByTags = await store.
|
|
2481
|
+
const logsByTags = await store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
2368
2482
|
|
|
2369
2483
|
expect(logsByTags).toEqual([
|
|
2370
2484
|
[
|
|
@@ -2374,8 +2488,8 @@ export function describeArchiverDataStore(
|
|
|
2374
2488
|
expect.objectContaining({
|
|
2375
2489
|
blockNumber: 1,
|
|
2376
2490
|
blockHash: L2BlockHash.fromField(await logsCheckpoints[1 - 1].checkpoint.blocks[0].header.hash()),
|
|
2377
|
-
log:
|
|
2378
|
-
isFromPublic:
|
|
2491
|
+
log: makePublicLog(tags[1]),
|
|
2492
|
+
isFromPublic: true,
|
|
2379
2493
|
}),
|
|
2380
2494
|
],
|
|
2381
2495
|
]);
|
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
UtilityFunctionWithMembershipProof,
|
|
18
18
|
} from '@aztec/stdlib/contract';
|
|
19
19
|
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
20
|
-
import type { LogFilter, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
20
|
+
import type { LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
21
21
|
import type { BlockHeader, TxHash, TxReceipt } from '@aztec/stdlib/tx';
|
|
22
22
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
23
23
|
|
|
@@ -318,16 +318,17 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
318
318
|
return this.#messageStore.getL1ToL2Messages(checkpointNumber);
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
321
|
+
getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
|
|
322
|
+
try {
|
|
323
|
+
return this.#logStore.getPrivateLogsByTags(tags);
|
|
324
|
+
} catch (err) {
|
|
325
|
+
return Promise.reject(err);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
|
|
329
330
|
try {
|
|
330
|
-
return this.#logStore.
|
|
331
|
+
return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
331
332
|
} catch (err) {
|
|
332
333
|
return Promise.reject(err);
|
|
333
334
|
}
|
|
@@ -4,6 +4,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
4
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
5
|
import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
|
|
6
6
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
7
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
7
8
|
import { L2BlockHash, L2BlockNew } from '@aztec/stdlib/block';
|
|
8
9
|
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
9
10
|
import {
|
|
@@ -13,6 +14,8 @@ import {
|
|
|
13
14
|
type LogFilter,
|
|
14
15
|
LogId,
|
|
15
16
|
PublicLog,
|
|
17
|
+
type SiloedTag,
|
|
18
|
+
Tag,
|
|
16
19
|
TxScopedL2Log,
|
|
17
20
|
} from '@aztec/stdlib/logs';
|
|
18
21
|
|
|
@@ -22,8 +25,12 @@ import type { BlockStore } from './block_store.js';
|
|
|
22
25
|
* A store for logs
|
|
23
26
|
*/
|
|
24
27
|
export class LogStore {
|
|
25
|
-
|
|
26
|
-
#
|
|
28
|
+
// `tag` --> private logs
|
|
29
|
+
#privateLogsByTag: AztecAsyncMap<string, Buffer[]>;
|
|
30
|
+
// `{contractAddress}_${tag}` --> public logs
|
|
31
|
+
#publicLogsByContractAndTag: AztecAsyncMap<string, Buffer[]>;
|
|
32
|
+
#privateLogKeysByBlock: AztecAsyncMap<number, string[]>;
|
|
33
|
+
#publicLogKeysByBlock: AztecAsyncMap<number, string[]>;
|
|
27
34
|
#publicLogsByBlock: AztecAsyncMap<number, Buffer>;
|
|
28
35
|
#contractClassLogsByBlock: AztecAsyncMap<number, Buffer>;
|
|
29
36
|
#logsMaxPageSize: number;
|
|
@@ -34,29 +41,42 @@ export class LogStore {
|
|
|
34
41
|
private blockStore: BlockStore,
|
|
35
42
|
logsMaxPageSize: number = 1000,
|
|
36
43
|
) {
|
|
37
|
-
this.#
|
|
38
|
-
this.#
|
|
44
|
+
this.#privateLogsByTag = db.openMap('archiver_private_tagged_logs_by_tag');
|
|
45
|
+
this.#publicLogsByContractAndTag = db.openMap('archiver_public_tagged_logs_by_tag');
|
|
46
|
+
this.#privateLogKeysByBlock = db.openMap('archiver_private_log_keys_by_block');
|
|
47
|
+
this.#publicLogKeysByBlock = db.openMap('archiver_public_log_keys_by_block');
|
|
39
48
|
this.#publicLogsByBlock = db.openMap('archiver_public_logs_by_block');
|
|
40
49
|
this.#contractClassLogsByBlock = db.openMap('archiver_contract_class_logs_by_block');
|
|
41
50
|
|
|
42
51
|
this.#logsMaxPageSize = logsMaxPageSize;
|
|
43
52
|
}
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Extracts tagged logs from a single block, grouping them into private and public maps.
|
|
56
|
+
*
|
|
57
|
+
* @param block - The L2 block to extract logs from.
|
|
58
|
+
* @returns An object containing the private and public tagged logs for the block.
|
|
59
|
+
*/
|
|
60
|
+
async #extractTaggedLogsFromBlock(block: L2BlockNew) {
|
|
46
61
|
const blockHash = L2BlockHash.fromField(await block.hash());
|
|
47
|
-
|
|
62
|
+
// SiloedTag (as string) -> array of log buffers.
|
|
63
|
+
const privateTaggedLogs = new Map<string, Buffer[]>();
|
|
64
|
+
// "{contractAddress}_{tag}" (as string) -> array of log buffers.
|
|
65
|
+
const publicTaggedLogs = new Map<string, Buffer[]>();
|
|
48
66
|
const dataStartIndexForBlock =
|
|
49
67
|
block.header.state.partial.noteHashTree.nextAvailableLeafIndex -
|
|
50
68
|
block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX;
|
|
69
|
+
|
|
51
70
|
block.body.txEffects.forEach((txEffect, txIndex) => {
|
|
52
71
|
const txHash = txEffect.txHash;
|
|
53
72
|
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
|
|
54
73
|
|
|
55
74
|
txEffect.privateLogs.forEach((log, logIndex) => {
|
|
75
|
+
// Private logs use SiloedTag (already siloed by kernel)
|
|
56
76
|
const tag = log.fields[0];
|
|
57
77
|
this.#log.debug(`Found private log with tag ${tag.toString()} in block ${block.number}`);
|
|
58
78
|
|
|
59
|
-
const currentLogs =
|
|
79
|
+
const currentLogs = privateTaggedLogs.get(tag.toString()) ?? [];
|
|
60
80
|
currentLogs.push(
|
|
61
81
|
new TxScopedL2Log(
|
|
62
82
|
txHash,
|
|
@@ -68,14 +88,19 @@ export class LogStore {
|
|
|
68
88
|
log,
|
|
69
89
|
).toBuffer(),
|
|
70
90
|
);
|
|
71
|
-
|
|
91
|
+
privateTaggedLogs.set(tag.toString(), currentLogs);
|
|
72
92
|
});
|
|
73
93
|
|
|
74
94
|
txEffect.publicLogs.forEach((log, logIndex) => {
|
|
95
|
+
// Public logs use Tag directly (not siloed) and are stored with contract address
|
|
75
96
|
const tag = log.fields[0];
|
|
76
|
-
|
|
97
|
+
const contractAddress = log.contractAddress;
|
|
98
|
+
const key = `${contractAddress.toString()}_${tag.toString()}`;
|
|
99
|
+
this.#log.debug(
|
|
100
|
+
`Found public log with tag ${tag.toString()} from contract ${contractAddress.toString()} in block ${block.number}`,
|
|
101
|
+
);
|
|
77
102
|
|
|
78
|
-
const currentLogs =
|
|
103
|
+
const currentLogs = publicTaggedLogs.get(key) ?? [];
|
|
79
104
|
currentLogs.push(
|
|
80
105
|
new TxScopedL2Log(
|
|
81
106
|
txHash,
|
|
@@ -87,49 +112,102 @@ export class LogStore {
|
|
|
87
112
|
log,
|
|
88
113
|
).toBuffer(),
|
|
89
114
|
);
|
|
90
|
-
|
|
115
|
+
publicTaggedLogs.set(key, currentLogs);
|
|
91
116
|
});
|
|
92
117
|
});
|
|
93
|
-
|
|
118
|
+
|
|
119
|
+
return { privateTaggedLogs, publicTaggedLogs };
|
|
94
120
|
}
|
|
95
121
|
|
|
96
122
|
/**
|
|
97
|
-
*
|
|
98
|
-
* @param blocks - The blocks
|
|
99
|
-
* @returns
|
|
123
|
+
* Extracts and aggregates tagged logs from a list of blocks.
|
|
124
|
+
* @param blocks - The blocks to extract logs from.
|
|
125
|
+
* @returns A map from tag (as string) to an array of serialized private logs belonging to that tag, and a map from
|
|
126
|
+
* "{contractAddress}_{tag}" (as string) to an array of serialized public logs belonging to that key.
|
|
100
127
|
*/
|
|
101
|
-
async
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
128
|
+
async #extractTaggedLogs(
|
|
129
|
+
blocks: L2BlockNew[],
|
|
130
|
+
): Promise<{ privateTaggedLogs: Map<string, Buffer[]>; publicTaggedLogs: Map<string, Buffer[]> }> {
|
|
131
|
+
const taggedLogsInBlocks = await Promise.all(blocks.map(block => this.#extractTaggedLogsFromBlock(block)));
|
|
132
|
+
|
|
133
|
+
// Now we merge the maps from each block into a single map.
|
|
134
|
+
const privateTaggedLogs = taggedLogsInBlocks.reduce((acc, { privateTaggedLogs }) => {
|
|
135
|
+
for (const [tag, logs] of privateTaggedLogs.entries()) {
|
|
105
136
|
const currentLogs = acc.get(tag) ?? [];
|
|
106
137
|
acc.set(tag, currentLogs.concat(logs));
|
|
107
138
|
}
|
|
108
139
|
return acc;
|
|
109
140
|
}, new Map<string, Buffer[]>());
|
|
110
|
-
|
|
141
|
+
|
|
142
|
+
const publicTaggedLogs = taggedLogsInBlocks.reduce((acc, { publicTaggedLogs }) => {
|
|
143
|
+
for (const [key, logs] of publicTaggedLogs.entries()) {
|
|
144
|
+
const currentLogs = acc.get(key) ?? [];
|
|
145
|
+
acc.set(key, currentLogs.concat(logs));
|
|
146
|
+
}
|
|
147
|
+
return acc;
|
|
148
|
+
}, new Map<string, Buffer[]>());
|
|
149
|
+
|
|
150
|
+
return { privateTaggedLogs, publicTaggedLogs };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Append new logs to the store's list.
|
|
155
|
+
* @param blocks - The blocks for which to add the logs.
|
|
156
|
+
* @returns True if the operation is successful.
|
|
157
|
+
*/
|
|
158
|
+
async addLogs(blocks: L2BlockNew[]): Promise<boolean> {
|
|
159
|
+
const { privateTaggedLogs, publicTaggedLogs } = await this.#extractTaggedLogs(blocks);
|
|
160
|
+
|
|
161
|
+
const keysOfPrivateLogsToUpdate = Array.from(privateTaggedLogs.keys());
|
|
162
|
+
const keysOfPublicLogsToUpdate = Array.from(publicTaggedLogs.keys());
|
|
111
163
|
|
|
112
164
|
return this.db.transactionAsync(async () => {
|
|
113
|
-
const
|
|
114
|
-
|
|
165
|
+
const currentPrivateTaggedLogs = await Promise.all(
|
|
166
|
+
keysOfPrivateLogsToUpdate.map(async key => ({
|
|
167
|
+
tag: key,
|
|
168
|
+
logBuffers: await this.#privateLogsByTag.getAsync(key),
|
|
169
|
+
})),
|
|
115
170
|
);
|
|
116
|
-
|
|
171
|
+
currentPrivateTaggedLogs.forEach(taggedLogBuffer => {
|
|
117
172
|
if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
|
|
118
|
-
|
|
173
|
+
privateTaggedLogs.set(
|
|
119
174
|
taggedLogBuffer.tag,
|
|
120
|
-
taggedLogBuffer.logBuffers!.concat(
|
|
175
|
+
taggedLogBuffer.logBuffers!.concat(privateTaggedLogs.get(taggedLogBuffer.tag)!),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const currentPublicTaggedLogs = await Promise.all(
|
|
181
|
+
keysOfPublicLogsToUpdate.map(async key => ({
|
|
182
|
+
key,
|
|
183
|
+
logBuffers: await this.#publicLogsByContractAndTag.getAsync(key),
|
|
184
|
+
})),
|
|
185
|
+
);
|
|
186
|
+
currentPublicTaggedLogs.forEach(taggedLogBuffer => {
|
|
187
|
+
if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
|
|
188
|
+
publicTaggedLogs.set(
|
|
189
|
+
taggedLogBuffer.key,
|
|
190
|
+
taggedLogBuffer.logBuffers!.concat(publicTaggedLogs.get(taggedLogBuffer.key)!),
|
|
121
191
|
);
|
|
122
192
|
}
|
|
123
193
|
});
|
|
194
|
+
|
|
124
195
|
for (const block of blocks) {
|
|
125
196
|
const blockHash = await block.hash();
|
|
126
197
|
|
|
127
|
-
const
|
|
128
|
-
for (const [tag, logs] of
|
|
129
|
-
await this.#
|
|
130
|
-
|
|
198
|
+
const privateTagsInBlock: string[] = [];
|
|
199
|
+
for (const [tag, logs] of privateTaggedLogs.entries()) {
|
|
200
|
+
await this.#privateLogsByTag.set(tag, logs);
|
|
201
|
+
privateTagsInBlock.push(tag);
|
|
131
202
|
}
|
|
132
|
-
await this.#
|
|
203
|
+
await this.#privateLogKeysByBlock.set(block.number, privateTagsInBlock);
|
|
204
|
+
|
|
205
|
+
const publicKeysInBlock: string[] = [];
|
|
206
|
+
for (const [key, logs] of publicTaggedLogs.entries()) {
|
|
207
|
+
await this.#publicLogsByContractAndTag.set(key, logs);
|
|
208
|
+
publicKeysInBlock.push(key);
|
|
209
|
+
}
|
|
210
|
+
await this.#publicLogKeysByBlock.set(block.number, publicKeysInBlock);
|
|
133
211
|
|
|
134
212
|
const publicLogsInBlock = block.body.txEffects
|
|
135
213
|
.map((txEffect, txIndex) =>
|
|
@@ -178,44 +256,55 @@ export class LogStore {
|
|
|
178
256
|
|
|
179
257
|
deleteLogs(blocks: L2BlockNew[]): Promise<boolean> {
|
|
180
258
|
return this.db.transactionAsync(async () => {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
259
|
+
await Promise.all(
|
|
260
|
+
blocks.map(async block => {
|
|
261
|
+
// Delete private logs
|
|
262
|
+
const privateKeys = (await this.#privateLogKeysByBlock.getAsync(block.number)) ?? [];
|
|
263
|
+
await Promise.all(privateKeys.map(tag => this.#privateLogsByTag.delete(tag)));
|
|
264
|
+
|
|
265
|
+
// Delete public logs
|
|
266
|
+
const publicKeys = (await this.#publicLogKeysByBlock.getAsync(block.number)) ?? [];
|
|
267
|
+
await Promise.all(publicKeys.map(key => this.#publicLogsByContractAndTag.delete(key)));
|
|
268
|
+
}),
|
|
269
|
+
);
|
|
189
270
|
|
|
190
271
|
await Promise.all(
|
|
191
272
|
blocks.map(block =>
|
|
192
273
|
Promise.all([
|
|
193
274
|
this.#publicLogsByBlock.delete(block.number),
|
|
194
|
-
this.#
|
|
275
|
+
this.#privateLogKeysByBlock.delete(block.number),
|
|
276
|
+
this.#publicLogKeysByBlock.delete(block.number),
|
|
195
277
|
this.#contractClassLogsByBlock.delete(block.number),
|
|
196
278
|
]),
|
|
197
279
|
),
|
|
198
280
|
);
|
|
199
281
|
|
|
200
|
-
await Promise.all(tagsToDelete.map(tag => this.#logsByTag.delete(tag.toString())));
|
|
201
282
|
return true;
|
|
202
283
|
});
|
|
203
284
|
}
|
|
204
285
|
|
|
205
286
|
/**
|
|
206
|
-
* Gets all logs that match any of the
|
|
207
|
-
*
|
|
208
|
-
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
209
|
-
* that tag.
|
|
287
|
+
* Gets all private logs that match any of the `tags`. For each tag, an array of matching logs is returned. An empty
|
|
288
|
+
* array implies no logs match that tag.
|
|
210
289
|
*/
|
|
211
|
-
async
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
290
|
+
async getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
|
|
291
|
+
const logs = await Promise.all(tags.map(tag => this.#privateLogsByTag.getAsync(tag.toString())));
|
|
292
|
+
|
|
293
|
+
return logs.map(logBuffers => logBuffers?.map(logBuffer => TxScopedL2Log.fromBuffer(logBuffer)) ?? []);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Gets all public logs that match any of the `tags` from the specified contract. For each tag, an array of matching
|
|
298
|
+
* logs is returned. An empty array implies no logs match that tag.
|
|
299
|
+
*/
|
|
300
|
+
async getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
|
|
301
|
+
const logs = await Promise.all(
|
|
302
|
+
tags.map(tag => {
|
|
303
|
+
const key = `${contractAddress.toString()}_${tag.value.toString()}`;
|
|
304
|
+
return this.#publicLogsByContractAndTag.getAsync(key);
|
|
305
|
+
}),
|
|
218
306
|
);
|
|
307
|
+
return logs.map(logBuffers => logBuffers?.map(logBuffer => TxScopedL2Log.fromBuffer(logBuffer)) ?? []);
|
|
219
308
|
}
|
|
220
309
|
|
|
221
310
|
/**
|
|
@@ -331,7 +331,7 @@ export async function getCheckpointBlobDataFromBlobs(
|
|
|
331
331
|
logger: Logger,
|
|
332
332
|
isHistoricalSync: boolean,
|
|
333
333
|
): Promise<CheckpointBlobData> {
|
|
334
|
-
const blobBodies = await blobClient.getBlobSidecar(blockHash, blobHashes,
|
|
334
|
+
const blobBodies = await blobClient.getBlobSidecar(blockHash, blobHashes, { isHistoricalSync });
|
|
335
335
|
if (blobBodies.length === 0) {
|
|
336
336
|
throw new NoBlobBodiesFoundError(checkpointNumber);
|
|
337
337
|
}
|
|
@@ -339,7 +339,7 @@ export async function getCheckpointBlobDataFromBlobs(
|
|
|
339
339
|
let checkpointBlobData: CheckpointBlobData;
|
|
340
340
|
try {
|
|
341
341
|
// Attempt to decode the checkpoint blob data.
|
|
342
|
-
checkpointBlobData = decodeCheckpointBlobDataFromBlobs(blobBodies
|
|
342
|
+
checkpointBlobData = decodeCheckpointBlobDataFromBlobs(blobBodies);
|
|
343
343
|
} catch (err: any) {
|
|
344
344
|
if (err instanceof BlobDeserializationError) {
|
|
345
345
|
logger.fatal(err.message);
|