Skip to content

Commit

Permalink
wip: restore latest block number
Browse files Browse the repository at this point in the history
  • Loading branch information
alexghr committed Sep 22, 2023
1 parent 85e504c commit 2437904
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 20 deletions.
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 @@ -36,6 +36,11 @@ export interface ArchiverConfig extends L1Addresses {
* Eth block from which we start scanning for L2Blocks.
*/
searchStartBlock: number;

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

/**
Expand All @@ -53,6 +58,7 @@ export function getConfigEnvVars(): ArchiverConfig {
SEARCH_START_BLOCK,
API_KEY,
INBOX_CONTRACT_ADDRESS,
DATA_DIRECTORY,
} = process.env;
return {
rpcUrl: ETHEREUM_HOST || 'http://127.0.0.1:8545/',
Expand All @@ -65,5 +71,6 @@ export function getConfigEnvVars(): ArchiverConfig {
: EthAddress.ZERO,
searchStartBlock: SEARCH_START_BLOCK ? +SEARCH_START_BLOCK : 0,
apiKey: API_KEY,
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 @@ -46,6 +46,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
15 changes: 11 additions & 4 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,15 @@ import {
getConfigEnvVars as getWorldStateConfig,
} from '@aztec/world-state';

import { LevelDown, default as leveldown } from 'leveldown';
import { 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;

/**
* The aztec node.
Expand Down Expand Up @@ -89,10 +92,14 @@ 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 = levelup(
config.dataDirectory
? createLevelDown(join(config.dataDirectory, config.rollupContract.toString()))
: createMemDown(),
);
const merkleTrees = await MerkleTrees.new(db, await CircuitsWasm.get());
const worldStateConfig: WorldStateConfig = getWorldStateConfig();
const worldStateSynchroniser = new ServerWorldStateSynchroniser(merkleTrees, archiver, worldStateConfig);
const worldStateSynchroniser = await ServerWorldStateSynchroniser.new(db, merkleTrees, archiver, worldStateConfig);

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

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 { ServerWorldStateSynchroniser } from './server_world_state_synchroniser.js';
Expand Down Expand Up @@ -96,12 +98,25 @@ const getMockBlock = (blockNumber: number, newContractsCommitments?: Buffer[]) =
return block;
};

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

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

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

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

it('can be constructed', () => {
expect(() => createSynchroniser(merkleTreeDb, rollupSource)).not.toThrow();
const performSubsequentSync = async (server: ServerWorldStateSynchroniser, 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(createSynchroniser(createMockDb(), merkleTreeDb, rollupSource)).resolves.toBeTruthy();
});

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

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

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

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

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

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

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

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

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

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

await performInitialSync(server);

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

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

await performInitialSync(server);

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

it('can immediately sync to a minimum block in the past', async () => {
const server = createSynchroniser(merkleTreeDb, rollupSource, 10000);
const server = await createSynchroniser(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_synchroniser', () => {
});

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

await performInitialSync(server);

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

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

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

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 createSynchroniser(db, merkleTreeDb, rollupSource, 10000);

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

const server = await createSynchroniser(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 createSynchroniser(db, merkleTreeDb, rollupSource, 10000);

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

const server = await createSynchroniser(db, merkleTreeDb, rollupSource, 10000);
await performSubsequentSync(server, 2);
await server.stop();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ import { SerialQueue } from '@aztec/foundation/fifo';
import { createDebugLogger } from '@aztec/foundation/log';
import { L2Block, L2BlockDownloader, L2BlockSource } from '@aztec/types';

import { LevelUp } from 'levelup';

import { MerkleTreeOperations, MerkleTrees } from '../index.js';
import { MerkleTreeOperationsFacade } from '../merkle-tree/merkle_tree_operations_facade.js';
import { WorldStateConfig } from './config.js';
import { WorldStateRunningState, WorldStateStatus, WorldStateSynchroniser } from './world_state_synchroniser.js';

const DB_KEY_BLOCK_NUMBER = 'latestBlockNumber';

/**
* Synchronises the world state with the L2 blocks from a L2BlockSource.
* The synchroniser will download the L2 blocks from the L2BlockSource and insert the new commitments into the merkle
Expand All @@ -24,7 +28,8 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser {
private runningPromise: Promise<void> = Promise.resolve();
private currentState: WorldStateRunningState = WorldStateRunningState.IDLE;

constructor(
private constructor(
private db: LevelUp,
private merkleTreeDb: MerkleTrees,
private l2BlockSource: L2BlockSource,
config: WorldStateConfig,
Expand All @@ -45,6 +50,22 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser {
return new MerkleTreeOperationsFacade(this.merkleTreeDb, false);
}

public static async new(
db: LevelUp,
merkleTreeDb: MerkleTrees,
l2BlockSource: L2BlockSource,
config: WorldStateConfig,
log = createDebugLogger('aztec:world_state'),
) {
const server = new ServerWorldStateSynchroniser(db, merkleTreeDb, l2BlockSource, config, log);
await server.init();
return server;
}

private async init() {
await this.restoreCurrentL2BlockNumber();
}

public async start() {
if (this.currentState === WorldStateRunningState.STOPPED) {
throw new Error('Synchroniser already stopped');
Expand Down Expand Up @@ -92,6 +113,7 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser {
await this.jobQueue.cancel();
await this.merkleTreeDb.stop();
await this.runningPromise;
await this.commitCurrentL2BlockNumber();
this.setCurrentState(WorldStateRunningState.STOPPED);
}

Expand Down Expand Up @@ -189,4 +211,23 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser {
this.currentState = newState;
this.log(`Moved to state ${WorldStateRunningState[this.currentState]}`);
}

private async commitCurrentL2BlockNumber() {
const hex = this.currentL2BlockNum.toString(16);
const encoded = Buffer.from(hex.length % 2 === 1 ? '0' + hex : hex, 'hex');

await this.db.put(DB_KEY_BLOCK_NUMBER, encoded);
this.log.debug(`Committed current L2 block number ${this.currentL2BlockNum} to db`);
}

private async restoreCurrentL2BlockNumber() {
try {
const encoded: Buffer = await this.db.get(DB_KEY_BLOCK_NUMBER);
this.currentL2BlockNum = parseInt(encoded.toString('hex'), 16);
this.log.debug(`Restored current L2 block number ${this.currentL2BlockNum} from db`);
} catch (err) {
this.log.debug('No current L2 block number found in db, starting from 0');
this.currentL2BlockNum = 0;
}
}
}
11 changes: 11 additions & 0 deletions yarn-project/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ __metadata:
"@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 Expand Up @@ -4450,6 +4451,16 @@ __metadata:
languageName: node
linkType: hard

"@types/leveldown@npm:^4.0.4":
version: 4.0.4
resolution: "@types/leveldown@npm:4.0.4"
dependencies:
"@types/abstract-leveldown": "*"
"@types/node": "*"
checksum: 630b2d2d1c48f83d14ab0f6c03ad2af1c427675c3692873c4fd3d673bde4140eabc028ce5736ad3d76aeea20769cf53df6f83468a4f0cf28f6d04dbb435edf48
languageName: node
linkType: hard

"@types/levelup@npm:^5.1.2":
version: 5.1.2
resolution: "@types/levelup@npm:5.1.2"
Expand Down

0 comments on commit 2437904

Please sign in to comment.