Skip to content

Commit

Permalink
feat: Initial prover-node package
Browse files Browse the repository at this point in the history
  • Loading branch information
spalladino committed Jul 19, 2024
1 parent 8529fe8 commit 2317c7f
Show file tree
Hide file tree
Showing 17 changed files with 436 additions and 0 deletions.
1 change: 1 addition & 0 deletions yarn-project/deploy_npm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,6 @@ deploy_package archiver
deploy_package p2p
deploy_package prover-client
deploy_package sequencer-client
deploy_package prover-node
deploy_package aztec-node
deploy_package txe
1 change: 1 addition & 0 deletions yarn-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"p2p-bootstrap",
"protocol-contracts",
"prover-client",
"prover-node",
"rollup-provider",
"sequencer-client",
"scripts",
Expand Down
1 change: 1 addition & 0 deletions yarn-project/prover-node/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@aztec/foundation/eslint');
1 change: 1 addition & 0 deletions yarn-project/prover-node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Prover Node
85 changes: 85 additions & 0 deletions yarn-project/prover-node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"name": "@aztec/prover-node",
"version": "0.1.0",
"type": "module",
"exports": {
".": "./dest/index.js"
},
"inherits": [
"../package.common.json"
],
"scripts": {
"build": "yarn clean && tsc -b",
"build:dev": "tsc -b --watch",
"clean": "rm -rf ./dest .tsbuildinfo",
"formatting": "run -T prettier --check ./src && run -T eslint ./src",
"formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
"bb": "node --no-warnings ./dest/bb/index.js",
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests"
},
"jest": {
"moduleNameMapper": {
"^(\\.{1,2}/.*)\\.[cm]?js$": "$1"
},
"testRegex": "./src/.*\\.test\\.(js|mjs|ts)$",
"rootDir": "./src",
"transform": {
"^.+\\.tsx?$": [
"@swc/jest",
{
"jsc": {
"parser": {
"syntax": "typescript",
"decorators": true
}
}
}
]
},
"extensionsToTreatAsEsm": [
".ts"
],
"reporters": [
[
"default",
{
"summaryThreshold": 9999
}
]
]
},
"dependencies": {
"@aztec/archiver": "workspace:^",
"@aztec/circuit-types": "workspace:^",
"@aztec/circuits.js": "workspace:^",
"@aztec/foundation": "workspace:^",
"@aztec/kv-store": "workspace:^",
"@aztec/prover-client": "workspace:^",
"@aztec/sequencer-client": "workspace:^",
"@aztec/simulator": "workspace:^",
"@aztec/telemetry-client": "workspace:^",
"@aztec/world-state": "workspace:^",
"source-map-support": "^0.5.21",
"tslib": "^2.4.0"
},
"devDependencies": {
"@jest/globals": "^29.5.0",
"@types/jest": "^29.5.0",
"@types/memdown": "^3.0.0",
"@types/node": "^18.7.23",
"@types/source-map-support": "^0.5.10",
"jest": "^29.5.0",
"jest-mock-extended": "^3.0.3",
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
},
"files": [
"dest",
"src",
"!*.test.*"
],
"types": "./dest/index.d.ts",
"engines": {
"node": ">=18"
}
}
25 changes: 25 additions & 0 deletions yarn-project/prover-node/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { type ArchiverConfig, getArchiverConfigFromEnv } from '@aztec/archiver';
import { type ProverClientConfig, getProverEnvVars } from '@aztec/prover-client';
import { type PublisherConfig, type TxSenderConfig, getTxSenderConfigFromEnv } from '@aztec/sequencer-client';
import { type WorldStateConfig, getWorldStateConfigFromEnv } from '@aztec/world-state';

import { type TxProviderConfig, getTxProviderConfigFromEnv } from './tx-provider/config.js';

export type ProverNodeConfig = ArchiverConfig &
ProverClientConfig &
WorldStateConfig &
PublisherConfig &
TxSenderConfig &
TxProviderConfig;

export function getProverNodeConfigFromEnv(): ProverNodeConfig {
const { PROOF_PUBLISH_RETRY_INTERVAL_MS } = process.env;
return {
...getArchiverConfigFromEnv(),
...getProverEnvVars(),
...getWorldStateConfigFromEnv(),
...getTxSenderConfigFromEnv('PROVER'),
...getTxProviderConfigFromEnv(),
l1PublishRetryIntervalMS: PROOF_PUBLISH_RETRY_INTERVAL_MS ? +PROOF_PUBLISH_RETRY_INTERVAL_MS : 1_000,
};
}
43 changes: 43 additions & 0 deletions yarn-project/prover-node/src/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { createArchiver } from '@aztec/archiver';
import { createDebugLogger } from '@aztec/foundation/log';
import { createStore } from '@aztec/kv-store/utils';
import { createProverClient } from '@aztec/prover-client';
import { getL1Publisher } from '@aztec/sequencer-client';
import { PublicProcessorFactory, createSimulationProvider } from '@aztec/simulator';
import { type TelemetryClient } from '@aztec/telemetry-client';
import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
import { createWorldStateSynchronizer } from '@aztec/world-state';

import { type ProverNodeConfig } from './config.js';
import { ProverNode } from './prover-node.js';
import { createTxProvider } from './tx-provider/factory.js';

/** Creates a new prover node given a config. */
export async function createProverNode(
config: ProverNodeConfig,
telemetry: TelemetryClient = new NoopTelemetryClient(),
log = createDebugLogger('aztec:prover'),
storeLog = createDebugLogger('aztec:prover:lmdb'),
) {
const store = await createStore(config, config.l1Contracts.rollupAddress, storeLog);

const archiver = await createArchiver(config, store, telemetry, { blockUntilSync: true });

const worldStateConfig = { ...config, worldStateProvenBlocksOnly: true };
const worldStateSynchronizer = await createWorldStateSynchronizer(worldStateConfig, store, archiver);
await worldStateSynchronizer.start();

const simulationProvider = await createSimulationProvider(config, log);

const prover = await createProverClient(config, worldStateSynchronizer, archiver);

// REFACTOR: Move publisher out of sequencer package and into an L1-related package
const publisher = getL1Publisher(config);

const latestWorldState = worldStateSynchronizer.getLatest();
const publicProcessorFactory = new PublicProcessorFactory(latestWorldState, archiver, simulationProvider, telemetry);

const txProvider = createTxProvider(config);

return new ProverNode(prover!, publicProcessorFactory, publisher, archiver, txProvider);
}
Empty file.
123 changes: 123 additions & 0 deletions yarn-project/prover-node/src/job/block-proving-job.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {
type BlockProver,
EmptyTxValidator,
type L2Block,
type L2BlockSource,
type ProcessedTx,
type Tx,
type TxHash,
type TxProvider,
} from '@aztec/circuit-types';
import { type Fr } from '@aztec/circuits.js';
import { createDebugLogger } from '@aztec/foundation/log';
import { type L1Publisher } from '@aztec/sequencer-client';
import { type PublicProcessor, type PublicProcessorFactory } from '@aztec/simulator';

export class BlockProvingJob {
private state: BlockProvingJobState = 'initialized';
private log = createDebugLogger('aztec:block-proving-job');

constructor(
private prover: BlockProver,
private publicProcessorFactory: PublicProcessorFactory,
private publisher: L1Publisher,
private l2BlockSource: L2BlockSource,
private txProvider: TxProvider,
) {}

public getState(): BlockProvingJobState {
return this.state;
}

public async run(fromBlock: number, toBlock: number) {
if (fromBlock !== toBlock) {
throw new Error(`Block ranges are not yet supported`);
}

this.log.info(`Starting block proving job`, { fromBlock, toBlock });
this.state = 'started';

// TODO: Fast-forward world state to fromBlock and/or await fromBlock to be published to the unproven chain

this.state = 'processing';

let historicalHeader = (await this.l2BlockSource.getBlock(fromBlock - 1))?.header;
for (let blockNumber = fromBlock; blockNumber <= toBlock; blockNumber++) {
const block = await this.getBlock(blockNumber);
const globalVariables = block.header.globalVariables;
const txHashes = block.body.txEffects.map(tx => tx.txHash);
const l1ToL2Messages: Fr[] = []; // TODO: grab L1 to L2 messages for this block

this.log.debug(`Starting block processing`, { blockNumber: block.number, blockHash: block.hash().toString() });
await this.prover.startNewBlock(txHashes.length, globalVariables, l1ToL2Messages);
const publicProcessor = await this.publicProcessorFactory.create(historicalHeader, globalVariables);

const txs = await this.getTxs(txHashes);
const txCount = block.body.numberOfTxsIncludingPadded;
await this.processTxs(publicProcessor, txs, txCount);

this.log.debug(`Processed all txs for block`, { blockNumber: block.number, blockHash: block.hash().toString() });
await this.prover.setBlockCompleted();

historicalHeader = block.header;
}

this.state = 'awaiting-prover';
const { block, aggregationObject, proof } = await this.prover.finaliseBlock();
this.log.info(`Finalised proof for block range`, { fromBlock, toBlock });

this.state = 'publishing-proof';
await this.publisher.submitProof(block.header, block.archive.root, aggregationObject, proof);
this.log.info(`Submitted proof for block range`, { fromBlock, toBlock });

this.state = 'completed';
}

private async getBlock(blockNumber: number): Promise<L2Block> {
const block = await this.l2BlockSource.getBlock(blockNumber);
if (!block) {
throw new Error(`Block ${blockNumber} not found in L2 block source`);
}
return block;
}

private async getTxs(txHashes: TxHash[]): Promise<Tx[]> {
const txs = await Promise.all(
txHashes.map(txHash => this.txProvider.getTxByHash(txHash).then(tx => [txHash, tx] as const)),
);
const notFound = txs.filter(([_, tx]) => !tx);
if (notFound.length) {
throw new Error(`Txs not found: ${notFound.map(([txHash]) => txHash.toString()).join(', ')}`);
}
return txs.map(([_, tx]) => tx!);
}

private async processTxs(
publicProcessor: PublicProcessor,
txs: Tx[],
totalNumberOfTxs: number,
): Promise<ProcessedTx[]> {
const [processedTxs, failedTxs] = await publicProcessor.process(
txs,
totalNumberOfTxs,
this.prover,
new EmptyTxValidator(),
);

if (failedTxs.length) {
throw new Error(
`Failed to process txs: ${failedTxs.map(({ tx, error }) => `${tx.getTxHash()} (${error})`).join(', ')}`,
);
}

return processedTxs;
}
}

export type BlockProvingJobState =
| 'initialized'
| 'started'
| 'processing'
| 'awaiting-prover'
| 'publishing-proof'
| 'completed';
51 changes: 51 additions & 0 deletions yarn-project/prover-node/src/prover-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { type L2BlockSource, type ProverClient, type TxProvider } from '@aztec/circuit-types';
import { createDebugLogger } from '@aztec/foundation/log';
import { type L1Publisher } from '@aztec/sequencer-client';
import { type PublicProcessorFactory } from '@aztec/simulator';

import { BlockProvingJob } from './job/block-proving-job.js';

export class ProverNode {
private log = createDebugLogger('aztec:prover-node');

constructor(
private prover: ProverClient,
private publicProcessorFactory: PublicProcessorFactory,
private publisher: L1Publisher,
private l2BlockSource: L2BlockSource,
private txProvider: TxProvider,
) {}

async stop() {
this.log.info('Stopping ProverNode');
await this.prover.stop();
await this.l2BlockSource.stop();
// TODO: Should we stop the L1Publisher as well?
this.log.info('Stopped ProverNode');
}

/**
* Creates a proof for a block range. Returns once the proof has been submitted to L1.
*/
public prove(fromBlock: number, toBlock: number) {
return this.createProvingJob().run(fromBlock, toBlock);
}

/**
* Starts a proving process and returns immediately.
*/
public startProof(fromBlock: number, toBlock: number) {
void this.createProvingJob().run(fromBlock, toBlock);
return Promise.resolve();
}

private createProvingJob() {
return new BlockProvingJob(
this.prover,
this.publicProcessorFactory,
this.publisher,
this.l2BlockSource,
this.txProvider,
);
}
}
10 changes: 10 additions & 0 deletions yarn-project/prover-node/src/tx-provider/aztec-node-tx-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { type AztecNode, type Tx, type TxHash, type TxProvider } from '@aztec/circuit-types';

/** Implements TxProvider by querying an Aztec node for the txs. */
export class AztecNodeTxProvider implements TxProvider {
constructor(private node: AztecNode) {}

getTxByHash(txHash: TxHash): Promise<Tx | undefined> {
return this.node.getTxByHash(txHash);
}
}
9 changes: 9 additions & 0 deletions yarn-project/prover-node/src/tx-provider/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type TxProviderConfig = {
txProviderNodeUrl: string | undefined;
};

export function getTxProviderConfigFromEnv(): TxProviderConfig {
return {
txProviderNodeUrl: process.env.TX_PROVIDER_NODE_URL,
};
}
13 changes: 13 additions & 0 deletions yarn-project/prover-node/src/tx-provider/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { type TxProvider, createAztecNodeClient } from '@aztec/circuit-types';

import { AztecNodeTxProvider } from './aztec-node-tx-provider.js';
import { type TxProviderConfig } from './config.js';

export function createTxProvider(config: TxProviderConfig): TxProvider {
if (config.txProviderNodeUrl) {
const node = createAztecNodeClient(config.txProviderNodeUrl);
return new AztecNodeTxProvider(node);
} else {
throw new Error(`Tx provider node URL is not set`);
}
}
3 changes: 3 additions & 0 deletions yarn-project/prover-node/src/tx-provider/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './aztec-node-tx-provider.js';
export * from './factory.js';
export * from './config.js';
Loading

0 comments on commit 2317c7f

Please sign in to comment.