diff --git a/yarn-project/aztec-node/package.json b/yarn-project/aztec-node/package.json index 43c4c4e7ad5b..7cffccf39b2d 100644 --- a/yarn-project/aztec-node/package.json +++ b/yarn-project/aztec-node/package.json @@ -49,6 +49,7 @@ }, "dependencies": { "@aztec/archiver": "workspace:^", + "@aztec/bb-prover": "workspace:^", "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/ethereum": "workspace:^", diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 92e1ce631741..e043bf0307a2 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -1,5 +1,7 @@ import { type ArchiveSource, Archiver, KVArchiverDataStore, createArchiverClient } from '@aztec/archiver'; +import { BBCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover'; import { + AggregateTxValidator, type AztecNode, type FromLogType, type GetUnencryptedLogsResponse, @@ -24,6 +26,7 @@ import { type TxHash, TxReceipt, TxStatus, + type TxValidator, partitionReverts, } from '@aztec/circuit-types'; import { @@ -49,7 +52,7 @@ import { AztecLmdbStore } from '@aztec/kv-store/lmdb'; 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 { TxProver } from '@aztec/prover-client'; import { type GlobalVariableBuilder, SequencerClient, getGlobalVariableBuilder } from '@aztec/sequencer-client'; import { PublicProcessorFactory, WASMSimulator } from '@aztec/simulator'; import { @@ -67,13 +70,15 @@ import { import { type AztecNodeConfig } from './config.js'; import { getSimulationProvider } from './simulator-factory.js'; +import { MetadataTxValidator } from './tx_validator/tx_metadata_validator.js'; +import { TxProofValidator } from './tx_validator/tx_proof_validator.js'; /** * The aztec node. */ export class AztecNodeService implements AztecNode { constructor( - protected readonly config: AztecNodeConfig, + protected config: AztecNodeConfig, protected readonly p2pClient: P2P, protected readonly blockSource: L2BlockSource, protected readonly encryptedLogsSource: L2LogsSource, @@ -86,7 +91,8 @@ export class AztecNodeService implements AztecNode { protected readonly version: number, protected readonly globalVariableBuilder: GlobalVariableBuilder, protected readonly merkleTreesDb: AztecKVStore, - private readonly prover: ProverClient, + private readonly prover: ProverClient | undefined, + private txValidator: TxValidator, private log = createDebugLogger('aztec:node'), ) { const message = @@ -145,9 +151,21 @@ export class AztecNodeService implements AztecNode { // start both and wait for them to sync from the block source await Promise.all([p2pClient.start(), worldStateSynchronizer.start()]); + const proofVerifier = config.realProofs ? await BBCircuitVerifier.new(config) : new TestCircuitVerifier(); + const txValidator = new AggregateTxValidator( + new MetadataTxValidator(config.chainId), + new TxProofValidator(proofVerifier), + ); + // start the prover if we have been told to const simulationProvider = await getSimulationProvider(config, log); - const prover = config.disableProver ? await DummyProver.new() : await TxProver.new(config, worldStateSynchronizer); + const prover = config.disableProver + ? undefined + : await TxProver.new(config, await proofVerifier.getVerificationKeys(), worldStateSynchronizer); + + if (!prover && !config.disableSequencer) { + throw new Error("Can't start a sequencer without a prover"); + } // now create the sequencer const sequencer = config.disableSequencer @@ -159,7 +177,7 @@ export class AztecNodeService implements AztecNode { archiver, archiver, archiver, - prover, + prover!, simulationProvider, ); @@ -178,6 +196,7 @@ export class AztecNodeService implements AztecNode { getGlobalVariableBuilder(config), store, prover, + txValidator, log, ); } @@ -190,7 +209,7 @@ export class AztecNodeService implements AztecNode { return this.sequencer; } - public getProver(): ProverClient { + public getProver(): ProverClient | undefined { return this.prover; } @@ -292,6 +311,13 @@ export class AztecNodeService implements AztecNode { */ public async sendTx(tx: Tx) { this.log.info(`Received tx ${tx.getTxHash()}`); + + const [_, invalidTxs] = await this.txValidator.validateTxs([tx]); + if (invalidTxs.length > 0) { + this.log.warn(`Rejecting tx ${tx.getTxHash()} because of validation errors`); + return; + } + await this.p2pClient!.sendTx(tx); } @@ -327,7 +353,7 @@ export class AztecNodeService implements AztecNode { await this.p2pClient.stop(); await this.worldStateSynchronizer.stop(); await this.blockSource.stop(); - await this.prover.stop(); + await this.prover?.stop(); this.log.info(`Stopped`); } @@ -684,8 +710,24 @@ export class AztecNodeService implements AztecNode { } public async setConfig(config: Partial): Promise { + const newConfig = { ...this.config, ...config }; this.sequencer?.updateSequencerConfig(config); - await this.prover.updateProverConfig(config); + await this.prover?.updateProverConfig(config); + + if (newConfig.realProofs !== this.config.realProofs) { + const proofVerifier = config.realProofs ? await BBCircuitVerifier.new(newConfig) : new TestCircuitVerifier(); + + this.txValidator = new AggregateTxValidator( + new MetadataTxValidator(this.chainId), + new TxProofValidator(proofVerifier), + ); + + await this.prover?.updateProverConfig({ + vks: await proofVerifier.getVerificationKeys(), + }); + } + + this.config = newConfig; } /** diff --git a/yarn-project/aztec-node/src/aztec-node/tx_validator/tx_metadata_validator.test.ts b/yarn-project/aztec-node/src/aztec-node/tx_validator/tx_metadata_validator.test.ts new file mode 100644 index 000000000000..2f825258f6d2 --- /dev/null +++ b/yarn-project/aztec-node/src/aztec-node/tx_validator/tx_metadata_validator.test.ts @@ -0,0 +1,29 @@ +import { mockTx, mockTxForRollup } from '@aztec/circuit-types'; +import { Fr } from '@aztec/circuits.js'; + +import { MetadataTxValidator } from './tx_metadata_validator.js'; + +describe('MetadataTxValidator', () => { + let chainId: Fr; + let validator: MetadataTxValidator; + + beforeEach(() => { + chainId = Fr.random(); + validator = new MetadataTxValidator(chainId); + }); + + it('allows only transactions for the right chain', async () => { + const goodTxs = [mockTx(1), mockTxForRollup(2)]; + const badTxs = [mockTx(3), mockTxForRollup(4)]; + + goodTxs.forEach(tx => { + tx.data.constants.txContext.chainId = chainId; + }); + + badTxs.forEach(tx => { + tx.data.constants.txContext.chainId = chainId.add(new Fr(1)); + }); + + await expect(validator.validateTxs([...goodTxs, ...badTxs])).resolves.toEqual([goodTxs, badTxs]); + }); +}); diff --git a/yarn-project/aztec-node/src/aztec-node/tx_validator/tx_metadata_validator.ts b/yarn-project/aztec-node/src/aztec-node/tx_validator/tx_metadata_validator.ts new file mode 100644 index 000000000000..46c6ae0ed2fd --- /dev/null +++ b/yarn-project/aztec-node/src/aztec-node/tx_validator/tx_metadata_validator.ts @@ -0,0 +1,40 @@ +import { Tx, type TxValidator } from '@aztec/circuit-types'; +import { Fr } from '@aztec/circuits.js'; +import { createDebugLogger } from '@aztec/foundation/log'; + +export class MetadataTxValidator implements TxValidator { + #log = createDebugLogger('aztec:sequencer:tx_validator:tx_metadata'); + #chainId: Fr; + + constructor(chainId: number | Fr) { + this.#chainId = new Fr(chainId); + } + + validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> { + const validTxs: Tx[] = []; + const invalidTxs: Tx[] = []; + for (const tx of txs) { + if (!this.#hasCorrectChainId(tx)) { + invalidTxs.push(tx); + continue; + } + + validTxs.push(tx); + } + + return Promise.resolve([validTxs, invalidTxs]); + } + + #hasCorrectChainId(tx: Tx): boolean { + if (!tx.data.constants.txContext.chainId.equals(this.#chainId)) { + this.#log.warn( + `Rejecting tx ${Tx.getHash( + tx, + )} because of incorrect chain ${tx.data.constants.txContext.chainId.toNumber()} != ${this.#chainId.toNumber()}`, + ); + return false; + } else { + return true; + } + } +} diff --git a/yarn-project/aztec-node/src/aztec-node/tx_validator/tx_proof_validator.ts b/yarn-project/aztec-node/src/aztec-node/tx_validator/tx_proof_validator.ts new file mode 100644 index 000000000000..030743256cf9 --- /dev/null +++ b/yarn-project/aztec-node/src/aztec-node/tx_validator/tx_proof_validator.ts @@ -0,0 +1,28 @@ +import { type ClientProtocolCircuitVerifier, Tx, type TxValidator } from '@aztec/circuit-types'; +import { createDebugLogger } from '@aztec/foundation/log'; + +export class TxProofValidator implements TxValidator { + #log = createDebugLogger('aztec:sequencer:tx_validator:private_proof'); + + constructor(private verifier: ClientProtocolCircuitVerifier) {} + + async validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> { + const validTxs: Tx[] = []; + const invalidTxs: Tx[] = []; + + for (const tx of txs) { + if (await this.verifier.verifyProof(tx)) { + validTxs.push(tx); + } else { + this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} for invalid proof`); + invalidTxs.push(tx); + } + } + + return [validTxs, invalidTxs]; + } + + validateTx(tx: Tx): Promise { + return this.verifier.verifyProof(tx); + } +} diff --git a/yarn-project/aztec/src/cli/cmds/start_node.ts b/yarn-project/aztec/src/cli/cmds/start_node.ts index 925871cda171..9245bd327083 100644 --- a/yarn-project/aztec/src/cli/cmds/start_node.ts +++ b/yarn-project/aztec/src/cli/cmds/start_node.ts @@ -88,7 +88,7 @@ export const startNode = async ( services.push({ node: nodeServer }); if (!nodeConfig.disableProver) { - const provingJobSource = createProvingJobSourceServer(node.getProver().getProvingJobSource()); + const provingJobSource = createProvingJobSourceServer(node.getProver()!.getProvingJobSource()); services.push({ provingJobSource }); } diff --git a/yarn-project/bb-prover/src/test/index.ts b/yarn-project/bb-prover/src/test/index.ts index 17fafbb7289a..3f84ad27da1a 100644 --- a/yarn-project/bb-prover/src/test/index.ts +++ b/yarn-project/bb-prover/src/test/index.ts @@ -1 +1,2 @@ export * from './test_circuit_prover.js'; +export * from './test_verifier.js'; diff --git a/yarn-project/bb-prover/src/test/test_verifier.ts b/yarn-project/bb-prover/src/test/test_verifier.ts new file mode 100644 index 000000000000..03ca11cd2777 --- /dev/null +++ b/yarn-project/bb-prover/src/test/test_verifier.ts @@ -0,0 +1,12 @@ +import { type ClientProtocolCircuitVerifier, type Tx } from '@aztec/circuit-types'; +import { type VerificationKeys, getMockVerificationKeys } from '@aztec/circuits.js'; + +export class TestCircuitVerifier implements ClientProtocolCircuitVerifier { + verifyProof(_tx: Tx): Promise { + return Promise.resolve(true); + } + + getVerificationKeys(): Promise { + return Promise.resolve(getMockVerificationKeys()); + } +} diff --git a/yarn-project/bb-prover/src/verifier/bb_verifier.ts b/yarn-project/bb-prover/src/verifier/bb_verifier.ts index a0b5d05d8ddc..6e1681fad338 100644 --- a/yarn-project/bb-prover/src/verifier/bb_verifier.ts +++ b/yarn-project/bb-prover/src/verifier/bb_verifier.ts @@ -1,7 +1,12 @@ -import { type Proof, type VerificationKeyData } from '@aztec/circuits.js'; +import { type ClientProtocolCircuitVerifier, Tx } from '@aztec/circuit-types'; +import { type Proof, type VerificationKeyData, type VerificationKeys } from '@aztec/circuits.js'; import { runInDirectory } from '@aztec/foundation/fs'; import { type DebugLogger, type LogFn, createDebugLogger } from '@aztec/foundation/log'; -import { type ProtocolArtifact, ProtocolCircuitArtifacts } from '@aztec/noir-protocol-circuits-types'; +import { + type ClientProtocolArtifact, + type ProtocolArtifact, + ProtocolCircuitArtifacts, +} from '@aztec/noir-protocol-circuits-types'; import * as fs from 'fs/promises'; @@ -9,7 +14,7 @@ import { BB_RESULT, generateContractForCircuit, generateKeyForNoirCircuit, verif import { type BBConfig } from '../config.js'; import { extractVkData } from '../verification_key/verification_key_data.js'; -export class BBCircuitVerifier { +export class BBCircuitVerifier implements ClientProtocolCircuitVerifier { private constructor( private config: BBConfig, private verificationKeys = new Map>(), @@ -112,4 +117,30 @@ export class BBCircuitVerifier { return fs.readFile(result.contractPath!, 'utf-8'); } + + async verifyProof(tx: Tx): Promise { + const { proof, enqueuedPublicFunctionCalls } = tx; + const expectedCircuit: ClientProtocolArtifact = + enqueuedPublicFunctionCalls.length > 0 ? 'PrivateKernelTailToPublicArtifact' : 'PrivateKernelTailArtifact'; + + try { + await this.verifyProofForCircuit(expectedCircuit, proof); + return true; + } catch (err) { + this.logger.warn(`Failed to verify ${expectedCircuit} proof for tx ${Tx.getHash(tx)}: ${String(err)}`); + return false; + } + } + + async getVerificationKeys(): Promise { + const [privateKernelCircuit, privateKernelToPublicCircuit] = await Promise.all([ + this.getVerificationKeyData('PrivateKernelTailArtifact'), + this.getVerificationKeyData('PrivateKernelTailToPublicArtifact'), + ]); + + return { + privateKernelCircuit, + privateKernelToPublicCircuit, + }; + } } diff --git a/yarn-project/circuit-types/src/interfaces/prover-client.ts b/yarn-project/circuit-types/src/interfaces/prover-client.ts index cf5ef775d2e3..8fd148361f5f 100644 --- a/yarn-project/circuit-types/src/interfaces/prover-client.ts +++ b/yarn-project/circuit-types/src/interfaces/prover-client.ts @@ -1,3 +1,5 @@ +import { type VerificationKeys } from '@aztec/circuits.js'; + import { type BlockProver } from './block-prover.js'; import { type ProvingJobSource } from './proving-job.js'; @@ -28,5 +30,5 @@ export interface ProverClient extends BlockProver { getProvingJobSource(): ProvingJobSource; - updateProverConfig(config: Partial): Promise; + updateProverConfig(config: Partial): Promise; } diff --git a/yarn-project/circuit-types/src/interfaces/server_circuit_prover.ts b/yarn-project/circuit-types/src/interfaces/server_circuit_prover.ts index 9a2b59e1c6bd..3c968d21a582 100644 --- a/yarn-project/circuit-types/src/interfaces/server_circuit_prover.ts +++ b/yarn-project/circuit-types/src/interfaces/server_circuit_prover.ts @@ -2,6 +2,7 @@ import { type PublicInputsAndProof, type PublicKernelNonTailRequest, type PublicKernelTailRequest, + type Tx, } from '@aztec/circuit-types'; import { type BaseOrMergeRollupPublicInputs, @@ -18,6 +19,7 @@ import { type RootParityInputs, type RootRollupInputs, type RootRollupPublicInputs, + type VerificationKeys, } from '@aztec/circuits.js'; /** @@ -104,3 +106,20 @@ export interface PublicProver { */ getPublicKernelCircuitProof(publicInputs: PublicKernelCircuitPublicInputs): Promise; } + +/** + * A verifier used by nodes to check tx proofs are valid. + */ +export interface ClientProtocolCircuitVerifier { + /** + * Verifies the private protocol circuit's proof. + * @param tx - The tx to verify the proof of + * @returns True if the proof is valid, false otherwise + */ + verifyProof(tx: Tx): Promise; + + /** + * Returns the verification keys used to verify tx proofs. + */ + getVerificationKeys(): Promise; +} diff --git a/yarn-project/circuit-types/src/tx/index.ts b/yarn-project/circuit-types/src/tx/index.ts index 6d69130adafa..0f134780c26c 100644 --- a/yarn-project/circuit-types/src/tx/index.ts +++ b/yarn-project/circuit-types/src/tx/index.ts @@ -4,4 +4,5 @@ export * from './tx_hash.js'; export * from './tx_receipt.js'; export * from './processed_tx.js'; export * from './public_simulation_output.js'; -export * from './tx_validator.js'; +export * from './validator/tx_validator.js'; +export * from './validator/aggregate_tx_validator.js'; diff --git a/yarn-project/circuit-types/src/tx/validator/aggregate_tx_validator.test.ts b/yarn-project/circuit-types/src/tx/validator/aggregate_tx_validator.test.ts new file mode 100644 index 000000000000..1eb7558bf25f --- /dev/null +++ b/yarn-project/circuit-types/src/tx/validator/aggregate_tx_validator.test.ts @@ -0,0 +1,29 @@ +import { type AnyTx, Tx, type TxHash, type TxValidator, mockTx } from '@aztec/circuit-types'; + +import { AggregateTxValidator } from './aggregate_tx_validator.js'; + +describe('AggregateTxValidator', () => { + it('allows txs that pass all validation', async () => { + const txs = [mockTx(0), mockTx(1), mockTx(2), mockTx(3), mockTx(4)]; + const agg = new AggregateTxValidator( + new TxDenyList(txs[0].getTxHash(), txs[1].getTxHash()), + new TxDenyList(txs[2].getTxHash(), txs[3].getTxHash()), + ); + + await expect(agg.validateTxs(txs)).resolves.toEqual([[txs[4]], [txs[0], txs[1], txs[2], txs[3]]]); + }); + + class TxDenyList implements TxValidator { + denyList: Set; + constructor(...txHashes: TxHash[]) { + this.denyList = new Set(txHashes.map(hash => hash.toString())); + } + + validateTxs(txs: AnyTx[]): Promise<[AnyTx[], AnyTx[]]> { + return Promise.resolve([ + txs.filter(tx => !this.denyList.has(Tx.getHash(tx).toString())), + txs.filter(tx => this.denyList.has(Tx.getHash(tx).toString())), + ]); + } + } +}); diff --git a/yarn-project/circuit-types/src/tx/validator/aggregate_tx_validator.ts b/yarn-project/circuit-types/src/tx/validator/aggregate_tx_validator.ts new file mode 100644 index 000000000000..6e78b2bfcecc --- /dev/null +++ b/yarn-project/circuit-types/src/tx/validator/aggregate_tx_validator.ts @@ -0,0 +1,26 @@ +import { type ProcessedTx } from '../processed_tx.js'; +import { type Tx } from '../tx.js'; +import { type TxValidator } from './tx_validator.js'; + +export class AggregateTxValidator implements TxValidator { + #validators: TxValidator[]; + constructor(...validators: TxValidator[]) { + if (validators.length === 0) { + throw new Error('At least one validator must be provided'); + } + + this.#validators = validators; + } + + async validateTxs(txs: T[]): Promise<[validTxs: T[], invalidTxs: T[]]> { + const invalidTxs: T[] = []; + let txPool = txs; + for (const validator of this.#validators) { + const [valid, invalid] = await validator.validateTxs(txPool); + invalidTxs.push(...invalid); + txPool = valid; + } + + return [txPool, invalidTxs]; + } +} diff --git a/yarn-project/circuit-types/src/tx/tx_validator.ts b/yarn-project/circuit-types/src/tx/validator/tx_validator.ts similarity index 64% rename from yarn-project/circuit-types/src/tx/tx_validator.ts rename to yarn-project/circuit-types/src/tx/validator/tx_validator.ts index c5bb16b561f4..336d36fe6480 100644 --- a/yarn-project/circuit-types/src/tx/tx_validator.ts +++ b/yarn-project/circuit-types/src/tx/validator/tx_validator.ts @@ -1,4 +1,5 @@ -import { type ProcessedTx, type Tx } from '@aztec/circuit-types'; +import { type ProcessedTx } from '../processed_tx.js'; +import { type Tx } from '../tx.js'; export type AnyTx = Tx | ProcessedTx; 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 cf89ebec9dae..abc6aedb478f 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 @@ -29,6 +29,7 @@ import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, type Proof, PublicDataUpdateRequest, + getMockVerificationKeys, makeEmptyProof, } from '@aztec/circuits.js'; import { fr, makeProof } from '@aztec/circuits.js/testing'; @@ -144,7 +145,7 @@ describe('L1Publisher integration', () => { }; const worldStateSynchronizer = new ServerWorldStateSynchronizer(tmpStore, builderDb, blockSource, worldStateConfig); await worldStateSynchronizer.start(); - builder = await TxProver.new(config, worldStateSynchronizer); + builder = await TxProver.new(config, getMockVerificationKeys(), worldStateSynchronizer); l2Proof = makeEmptyProof(); publisher = getL1Publisher({ diff --git a/yarn-project/end-to-end/src/e2e_prover/e2e_prover.test.ts b/yarn-project/end-to-end/src/e2e_prover/e2e_prover.test.ts index ace44568c50a..ef36701bfae8 100644 --- a/yarn-project/end-to-end/src/e2e_prover/e2e_prover.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/e2e_prover.test.ts @@ -1,26 +1,20 @@ -import { type Fr, type Tx } from '@aztec/aztec.js'; -import { type BBNativeProofCreator } from '@aztec/bb-prover'; +import { type Fr } from '@aztec/aztec.js'; import { getTestData, isGenerateTestDataEnabled, writeTestData } from '@aztec/foundation/testing'; -import { type ClientProtocolArtifact } from '@aztec/noir-protocol-circuits-types'; import { FullProverTest } from './e2e_prover_test.js'; const TIMEOUT = 1_800_000; -async function verifyProof(circuitType: ClientProtocolArtifact, tx: Tx, proofCreator: BBNativeProofCreator) { - await expect(proofCreator.verifyProofForProtocolCircuit(circuitType, tx.proof)).resolves.not.toThrow(); -} - describe('full_prover', () => { const t = new FullProverTest('full_prover'); - let { provenAssets, accounts, tokenSim, logger, proofCreator, wallets } = t; + let { provenAssets, accounts, tokenSim, logger, wallets } = t; beforeAll(async () => { await t.applyBaseSnapshots(); await t.applyMintSnapshot(); await t.setup(); await t.deployVerifier(); - ({ provenAssets, accounts, tokenSim, logger, proofCreator, wallets } = t); + ({ provenAssets, accounts, tokenSim, logger, wallets } = t); }); afterAll(async () => { @@ -60,11 +54,11 @@ describe('full_prover', () => { // This will recursively verify all app and kernel circuits involved in the private stage of this transaction! logger.info(`Verifying kernel tail to public proof`); - await verifyProof('PrivateKernelTailToPublicArtifact', publicTx, proofCreator!); + await expect(t.circuitProofVerifier?.verifyProof(publicTx)).resolves.not.toThrow(); // This will recursively verify all app and kernel circuits involved in the private stage of this transaction! logger.info(`Verifying private kernel tail proof`); - await verifyProof('PrivateKernelTailArtifact', privateTx, proofCreator!); + await expect(t.circuitProofVerifier?.verifyProof(privateTx)).resolves.not.toThrow(); const sentPrivateTx = privateInteraction.send(); const sentPublicTx = publicInteraction.send(); @@ -98,4 +92,20 @@ describe('full_prover', () => { }, TIMEOUT, ); + + it('rejects txs with invalid proofs', async () => { + const privateInteraction = t.fakeProofsAsset.methods.transfer(accounts[0].address, accounts[1].address, 1, 0); + const publicInteraction = t.fakeProofsAsset.methods.transfer_public(accounts[0].address, accounts[1].address, 1, 0); + + const sentPrivateTx = privateInteraction.send(); + const sentPublicTx = publicInteraction.send(); + + const results = await Promise.allSettled([ + sentPrivateTx.wait({ timeout: 10, interval: 0.1 }), + sentPublicTx.wait({ timeout: 10, interval: 0.1 }), + ]); + + expect(String((results[0] as PromiseRejectedResult).reason)).toMatch(/Tx dropped by P2P node/); + expect(String((results[1] as PromiseRejectedResult).reason)).toMatch(/Tx dropped by P2P node/); + }); }); diff --git a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts index c73e905b4dad..d89787db10b1 100644 --- a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts @@ -14,7 +14,7 @@ import { createDebugLogger, deployL1Contract, } from '@aztec/aztec.js'; -import { BBCircuitVerifier, BBNativeProofCreator } from '@aztec/bb-prover'; +import { BBCircuitVerifier } from '@aztec/bb-prover'; import { RollupAbi } from '@aztec/l1-artifacts'; import { TokenContract } from '@aztec/noir-contracts.js'; import { type PXEService } from '@aztec/pxe'; @@ -61,14 +61,13 @@ export class FullProverTest { keys: Array<[Fr, Fq]> = []; wallets: AccountWalletWithSecretKey[] = []; accounts: CompleteAddress[] = []; - asset!: TokenContract; + fakeProofsAsset!: TokenContract; tokenSim!: TokenSimulator; aztecNode!: AztecNode; pxe!: PXEService; private provenComponents: ProvenSetup[] = []; private bbConfigCleanup?: () => Promise; private acvmConfigCleanup?: () => Promise; - proofCreator?: BBNativeProofCreator; circuitProofVerifier?: BBCircuitVerifier; provenAssets: TokenContract[] = []; private context!: SubsystemsContext; @@ -116,16 +115,16 @@ export class FullProverTest { }, async ({ tokenContractAddress }) => { // Restore the token contract state. - this.asset = await TokenContract.at(tokenContractAddress, this.wallets[0]); - this.logger.verbose(`Token contract address: ${this.asset.address}`); + this.fakeProofsAsset = await TokenContract.at(tokenContractAddress, this.wallets[0]); + this.logger.verbose(`Token contract address: ${this.fakeProofsAsset.address}`); this.tokenSim = new TokenSimulator( - this.asset, + this.fakeProofsAsset, this.logger, this.accounts.map(a => a.address), ); - expect(await this.asset.methods.admin().simulate()).toBe(this.accounts[0].address.toBigInt()); + expect(await this.fakeProofsAsset.methods.admin().simulate()).toBe(this.accounts[0].address.toBigInt()); }, ); } @@ -160,24 +159,21 @@ export class FullProverTest { minTxsPerBlock: 2, // min 2 txs per block }); - this.proofCreator = new BBNativeProofCreator(bbConfig.bbBinaryPath, bbConfig.bbWorkingDirectory); - this.logger.debug(`Main setup completed, initializing full prover PXE and Node...`); for (let i = 0; i < 2; i++) { const result = await setupPXEService( this.aztecNode, { - proverEnabled: false, + proverEnabled: true, bbBinaryPath: bbConfig?.bbBinaryPath, bbWorkingDirectory: bbConfig?.bbWorkingDirectory, }, undefined, true, - this.proofCreator, ); - this.logger.debug(`Contract address ${this.asset.address}`); - await result.pxe.registerContract(this.asset); + this.logger.debug(`Contract address ${this.fakeProofsAsset.address}`); + await result.pxe.registerContract(this.fakeProofsAsset); for (let i = 0; i < 2; i++) { await waitRegisteredAccountSynced( @@ -201,7 +197,7 @@ export class FullProverTest { }); const provenWallet = await account.getWallet(); - const asset = await TokenContract.at(this.asset.address, provenWallet); + const asset = await TokenContract.at(this.fakeProofsAsset.address, provenWallet); this.provenComponents.push({ pxe: result.pxe, teardown: result.teardown, @@ -236,7 +232,7 @@ export class FullProverTest { const extendedNote = new ExtendedNote( note, this.accounts[accountIndex].address, - this.asset.address, + this.fakeProofsAsset.address, TokenContract.storage.pending_shields.slot, TokenContract.notes.TransparentNote.id, txHash, @@ -248,7 +244,7 @@ export class FullProverTest { await this.snapshotManager.snapshot( 'mint', async () => { - const { asset, accounts } = this; + const { fakeProofsAsset: asset, accounts } = this; const amount = 10000n; this.logger.verbose(`Minting ${amount} publicly...`); @@ -268,7 +264,7 @@ export class FullProverTest { }, async ({ amount }) => { const { - asset, + fakeProofsAsset: asset, accounts: [{ address }], tokenSim, } = this; diff --git a/yarn-project/prover-client/src/dummy-prover.ts b/yarn-project/prover-client/src/dummy-prover.ts deleted file mode 100644 index c24268344d7f..000000000000 --- a/yarn-project/prover-client/src/dummy-prover.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { - type BlockResult, - L2Block, - PROVING_STATUS, - type ProcessedTx, - type ProverClient, - type ProverConfig, - type ProvingJob, - type ProvingJobSource, - type ProvingRequest, - type ProvingSuccess, - type ProvingTicket, -} from '@aztec/circuit-types'; -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(); - } - - public stop(): Promise { - return Promise.resolve(); - } - - public static new(): Promise { - return Promise.resolve(new DummyProver()); - } - - startNewBlock( - _numTxs: number, - _globalVariables: GlobalVariables, - _newL1ToL2Messages: Fr[], - _emptyTx: ProcessedTx, - ): Promise { - const result: ProvingSuccess = { - status: PROVING_STATUS.SUCCESS, - }; - const ticket: ProvingTicket = { - provingPromise: Promise.resolve(result), - }; - return Promise.resolve(ticket); - } - - addNewTx(_tx: ProcessedTx): Promise { - return Promise.resolve(); - } - - cancelBlock(): void {} - - finaliseBlock(): Promise { - return Promise.resolve({ - block: L2Block.empty(), - proof: makeEmptyProof(), - aggregationObject: [], - }); - } - - setBlockCompleted(): Promise { - return Promise.resolve(); - } - - getProvingJobSource(): ProvingJobSource { - return this.jobs; - } - - updateProverConfig(_config: Partial): Promise { - return Promise.resolve(); - } -} - -class DummyProvingJobSource implements ProvingJobSource { - getProvingJob(): Promise | undefined> { - return Promise.resolve(undefined); - } - - rejectProvingJob(): Promise { - return Promise.resolve(); - } - - resolveProvingJob(): Promise { - return Promise.resolve(); - } -} diff --git a/yarn-project/prover-client/src/index.ts b/yarn-project/prover-client/src/index.ts index 79268e339e61..1c2f83414e56 100644 --- a/yarn-project/prover-client/src/index.ts +++ b/yarn-project/prover-client/src/index.ts @@ -2,4 +2,3 @@ export { ProverClient } from '@aztec/circuit-types'; export * from './tx-prover/tx-prover.js'; export * from './config.js'; -export * from './dummy-prover.js'; 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 cdbd246960f7..3e57feac852c 100644 --- a/yarn-project/prover-client/src/tx-prover/tx-prover.ts +++ b/yarn-project/prover-client/src/tx-prover/tx-prover.ts @@ -1,4 +1,4 @@ -import { BBCircuitVerifier, type BBConfig, BBNativeRollupProver, TestCircuitProver } from '@aztec/bb-prover'; +import { BBNativeRollupProver, TestCircuitProver } from '@aztec/bb-prover'; import { type ProcessedTx } from '@aztec/circuit-types'; import { type BlockResult, @@ -7,8 +7,7 @@ import { type ProvingTicket, type ServerCircuitProver, } from '@aztec/circuit-types/interfaces'; -import { type Fr, type GlobalVariables, type VerificationKeys, getMockVerificationKeys } from '@aztec/circuits.js'; -import { createDebugLogger } from '@aztec/foundation/log'; +import { type Fr, type GlobalVariables, type VerificationKeys } from '@aztec/circuits.js'; import { NativeACVMSimulator } from '@aztec/simulator'; import { type WorldStateSynchronizer } from '@aztec/world-state'; @@ -17,21 +16,6 @@ import { ProvingOrchestrator } from '../orchestrator/orchestrator.js'; import { MemoryProvingQueue } from '../prover-agent/memory-proving-queue.js'; import { ProverAgent } from '../prover-agent/prover-agent.js'; -const logger = createDebugLogger('aztec:tx-prover'); - -const PRIVATE_KERNEL = 'PrivateKernelTailArtifact'; -const PRIVATE_KERNEL_TO_PUBLIC = 'PrivateKernelTailToPublicArtifact'; - -async function retrieveRealPrivateKernelVerificationKeys(config: BBConfig) { - logger.info(`Retrieving private kernel verification keys`); - const bbVerifier = await BBCircuitVerifier.new(config, [PRIVATE_KERNEL, PRIVATE_KERNEL_TO_PUBLIC]); - const vks: VerificationKeys = { - privateKernelCircuit: await bbVerifier.getVerificationKeyData(PRIVATE_KERNEL), - privateKernelToPublicCircuit: await bbVerifier.getVerificationKeyData(PRIVATE_KERNEL_TO_PUBLIC), - }; - return vks; -} - /** * A prover accepting individual transaction requests */ @@ -49,16 +33,16 @@ export class TxProver implements ProverClient { this.orchestrator = new ProvingOrchestrator(worldStateSynchronizer.getLatest(), this.queue); } - async updateProverConfig(config: Partial): Promise { + async updateProverConfig(config: Partial): Promise { const newConfig = { ...this.config, ...config }; - if (newConfig.realProofs !== this.config.realProofs) { - this.vks = await (newConfig.realProofs - ? retrieveRealPrivateKernelVerificationKeys(newConfig) - : getMockVerificationKeys()); + if (config.vks) { + this.vks = config.vks; + } + if (newConfig.realProofs !== this.config.realProofs && this.agent) { const circuitProver = await TxProver.buildCircuitProver(newConfig); - this.agent?.setCircuitProver(circuitProver); + this.agent.setCircuitProver(circuitProver); } if (this.config.proverAgentConcurrency !== newConfig.proverAgentConcurrency) { @@ -93,12 +77,17 @@ export class TxProver implements ProverClient { } /** - * + * Creates a new prover client and starts it * @param config - The prover configuration. + * @param vks - The verification keys for the prover * @param worldStateSynchronizer - An instance of the world state * @returns An instance of the prover, constructed and started. */ - public static async new(config: ProverClientConfig, worldStateSynchronizer: WorldStateSynchronizer) { + public static async new( + config: ProverClientConfig, + vks: VerificationKeys, + worldStateSynchronizer: WorldStateSynchronizer, + ) { const agent = config.proverAgentEnabled ? new ProverAgent( await TxProver.buildCircuitProver(config), @@ -107,10 +96,6 @@ export class TxProver implements ProverClient { ) : undefined; - const vks = await (config.realProofs - ? retrieveRealPrivateKernelVerificationKeys(config) - : getMockVerificationKeys()); - const prover = new TxProver(config, worldStateSynchronizer, vks, agent); await prover.start(); return prover; diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index 5ec55a03db9c..a8b4f6c8247c 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -54,7 +54,7 @@ export class SequencerClient { l2BlockSource, l1ToL2MessageSource, publicProcessorFactory, - new TxValidatorFactory(merkleTreeDb, contractDataSource, config.l1Contracts.gasPortalAddress), + new TxValidatorFactory(merkleTreeDb, contractDataSource), config, ); diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index f08ba14dea47..3ea24887d2e0 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -110,7 +110,7 @@ describe('sequencer', () => { l2BlockSource, l1ToL2MessageSource, publicProcessorFactory, - new TxValidatorFactory(merkleTreeOps, contractSource, EthAddress.random()), + new TxValidatorFactory(merkleTreeOps, contractSource), ); }); diff --git a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts index 3a67e1a459d4..f31f2862680d 100644 --- a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts +++ b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts @@ -1,5 +1,5 @@ import { type AllowedFunction, type ProcessedTx, type Tx, type TxValidator } from '@aztec/circuit-types'; -import { type EthAddress, type GlobalVariables } from '@aztec/circuits.js'; +import { type GlobalVariables } from '@aztec/circuits.js'; import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token'; import { WorldStateDB, WorldStatePublicDB } from '@aztec/simulator'; import { type ContractDataSource } from '@aztec/types/contracts'; @@ -12,11 +12,7 @@ import { MetadataTxValidator } from './metadata_validator.js'; import { PhasesTxValidator } from './phases_validator.js'; export class TxValidatorFactory { - constructor( - private merkleTreeDb: MerkleTreeOperations, - private contractDataSource: ContractDataSource, - private gasPortalAddress: EthAddress, - ) {} + constructor(private merkleTreeDb: MerkleTreeOperations, private contractDataSource: ContractDataSource) {} validatorForNewTxs(globalVariables: GlobalVariables, setupAllowList: AllowedFunction[]): TxValidator { return new AggregateTxValidator(