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: restore latest block number #2474

Merged
merged 2 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 7 additions & 0 deletions yarn-project/archiver/src/archiver/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ export interface ArchiverConfig {
* The deployed L1 contract addresses
*/
l1Contracts: L1ContractAddresses;

/**
* Optional dir to store data. If omitted will store in memory.
*/
dataDirectory?: string;
}

/**
Expand All @@ -59,6 +64,7 @@ export function getConfigEnvVars(): ArchiverConfig {
API_KEY,
INBOX_CONTRACT_ADDRESS,
REGISTRY_CONTRACT_ADDRESS,
DATA_DIRECTORY,
} = process.env;
// Populate the relevant addresses for use by the archiver.
const addresses: L1ContractAddresses = {
Expand All @@ -78,5 +84,6 @@ export function getConfigEnvVars(): ArchiverConfig {
searchStartBlock: SEARCH_START_BLOCK ? +SEARCH_START_BLOCK : 0,
apiKey: API_KEY,
l1Contracts: addresses,
dataDirectory: DATA_DIRECTORY,
};
}
1 change: 1 addition & 0 deletions yarn-project/aztec-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@jest/globals": "^29.5.0",
"@rushstack/eslint-patch": "^1.1.4",
"@types/jest": "^29.5.0",
"@types/leveldown": "^4.0.4",
"@types/levelup": "^5.1.2",
"@types/memdown": "^3.0.0",
"@types/node": "^18.7.23",
Expand Down
60 changes: 60 additions & 0 deletions yarn-project/aztec-node/src/aztec-node/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { LevelDown, default as leveldown } from 'leveldown';
import { LevelUp, default as levelup } from 'levelup';
import { MemDown, default as memdown } from 'memdown';
import { join } from 'node:path';

import { AztecNodeConfig } from './config.js';

export const createMemDown = () => (memdown as any)() as MemDown<any, any>;
export const createLevelDown = (path: string) => (leveldown as any)(path) as LevelDown;

const DB_SUBDIR = 'aztec-node';
const NODE_METADATA_KEY = '@@aztec_node_metadata';

/**
* The metadata for an aztec node.
*/
type NodeMetadata = {
/**
* The address of the rollup contract on L1
*/
rollupContractAddress: string;
};
Comment on lines +17 to +22
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Might be a bit too much but I was thinking we could save other bits of info about the previous run of the node and do things at boot based on what was saved (data migrations maybe?)


/**
* Opens the database for the aztec node.
* @param config - The configuration to be used by the aztec node.
* @returns The database for the aztec node.
*/
export async function openDb(config: AztecNodeConfig): Promise<LevelUp> {
const nodeMetadata: NodeMetadata = {
rollupContractAddress: config.l1Contracts.rollupAddress.toString(),
};

const db = levelup(config.dataDirectory ? createLevelDown(join(config.dataDirectory, DB_SUBDIR)) : createMemDown());
const prevNodeMetadata = await getNodeMetadata(db);

// if the rollup addresses are different, wipe the local database and start over
if (nodeMetadata.rollupContractAddress !== prevNodeMetadata.rollupContractAddress) {
await db.clear();
}

await db.put(NODE_METADATA_KEY, JSON.stringify(nodeMetadata));
return db;
}

/**
* Gets the metadata for the aztec node.
* @param db - The database for the aztec node.
* @returns Node metadata.
*/
async function getNodeMetadata(db: LevelUp): Promise<NodeMetadata> {
try {
const value: Buffer = await db.get(NODE_METADATA_KEY);
return JSON.parse(value.toString('utf-8'));
} catch {
return {
rollupContractAddress: '',
};
}
}
14 changes: 6 additions & 8 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,10 @@ import {
getConfigEnvVars as getWorldStateConfig,
} from '@aztec/world-state';

import { default as levelup } from 'levelup';
import { MemDown, default as memdown } from 'memdown';
import levelup from 'levelup';

import { AztecNodeConfig } from './config.js';

export const createMemDown = () => (memdown as any)() as MemDown<any, any>;
import { openDb } from './db.js';

/**
* The aztec node.
Expand Down Expand Up @@ -90,10 +88,10 @@ export class AztecNodeService implements AztecNode {
const p2pClient = await createP2PClient(config, new InMemoryTxPool(), archiver);

// now create the merkle trees and the world state syncher
const merkleTreesDb = levelup(createMemDown());
const merkleTrees = await MerkleTrees.new(merkleTreesDb, await CircuitsWasm.get());
const db = await openDb(config);
const merkleTrees = await MerkleTrees.new(db, await CircuitsWasm.get());
const worldStateConfig: WorldStateConfig = getWorldStateConfig();
const worldStateSynchronizer = new ServerWorldStateSynchronizer(merkleTrees, archiver, worldStateConfig);
const worldStateSynchronizer = await ServerWorldStateSynchronizer.new(db, merkleTrees, archiver, worldStateConfig);

// start both and wait for them to sync from the block source
await Promise.all([p2pClient.start(), worldStateSynchronizer.start()]);
Expand All @@ -120,7 +118,7 @@ export class AztecNodeService implements AztecNode {
config.chainId,
config.version,
getGlobalVariableBuilder(config),
merkleTreesDb,
db,
);
}

Expand Down
2 changes: 2 additions & 0 deletions yarn-project/end-to-end/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@types/lodash.times": "^4.3.7",
"@types/lodash.zip": "^4.2.7",
"@types/lodash.zipwith": "^4.2.7",
"@types/memdown": "^3.0.3",
"@types/node": "^18.7.23",
"jest": "^29.5.0",
"koa": "^2.14.2",
Expand All @@ -57,6 +58,7 @@
"lodash.times": "^4.3.2",
"lodash.zip": "^4.2.0",
"lodash.zipwith": "^4.2.0",
"memdown": "^6.1.1",
"puppeteer": "^21.3.4",
"string-argv": "^0.3.2",
"ts-jest": "^29.1.0",
Expand Down
5 changes: 3 additions & 2 deletions yarn-project/end-to-end/src/integration_l1_publisher.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createMemDown, getConfigEnvVars } from '@aztec/aztec-node';
import { getConfigEnvVars } from '@aztec/aztec-node';
import {
AztecAddress,
GlobalVariables,
Expand Down Expand Up @@ -33,6 +33,7 @@ import { MerkleTreeOperations, MerkleTrees } from '@aztec/world-state';

import { beforeEach, describe, expect, it } from '@jest/globals';
import { default as levelup } from 'levelup';
import memdown from 'memdown';
import {
Address,
Chain,
Expand Down Expand Up @@ -123,7 +124,7 @@ describe('L1Publisher integration', () => {
publicClient,
});

builderDb = await MerkleTrees.new(levelup(createMemDown())).then(t => t.asLatest());
builderDb = await MerkleTrees.new(levelup((memdown as any)())).then(t => t.asLatest());
const vks = getVerificationKeys();
const simulator = await WasmRollupCircuitSimulator.new();
const prover = new EmptyRollupProver();
Expand Down
1 change: 1 addition & 0 deletions yarn-project/world-state/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@types/memdown": "^3.0.0",
"@types/node": "^18.7.23",
"jest": "^29.5.0",
"memdown": "^6.1.1",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import {
} from '@aztec/types';

import { jest } from '@jest/globals';
import levelup from 'levelup';
import times from 'lodash.times';
import { default as memdown } from 'memdown';

import { MerkleTreeDb, MerkleTrees, WorldStateConfig } from '../index.js';
import { ServerWorldStateSynchronizer } from './server_world_state_synchronizer.js';
Expand Down Expand Up @@ -96,12 +98,25 @@ const getMockBlock = (blockNumber: number, newContractsCommitments?: Buffer[]) =
return block;
};

const createSynchronizer = (merkleTreeDb: any, rollupSource: any, blockCheckInterval = 100) => {
const createMockDb = () => levelup((memdown as any)());

const createSynchronizer = async (
db: levelup.LevelUp,
merkleTreeDb: any,
rollupSource: any,
blockCheckInterval = 100,
) => {
const worldStateConfig: WorldStateConfig = {
worldStateBlockCheckIntervalMS: blockCheckInterval,
l2QueueSize: 1000,
};
return new ServerWorldStateSynchronizer(merkleTreeDb as MerkleTrees, rollupSource as L2BlockSource, worldStateConfig);

return await ServerWorldStateSynchronizer.new(
db,
merkleTreeDb as MerkleTrees,
rollupSource as L2BlockSource,
worldStateConfig,
);
};

const log = createDebugLogger('aztec:server_world_state_synchronizer_test');
Expand Down Expand Up @@ -152,12 +167,32 @@ describe('server_world_state_synchronizer', () => {
expect(status.syncedToL2Block).toBe(LATEST_BLOCK_NUMBER);
};

it('can be constructed', () => {
expect(() => createSynchronizer(merkleTreeDb, rollupSource)).not.toThrow();
const performSubsequentSync = async (server: ServerWorldStateSynchronizer, count: number) => {
// test initial state
let status = await server.status();
expect(status.syncedToL2Block).toEqual(LATEST_BLOCK_NUMBER);
expect(status.state).toEqual(WorldStateRunningState.IDLE);

// create the initial blocks
nextBlocks = Array(count)
.fill(0)
.map((_, index: number) => getMockBlock(LATEST_BLOCK_NUMBER + index + 1));

rollupSource.getBlockNumber.mockReturnValueOnce(LATEST_BLOCK_NUMBER + count);

// start the sync process and await it
await server.start().catch(err => log.error('Sync not completed: ', err));

status = await server.status();
expect(status.syncedToL2Block).toBe(LATEST_BLOCK_NUMBER + count);
};

it('can be constructed', async () => {
await expect(createSynchronizer(createMockDb(), merkleTreeDb, rollupSource)).resolves.toBeTruthy();
});

it('updates sync progress', async () => {
const server = createSynchronizer(merkleTreeDb, rollupSource);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource);

// test initial state
let status = await server.status();
Expand Down Expand Up @@ -206,7 +241,7 @@ describe('server_world_state_synchronizer', () => {
});

it('enables blocking until synced', async () => {
const server = createSynchronizer(merkleTreeDb, rollupSource);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource);
let currentBlockNumber = 0;

const newBlocks = async () => {
Expand Down Expand Up @@ -237,7 +272,7 @@ describe('server_world_state_synchronizer', () => {
});

it('handles multiple calls to start', async () => {
const server = createSynchronizer(merkleTreeDb, rollupSource);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource);
let currentBlockNumber = 0;

const newBlocks = async () => {
Expand All @@ -264,7 +299,7 @@ describe('server_world_state_synchronizer', () => {
});

it('immediately syncs if no new blocks', async () => {
const server = createSynchronizer(merkleTreeDb, rollupSource);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource);
rollupSource.getBlockNumber.mockImplementationOnce(() => {
return Promise.resolve(0);
});
Expand All @@ -282,7 +317,7 @@ describe('server_world_state_synchronizer', () => {
});

it("can't be started if already stopped", async () => {
const server = createSynchronizer(merkleTreeDb, rollupSource);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource);
rollupSource.getBlockNumber.mockImplementationOnce(() => {
return Promise.resolve(0);
});
Expand All @@ -297,7 +332,7 @@ describe('server_world_state_synchronizer', () => {

it('adds the received L2 blocks', async () => {
merkleTreeDb.handleL2Block.mockReset();
const server = createSynchronizer(merkleTreeDb, rollupSource);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource);
const totalBlocks = LATEST_BLOCK_NUMBER + 1;
nextBlocks = Array(totalBlocks)
.fill(0)
Expand All @@ -310,7 +345,7 @@ describe('server_world_state_synchronizer', () => {
});

it('can immediately sync to latest', async () => {
const server = createSynchronizer(merkleTreeDb, rollupSource, 10000);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource, 10000);

await performInitialSync(server);

Expand Down Expand Up @@ -338,7 +373,7 @@ describe('server_world_state_synchronizer', () => {
});

it('can immediately sync to a minimum block number', async () => {
const server = createSynchronizer(merkleTreeDb, rollupSource, 10000);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource, 10000);

await performInitialSync(server);

Expand All @@ -363,7 +398,7 @@ describe('server_world_state_synchronizer', () => {
});

it('can immediately sync to a minimum block in the past', async () => {
const server = createSynchronizer(merkleTreeDb, rollupSource, 10000);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource, 10000);

await performInitialSync(server);
// syncing to a block in the past should succeed
Expand All @@ -385,7 +420,7 @@ describe('server_world_state_synchronizer', () => {
});

it('throws if you try to sync to an unavailable block', async () => {
const server = createSynchronizer(merkleTreeDb, rollupSource, 10000);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource, 10000);

await performInitialSync(server);

Expand All @@ -411,7 +446,7 @@ describe('server_world_state_synchronizer', () => {
});

it('throws if you try to immediate sync when not running', async () => {
const server = createSynchronizer(merkleTreeDb, rollupSource, 10000);
const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource, 10000);

// test initial state
const status = await server.status();
Expand All @@ -425,4 +460,31 @@ describe('server_world_state_synchronizer', () => {

await expect(server.syncImmediate()).rejects.toThrow(`World State is not running, unable to perform sync`);
});

it('restores the last synced block', async () => {
const db = createMockDb();
const initialServer = await createSynchronizer(db, merkleTreeDb, rollupSource, 10000);

await performInitialSync(initialServer);
await initialServer.stop();

const server = await createSynchronizer(db, merkleTreeDb, rollupSource, 10000);
const status = await server.status();
expect(status).toEqual({
state: WorldStateRunningState.IDLE,
syncedToL2Block: LATEST_BLOCK_NUMBER,
});
});

it('starts syncing from the last block', async () => {
const db = createMockDb();
const initialServer = await createSynchronizer(db, merkleTreeDb, rollupSource, 10000);

await performInitialSync(initialServer);
await initialServer.stop();

const server = await createSynchronizer(db, merkleTreeDb, rollupSource, 10000);
await performSubsequentSync(server, 2);
await server.stop();
});
});
Loading