Skip to content

Commit b12db36

Browse files
committed
refactor: minor archiver refactor
1 parent 7bb2a38 commit b12db36

File tree

7 files changed

+221
-207
lines changed

7 files changed

+221
-207
lines changed

yarn-project/archiver/src/archiver/archiver.ts

+184-96
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
type FromLogType,
33
type GetUnencryptedLogsResponse,
4+
type InboxLeaf,
45
type L1ToL2MessageSource,
56
type L2Block,
67
type L2BlockL2Logs,
@@ -13,14 +14,15 @@ import {
1314
type TxReceipt,
1415
type UnencryptedL2Log,
1516
} from '@aztec/circuit-types';
16-
import { ContractClassRegisteredEvent, type FunctionSelector } from '@aztec/circuits.js';
1717
import {
18+
ContractClassRegisteredEvent,
1819
ContractInstanceDeployedEvent,
20+
type FunctionSelector,
1921
PrivateFunctionBroadcastedEvent,
2022
UnconstrainedFunctionBroadcastedEvent,
2123
isValidPrivateFunctionMembershipProof,
2224
isValidUnconstrainedFunctionMembershipProof,
23-
} from '@aztec/circuits.js/contract';
25+
} from '@aztec/circuits.js';
2426
import { createEthereumChain } from '@aztec/ethereum';
2527
import { type ContractArtifact } from '@aztec/foundation/abi';
2628
import { type AztecAddress } from '@aztec/foundation/aztec-address';
@@ -52,11 +54,12 @@ import {
5254
http,
5355
} from 'viem';
5456

55-
import { type ArchiverDataStore } from './archiver_store.js';
57+
import { type ArchiverDataStore, type ArchiverL1SynchPoint } from './archiver_store.js';
5658
import { type ArchiverConfig } from './config.js';
5759
import { retrieveBlockFromRollup, retrieveL1ToL2Messages } from './data_retrieval.js';
5860
import { ArchiverInstrumentation } from './instrumentation.js';
59-
import { type SingletonDataRetrieval } from './structs/data_retrieval.js';
61+
import { type DataRetrieval, type SingletonDataRetrieval } from './structs/data_retrieval.js';
62+
import { type L1Published } from './structs/published.js';
6063

6164
/**
6265
* Helper interface to combine all sources this archiver implementation provides.
@@ -77,6 +80,8 @@ export class Archiver implements ArchiveSource {
7780
private rollup: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, Chain>>;
7881
private inbox: GetContractReturnType<typeof InboxAbi, PublicClient<HttpTransport, Chain>>;
7982

83+
private store: ArchiverStoreHelper;
84+
8085
/**
8186
* Creates a new instance of the Archiver.
8287
* @param publicClient - A client for interacting with the Ethereum node.
@@ -90,14 +95,16 @@ export class Archiver implements ArchiveSource {
9095
constructor(
9196
private readonly publicClient: PublicClient<HttpTransport, Chain>,
9297
private readonly rollupAddress: EthAddress,
93-
private readonly inboxAddress: EthAddress,
98+
readonly inboxAddress: EthAddress,
9499
private readonly registryAddress: EthAddress,
95-
private readonly store: ArchiverDataStore,
100+
readonly dataStore: ArchiverDataStore,
96101
private readonly pollingIntervalMs = 10_000,
97102
private readonly instrumentation: ArchiverInstrumentation,
98103
private readonly l1StartBlock: bigint = 0n,
99104
private readonly log: DebugLogger = createDebugLogger('aztec:archiver'),
100105
) {
106+
this.store = new ArchiverStoreHelper(dataStore);
107+
101108
this.rollup = getContract({
102109
address: rollupAddress.toString(),
103110
abi: RollupAbi,
@@ -340,29 +347,6 @@ export class Archiver implements ArchiveSource {
340347
.join(',')} with last processed L1 block ${lastProcessedL1BlockNumber}`,
341348
);
342349

343-
await Promise.all(
344-
retrievedBlocks.map(block => {
345-
return this.store.addLogs(
346-
block.data.body.noteEncryptedLogs,
347-
block.data.body.encryptedLogs,
348-
block.data.body.unencryptedLogs,
349-
block.data.number,
350-
);
351-
}),
352-
);
353-
354-
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
355-
await Promise.all(
356-
retrievedBlocks.map(async block => {
357-
const blockLogs = block.data.body.txEffects
358-
.flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
359-
.flatMap(txLog => txLog.unrollLogs());
360-
await this.storeRegisteredContractClasses(blockLogs, block.data.number);
361-
await this.storeDeployedContractInstances(blockLogs, block.data.number);
362-
await this.storeBroadcastedIndividualFunctions(blockLogs, block.data.number);
363-
}),
364-
);
365-
366350
const timer = new Timer();
367351
await this.store.addBlocks(retrievedBlocks);
368352
this.instrumentation.processNewBlocks(
@@ -373,73 +357,6 @@ export class Archiver implements ArchiveSource {
373357
this.log.verbose(`Processed ${retrievedBlocks.length} new L2 blocks up to ${lastL2BlockNumber}`);
374358
}
375359

376-
/**
377-
* Extracts and stores contract classes out of ContractClassRegistered events emitted by the class registerer contract.
378-
* @param allLogs - All logs emitted in a bunch of blocks.
379-
*/
380-
private async storeRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number) {
381-
const contractClasses = ContractClassRegisteredEvent.fromLogs(allLogs, ClassRegistererAddress).map(e =>
382-
e.toContractClassPublic(),
383-
);
384-
if (contractClasses.length > 0) {
385-
contractClasses.forEach(c => this.log.verbose(`Registering contract class ${c.id.toString()}`));
386-
await this.store.addContractClasses(contractClasses, blockNum);
387-
}
388-
}
389-
390-
/**
391-
* Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
392-
* @param allLogs - All logs emitted in a bunch of blocks.
393-
*/
394-
private async storeDeployedContractInstances(allLogs: UnencryptedL2Log[], blockNum: number) {
395-
const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs).map(e => e.toContractInstance());
396-
if (contractInstances.length > 0) {
397-
contractInstances.forEach(c => this.log.verbose(`Storing contract instance at ${c.address.toString()}`));
398-
await this.store.addContractInstances(contractInstances, blockNum);
399-
}
400-
}
401-
402-
private async storeBroadcastedIndividualFunctions(allLogs: UnencryptedL2Log[], _blockNum: number) {
403-
// Filter out private and unconstrained function broadcast events
404-
const privateFnEvents = PrivateFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
405-
const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
406-
407-
// Group all events by contract class id
408-
for (const [classIdString, classEvents] of Object.entries(
409-
groupBy([...privateFnEvents, ...unconstrainedFnEvents], e => e.contractClassId.toString()),
410-
)) {
411-
const contractClassId = Fr.fromString(classIdString);
412-
const contractClass = await this.store.getContractClass(contractClassId);
413-
if (!contractClass) {
414-
this.log.warn(`Skipping broadcasted functions as contract class ${contractClassId.toString()} was not found`);
415-
continue;
416-
}
417-
418-
// Split private and unconstrained functions, and filter out invalid ones
419-
const allFns = classEvents.map(e => e.toFunctionWithMembershipProof());
420-
const privateFns = allFns.filter(
421-
(fn): fn is ExecutablePrivateFunctionWithMembershipProof => 'unconstrainedFunctionsArtifactTreeRoot' in fn,
422-
);
423-
const unconstrainedFns = allFns.filter(
424-
(fn): fn is UnconstrainedFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn,
425-
);
426-
const validPrivateFns = privateFns.filter(fn => isValidPrivateFunctionMembershipProof(fn, contractClass));
427-
const validUnconstrainedFns = unconstrainedFns.filter(fn =>
428-
isValidUnconstrainedFunctionMembershipProof(fn, contractClass),
429-
);
430-
const validFnCount = validPrivateFns.length + validUnconstrainedFns.length;
431-
if (validFnCount !== allFns.length) {
432-
this.log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`);
433-
}
434-
435-
// Store the functions in the contract class in a single operation
436-
if (validFnCount > 0) {
437-
this.log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`);
438-
}
439-
await this.store.addFunctions(contractClassId, validPrivateFns, validUnconstrainedFns);
440-
}
441-
}
442-
443360
/**
444361
* Stops the archiver.
445362
* @returns A promise signalling completion of the stop process.
@@ -597,3 +514,174 @@ export class Archiver implements ArchiveSource {
597514
return this.store.getContractArtifact(address);
598515
}
599516
}
517+
518+
/**
519+
* A helper class that we use to deal with some of the logic needed when adding blocks.
520+
*
521+
* I would have preferred to not have this type. But it is useful for handling the logic that any
522+
* store would need to include otherwise while exposing fewer functions and logic directly to the archiver.
523+
*/
524+
class ArchiverStoreHelper
525+
implements Omit<ArchiverDataStore, 'addLogs' | 'addContractClasses' | 'addContractInstances' | 'addFunctions'>
526+
{
527+
#log = createDebugLogger('aztec:archiver:block-helper');
528+
529+
constructor(private readonly store: ArchiverDataStore) {}
530+
531+
/**
532+
* Extracts and stores contract classes out of ContractClassRegistered events emitted by the class registerer contract.
533+
* @param allLogs - All logs emitted in a bunch of blocks.
534+
*/
535+
async #storeRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number) {
536+
const contractClasses = ContractClassRegisteredEvent.fromLogs(allLogs, ClassRegistererAddress).map(e =>
537+
e.toContractClassPublic(),
538+
);
539+
if (contractClasses.length > 0) {
540+
contractClasses.forEach(c => this.#log.verbose(`Registering contract class ${c.id.toString()}`));
541+
return await this.store.addContractClasses(contractClasses, blockNum);
542+
}
543+
return true;
544+
}
545+
546+
/**
547+
* Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
548+
* @param allLogs - All logs emitted in a bunch of blocks.
549+
*/
550+
async #storeDeployedContractInstances(allLogs: UnencryptedL2Log[], blockNum: number) {
551+
const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs).map(e => e.toContractInstance());
552+
if (contractInstances.length > 0) {
553+
contractInstances.forEach(c => this.#log.verbose(`Storing contract instance at ${c.address.toString()}`));
554+
return await this.store.addContractInstances(contractInstances, blockNum);
555+
}
556+
return true;
557+
}
558+
559+
async #storeBroadcastedIndividualFunctions(allLogs: UnencryptedL2Log[], _blockNum: number) {
560+
// Filter out private and unconstrained function broadcast events
561+
const privateFnEvents = PrivateFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
562+
const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
563+
564+
// Group all events by contract class id
565+
for (const [classIdString, classEvents] of Object.entries(
566+
groupBy([...privateFnEvents, ...unconstrainedFnEvents], e => e.contractClassId.toString()),
567+
)) {
568+
const contractClassId = Fr.fromString(classIdString);
569+
const contractClass = await this.getContractClass(contractClassId);
570+
if (!contractClass) {
571+
this.#log.warn(`Skipping broadcasted functions as contract class ${contractClassId.toString()} was not found`);
572+
continue;
573+
}
574+
575+
// Split private and unconstrained functions, and filter out invalid ones
576+
const allFns = classEvents.map(e => e.toFunctionWithMembershipProof());
577+
const privateFns = allFns.filter(
578+
(fn): fn is ExecutablePrivateFunctionWithMembershipProof => 'unconstrainedFunctionsArtifactTreeRoot' in fn,
579+
);
580+
const unconstrainedFns = allFns.filter(
581+
(fn): fn is UnconstrainedFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn,
582+
);
583+
const validPrivateFns = privateFns.filter(fn => isValidPrivateFunctionMembershipProof(fn, contractClass));
584+
const validUnconstrainedFns = unconstrainedFns.filter(fn =>
585+
isValidUnconstrainedFunctionMembershipProof(fn, contractClass),
586+
);
587+
const validFnCount = validPrivateFns.length + validUnconstrainedFns.length;
588+
if (validFnCount !== allFns.length) {
589+
this.#log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`);
590+
}
591+
592+
// Store the functions in the contract class in a single operation
593+
if (validFnCount > 0) {
594+
this.#log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`);
595+
}
596+
return await this.store.addFunctions(contractClassId, validPrivateFns, validUnconstrainedFns);
597+
}
598+
return true;
599+
}
600+
601+
async addBlocks(blocks: L1Published<L2Block>[]): Promise<boolean> {
602+
return [
603+
this.store.addLogs(blocks.map(block => block.data)),
604+
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
605+
...(await Promise.all(
606+
blocks.map(async block => {
607+
const blockLogs = block.data.body.txEffects
608+
.flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
609+
.flatMap(txLog => txLog.unrollLogs());
610+
611+
return (
612+
await Promise.all([
613+
this.#storeRegisteredContractClasses(blockLogs, block.data.number),
614+
this.#storeDeployedContractInstances(blockLogs, block.data.number),
615+
this.#storeBroadcastedIndividualFunctions(blockLogs, block.data.number),
616+
])
617+
).every(Boolean);
618+
}),
619+
)),
620+
this.store.addBlocks(blocks),
621+
].every(Boolean);
622+
}
623+
624+
getBlocks(from: number, limit: number): Promise<L1Published<L2Block>[]> {
625+
return this.store.getBlocks(from, limit);
626+
}
627+
628+
getTxEffect(txHash: TxHash): Promise<TxEffect | undefined> {
629+
return this.store.getTxEffect(txHash);
630+
}
631+
632+
getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
633+
return this.store.getSettledTxReceipt(txHash);
634+
}
635+
addL1ToL2Messages(messages: DataRetrieval<InboxLeaf>): Promise<boolean> {
636+
return this.store.addL1ToL2Messages(messages);
637+
}
638+
getL1ToL2Messages(blockNumber: bigint): Promise<Fr[]> {
639+
return this.store.getL1ToL2Messages(blockNumber);
640+
}
641+
getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise<bigint | undefined> {
642+
return this.store.getL1ToL2MessageIndex(l1ToL2Message, startIndex);
643+
}
644+
getLogs<TLogType extends LogType>(
645+
from: number,
646+
limit: number,
647+
logType: TLogType,
648+
): Promise<L2BlockL2Logs<FromLogType<TLogType>>[]> {
649+
return this.store.getLogs(from, limit, logType);
650+
}
651+
getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
652+
return this.store.getUnencryptedLogs(filter);
653+
}
654+
getSynchedL2BlockNumber(): Promise<number> {
655+
return this.store.getSynchedL2BlockNumber();
656+
}
657+
getProvenL2BlockNumber(): Promise<number> {
658+
return this.store.getProvenL2BlockNumber();
659+
}
660+
setProvenL2BlockNumber(l2BlockNumber: SingletonDataRetrieval<number>): Promise<void> {
661+
return this.store.setProvenL2BlockNumber(l2BlockNumber);
662+
}
663+
setBlockSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
664+
return this.store.setBlockSynchedL1BlockNumber(l1BlockNumber);
665+
}
666+
setMessageSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
667+
return this.store.setMessageSynchedL1BlockNumber(l1BlockNumber);
668+
}
669+
getSynchPoint(): Promise<ArchiverL1SynchPoint> {
670+
return this.store.getSynchPoint();
671+
}
672+
getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
673+
return this.store.getContractClass(id);
674+
}
675+
getContractInstance(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
676+
return this.store.getContractInstance(address);
677+
}
678+
getContractClassIds(): Promise<Fr[]> {
679+
return this.store.getContractClassIds();
680+
}
681+
addContractArtifact(address: AztecAddress, contract: ContractArtifact): Promise<void> {
682+
return this.store.addContractArtifact(address, contract);
683+
}
684+
getContractArtifact(address: AztecAddress): Promise<ContractArtifact | undefined> {
685+
return this.store.getContractArtifact(address);
686+
}
687+
}

yarn-project/archiver/src/archiver/archiver_store.ts

+2-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import {
2-
type EncryptedL2BlockL2Logs,
3-
type EncryptedNoteL2BlockL2Logs,
42
type FromLogType,
53
type GetUnencryptedLogsResponse,
64
type InboxLeaf,
@@ -11,7 +9,6 @@ import {
119
type TxEffect,
1210
type TxHash,
1311
type TxReceipt,
14-
type UnencryptedL2BlockL2Logs,
1512
} from '@aztec/circuit-types';
1613
import { type Fr } from '@aztec/circuits.js';
1714
import { type ContractArtifact } from '@aztec/foundation/abi';
@@ -74,18 +71,10 @@ export interface ArchiverDataStore {
7471

7572
/**
7673
* Append new logs to the store's list.
77-
* @param noteEncryptedLogs - The note encrypted logs to be added to the store.
78-
* @param encryptedLogs - The encrypted logs to be added to the store.
79-
* @param unencryptedLogs - The unencrypted logs to be added to the store.
80-
* @param blockNumber - The block for which to add the logs.
74+
* @param blocks - The blocks for which to add the logs.
8175
* @returns True if the operation is successful.
8276
*/
83-
addLogs(
84-
noteEncryptedLogs: EncryptedNoteL2BlockL2Logs | undefined,
85-
encryptedLogs: EncryptedL2BlockL2Logs | undefined,
86-
unencryptedLogs: UnencryptedL2BlockL2Logs | undefined,
87-
blockNumber: number,
88-
): Promise<boolean>;
77+
addLogs(blocks: L2Block[]): Promise<boolean>;
8978

9079
/**
9180
* Append L1 to L2 messages to the store.

0 commit comments

Comments
 (0)