From 9a70040211d205f472aa31924649c2fef999eb49 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 1 Feb 2024 15:31:39 +0000 Subject: [PATCH] chore(acir)!: Move `is_recursive` flag to be part of the circuit definition (#4221) Resolves #4222 Currently in order to specify whether we want to use a prover that produces SNARK recursion friendly proofs, we must pass a flag from the tooling infrastructure. This PR moves it be part of the circuit definition itself. The flag now lives on the Builder and is set when we call `create_circuit` in the acir format. The proof produced when this flag is true should be friendly for recursive verification inside of another SNARK. For example, a recursive friendly proof may use Blake3Pedersen for hashing in its transcript, while we still want a prove that uses Keccak for its transcript in order to be able to verify SNARKs on Ethereum. However, a verifier does not need a full circuit description and should be able to verify a proof with just the verification key and the proof. An `is_recursive_circuit` field was thus added to the verification key as well so that we can specify the accurate verifier to use for a given proof without the full circuit description. --------- Signed-off-by: kevaundray Co-authored-by: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Co-authored-by: kevaundray --- acvm-repo/acir/codegen/acir.cpp | 4 + acvm-repo/acir/src/circuit/mod.rs | 7 ++ .../test/browser/compile_prove_verify.test.ts | 4 +- .../test/browser/recursion.test.ts | 14 ++-- .../onchain_recursive_verification.test.ts | 14 ++-- .../test/node/smart_contract_verifier.test.ts | 4 +- compiler/noirc_evaluator/src/ssa.rs | 2 + compiler/noirc_frontend/src/ast/function.rs | 2 + .../src/hir/resolution/errors.rs | 14 ++++ .../src/hir/resolution/resolver.rs | 8 +- .../noirc_frontend/src/hir_def/function.rs | 2 +- compiler/noirc_frontend/src/lexer/token.rs | 4 + .../src/monomorphization/ast.rs | 4 + .../src/monomorphization/mod.rs | 4 + .../assert_statement_recursive/Nargo.toml | 7 ++ .../assert_statement_recursive/Prover.toml | 2 + .../assert_statement_recursive/src/main.nr | 11 +++ tooling/backend_interface/src/cli/prove.rs | 7 +- tooling/backend_interface/src/cli/verify.rs | 15 +--- tooling/backend_interface/src/proof_system.rs | 15 +--- .../backend_interface/src/smart_contract.rs | 1 + tooling/nargo_cli/src/cli/prove_cmd.rs | 5 +- tooling/nargo_cli/src/cli/verify_cmd.rs | 2 +- tooling/noir_js/src/program.ts | 12 +-- tooling/noir_js/test/node/e2e.test.ts | 25 ++++--- .../noir_js_backend_barretenberg/src/index.ts | 74 +++---------------- tooling/noir_js_types/src/types.ts | 17 ++--- 27 files changed, 134 insertions(+), 146 deletions(-) create mode 100644 test_programs/execution_success/assert_statement_recursive/Nargo.toml create mode 100644 test_programs/execution_success/assert_statement_recursive/Prover.toml create mode 100644 test_programs/execution_success/assert_statement_recursive/src/main.nr diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index 1b9b9c2e5bd..bdee08794e6 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -1057,6 +1057,7 @@ namespace Circuit { Circuit::PublicInputs public_parameters; Circuit::PublicInputs return_values; std::vector> assert_messages; + bool recursive; friend bool operator==(const Circuit&, const Circuit&); std::vector bincodeSerialize() const; @@ -4889,6 +4890,7 @@ namespace Circuit { if (!(lhs.public_parameters == rhs.public_parameters)) { return false; } if (!(lhs.return_values == rhs.return_values)) { return false; } if (!(lhs.assert_messages == rhs.assert_messages)) { return false; } + if (!(lhs.recursive == rhs.recursive)) { return false; } return true; } @@ -4919,6 +4921,7 @@ void serde::Serializable::serialize(const Circuit::Circuit &ob serde::Serializable::serialize(obj.public_parameters, serializer); serde::Serializable::serialize(obj.return_values, serializer); serde::Serializable::serialize(obj.assert_messages, serializer); + serde::Serializable::serialize(obj.recursive, serializer); serializer.decrease_container_depth(); } @@ -4933,6 +4936,7 @@ Circuit::Circuit serde::Deserializable::deserialize(Deserializ obj.public_parameters = serde::Deserializable::deserialize(deserializer); obj.return_values = serde::Deserializable::deserialize(deserializer); obj.assert_messages = serde::Deserializable::deserialize(deserializer); + obj.recursive = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } diff --git a/acvm-repo/acir/src/circuit/mod.rs b/acvm-repo/acir/src/circuit/mod.rs index b248b30b1d9..ccfb19bbf05 100644 --- a/acvm-repo/acir/src/circuit/mod.rs +++ b/acvm-repo/acir/src/circuit/mod.rs @@ -39,6 +39,11 @@ pub struct Circuit { // c++ code at the moment when it is, due to OpcodeLocation needing a comparison // implementation which is never generated. pub assert_messages: Vec<(OpcodeLocation, String)>, + + /// States whether the backend should use a SNARK recursion friendly prover. + /// If implemented by a backend, this means that proofs generated with this circuit + /// will be friendly for recursively verifying inside of another SNARK. + pub recursive: bool, } impl Circuit { @@ -318,6 +323,7 @@ mod tests { public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2), Witness(12)])), return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(4), Witness(12)])), assert_messages: Default::default(), + recursive: false, }; fn read_write(circuit: Circuit) -> (Circuit, Circuit) { @@ -348,6 +354,7 @@ mod tests { public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])), return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])), assert_messages: Default::default(), + recursive: false, }; let json = serde_json::to_string_pretty(&circuit).unwrap(); diff --git a/compiler/integration-tests/test/browser/compile_prove_verify.test.ts b/compiler/integration-tests/test/browser/compile_prove_verify.test.ts index 0a829def09e..dba51895bb8 100644 --- a/compiler/integration-tests/test/browser/compile_prove_verify.test.ts +++ b/compiler/integration-tests/test/browser/compile_prove_verify.test.ts @@ -59,11 +59,11 @@ test_cases.forEach((testInfo) => { // JS Proving - const proofWithPublicInputs = await program.generateFinalProof(inputs); + const proofWithPublicInputs = await program.generateProof(inputs); // JS verification - const verified = await program.verifyFinalProof(proofWithPublicInputs); + const verified = await program.verifyProof(proofWithPublicInputs); expect(verified, 'Proof fails verification in JS').to.be.true; }); diff --git a/compiler/integration-tests/test/browser/recursion.test.ts b/compiler/integration-tests/test/browser/recursion.test.ts index 80199de5701..a8927aa6a75 100644 --- a/compiler/integration-tests/test/browser/recursion.test.ts +++ b/compiler/integration-tests/test/browser/recursion.test.ts @@ -19,7 +19,7 @@ await newABICoder(); await initACVM(); const base_relative_path = '../../../../..'; -const circuit_main = 'test_programs/execution_success/assert_statement'; +const circuit_main = 'test_programs/execution_success/assert_statement_recursive'; const circuit_recursion = 'compiler/integration-tests/circuits/recursion'; async function getCircuit(projectPath: string) { @@ -48,15 +48,15 @@ describe('It compiles noir program code, receiving circuit bytes and abi object. const { witness: main_witnessUint8Array } = await new Noir(main_program).execute(main_inputs); - const main_proof = await main_backend.generateIntermediateProof(main_witnessUint8Array); - const main_verification = await main_backend.verifyIntermediateProof(main_proof); + const main_proof = await main_backend.generateProof(main_witnessUint8Array); + const main_verification = await main_backend.verifyProof(main_proof); logger.debug('main_verification', main_verification); expect(main_verification).to.be.true; const numPublicInputs = 1; - const { proofAsFields, vkAsFields, vkHash } = await main_backend.generateIntermediateProofArtifacts( + const { proofAsFields, vkAsFields, vkHash } = await main_backend.generateRecursiveProofArtifacts( main_proof, numPublicInputs, ); @@ -76,20 +76,20 @@ describe('It compiles noir program code, receiving circuit bytes and abi object. const { witness: recursion_witnessUint8Array } = await new Noir(recursion_program).execute(recursion_inputs); - const recursion_proof = await recursion_backend.generateFinalProof(recursion_witnessUint8Array); + const recursion_proof = await recursion_backend.generateProof(recursion_witnessUint8Array); // Causes an "unreachable" error. // Due to the fact that it's a non-recursive proof? // // const recursion_numPublicInputs = 1; - // const { proofAsFields: recursion_proofAsFields } = await recursion_backend.generateIntermediateProofArtifacts( + // const { proofAsFields: recursion_proofAsFields } = await recursion_backend.generateRecursiveProofArtifacts( // recursion_proof, // recursion_numPublicInputs, // ); // // logger.debug('recursion_proofAsFields', recursion_proofAsFields); - const recursion_verification = await recursion_backend.verifyFinalProof(recursion_proof); + const recursion_verification = await recursion_backend.verifyProof(recursion_proof); logger.debug('recursion_verification', recursion_verification); diff --git a/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts b/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts index 9cdd80edc15..6147f770f16 100644 --- a/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts +++ b/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts @@ -16,7 +16,7 @@ it(`smart contract can verify a recursive proof`, async () => { const fm = createFileManager(basePath); const innerCompilationResult = await compile( fm, - join(basePath, './test_programs/execution_success/assert_statement'), + join(basePath, './test_programs/execution_success/assert_statement_recursive'), ); if (!('program' in innerCompilationResult)) { throw new Error('Compilation failed'); @@ -38,17 +38,17 @@ it(`smart contract can verify a recursive proof`, async () => { const inner = new Noir(innerProgram); const inner_prover_toml = readFileSync( - join(basePath, `./test_programs/execution_success/assert_statement/Prover.toml`), + join(basePath, `./test_programs/execution_success/assert_statement_recursive/Prover.toml`), ).toString(); const inner_inputs = toml.parse(inner_prover_toml); const { witness: main_witness } = await inner.execute(inner_inputs); - const intermediate_proof = await inner_backend.generateIntermediateProof(main_witness); + const intermediate_proof = await inner_backend.generateProof(main_witness); - expect(await inner_backend.verifyIntermediateProof(intermediate_proof)).to.be.true; + expect(await inner_backend.verifyProof(intermediate_proof)).to.be.true; - const { proofAsFields, vkAsFields, vkHash } = await inner_backend.generateIntermediateProofArtifacts( + const { proofAsFields, vkAsFields, vkHash } = await inner_backend.generateRecursiveProofArtifacts( intermediate_proof, 1, // 1 public input ); @@ -65,8 +65,8 @@ it(`smart contract can verify a recursive proof`, async () => { key_hash: vkHash, }; - const recursion_proof = await recursion.generateFinalProof(recursion_inputs); - expect(await recursion.verifyFinalProof(recursion_proof)).to.be.true; + const recursion_proof = await recursion.generateProof(recursion_inputs); + expect(await recursion.verifyProof(recursion_proof)).to.be.true; // Smart contract verification diff --git a/compiler/integration-tests/test/node/smart_contract_verifier.test.ts b/compiler/integration-tests/test/node/smart_contract_verifier.test.ts index d870956ea7a..79a0520da32 100644 --- a/compiler/integration-tests/test/node/smart_contract_verifier.test.ts +++ b/compiler/integration-tests/test/node/smart_contract_verifier.test.ts @@ -46,11 +46,11 @@ test_cases.forEach((testInfo) => { const prover_toml = readFileSync(resolve(`${base_relative_path}/${test_case}/Prover.toml`)).toString(); const inputs = toml.parse(prover_toml); - const proofData = await program.generateFinalProof(inputs); + const proofData = await program.generateProof(inputs); // JS verification - const verified = await program.verifyFinalProof(proofData); + const verified = await program.verifyProof(proofData); expect(verified, 'Proof fails verification in JS').to.be.true; // Smart contract verification diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 0e3076923e0..108737f1f75 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -85,6 +85,7 @@ pub fn create_circuit( enable_brillig_logging: bool, ) -> Result<(Circuit, DebugInfo, Vec, Vec, Vec), RuntimeError> { let func_sig = program.main_function_signature.clone(); + let recursive = program.recursive; let mut generated_acir = optimize_into_acir(program, enable_ssa_logging, enable_brillig_logging)?; let opcodes = generated_acir.take_opcodes(); @@ -111,6 +112,7 @@ pub fn create_circuit( public_parameters, return_values, assert_messages: assert_messages.into_iter().collect(), + recursive, }; // This converts each im::Vector in the BTreeMap to a Vec diff --git a/compiler/noirc_frontend/src/ast/function.rs b/compiler/noirc_frontend/src/ast/function.rs index f20fc54b101..46f0ac0fa0f 100644 --- a/compiler/noirc_frontend/src/ast/function.rs +++ b/compiler/noirc_frontend/src/ast/function.rs @@ -29,6 +29,7 @@ pub enum FunctionKind { Builtin, Normal, Oracle, + Recursive, } impl NoirFunction { @@ -106,6 +107,7 @@ impl From for NoirFunction { Some(FunctionAttribute::Foreign(_)) => FunctionKind::LowLevel, Some(FunctionAttribute::Test { .. }) => FunctionKind::Normal, Some(FunctionAttribute::Oracle(_)) => FunctionKind::Oracle, + Some(FunctionAttribute::Recursive) => FunctionKind::Recursive, None => FunctionKind::Normal, }; diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 7bd4de77e84..0752838c82e 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -84,6 +84,8 @@ pub enum ResolverError { InvalidTypeForEntryPoint { span: Span }, #[error("Nested slices are not supported")] NestedSlices { span: Span }, + #[error("#[recursive] attribute is only allowed on entry points to a program")] + MisplacedRecursiveAttribute { ident: Ident }, #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] LowLevelFunctionOutsideOfStdlib { ident: Ident }, } @@ -313,6 +315,18 @@ impl From for Diagnostic { "Try to use a constant sized array instead".into(), span, ), + ResolverError::MisplacedRecursiveAttribute { ident } => { + let name = &ident.0.contents; + + let mut diag = Diagnostic::simple_error( + format!("misplaced #[recursive] attribute on function {name} rather than the main function"), + "misplaced #[recursive] attribute".to_string(), + ident.0.span(), + ); + + diag.add_note("The `#[recursive]` attribute specifies to the backend whether it should use a prover which generates proofs that are friendly for recursive verification in another circuit".to_owned()); + diag + } ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( "Definition of low-level function outside of standard library".into(), "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 8243b684c8a..f025f817b09 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -414,7 +414,7 @@ impl<'a> Resolver<'a> { FunctionKind::Builtin | FunctionKind::LowLevel | FunctionKind::Oracle => { HirFunction::empty() } - FunctionKind::Normal => { + FunctionKind::Normal | FunctionKind::Recursive => { let expr_id = self.intern_block(func.def.body); self.interner.push_expr_location(expr_id, func.def.span, self.file); HirFunction::unchecked_from_expr(expr_id) @@ -923,6 +923,12 @@ impl<'a> Resolver<'a> { { self.push_err(ResolverError::NecessaryPub { ident: func.name_ident().clone() }); } + // '#[recursive]' attribute is only allowed for entry point functions + if !self.is_entry_point_function(func) && func.kind == FunctionKind::Recursive { + self.push_err(ResolverError::MisplacedRecursiveAttribute { + ident: func.name_ident().clone(), + }); + } if !self.distinct_allowed(func) && func.def.return_distinctness != Distinctness::DuplicationAllowed diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 9fff301f5f7..78f44696b72 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -127,7 +127,7 @@ impl FuncMeta { pub fn can_ignore_return_type(&self) -> bool { match self.kind { FunctionKind::LowLevel | FunctionKind::Builtin | FunctionKind::Oracle => true, - FunctionKind::Normal => false, + FunctionKind::Normal | FunctionKind::Recursive => false, } } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 835a0baae3f..5d08ab03ad3 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -491,6 +491,7 @@ impl Attribute { Attribute::Function(FunctionAttribute::Oracle(name.to_string())) } ["test"] => Attribute::Function(FunctionAttribute::Test(TestScope::None)), + ["recursive"] => Attribute::Function(FunctionAttribute::Recursive), ["test", name] => { validate(name)?; let malformed_scope = @@ -541,6 +542,7 @@ pub enum FunctionAttribute { Builtin(String), Oracle(String), Test(TestScope), + Recursive, } impl FunctionAttribute { @@ -578,6 +580,7 @@ impl fmt::Display for FunctionAttribute { FunctionAttribute::Foreign(ref k) => write!(f, "#[foreign({k})]"), FunctionAttribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), FunctionAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), + FunctionAttribute::Recursive => write!(f, "#[recursive]"), } } } @@ -621,6 +624,7 @@ impl AsRef for FunctionAttribute { FunctionAttribute::Builtin(string) => string, FunctionAttribute::Oracle(string) => string, FunctionAttribute::Test { .. } => "", + FunctionAttribute::Recursive => "", } } } diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 42a618e7d77..515d9710882 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -246,6 +246,8 @@ pub struct Program { pub return_distinctness: Distinctness, pub return_location: Option, pub return_visibility: Visibility, + /// Indicates to a backend whether a SNARK-friendly prover should be used. + pub recursive: bool, } impl Program { @@ -255,6 +257,7 @@ impl Program { return_distinctness: Distinctness, return_location: Option, return_visibility: Visibility, + recursive: bool, ) -> Program { Program { functions, @@ -262,6 +265,7 @@ impl Program { return_distinctness, return_location, return_visibility, + recursive, } } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 67b246a02ce..0334e01af5d 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -117,6 +117,7 @@ pub fn monomorphize(main: node_interner::FuncId, interner: &NodeInterner) -> Pro meta.return_distinctness, monomorphizer.return_location, meta.return_visibility, + meta.kind == FunctionKind::Recursive, ) } @@ -195,6 +196,9 @@ impl<'interner> Monomorphizer<'interner> { _ => unreachable!("Oracle function must have an oracle attribute"), } } + FunctionKind::Recursive => { + unreachable!("Only main can be specified as recursive, which should already be checked"); + } } } } diff --git a/test_programs/execution_success/assert_statement_recursive/Nargo.toml b/test_programs/execution_success/assert_statement_recursive/Nargo.toml new file mode 100644 index 00000000000..2a5b02cad00 --- /dev/null +++ b/test_programs/execution_success/assert_statement_recursive/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_statement_recursive" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/assert_statement_recursive/Prover.toml b/test_programs/execution_success/assert_statement_recursive/Prover.toml new file mode 100644 index 00000000000..5d1dc99124f --- /dev/null +++ b/test_programs/execution_success/assert_statement_recursive/Prover.toml @@ -0,0 +1,2 @@ +x = "3" +y = "3" diff --git a/test_programs/execution_success/assert_statement_recursive/src/main.nr b/test_programs/execution_success/assert_statement_recursive/src/main.nr new file mode 100644 index 00000000000..687a0d324ba --- /dev/null +++ b/test_programs/execution_success/assert_statement_recursive/src/main.nr @@ -0,0 +1,11 @@ +// Tests a very simple program. +// +// The features being tested is assertion +// This is the same as the `assert_statement` test except we specify +// that the backend should use a prover which will construct proofs +// friendly to recursive verification in another SNARK. +#[recursive] +fn main(x: Field, y: pub Field) { + assert(x == y, "x and y are not equal"); + assert_eq(x, y, "x and y are not equal"); +} \ No newline at end of file diff --git a/tooling/backend_interface/src/cli/prove.rs b/tooling/backend_interface/src/cli/prove.rs index c12e516db57..c63d8afab54 100644 --- a/tooling/backend_interface/src/cli/prove.rs +++ b/tooling/backend_interface/src/cli/prove.rs @@ -13,7 +13,6 @@ use super::string_from_stderr; /// The proof will be written to the specified output file. pub(crate) struct ProveCommand { pub(crate) crs_path: PathBuf, - pub(crate) is_recursive: bool, pub(crate) bytecode_path: PathBuf, pub(crate) witness_path: PathBuf, } @@ -33,10 +32,6 @@ impl ProveCommand { .arg("-o") .arg("-"); - if self.is_recursive { - command.arg("-r"); - } - let output = command.output()?; if output.status.success() { Ok(output.stdout) @@ -61,7 +56,7 @@ fn prove_command() -> Result<(), BackendError> { std::fs::File::create(&witness_path).expect("file should be created"); let crs_path = backend.backend_directory(); - let prove_command = ProveCommand { crs_path, bytecode_path, witness_path, is_recursive: false }; + let prove_command = ProveCommand { crs_path, bytecode_path, witness_path }; let proof = prove_command.run(backend.binary_path())?; assert_eq!(proof, "proof".as_bytes()); diff --git a/tooling/backend_interface/src/cli/verify.rs b/tooling/backend_interface/src/cli/verify.rs index a31f476d84c..1a4ba50b7de 100644 --- a/tooling/backend_interface/src/cli/verify.rs +++ b/tooling/backend_interface/src/cli/verify.rs @@ -6,7 +6,6 @@ use crate::BackendError; /// to verify a proof pub(crate) struct VerifyCommand { pub(crate) crs_path: PathBuf, - pub(crate) is_recursive: bool, pub(crate) proof_path: PathBuf, pub(crate) vk_path: PathBuf, } @@ -24,10 +23,6 @@ impl VerifyCommand { .arg("-k") .arg(self.vk_path); - if self.is_recursive { - command.arg("-r"); - } - let output = command.output()?; // We currently do not distinguish between an invalid proof and an error inside the backend. @@ -64,18 +59,12 @@ fn verify_command() -> Result<(), BackendError> { write_vk_command.run(backend.binary_path())?; - let prove_command = ProveCommand { - crs_path: crs_path.clone(), - is_recursive: false, - bytecode_path, - witness_path, - }; + let prove_command = ProveCommand { crs_path: crs_path.clone(), bytecode_path, witness_path }; let proof = prove_command.run(backend.binary_path())?; write_to_file(&proof, &proof_path); - let verify_command = - VerifyCommand { crs_path, is_recursive: false, proof_path, vk_path: vk_path_output }; + let verify_command = VerifyCommand { crs_path, proof_path, vk_path: vk_path_output }; let verified = verify_command.run(backend.binary_path())?; assert!(verified); diff --git a/tooling/backend_interface/src/proof_system.rs b/tooling/backend_interface/src/proof_system.rs index 595cd7e2020..9369c91fa94 100644 --- a/tooling/backend_interface/src/proof_system.rs +++ b/tooling/backend_interface/src/proof_system.rs @@ -55,7 +55,6 @@ impl Backend { &self, circuit: &Circuit, witness_values: WitnessMap, - is_recursive: bool, ) -> Result, BackendError> { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -76,13 +75,9 @@ impl Backend { write_to_file(&serialized_circuit, &bytecode_path); // Create proof and store it in the specified path - let proof_with_public_inputs = ProveCommand { - crs_path: self.crs_directory(), - is_recursive, - bytecode_path, - witness_path, - } - .run(binary_path)?; + let proof_with_public_inputs = + ProveCommand { crs_path: self.crs_directory(), bytecode_path, witness_path } + .run(binary_path)?; let proof = bb_abstraction_leaks::remove_public_inputs( circuit.public_inputs().0.len(), @@ -97,7 +92,6 @@ impl Backend { proof: &[u8], public_inputs: WitnessMap, circuit: &Circuit, - is_recursive: bool, ) -> Result { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -127,8 +121,7 @@ impl Backend { .run(binary_path)?; // Verify the proof - VerifyCommand { crs_path: self.crs_directory(), is_recursive, proof_path, vk_path } - .run(binary_path) + VerifyCommand { crs_path: self.crs_directory(), proof_path, vk_path }.run(binary_path) } pub fn get_intermediate_proof_artifacts( diff --git a/tooling/backend_interface/src/smart_contract.rs b/tooling/backend_interface/src/smart_contract.rs index 2548079f8e3..524832c6308 100644 --- a/tooling/backend_interface/src/smart_contract.rs +++ b/tooling/backend_interface/src/smart_contract.rs @@ -56,6 +56,7 @@ mod tests { public_parameters: PublicInputs::default(), return_values: PublicInputs::default(), assert_messages: Default::default(), + recursive: false, }; let contract = get_mock_backend()?.eth_contract(&circuit)?; diff --git a/tooling/nargo_cli/src/cli/prove_cmd.rs b/tooling/nargo_cli/src/cli/prove_cmd.rs index 1d20e97af85..cc39b0535bc 100644 --- a/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -138,12 +138,11 @@ pub(crate) fn prove_package( Format::Toml, )?; - let proof = backend.prove(&compiled_program.circuit, solved_witness, false)?; + let proof = backend.prove(&compiled_program.circuit, solved_witness)?; if check_proof { let public_inputs = public_abi.encode(&public_inputs, return_value)?; - let valid_proof = - backend.verify(&proof, public_inputs, &compiled_program.circuit, false)?; + let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit)?; if !valid_proof { return Err(CliError::InvalidProof("".into())); diff --git a/tooling/nargo_cli/src/cli/verify_cmd.rs b/tooling/nargo_cli/src/cli/verify_cmd.rs index ea4aaa051bb..66b88a22f2a 100644 --- a/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -102,7 +102,7 @@ fn verify_package( let proof = load_hex_data(&proof_path)?; - let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit, false)?; + let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit)?; if valid_proof { Ok(()) diff --git a/tooling/noir_js/src/program.ts b/tooling/noir_js/src/program.ts index 809943727eb..8d80ec3a247 100644 --- a/tooling/noir_js/src/program.ts +++ b/tooling/noir_js/src/program.ts @@ -67,13 +67,13 @@ export class Noir { * * @example * ```typescript - * async generateFinalProof(input) + * async generateProof(input) * ``` * */ - async generateFinalProof(inputs: InputMap, foreignCallHandler?: ForeignCallHandler): Promise { + async generateProof(inputs: InputMap, foreignCallHandler?: ForeignCallHandler): Promise { const { witness } = await this.execute(inputs, foreignCallHandler); - return this.getBackend().generateFinalProof(witness); + return this.getBackend().generateProof(witness); } /** @@ -84,11 +84,11 @@ export class Noir { * * @example * ```typescript - * async verifyFinalProof(proof) + * async verifyProof(proof) * ``` * */ - async verifyFinalProof(proofData: ProofData): Promise { - return this.getBackend().verifyFinalProof(proofData); + async verifyProof(proofData: ProofData): Promise { + return this.getBackend().verifyProof(proofData); } } diff --git a/tooling/noir_js/test/node/e2e.test.ts b/tooling/noir_js/test/node/e2e.test.ts index 33d64377b06..8921314e8ea 100644 --- a/tooling/noir_js/test/node/e2e.test.ts +++ b/tooling/noir_js/test/node/e2e.test.ts @@ -21,10 +21,10 @@ it('end-to-end proof creation and verification (outer)', async () => { // // Proof creation const prover = new Backend(assert_lt_program); - const proof = await prover.generateFinalProof(witness); + const proof = await prover.generateProof(witness); // Proof verification - const isValid = await prover.verifyFinalProof(proof); + const isValid = await prover.verifyProof(proof); expect(isValid).to.be.true; }); @@ -40,13 +40,14 @@ it('end-to-end proof creation and verification (outer) -- Program API', async () // Initialize program const program = new Noir(assert_lt_program, backend); // Generate proof - const proof = await program.generateFinalProof(inputs); + const proof = await program.generateProof(inputs); // Proof verification - const isValid = await program.verifyFinalProof(proof); + const isValid = await program.verifyProof(proof); expect(isValid).to.be.true; }); +// TODO: maybe switch to using assert_statement_recursive here to test both options it('end-to-end proof creation and verification (inner)', async () => { // Noir.Js part const inputs = { @@ -62,10 +63,10 @@ it('end-to-end proof creation and verification (inner)', async () => { // // Proof creation const prover = new Backend(assert_lt_program); - const proof = await prover.generateIntermediateProof(witness); + const proof = await prover.generateProof(witness); // Proof verification - const isValid = await prover.verifyIntermediateProof(proof); + const isValid = await prover.verifyProof(proof); expect(isValid).to.be.true; }); @@ -83,10 +84,10 @@ it('end-to-end proving and verification with different instances', async () => { // bb.js part const prover = new Backend(assert_lt_program); - const proof = await prover.generateFinalProof(witness); + const proof = await prover.generateProof(witness); const verifier = new Backend(assert_lt_program); - const proof_is_valid = await verifier.verifyFinalProof(proof); + const proof_is_valid = await verifier.verifyProof(proof); expect(proof_is_valid).to.be.true; }); @@ -115,14 +116,14 @@ it('[BUG] -- bb.js null function or function signature mismatch (outer-inner) ', const prover = new Backend(assert_lt_program); // Create a proof using both proving systems, the majority of the time // one would only use outer proofs. - const proofOuter = await prover.generateFinalProof(witness); - const _proofInner = await prover.generateIntermediateProof(witness); + const proofOuter = await prover.generateProof(witness); + const _proofInner = await prover.generateProof(witness); // Proof verification // - const isValidOuter = await prover.verifyFinalProof(proofOuter); + const isValidOuter = await prover.verifyProof(proofOuter); expect(isValidOuter).to.be.true; // We can also try verifying an inner proof and it will fail. - const isValidInner = await prover.verifyIntermediateProof(_proofInner); + const isValidInner = await prover.verifyProof(_proofInner); expect(isValidInner).to.be.true; }); diff --git a/tooling/noir_js_backend_barretenberg/src/index.ts b/tooling/noir_js_backend_barretenberg/src/index.ts index 61094a3451f..4d5b6389404 100644 --- a/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/tooling/noir_js_backend_barretenberg/src/index.ts @@ -47,47 +47,15 @@ export class BarretenbergBackend implements Backend { } } - /** - * Generate a final proof. This is the proof for the circuit which will verify - * intermediate proofs and or can be seen as the proof created for regular circuits. - */ - async generateFinalProof(decompressedWitness: Uint8Array): Promise { - // The settings for this proof are the same as the settings for a "normal" proof - // i.e. one that is not in the recursive setting. - const makeEasyToVerifyInCircuit = false; - return this.generateProof(decompressedWitness, makeEasyToVerifyInCircuit); - } - - /** - * Generates an intermediate proof. This is the proof that can be verified - * in another circuit. - * - * This is sometimes referred to as a recursive proof. - * We avoid this terminology as the only property of this proof - * that matters is the fact that it is easy to verify in another circuit. - * We _could_ choose to verify this proof outside of a circuit just as easily. - * - * @example - * ```typescript - * const intermediateProof = await backend.generateIntermediateProof(witness); - * ``` - */ - async generateIntermediateProof(witness: Uint8Array): Promise { - // We set `makeEasyToVerifyInCircuit` to true, which will tell the backend to - // generate the proof using components that will make the proof - // easier to verify in a circuit. - const makeEasyToVerifyInCircuit = true; - return this.generateProof(witness, makeEasyToVerifyInCircuit); - } - - /** @ignore */ - async generateProof(compressedWitness: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { + /** @description Generates a proof */ + async generateProof(compressedWitness: Uint8Array): Promise { await this.instantiate(); + // TODO: Change once `@aztec/bb.js` version is updated to use methods without isRecursive flag const proofWithPublicInputs = await this.api.acirCreateProof( this.acirComposer, this.acirUncompressedBytecode, gunzip(compressedWitness), - makeEasyToVerifyInCircuit, + false, ); const splitIndex = proofWithPublicInputs.length - numBytesInProofWithoutPublicInputs; @@ -105,17 +73,17 @@ export class BarretenbergBackend implements Backend { * Instead of passing the proof and verification key as a byte array, we pass them * as fields which makes it cheaper to verify in a circuit. * - * The proof that is passed here will have been created using the `generateIntermediateProof` - * method. + * The proof that is passed here will have been created using a circuit + * that has the #[recursive] attribute on its `main` method. * * The number of public inputs denotes how many public inputs are in the inner proof. * * @example * ```typescript - * const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); + * const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); * ``` */ - async generateIntermediateProofArtifacts( + async generateRecursiveProofArtifacts( proofData: ProofData, numOfPublicInputs = 0, ): Promise<{ @@ -143,31 +111,13 @@ export class BarretenbergBackend implements Backend { }; } - async verifyFinalProof(proofData: ProofData): Promise { + /** @description Verifies a proof */ + async verifyProof(proofData: ProofData): Promise { const proof = reconstructProofWithPublicInputs(proofData); - const makeEasyToVerifyInCircuit = false; - const verified = await this.verifyProof(proof, makeEasyToVerifyInCircuit); - return verified; - } - - /** - * - * @example - * ```typescript - * const isValidIntermediate = await backend.verifyIntermediateProof(proof); - * ``` - */ - async verifyIntermediateProof(proofData: ProofData): Promise { - const proof = reconstructProofWithPublicInputs(proofData); - const makeEasyToVerifyInCircuit = true; - return this.verifyProof(proof, makeEasyToVerifyInCircuit); - } - - /** @ignore */ - async verifyProof(proof: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { await this.instantiate(); await this.api.acirInitVerificationKey(this.acirComposer); - return await this.api.acirVerifyProof(this.acirComposer, proof, makeEasyToVerifyInCircuit); + // TODO: Change once `@aztec/bb.js` version is updated to use methods without isRecursive flag + return await this.api.acirVerifyProof(this.acirComposer, proof, false); } async destroy(): Promise { diff --git a/tooling/noir_js_types/src/types.ts b/tooling/noir_js_types/src/types.ts index ee4921bd606..84148c5d855 100644 --- a/tooling/noir_js_types/src/types.ts +++ b/tooling/noir_js_types/src/types.ts @@ -4,18 +4,14 @@ export { Abi, WitnessMap } from '@noir-lang/noirc_abi'; export interface Backend { /** - * @description Generates a final proof (not meant to be verified in another circuit) */ - generateFinalProof(decompressedWitness: Uint8Array): Promise; - - /** - * @description Generates an intermediate proof (meant to be verified in another circuit) */ - generateIntermediateProof(decompressedWitness: Uint8Array): Promise; + * @description Generates a proof */ + generateProof(decompressedWitness: Uint8Array): Promise; /** * * @description Retrieves the artifacts from a proof in the Field format */ - generateIntermediateProofArtifacts( + generateRecursiveProofArtifacts( proofData: ProofData, numOfPublicInputs: number, ): Promise<{ @@ -28,11 +24,8 @@ export interface Backend { }>; /** - * @description Verifies a final proof */ - verifyFinalProof(proofData: ProofData): Promise; - - /** @description Verifies an intermediate proof */ - verifyIntermediateProof(proofData: ProofData): Promise; + * @description Verifies a proof */ + verifyProof(proofData: ProofData): Promise; /** * @description Destroys the backend */