Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: persistent archiver store #3410

Merged
merged 31 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5ba9aff
feat: add jest helpers to @aztec/types
alexghr Nov 23, 2023
f3869b6
refactor: split MemoryArchiverStore to own file
alexghr Nov 23, 2023
de93bfc
refactor: addLogs per block
alexghr Nov 23, 2023
051549d
test: add comprehensive archiver_store test suite
alexghr Nov 23, 2023
0b378e4
feat: add archiver store backed by LMDB
alexghr Nov 23, 2023
bdf9c5c
fix: await contracts added to the archiver store
alexghr Nov 23, 2023
1b82206
feat: use persistent archiver store
alexghr Nov 23, 2023
d21cb49
Merge branch 'master' into alexg/feat/persistent-archiver-store
alexghr Nov 23, 2023
02f924b
fix: L1ToL2Message.random takes optional entryKey
alexghr Nov 23, 2023
d84ab77
fix: typing error in ts-jest
alexghr Nov 23, 2023
fd95142
fix: parse aztec address from string
alexghr Nov 23, 2023
b33ce02
refactor: add loop labels for more elegant breaks
alexghr Nov 23, 2023
ca43db3
refactor: use of transactions
alexghr Nov 23, 2023
8fc557e
Merge branch 'master' into alexg/feat/persistent-archiver-store
alexghr Nov 24, 2023
8653839
fix: merkle trees store last globalVariablesHash
alexghr Nov 27, 2023
a4235f9
fix: store/restore indexed tree's leaves to db
alexghr Nov 27, 2023
20c5acd
fix: pass global vars hash
alexghr Nov 27, 2023
5e202ef
refactor: inject archiver store into archiver
alexghr Nov 27, 2023
b0955a4
refactor: archiver gets L1 block number from store
alexghr Nov 27, 2023
600002b
refactor: use private property for l1BlockNumber
alexghr Nov 27, 2023
f724acd
Merge branch 'master' into alexg/feat/persistent-archiver-store
alexghr Nov 27, 2023
8a8a60b
refactor: segregate tx and contract indexed data
alexghr Nov 27, 2023
beb0102
refactor: add debug logging to lmdb archiver store
alexghr Nov 27, 2023
ad2093a
fix: use uint32 for block keys
alexghr Nov 27, 2023
4010d8a
fix: process L1->L2 messages just once
alexghr Nov 27, 2023
9b46e88
refactor: store entry keys as buffers in dup table
alexghr Nov 28, 2023
04e7e73
Merge remote-tracking branch 'origin/master' into alexg/feat/persiste…
alexghr Nov 28, 2023
4a85244
fix: memory l1tol2 message store is idempotent
alexghr Nov 28, 2023
aa07751
test: verify archiver store is idempotent
alexghr Nov 28, 2023
1cb5e12
docs: update archiver sync doc
alexghr Nov 28, 2023
801fb6f
fix: slow tree test more lenient
alexghr Nov 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@
"auditability",
"hardfork",
"composablity",
"counterparty"
"counterparty",
"lmdb"
],
"ignorePaths": [
"node_modules/",
Expand All @@ -253,7 +254,5 @@
"lib",
"*.cmake"
],
"flagWords": [
"anonymous"
]
"flagWords": ["anonymous"]
}
1 change: 1 addition & 0 deletions yarn-project/archiver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@aztec/types": "workspace:^",
"@types/lodash.omit": "^4.5.7",
"debug": "^4.3.4",
"lmdb": "^2.9.1",
"lodash.omit": "^4.5.0",
"tsc-watch": "^6.0.0",
"tslib": "^2.5.0",
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/archiver/src/archiver/archiver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import times from 'lodash.times';
import { Chain, HttpTransport, Log, PublicClient, Transaction, encodeFunctionData, toHex } from 'viem';

import { Archiver } from './archiver.js';
import { ArchiverDataStore, MemoryArchiverStore } from './archiver_store.js';
import { ArchiverDataStore } from './archiver_store.js';
import { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js';

describe('Archiver', () => {
const rollupAddress = EthAddress.ZERO.toString();
Expand Down
46 changes: 26 additions & 20 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@ import {
TxHash,
} from '@aztec/types';

import { RootDatabase } from 'lmdb';
import omit from 'lodash.omit';
import { Chain, HttpTransport, PublicClient, createPublicClient, getContract, http } from 'viem';

import { ArchiverDataStore, MemoryArchiverStore } from './archiver_store.js';
import { ArchiverDataStore } from './archiver_store.js';
import { ArchiverConfig } from './config.js';
import {
retrieveBlocks,
retrieveNewCancelledL1ToL2Messages,
retrieveNewContractData,
retrieveNewPendingL1ToL2Messages,
} from './data_retrieval.js';
import { LMDBArchiverStore } from './lmdb_archiver_store.js';
import { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js';

/**
* Pulls L2 blocks in a non-blocking manner and provides interface for their retrieval.
Expand Down Expand Up @@ -93,10 +96,15 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource
/**
* Creates a new instance of the Archiver and blocks until it syncs from chain.
* @param config - The archiver's desired configuration.
* @param rootDB - The database for the archiver.
* @param blockUntilSynced - If true, blocks until the archiver has fully synced.
* @returns - An instance of the archiver.
*/
public static async createAndSync(config: ArchiverConfig, blockUntilSynced = true): Promise<Archiver> {
public static async createAndSync(
config: ArchiverConfig,
rootDB?: RootDatabase,
blockUntilSynced = true,
): Promise<Archiver> {
const chain = createEthereumChain(config.rpcUrl, config.apiKey);
const publicClient = createPublicClient({
chain: chain.chainInfo,
Expand All @@ -113,7 +121,9 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource
});
const searchStartBlock = Number((await registryContract.read.getCurrentSnapshot()).blockNumber);

const archiverStore = new MemoryArchiverStore(config.maxLogs ?? 1000);
const archiverStore = rootDB
? new LMDBArchiverStore(rootDB, config.maxLogs ?? 1000)
: new MemoryArchiverStore(config.maxLogs ?? 1000);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left the old MemoryArchiverStore in as a reference for the code review and in case we might need it for other tests but otherwise all code paths should pass a rootDB now and use a temporary LMDB instance if persistance isn't needed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this function should just accept the ArchiverStore interface and the concrete implementation is created at node setup. Otherwise, if I wanted to add a different DB implementation I would need to pollute the arguments.

const archiver = new Archiver(
publicClient,
config.l1Contracts.rollupAddress,
Expand Down Expand Up @@ -252,26 +262,22 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource

this.log(`Retrieved ${retrievedBlocks.retrievedData.length} block(s) from chain`);

// store encrypted logs from L2 Blocks that we have retrieved
const encryptedLogs = retrievedBlocks.retrievedData.map(block => {
return block.newEncryptedLogs!;
});
await this.store.addLogs(encryptedLogs, LogType.ENCRYPTED);

// store unencrypted logs from L2 Blocks that we have retrieved
const unencryptedLogs = retrievedBlocks.retrievedData.map(block => {
return block.newUnencryptedLogs!;
});
await this.store.addLogs(unencryptedLogs, LogType.UNENCRYPTED);
await Promise.all(
retrievedBlocks.retrievedData.map(block =>
this.store.addLogs(block.newEncryptedLogs, block.newUnencryptedLogs, block.number),
),
);

// store contracts for which we have retrieved L2 blocks
const lastKnownL2BlockNum = retrievedBlocks.retrievedData[retrievedBlocks.retrievedData.length - 1].number;
retrievedContracts.retrievedData.forEach(async ([contracts, l2BlockNum], index) => {
this.log(`Retrieved extended contract data for l2 block number: ${index}`);
if (l2BlockNum <= lastKnownL2BlockNum) {
await this.store.addExtendedContractData(contracts, l2BlockNum);
}
});
await Promise.all(
retrievedContracts.retrievedData.map(async ([contracts, l2BlockNum]) => {
this.log(`Retrieved extended contract data for l2 block number: ${l2BlockNum}`);
if (l2BlockNum <= lastKnownL2BlockNum) {
await this.store.addExtendedContractData(contracts, l2BlockNum);
}
}),
);

// from retrieved L2Blocks, confirm L1 to L2 messages that have been published
// from each l2block fetch all messageKeys in a flattened array:
Expand Down
Loading