@aztec/archiver 0.62.0 → 0.63.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 +11 -2
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +43 -19
- package/dest/archiver/archiver_store.d.ts +8 -2
- 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 +64 -25
- package/dest/archiver/config.d.ts +2 -6
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +3 -6
- package/dest/archiver/epoch_helpers.d.ts +10 -10
- package/dest/archiver/epoch_helpers.d.ts.map +1 -1
- package/dest/archiver/epoch_helpers.js +9 -10
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +8 -2
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +14 -1
- package/dest/archiver/kv_archiver_store/log_store.d.ts +8 -2
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +144 -57
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +13 -4
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +136 -31
- package/dest/factory.d.ts +5 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +2 -2
- package/dest/rpc/index.d.ts +3 -2
- package/dest/rpc/index.d.ts.map +1 -1
- package/dest/rpc/index.js +10 -3
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +4 -2
- package/package.json +10 -10
- package/src/archiver/archiver.ts +54 -24
- package/src/archiver/archiver_store.ts +9 -2
- package/src/archiver/archiver_store_test_suite.ts +85 -26
- package/src/archiver/config.ts +11 -12
- package/src/archiver/epoch_helpers.ts +16 -12
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +15 -2
- package/src/archiver/kv_archiver_store/log_store.ts +176 -58
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +152 -27
- package/src/factory.ts +6 -3
- package/src/rpc/index.ts +11 -2
- package/src/test/mock_l2_block_source.ts +3 -1
- package/dest/rpc/archiver_client.d.ts +0 -3
- package/dest/rpc/archiver_client.d.ts.map +0 -1
- package/dest/rpc/archiver_client.js +0 -12
- package/dest/rpc/archiver_server.d.ts +0 -9
- package/dest/rpc/archiver_server.d.ts.map +0 -1
- package/dest/rpc/archiver_server.js +0 -20
- package/src/rpc/archiver_client.ts +0 -29
- package/src/rpc/archiver_server.ts +0 -35
package/src/archiver/archiver.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type
|
|
2
|
+
type EncryptedL2Log,
|
|
3
3
|
type FromLogType,
|
|
4
4
|
type GetUnencryptedLogsResponse,
|
|
5
5
|
type InboxLeaf,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type TxEffect,
|
|
16
16
|
type TxHash,
|
|
17
17
|
type TxReceipt,
|
|
18
|
+
type TxScopedL2Log,
|
|
18
19
|
type UnencryptedL2Log,
|
|
19
20
|
} from '@aztec/circuit-types';
|
|
20
21
|
import {
|
|
@@ -160,6 +161,8 @@ export class Archiver implements ArchiveSource {
|
|
|
160
161
|
rollup.read.GENESIS_TIME(),
|
|
161
162
|
] as const);
|
|
162
163
|
|
|
164
|
+
const { aztecEpochDuration: epochDuration, aztecSlotDuration: slotDuration, ethereumSlotDuration } = config;
|
|
165
|
+
|
|
163
166
|
const archiver = new Archiver(
|
|
164
167
|
publicClient,
|
|
165
168
|
config.l1Contracts.rollupAddress,
|
|
@@ -168,7 +171,7 @@ export class Archiver implements ArchiveSource {
|
|
|
168
171
|
archiverStore,
|
|
169
172
|
config.archiverPollingIntervalMS ?? 10_000,
|
|
170
173
|
new ArchiverInstrumentation(telemetry),
|
|
171
|
-
{ l1StartBlock, l1GenesisTime },
|
|
174
|
+
{ l1StartBlock, l1GenesisTime, epochDuration, slotDuration, ethereumSlotDuration },
|
|
172
175
|
);
|
|
173
176
|
await archiver.start(blockUntilSynced);
|
|
174
177
|
return archiver;
|
|
@@ -246,6 +249,12 @@ export class Archiver implements ArchiveSource {
|
|
|
246
249
|
// ********** Events that are processed per L1 block **********
|
|
247
250
|
await this.handleL1ToL2Messages(blockUntilSynced, messagesSynchedTo, currentL1BlockNumber);
|
|
248
251
|
|
|
252
|
+
// Store latest l1 block number and timestamp seen. Used for epoch and slots calculations.
|
|
253
|
+
if (!this.l1BlockNumber || this.l1BlockNumber < currentL1BlockNumber) {
|
|
254
|
+
this.l1Timestamp = (await this.publicClient.getBlock({ blockNumber: currentL1BlockNumber })).timestamp;
|
|
255
|
+
this.l1BlockNumber = currentL1BlockNumber;
|
|
256
|
+
}
|
|
257
|
+
|
|
249
258
|
// ********** Events that are processed per L2 block **********
|
|
250
259
|
if (currentL1BlockNumber > blocksSynchedTo) {
|
|
251
260
|
// First we retrieve new L2 blocks
|
|
@@ -257,21 +266,17 @@ export class Archiver implements ArchiveSource {
|
|
|
257
266
|
// up to which point we're pruning, and then requesting L2 blocks up to that point only.
|
|
258
267
|
await this.handleEpochPrune(provenBlockNumber, currentL1BlockNumber);
|
|
259
268
|
}
|
|
260
|
-
|
|
261
|
-
// Store latest l1 block number and timestamp seen. Used for epoch and slots calculations.
|
|
262
|
-
if (!this.l1BlockNumber || this.l1BlockNumber < currentL1BlockNumber) {
|
|
263
|
-
this.l1Timestamp = await this.publicClient.getBlock({ blockNumber: currentL1BlockNumber }).then(b => b.timestamp);
|
|
264
|
-
this.l1BlockNumber = currentL1BlockNumber;
|
|
265
|
-
}
|
|
266
269
|
}
|
|
267
270
|
|
|
268
271
|
/** Checks if there'd be a reorg for the next block submission and start pruning now. */
|
|
269
272
|
private async handleEpochPrune(provenBlockNumber: bigint, currentL1BlockNumber: bigint) {
|
|
270
273
|
const localPendingBlockNumber = BigInt(await this.getBlockNumber());
|
|
271
274
|
|
|
275
|
+
const time = (this.l1Timestamp ?? 0n) + BigInt(this.l1constants.ethereumSlotDuration);
|
|
276
|
+
|
|
272
277
|
const canPrune =
|
|
273
278
|
localPendingBlockNumber > provenBlockNumber &&
|
|
274
|
-
(await this.rollup.read.
|
|
279
|
+
(await this.rollup.read.canPruneAtTime([time], { blockNumber: currentL1BlockNumber }));
|
|
275
280
|
|
|
276
281
|
if (canPrune) {
|
|
277
282
|
this.log.verbose(`L2 prune will occur on next submission. Rolling back to last proven block.`);
|
|
@@ -499,7 +504,7 @@ export class Archiver implements ArchiveSource {
|
|
|
499
504
|
}
|
|
500
505
|
|
|
501
506
|
public async getBlocksForEpoch(epochNumber: bigint): Promise<L2Block[]> {
|
|
502
|
-
const [start, end] = getSlotRangeForEpoch(epochNumber);
|
|
507
|
+
const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
|
|
503
508
|
const blocks: L2Block[] = [];
|
|
504
509
|
|
|
505
510
|
// Walk the list of blocks backwards and filter by slots matching the requested epoch.
|
|
@@ -520,7 +525,7 @@ export class Archiver implements ArchiveSource {
|
|
|
520
525
|
// The epoch is complete if the current L2 block is the last one in the epoch (or later)
|
|
521
526
|
const header = await this.getBlockHeader('latest');
|
|
522
527
|
const slot = header?.globalVariables.slotNumber.toBigInt();
|
|
523
|
-
const [_startSlot, endSlot] = getSlotRangeForEpoch(epochNumber);
|
|
528
|
+
const [_startSlot, endSlot] = getSlotRangeForEpoch(epochNumber, this.l1constants);
|
|
524
529
|
if (slot && slot >= endSlot) {
|
|
525
530
|
return true;
|
|
526
531
|
}
|
|
@@ -634,7 +639,7 @@ export class Archiver implements ArchiveSource {
|
|
|
634
639
|
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
635
640
|
* that tag.
|
|
636
641
|
*/
|
|
637
|
-
getLogsByTags(tags: Fr[]): Promise<
|
|
642
|
+
getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
|
|
638
643
|
return this.store.getLogsByTags(tags);
|
|
639
644
|
}
|
|
640
645
|
|
|
@@ -647,6 +652,15 @@ export class Archiver implements ArchiveSource {
|
|
|
647
652
|
return this.store.getUnencryptedLogs(filter);
|
|
648
653
|
}
|
|
649
654
|
|
|
655
|
+
/**
|
|
656
|
+
* Gets contract class logs based on the provided filter.
|
|
657
|
+
* @param filter - The filter to apply to the logs.
|
|
658
|
+
* @returns The requested logs.
|
|
659
|
+
*/
|
|
660
|
+
getContractClassLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
|
|
661
|
+
return this.store.getContractClassLogs(filter);
|
|
662
|
+
}
|
|
663
|
+
|
|
650
664
|
/**
|
|
651
665
|
* Gets the number of the latest L2 block processed by the block source implementation.
|
|
652
666
|
* @returns The number of the latest L2 block processed by the block source implementation.
|
|
@@ -785,7 +799,7 @@ class ArchiverStoreHelper
|
|
|
785
799
|
* Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
|
|
786
800
|
* @param allLogs - All logs emitted in a bunch of blocks.
|
|
787
801
|
*/
|
|
788
|
-
async #updateDeployedContractInstances(allLogs:
|
|
802
|
+
async #updateDeployedContractInstances(allLogs: EncryptedL2Log[], blockNum: number, operation: Operation) {
|
|
789
803
|
const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs).map(e => e.toContractInstance());
|
|
790
804
|
if (contractInstances.length > 0) {
|
|
791
805
|
contractInstances.forEach(c =>
|
|
@@ -864,15 +878,18 @@ class ArchiverStoreHelper
|
|
|
864
878
|
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
865
879
|
...(await Promise.all(
|
|
866
880
|
blocks.map(async block => {
|
|
867
|
-
const
|
|
868
|
-
.flatMap(txEffect => (txEffect ? [txEffect.
|
|
881
|
+
const contractClassLogs = block.data.body.txEffects
|
|
882
|
+
.flatMap(txEffect => (txEffect ? [txEffect.contractClassLogs] : []))
|
|
883
|
+
.flatMap(txLog => txLog.unrollLogs());
|
|
884
|
+
// ContractInstanceDeployed event logs are now broadcast in .encryptedLogs
|
|
885
|
+
const allEncryptedLogs = block.data.body.txEffects
|
|
886
|
+
.flatMap(txEffect => (txEffect ? [txEffect.encryptedLogs] : []))
|
|
869
887
|
.flatMap(txLog => txLog.unrollLogs());
|
|
870
|
-
|
|
871
888
|
return (
|
|
872
889
|
await Promise.all([
|
|
873
|
-
this.#updateRegisteredContractClasses(
|
|
874
|
-
this.#updateDeployedContractInstances(
|
|
875
|
-
this.#storeBroadcastedIndividualFunctions(
|
|
890
|
+
this.#updateRegisteredContractClasses(contractClassLogs, block.data.number, Operation.Store),
|
|
891
|
+
this.#updateDeployedContractInstances(allEncryptedLogs, block.data.number, Operation.Store),
|
|
892
|
+
this.#storeBroadcastedIndividualFunctions(contractClassLogs, block.data.number),
|
|
876
893
|
])
|
|
877
894
|
).every(Boolean);
|
|
878
895
|
}),
|
|
@@ -894,11 +911,15 @@ class ArchiverStoreHelper
|
|
|
894
911
|
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
895
912
|
...(await Promise.all(
|
|
896
913
|
blocks.map(async block => {
|
|
897
|
-
const
|
|
898
|
-
.flatMap(txEffect => (txEffect ? [txEffect.
|
|
914
|
+
const contractClassLogs = block.data.body.txEffects
|
|
915
|
+
.flatMap(txEffect => (txEffect ? [txEffect.contractClassLogs] : []))
|
|
899
916
|
.flatMap(txLog => txLog.unrollLogs());
|
|
900
|
-
|
|
901
|
-
|
|
917
|
+
// ContractInstanceDeployed event logs are now broadcast in .encryptedLogs
|
|
918
|
+
const allEncryptedLogs = block.data.body.txEffects
|
|
919
|
+
.flatMap(txEffect => (txEffect ? [txEffect.encryptedLogs] : []))
|
|
920
|
+
.flatMap(txLog => txLog.unrollLogs());
|
|
921
|
+
await this.#updateRegisteredContractClasses(contractClassLogs, block.data.number, Operation.Delete);
|
|
922
|
+
await this.#updateDeployedContractInstances(allEncryptedLogs, block.data.number, Operation.Delete);
|
|
902
923
|
}),
|
|
903
924
|
)),
|
|
904
925
|
this.store.deleteLogs(blocks.map(b => b.data)),
|
|
@@ -934,12 +955,15 @@ class ArchiverStoreHelper
|
|
|
934
955
|
): Promise<L2BlockL2Logs<FromLogType<TLogType>>[]> {
|
|
935
956
|
return this.store.getLogs(from, limit, logType);
|
|
936
957
|
}
|
|
937
|
-
getLogsByTags(tags: Fr[]): Promise<
|
|
958
|
+
getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
|
|
938
959
|
return this.store.getLogsByTags(tags);
|
|
939
960
|
}
|
|
940
961
|
getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
|
|
941
962
|
return this.store.getUnencryptedLogs(filter);
|
|
942
963
|
}
|
|
964
|
+
getContractClassLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
|
|
965
|
+
return this.store.getContractClassLogs(filter);
|
|
966
|
+
}
|
|
943
967
|
getSynchedL2BlockNumber(): Promise<number> {
|
|
944
968
|
return this.store.getSynchedL2BlockNumber();
|
|
945
969
|
}
|
|
@@ -987,9 +1011,15 @@ class ArchiverStoreHelper
|
|
|
987
1011
|
type L1RollupConstants = {
|
|
988
1012
|
l1StartBlock: bigint;
|
|
989
1013
|
l1GenesisTime: bigint;
|
|
1014
|
+
slotDuration: number;
|
|
1015
|
+
epochDuration: number;
|
|
1016
|
+
ethereumSlotDuration: number;
|
|
990
1017
|
};
|
|
991
1018
|
|
|
992
1019
|
const EmptyL1RollupConstants: L1RollupConstants = {
|
|
993
1020
|
l1StartBlock: 0n,
|
|
994
1021
|
l1GenesisTime: 0n,
|
|
1022
|
+
epochDuration: 0,
|
|
1023
|
+
slotDuration: 0,
|
|
1024
|
+
ethereumSlotDuration: 0,
|
|
995
1025
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type EncryptedL2NoteLog,
|
|
3
2
|
type FromLogType,
|
|
4
3
|
type GetUnencryptedLogsResponse,
|
|
5
4
|
type InboxLeaf,
|
|
@@ -10,6 +9,7 @@ import {
|
|
|
10
9
|
type TxEffect,
|
|
11
10
|
type TxHash,
|
|
12
11
|
type TxReceipt,
|
|
12
|
+
type TxScopedL2Log,
|
|
13
13
|
} from '@aztec/circuit-types';
|
|
14
14
|
import {
|
|
15
15
|
type ContractClassPublic,
|
|
@@ -142,7 +142,7 @@ export interface ArchiverDataStore {
|
|
|
142
142
|
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
143
143
|
* that tag.
|
|
144
144
|
*/
|
|
145
|
-
getLogsByTags(tags: Fr[]): Promise<
|
|
145
|
+
getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]>;
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
148
|
* Gets unencrypted logs based on the provided filter.
|
|
@@ -151,6 +151,13 @@ export interface ArchiverDataStore {
|
|
|
151
151
|
*/
|
|
152
152
|
getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse>;
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Gets contract class logs based on the provided filter.
|
|
156
|
+
* @param filter - The filter to apply to the logs.
|
|
157
|
+
* @returns The requested logs.
|
|
158
|
+
*/
|
|
159
|
+
getContractClassLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse>;
|
|
160
|
+
|
|
154
161
|
/**
|
|
155
162
|
* Gets the number of the latest L2 block processed.
|
|
156
163
|
* @returns The number of the latest L2 block processed.
|
|
@@ -344,14 +344,24 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
344
344
|
describe('getLogsByTags', () => {
|
|
345
345
|
const txsPerBlock = 4;
|
|
346
346
|
const numPrivateFunctionCalls = 3;
|
|
347
|
-
const
|
|
347
|
+
const numPublicFunctionCalls = 1;
|
|
348
|
+
const numEncryptedLogsPerFn = 2;
|
|
349
|
+
const numUnencryptedLogsPerFn = 1;
|
|
348
350
|
const numBlocks = 10;
|
|
349
351
|
let blocks: L1Published<L2Block>[];
|
|
350
|
-
let
|
|
352
|
+
let encryptedLogTags: { [i: number]: { [j: number]: Buffer[] } } = {};
|
|
353
|
+
let unencryptedLogTags: { [i: number]: { [j: number]: Buffer[] } } = {};
|
|
351
354
|
|
|
352
355
|
beforeEach(async () => {
|
|
353
356
|
blocks = times(numBlocks, (index: number) => ({
|
|
354
|
-
data: L2Block.random(
|
|
357
|
+
data: L2Block.random(
|
|
358
|
+
index + 1,
|
|
359
|
+
txsPerBlock,
|
|
360
|
+
numPrivateFunctionCalls,
|
|
361
|
+
numPublicFunctionCalls,
|
|
362
|
+
numEncryptedLogsPerFn,
|
|
363
|
+
numUnencryptedLogsPerFn,
|
|
364
|
+
),
|
|
355
365
|
l1: { blockNumber: BigInt(index), blockHash: `0x${index}`, timestamp: BigInt(index) },
|
|
356
366
|
}));
|
|
357
367
|
// Last block has the note encrypted log tags of the first tx copied from the previous block
|
|
@@ -373,46 +383,94 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
373
383
|
await store.addBlocks(blocks);
|
|
374
384
|
await store.addLogs(blocks.map(b => b.data));
|
|
375
385
|
|
|
376
|
-
|
|
386
|
+
encryptedLogTags = {};
|
|
387
|
+
unencryptedLogTags = {};
|
|
377
388
|
blocks.forEach((b, blockIndex) => {
|
|
378
|
-
if (!
|
|
379
|
-
|
|
389
|
+
if (!encryptedLogTags[blockIndex]) {
|
|
390
|
+
encryptedLogTags[blockIndex] = {};
|
|
391
|
+
}
|
|
392
|
+
if (!unencryptedLogTags[blockIndex]) {
|
|
393
|
+
unencryptedLogTags[blockIndex] = {};
|
|
380
394
|
}
|
|
381
395
|
b.data.body.noteEncryptedLogs.txLogs.forEach((txLogs, txIndex) => {
|
|
382
|
-
if (!
|
|
383
|
-
|
|
396
|
+
if (!encryptedLogTags[blockIndex][txIndex]) {
|
|
397
|
+
encryptedLogTags[blockIndex][txIndex] = [];
|
|
398
|
+
}
|
|
399
|
+
encryptedLogTags[blockIndex][txIndex].push(...txLogs.unrollLogs().map(log => log.data.subarray(0, 32)));
|
|
400
|
+
});
|
|
401
|
+
b.data.body.unencryptedLogs.txLogs.forEach((txLogs, txIndex) => {
|
|
402
|
+
if (!unencryptedLogTags[blockIndex][txIndex]) {
|
|
403
|
+
unencryptedLogTags[blockIndex][txIndex] = [];
|
|
384
404
|
}
|
|
385
|
-
|
|
405
|
+
unencryptedLogTags[blockIndex][txIndex].push(...txLogs.unrollLogs().map(log => log.data.subarray(0, 32)));
|
|
386
406
|
});
|
|
387
407
|
});
|
|
388
408
|
});
|
|
389
409
|
|
|
390
|
-
it('is possible to batch request
|
|
410
|
+
it('is possible to batch request encrypted logs of a tx via tags', async () => {
|
|
391
411
|
// get random tx from any block that's not the last one
|
|
392
412
|
const targetBlockIndex = randomInt(numBlocks - 2);
|
|
393
413
|
const targetTxIndex = randomInt(txsPerBlock);
|
|
394
414
|
|
|
395
415
|
const logsByTags = await store.getLogsByTags(
|
|
396
|
-
|
|
416
|
+
encryptedLogTags[targetBlockIndex][targetTxIndex].map(buffer => new Fr(buffer)),
|
|
397
417
|
);
|
|
398
418
|
|
|
399
|
-
const expectedResponseSize = numPrivateFunctionCalls *
|
|
419
|
+
const expectedResponseSize = numPrivateFunctionCalls * numEncryptedLogsPerFn;
|
|
400
420
|
expect(logsByTags.length).toEqual(expectedResponseSize);
|
|
401
421
|
|
|
402
422
|
logsByTags.forEach((logsByTag, logIndex) => {
|
|
403
423
|
expect(logsByTag).toHaveLength(1);
|
|
404
|
-
const [
|
|
405
|
-
expect(
|
|
406
|
-
|
|
424
|
+
const [scopedLog] = logsByTag;
|
|
425
|
+
expect(scopedLog.txHash).toEqual(blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash);
|
|
426
|
+
expect(scopedLog.logData).toEqual(
|
|
427
|
+
blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex].data,
|
|
428
|
+
);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// TODO: Allow this test when #9835 is fixed and tags can be correctly decoded
|
|
433
|
+
it.skip('is possible to batch request all logs (encrypted and unencrypted) of a tx via tags', async () => {
|
|
434
|
+
// get random tx from any block that's not the last one
|
|
435
|
+
const targetBlockIndex = randomInt(numBlocks - 2);
|
|
436
|
+
const targetTxIndex = randomInt(txsPerBlock);
|
|
437
|
+
|
|
438
|
+
const logsByTags = await store.getLogsByTags(
|
|
439
|
+
encryptedLogTags[targetBlockIndex][targetTxIndex]
|
|
440
|
+
.concat(unencryptedLogTags[targetBlockIndex][targetTxIndex])
|
|
441
|
+
.map(buffer => new Fr(buffer)),
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
const expectedResponseSize =
|
|
445
|
+
numPrivateFunctionCalls * numEncryptedLogsPerFn + numPublicFunctionCalls * numUnencryptedLogsPerFn;
|
|
446
|
+
expect(logsByTags.length).toEqual(expectedResponseSize);
|
|
447
|
+
|
|
448
|
+
const encryptedLogsByTags = logsByTags.slice(0, numPrivateFunctionCalls * numEncryptedLogsPerFn);
|
|
449
|
+
const unencryptedLogsByTags = logsByTags.slice(numPrivateFunctionCalls * numEncryptedLogsPerFn);
|
|
450
|
+
encryptedLogsByTags.forEach((logsByTag, logIndex) => {
|
|
451
|
+
expect(logsByTag).toHaveLength(1);
|
|
452
|
+
const [scopedLog] = logsByTag;
|
|
453
|
+
expect(scopedLog.txHash).toEqual(blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash);
|
|
454
|
+
expect(scopedLog.logData).toEqual(
|
|
455
|
+
blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex].data,
|
|
456
|
+
);
|
|
457
|
+
});
|
|
458
|
+
unencryptedLogsByTags.forEach((logsByTag, logIndex) => {
|
|
459
|
+
expect(logsByTag).toHaveLength(1);
|
|
460
|
+
const [scopedLog] = logsByTag;
|
|
461
|
+
expect(scopedLog.logData).toEqual(
|
|
462
|
+
blocks[targetBlockIndex].data.body.unencryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex].data,
|
|
407
463
|
);
|
|
408
464
|
});
|
|
409
465
|
});
|
|
410
466
|
|
|
411
|
-
it('is possible to batch request
|
|
467
|
+
it('is possible to batch request logs of different blocks via tags', async () => {
|
|
412
468
|
// get first tx of first block and second tx of second block
|
|
413
|
-
const logsByTags = await store.getLogsByTags(
|
|
469
|
+
const logsByTags = await store.getLogsByTags(
|
|
470
|
+
[...encryptedLogTags[0][0], ...encryptedLogTags[1][1]].map(buffer => new Fr(buffer)),
|
|
471
|
+
);
|
|
414
472
|
|
|
415
|
-
const expectedResponseSize = 2 * numPrivateFunctionCalls *
|
|
473
|
+
const expectedResponseSize = 2 * numPrivateFunctionCalls * numEncryptedLogsPerFn;
|
|
416
474
|
expect(logsByTags.length).toEqual(expectedResponseSize);
|
|
417
475
|
|
|
418
476
|
logsByTags.forEach(logsByTag => expect(logsByTag).toHaveLength(1));
|
|
@@ -420,14 +478,14 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
420
478
|
|
|
421
479
|
it('is possible to batch request logs that have the same tag but different content', async () => {
|
|
422
480
|
// get first tx of last block
|
|
423
|
-
const logsByTags = await store.getLogsByTags(
|
|
481
|
+
const logsByTags = await store.getLogsByTags(encryptedLogTags[numBlocks - 1][0].map(buffer => new Fr(buffer)));
|
|
424
482
|
|
|
425
|
-
const expectedResponseSize = numPrivateFunctionCalls *
|
|
483
|
+
const expectedResponseSize = numPrivateFunctionCalls * numEncryptedLogsPerFn;
|
|
426
484
|
expect(logsByTags.length).toEqual(expectedResponseSize);
|
|
427
485
|
|
|
428
486
|
logsByTags.forEach(logsByTag => {
|
|
429
487
|
expect(logsByTag).toHaveLength(2);
|
|
430
|
-
const [tag0, tag1] = logsByTag.map(
|
|
488
|
+
const [tag0, tag1] = logsByTag.map(scopedLog => new Fr(scopedLog.logData.subarray(0, 32)));
|
|
431
489
|
expect(tag0).toEqual(tag1);
|
|
432
490
|
});
|
|
433
491
|
});
|
|
@@ -439,10 +497,10 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
439
497
|
|
|
440
498
|
const logsByTags = await store.getLogsByTags([
|
|
441
499
|
Fr.random(),
|
|
442
|
-
...
|
|
500
|
+
...encryptedLogTags[targetBlockIndex][targetTxIndex].slice(1).map(buffer => new Fr(buffer)),
|
|
443
501
|
]);
|
|
444
502
|
|
|
445
|
-
const expectedResponseSize = numPrivateFunctionCalls *
|
|
503
|
+
const expectedResponseSize = numPrivateFunctionCalls * numEncryptedLogsPerFn;
|
|
446
504
|
expect(logsByTags.length).toEqual(expectedResponseSize);
|
|
447
505
|
|
|
448
506
|
const [emptyLogsByTag, ...populatedLogsByTags] = logsByTags;
|
|
@@ -450,9 +508,10 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
450
508
|
|
|
451
509
|
populatedLogsByTags.forEach((logsByTag, logIndex) => {
|
|
452
510
|
expect(logsByTag).toHaveLength(1);
|
|
453
|
-
const [
|
|
454
|
-
expect(
|
|
455
|
-
|
|
511
|
+
const [scopedLog] = logsByTag;
|
|
512
|
+
expect(scopedLog.txHash).toEqual(blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash);
|
|
513
|
+
expect(scopedLog.logData).toEqual(
|
|
514
|
+
blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex + 1].data,
|
|
456
515
|
);
|
|
457
516
|
});
|
|
458
517
|
});
|
package/src/archiver/config.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type L1ContractAddresses,
|
|
3
|
+
type L1ContractsConfig,
|
|
4
|
+
type L1ReaderConfig,
|
|
5
|
+
l1ContractsConfigMappings,
|
|
6
|
+
l1ReaderConfigMappings,
|
|
7
|
+
} from '@aztec/ethereum';
|
|
2
8
|
import { type ConfigMappingsType, getConfigFromMappings, numberConfigHelper } from '@aztec/foundation/config';
|
|
3
9
|
|
|
4
10
|
/**
|
|
@@ -32,14 +38,10 @@ export type ArchiverConfig = {
|
|
|
32
38
|
*/
|
|
33
39
|
l1Contracts: L1ContractAddresses;
|
|
34
40
|
|
|
35
|
-
/**
|
|
36
|
-
* Optional dir to store data. If omitted will store in memory.
|
|
37
|
-
*/
|
|
38
|
-
dataDirectory: string | undefined;
|
|
39
|
-
|
|
40
41
|
/** The max number of logs that can be obtained in 1 "getUnencryptedLogs" call. */
|
|
41
42
|
maxLogs?: number;
|
|
42
|
-
} & L1ReaderConfig
|
|
43
|
+
} & L1ReaderConfig &
|
|
44
|
+
L1ContractsConfig;
|
|
43
45
|
|
|
44
46
|
export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
45
47
|
archiverUrl: {
|
|
@@ -50,11 +52,7 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
|
50
52
|
archiverPollingIntervalMS: {
|
|
51
53
|
env: 'ARCHIVER_POLLING_INTERVAL_MS',
|
|
52
54
|
description: 'The polling interval in ms for retrieving new L2 blocks and encrypted logs.',
|
|
53
|
-
...numberConfigHelper(
|
|
54
|
-
},
|
|
55
|
-
dataDirectory: {
|
|
56
|
-
env: 'DATA_DIRECTORY',
|
|
57
|
-
description: 'Optional dir to store data. If omitted will store in memory.',
|
|
55
|
+
...numberConfigHelper(1_000),
|
|
58
56
|
},
|
|
59
57
|
maxLogs: {
|
|
60
58
|
env: 'ARCHIVER_MAX_LOGS',
|
|
@@ -67,6 +65,7 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
|
67
65
|
description: 'The polling interval viem uses in ms',
|
|
68
66
|
...numberConfigHelper(1000),
|
|
69
67
|
},
|
|
68
|
+
...l1ContractsConfigMappings,
|
|
70
69
|
};
|
|
71
70
|
|
|
72
71
|
/**
|
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
type TimeConstants = {
|
|
2
|
+
l1GenesisTime: bigint;
|
|
3
|
+
epochDuration: number;
|
|
4
|
+
slotDuration: number;
|
|
5
|
+
};
|
|
2
6
|
|
|
3
7
|
/** Returns the slot number for a given timestamp. */
|
|
4
|
-
export function getSlotAtTimestamp(ts: bigint, constants:
|
|
5
|
-
return ts < constants.l1GenesisTime ? 0n : (ts - constants.l1GenesisTime) / BigInt(
|
|
8
|
+
export function getSlotAtTimestamp(ts: bigint, constants: Pick<TimeConstants, 'l1GenesisTime' | 'slotDuration'>) {
|
|
9
|
+
return ts < constants.l1GenesisTime ? 0n : (ts - constants.l1GenesisTime) / BigInt(constants.slotDuration);
|
|
6
10
|
}
|
|
7
11
|
|
|
8
12
|
/** Returns the epoch number for a given timestamp. */
|
|
9
|
-
export function getEpochNumberAtTimestamp(ts: bigint, constants:
|
|
10
|
-
return getSlotAtTimestamp(ts, constants) / BigInt(
|
|
13
|
+
export function getEpochNumberAtTimestamp(ts: bigint, constants: TimeConstants) {
|
|
14
|
+
return getSlotAtTimestamp(ts, constants) / BigInt(constants.epochDuration);
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
/** Returns the range of slots (inclusive) for a given epoch number. */
|
|
14
|
-
export function getSlotRangeForEpoch(epochNumber: bigint) {
|
|
15
|
-
const startSlot = epochNumber * BigInt(
|
|
16
|
-
return [startSlot, startSlot + BigInt(
|
|
18
|
+
export function getSlotRangeForEpoch(epochNumber: bigint, constants: Pick<TimeConstants, 'epochDuration'>) {
|
|
19
|
+
const startSlot = epochNumber * BigInt(constants.epochDuration);
|
|
20
|
+
return [startSlot, startSlot + BigInt(constants.epochDuration) - 1n];
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
/** Returns the range of L1 timestamps (inclusive) for a given epoch number. */
|
|
20
|
-
export function getTimestampRangeForEpoch(epochNumber: bigint, constants:
|
|
21
|
-
const [startSlot, endSlot] = getSlotRangeForEpoch(epochNumber);
|
|
24
|
+
export function getTimestampRangeForEpoch(epochNumber: bigint, constants: TimeConstants) {
|
|
25
|
+
const [startSlot, endSlot] = getSlotRangeForEpoch(epochNumber, constants);
|
|
22
26
|
return [
|
|
23
|
-
constants.l1GenesisTime + startSlot * BigInt(
|
|
24
|
-
constants.l1GenesisTime + endSlot * BigInt(
|
|
27
|
+
constants.l1GenesisTime + startSlot * BigInt(constants.slotDuration),
|
|
28
|
+
constants.l1GenesisTime + endSlot * BigInt(constants.slotDuration),
|
|
25
29
|
];
|
|
26
30
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type EncryptedL2NoteLog,
|
|
3
2
|
type FromLogType,
|
|
4
3
|
type GetUnencryptedLogsResponse,
|
|
5
4
|
type InboxLeaf,
|
|
@@ -10,6 +9,7 @@ import {
|
|
|
10
9
|
type TxEffect,
|
|
11
10
|
type TxHash,
|
|
12
11
|
type TxReceipt,
|
|
12
|
+
type TxScopedL2Log,
|
|
13
13
|
} from '@aztec/circuit-types';
|
|
14
14
|
import {
|
|
15
15
|
type ContractClassPublic,
|
|
@@ -245,7 +245,7 @@ export class KVArchiverDataStore implements ArchiverDataStore {
|
|
|
245
245
|
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
246
246
|
* that tag.
|
|
247
247
|
*/
|
|
248
|
-
getLogsByTags(tags: Fr[]): Promise<
|
|
248
|
+
getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
|
|
249
249
|
try {
|
|
250
250
|
return this.#logStore.getLogsByTags(tags);
|
|
251
251
|
} catch (err) {
|
|
@@ -266,6 +266,19 @@ export class KVArchiverDataStore implements ArchiverDataStore {
|
|
|
266
266
|
}
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Gets contract class logs based on the provided filter.
|
|
271
|
+
* @param filter - The filter to apply to the logs.
|
|
272
|
+
* @returns The requested logs.
|
|
273
|
+
*/
|
|
274
|
+
getContractClassLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
|
|
275
|
+
try {
|
|
276
|
+
return Promise.resolve(this.#logStore.getContractClassLogs(filter));
|
|
277
|
+
} catch (err) {
|
|
278
|
+
return Promise.reject(err);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
269
282
|
/**
|
|
270
283
|
* Gets the number of the latest L2 block processed.
|
|
271
284
|
* @returns The number of the latest L2 block processed.
|