diff --git a/yarn-project/aztec-node/src/aztec-node/config.ts b/yarn-project/aztec-node/src/aztec-node/config.ts index 125ec88b2878..4b644ca3b783 100644 --- a/yarn-project/aztec-node/src/aztec-node/config.ts +++ b/yarn-project/aztec-node/src/aztec-node/config.ts @@ -17,6 +17,8 @@ export type AztecNodeConfig = ArchiverConfig & /** A URL for an archiver service that the node will use. */ archiverUrl?: string; + + proverAgents: number; }; /** @@ -24,15 +26,21 @@ export type AztecNodeConfig = ArchiverConfig & * @returns A valid aztec node config. */ export function getConfigEnvVars(): AztecNodeConfig { - const { SEQ_DISABLED, PROVER_DISABLED } = process.env; + const { SEQ_DISABLED, PROVER_DISABLED, ARCHIVER_URL, PROVER_AGENTS = '1' } = process.env; + let proverAgents = parseInt(PROVER_AGENTS, 10); + if (Number.isNaN(proverAgents)) { + proverAgents = 1; + } + const allEnvVars: AztecNodeConfig = { ...getSequencerVars(), ...getArchiverVars(), ...getP2PConfigEnvVars(), ...getWorldStateVars(), disableSequencer: !!SEQ_DISABLED, - disableProver: !!PROVER_DISABLED, - archiverUrl: process.env.ARCHIVER_URL, + archiverUrl: ARCHIVER_URL, + disableProver: PROVER_DISABLED === '1', + proverAgents, }; return allEnvVars; diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index d0bfaa8ff321..3bdd0e2227a1 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -51,6 +51,7 @@ import { initStoreForRollup, openTmpStore } from '@aztec/kv-store/utils'; import { SHA256Trunc, StandardTree } from '@aztec/merkle-tree'; import { AztecKVTxPool, type P2P, createP2PClient } from '@aztec/p2p'; import { DummyProver, TxProver } from '@aztec/prover-client'; +import { ProverPool } from '@aztec/prover-client/prover-pool'; import { type GlobalVariableBuilder, SequencerClient, getGlobalVariableBuilder } from '@aztec/sequencer-client'; import { PublicProcessorFactory, WASMSimulator } from '@aztec/simulator'; import { @@ -150,7 +151,7 @@ export class AztecNodeService implements AztecNode { const simulationProvider = await getSimulationProvider(config, log); const prover = config.disableProver ? await DummyProver.new() - : await TxProver.new(config, worldStateSynchronizer, simulationProvider); + : await TxProver.new(worldStateSynchronizer, ProverPool.testPool(simulationProvider, config.proverAgents)); // now create the sequencer const sequencer = config.disableSequencer @@ -193,6 +194,10 @@ export class AztecNodeService implements AztecNode { return this.sequencer; } + public getProver(): ProverClient { + return this.prover; + } + /** * Method to return the currently deployed L1 contract addresses. * @returns - The currently deployed L1 contract addresses. diff --git a/yarn-project/aztec-node/src/index.ts b/yarn-project/aztec-node/src/index.ts index 0cf8b3301480..6a49628be2bc 100644 --- a/yarn-project/aztec-node/src/index.ts +++ b/yarn-project/aztec-node/src/index.ts @@ -1,3 +1,5 @@ +export { getSimulationProvider } from './aztec-node/simulator-factory.js'; + export * from './aztec-node/config.js'; export * from './aztec-node/server.js'; export * from './aztec-node/http_rpc_server.js'; diff --git a/yarn-project/aztec/package.json b/yarn-project/aztec/package.json index 5d3874c5a859..c18166ba9e70 100644 --- a/yarn-project/aztec/package.json +++ b/yarn-project/aztec/package.json @@ -42,6 +42,7 @@ "@aztec/noir-contracts.js": "workspace:^", "@aztec/p2p": "workspace:^", "@aztec/protocol-contracts": "workspace:^", + "@aztec/prover-client": "workspace:^", "@aztec/pxe": "workspace:^", "abitype": "^0.8.11", "commander": "^11.1.0", diff --git a/yarn-project/aztec/src/cli/cli.ts b/yarn-project/aztec/src/cli/cli.ts index 525b3e5d741d..ec6a48eb709e 100644 --- a/yarn-project/aztec/src/cli/cli.ts +++ b/yarn-project/aztec/src/cli/cli.ts @@ -56,6 +56,9 @@ export function getProgram(userLog: LogFn, debugLogger: DebugLogger): Command { } else if (options.p2pBootstrap) { const { startP2PBootstrap } = await import('./cmds/start_p2p_bootstrap.js'); await startP2PBootstrap(options, signalHandlers, userLog, debugLogger); + } else if (options.prover) { + const { startProver } = await import('./cmds/start_prover.js'); + services = await startProver(options, signalHandlers, userLog); } if (services.length) { const rpcServer = createNamespacedJsonRpcServer(services, debugLogger); diff --git a/yarn-project/aztec/src/cli/cmds/start_node.ts b/yarn-project/aztec/src/cli/cmds/start_node.ts index 3b29bb8ee4bb..5c8df45e2cc4 100644 --- a/yarn-project/aztec/src/cli/cmds/start_node.ts +++ b/yarn-project/aztec/src/cli/cmds/start_node.ts @@ -6,6 +6,7 @@ import { import { NULL_KEY } from '@aztec/ethereum'; import { type ServerList } from '@aztec/foundation/json-rpc/server'; import { type LogFn } from '@aztec/foundation/log'; +import { createProvingJobSourceServer } from '@aztec/prover-client/prover-pool'; import { type PXEServiceConfig, createPXERpcServer, getPXEServiceConfig } from '@aztec/pxe'; import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'; @@ -64,7 +65,10 @@ export const startNode = async ( } if (!options.prover) { + userLog(`Prover is disabled, using mocked proofs`); nodeConfig.disableProver = true; + } else { + nodeConfig = mergeEnvVarsAndCliOptions(nodeConfig, parseModuleOptions(options.prover)); } // Create and start Aztec Node. @@ -74,6 +78,11 @@ export const startNode = async ( // Add node to services list services.push({ node: nodeServer }); + if (!nodeConfig.disableProver) { + const provingJobSource = createProvingJobSourceServer(node.getProver().getProvingJobSource()); + services.push({ provingJobSource }); + } + // Add node stop function to signal handlers signalHandlers.push(node.stop); diff --git a/yarn-project/aztec/src/cli/cmds/start_prover.ts b/yarn-project/aztec/src/cli/cmds/start_prover.ts new file mode 100644 index 000000000000..cb1fb39e5805 --- /dev/null +++ b/yarn-project/aztec/src/cli/cmds/start_prover.ts @@ -0,0 +1,34 @@ +import { getSimulationProvider } from '@aztec/aztec-node'; +import { type ProvingJobSource } from '@aztec/circuit-types'; +import { ProverPool, createProvingJobSourceClient } from '@aztec/prover-client/prover-pool'; + +import { type ServiceStarter, parseModuleOptions } from '../util.js'; + +type ProverOptions = Partial<{ + jobsUrl: string; + agents: string; +}>; + +export const startProver: ServiceStarter = async (options, signalHandlers, logger) => { + const proverOptions: ProverOptions = parseModuleOptions(options.prover); + let source: ProvingJobSource; + + if (typeof proverOptions.jobsUrl === 'string') { + logger(`Connecting to prover at ${proverOptions.jobsUrl}`); + source = createProvingJobSourceClient(proverOptions.jobsUrl, 'provingJobSource'); + } else { + throw new Error('Starting prover without an orchestrator is not supported'); + } + + const agentCount = proverOptions.agents ? parseInt(proverOptions.agents, 10) : 1; + if (agentCount === 0 || !Number.isSafeInteger(agentCount)) { + throw new Error('Cannot start prover without agents'); + } + + logger(`Starting ${agentCount} prover agents`); + const pool = ProverPool.testPool(await getSimulationProvider(options), agentCount); + await pool.start(source); + signalHandlers.push(() => pool.stop()); + + return Promise.resolve([]); +}; diff --git a/yarn-project/aztec/src/cli/util.ts b/yarn-project/aztec/src/cli/util.ts index 1a14868638be..db16f546c7b5 100644 --- a/yarn-project/aztec/src/cli/util.ts +++ b/yarn-project/aztec/src/cli/util.ts @@ -3,10 +3,15 @@ import { type AztecNodeConfig } from '@aztec/aztec-node'; import { type AccountManager, type Fr } from '@aztec/aztec.js'; import { type L1ContractAddresses, l1ContractsNames } from '@aztec/ethereum'; import { EthAddress } from '@aztec/foundation/eth-address'; +import { type ServerList } from '@aztec/foundation/json-rpc/server'; import { type LogFn, createConsoleLogger } from '@aztec/foundation/log'; import { type P2PConfig } from '@aztec/p2p'; import { type PXEService, type PXEServiceConfig } from '@aztec/pxe'; +export interface ServiceStarter { + (options: T, signalHandlers: (() => Promise)[], logger: LogFn): Promise; +} + /** * Checks if the object has l1Contracts property * @param obj - The object to check diff --git a/yarn-project/aztec/tsconfig.json b/yarn-project/aztec/tsconfig.json index ff9caf3d51b6..5124c188a848 100644 --- a/yarn-project/aztec/tsconfig.json +++ b/yarn-project/aztec/tsconfig.json @@ -51,6 +51,9 @@ { "path": "../protocol-contracts" }, + { + "path": "../prover-client" + }, { "path": "../pxe" } diff --git a/yarn-project/circuit-types/src/interfaces/prover-client.ts b/yarn-project/circuit-types/src/interfaces/prover-client.ts index 5c2ef55b8d57..8e55d3a2dbbb 100644 --- a/yarn-project/circuit-types/src/interfaces/prover-client.ts +++ b/yarn-project/circuit-types/src/interfaces/prover-client.ts @@ -1,4 +1,5 @@ import { type BlockProver } from './block-prover.js'; +import { type ProvingJobSource } from './proving-job.js'; /** * The interface to the prover client. @@ -8,4 +9,6 @@ export interface ProverClient extends BlockProver { start(): Promise; stop(): Promise; + + getProvingJobSource(): ProvingJobSource; } diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index b72df9815b38..bea0b56d8aa9 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -36,6 +36,7 @@ import { openTmpStore } from '@aztec/kv-store/utils'; import { AvailabilityOracleAbi, InboxAbi, OutboxAbi, RollupAbi } from '@aztec/l1-artifacts'; import { SHA256Trunc, StandardTree } from '@aztec/merkle-tree'; import { TxProver } from '@aztec/prover-client'; +import { ProverPool } from '@aztec/prover-client/prover-pool'; import { type L1Publisher, getL1Publisher } from '@aztec/sequencer-client'; import { WASMSimulator } from '@aztec/simulator'; import { MerkleTrees, ServerWorldStateSynchronizer, type WorldStateConfig } from '@aztec/world-state'; @@ -143,7 +144,7 @@ describe('L1Publisher integration', () => { }; const worldStateSynchronizer = new ServerWorldStateSynchronizer(tmpStore, builderDb, blockSource, worldStateConfig); await worldStateSynchronizer.start(); - builder = await TxProver.new({}, worldStateSynchronizer, new WASMSimulator()); + builder = await TxProver.new(worldStateSynchronizer, ProverPool.testPool(new WASMSimulator(), 4)); l2Proof = Buffer.alloc(0); publisher = getL1Publisher({ diff --git a/yarn-project/prover-client/package.json b/yarn-project/prover-client/package.json index 989fd4ff340d..833d2c830176 100644 --- a/yarn-project/prover-client/package.json +++ b/yarn-project/prover-client/package.json @@ -2,7 +2,10 @@ "name": "@aztec/prover-client", "version": "0.1.0", "type": "module", - "exports": "./dest/index.js", + "exports": { + ".": "./dest/index.js", + "./prover-pool": "./dest/prover-pool/index.js" + }, "bin": { "bb-cli": "./dest/bb/index.js" }, diff --git a/yarn-project/prover-client/src/dummy-prover.ts b/yarn-project/prover-client/src/dummy-prover.ts index de61267c7384..ed9b1fdf06ab 100644 --- a/yarn-project/prover-client/src/dummy-prover.ts +++ b/yarn-project/prover-client/src/dummy-prover.ts @@ -4,6 +4,9 @@ import { PROVING_STATUS, type ProcessedTx, type ProverClient, + type ProvingJob, + type ProvingJobSource, + type ProvingRequest, type ProvingSuccess, type ProvingTicket, } from '@aztec/circuit-types'; @@ -11,6 +14,8 @@ import { type GlobalVariables, makeEmptyProof } from '@aztec/circuits.js'; import { type Fr } from '@aztec/foundation/fields'; export class DummyProver implements ProverClient { + jobs = new DummyProvingJobSource(); + public start(): Promise { return Promise.resolve(); } @@ -54,4 +59,22 @@ export class DummyProver implements ProverClient { setBlockCompleted(): Promise { return Promise.resolve(); } + + getProvingJobSource(): ProvingJobSource { + return this.jobs; + } +} + +class DummyProvingJobSource implements ProvingJobSource { + getProvingJob(): Promise | null> { + return Promise.resolve(null); + } + + rejectProvingJob(): Promise { + return Promise.resolve(); + } + + resolveProvingJob(): Promise { + return Promise.resolve(); + } } diff --git a/yarn-project/prover-client/src/prover-pool/rpc.ts b/yarn-project/prover-client/src/prover-pool/rpc.ts new file mode 100644 index 000000000000..4f0dd5ca989a --- /dev/null +++ b/yarn-project/prover-client/src/prover-pool/rpc.ts @@ -0,0 +1,60 @@ +import { type ProvingJobSource } from '@aztec/circuit-types'; +import { + BaseOrMergeRollupPublicInputs, + BaseParityInputs, + BaseRollupInputs, + MergeRollupInputs, + ParityPublicInputs, + Proof, + PublicKernelCircuitPrivateInputs, + RootParityInputs, + RootRollupInputs, + RootRollupPublicInputs, +} from '@aztec/circuits.js'; +import { createJsonRpcClient, makeFetch } from '@aztec/foundation/json-rpc/client'; +import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; + +export function createProvingJobSourceServer(queue: ProvingJobSource): JsonRpcServer { + return new JsonRpcServer( + queue, + { + BaseParityInputs, + BaseOrMergeRollupPublicInputs, + BaseRollupInputs, + MergeRollupInputs, + ParityPublicInputs, + Proof, + RootParityInputs, + RootRollupInputs, + RootRollupPublicInputs, + PublicKernelCircuitPrivateInputs, + }, + {}, + ); +} + +export function createProvingJobSourceClient( + url: string, + namespace?: string, + fetch = makeFetch([1, 2, 3], false), +): ProvingJobSource { + return createJsonRpcClient( + url, + { + BaseParityInputs, + BaseOrMergeRollupPublicInputs, + BaseRollupInputs, + MergeRollupInputs, + ParityPublicInputs, + Proof, + RootParityInputs, + RootRollupInputs, + RootRollupPublicInputs, + PublicKernelCircuitPrivateInputs, + }, + {}, + false, + namespace, + fetch, + ) as ProvingJobSource; +} diff --git a/yarn-project/prover-client/src/tx-prover/tx-prover.ts b/yarn-project/prover-client/src/tx-prover/tx-prover.ts index 14f36024a39c..bc45cc582bf9 100644 --- a/yarn-project/prover-client/src/tx-prover/tx-prover.ts +++ b/yarn-project/prover-client/src/tx-prover/tx-prover.ts @@ -1,50 +1,45 @@ import { type ProcessedTx } from '@aztec/circuit-types'; -import { type BlockResult, type ProverClient, type ProvingTicket } from '@aztec/circuit-types/interfaces'; +import { + type BlockResult, + type ProverClient, + type ProvingJobSource, + type ProvingTicket, +} from '@aztec/circuit-types/interfaces'; import { type Fr, type GlobalVariables } from '@aztec/circuits.js'; -import { type SimulationProvider } from '@aztec/simulator'; import { type WorldStateSynchronizer } from '@aztec/world-state'; -import { type ProverConfig } from '../config.js'; import { type VerificationKeys, getVerificationKeys } from '../mocks/verification_keys.js'; import { ProvingOrchestrator } from '../orchestrator/orchestrator.js'; -import { CircuitProverAgent } from '../prover-pool/circuit-prover-agent.js'; -import { ProverPool } from '../prover-pool/prover-pool.js'; -import { TestCircuitProver } from '../prover/test_circuit_prover.js'; +import { MemoryProvingQueue } from '../prover-pool/memory-proving-queue.js'; +import { type ProverPool } from '../prover-pool/prover-pool.js'; /** * A prover accepting individual transaction requests */ export class TxProver implements ProverClient { private orchestrator: ProvingOrchestrator; - private proverPool: ProverPool; + private queue = new MemoryProvingQueue(); constructor( private worldStateSynchronizer: WorldStateSynchronizer, - simulationProvider: SimulationProvider, protected vks: VerificationKeys, - agentCount = 4, - agentPollIntervalMS = 10, + private proverPool?: ProverPool, ) { - this.proverPool = new ProverPool( - agentCount, - i => new CircuitProverAgent(new TestCircuitProver(simulationProvider), agentPollIntervalMS, `${i}`), - ); - - this.orchestrator = new ProvingOrchestrator(worldStateSynchronizer.getLatest(), this.proverPool.queue); + this.orchestrator = new ProvingOrchestrator(worldStateSynchronizer.getLatest(), this.queue); } /** * Starts the prover instance */ public async start() { - await this.proverPool.start(); + await this.proverPool?.start(this.queue); } /** * Stops the prover instance */ public async stop() { - await this.proverPool.stop(); + await this.proverPool?.stop(); } /** @@ -53,12 +48,8 @@ export class TxProver implements ProverClient { * @param worldStateSynchronizer - An instance of the world state * @returns An instance of the prover, constructed and started. */ - public static async new( - config: ProverConfig, - worldStateSynchronizer: WorldStateSynchronizer, - simulationProvider: SimulationProvider, - ) { - const prover = new TxProver(worldStateSynchronizer, simulationProvider, getVerificationKeys()); + public static async new(worldStateSynchronizer: WorldStateSynchronizer, proverPool?: ProverPool) { + const prover = new TxProver(worldStateSynchronizer, getVerificationKeys(), proverPool); await prover.start(); return prover; } @@ -110,4 +101,8 @@ export class TxProver implements ProverClient { public setBlockCompleted(): Promise { return this.orchestrator.setBlockCompleted(); } + + getProvingJobSource(): ProvingJobSource { + return this.queue; + } } diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 12ef01aca7c0..00775c2ec57c 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -207,6 +207,7 @@ __metadata: "@aztec/noir-contracts.js": "workspace:^" "@aztec/p2p": "workspace:^" "@aztec/protocol-contracts": "workspace:^" + "@aztec/prover-client": "workspace:^" "@aztec/pxe": "workspace:^" "@jest/globals": ^29.5.0 "@types/jest": ^29.5.0