From c93c7380c705fcec5c77bfc436c2f5ea085edd77 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 31 May 2024 16:44:54 +0100 Subject: [PATCH] feat!: separate proving from `noir_js` (#5072) # Description ## Problem\* Resolves ## Summary\* This removes proving/verification functionality from `noir_js` in favour of interacting with the backend. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Maxim Vezenov --- .../circuits/assert_lt/Nargo.toml | 5 ++ .../circuits/assert_lt/src/main.nr | 10 +++ .../circuits/fold_fibonacci/Nargo.toml | 7 +++ .../circuits/fold_fibonacci/src/main.nr | 12 ++++ compiler/integration-tests/package.json | 2 +- .../scripts/codegen-verifiers.sh | 1 + .../scripts/compile-programs.sh | 12 ++++ compiler/integration-tests/scripts/setup.sh | 8 +++ .../test/browser/compile_prove_verify.test.ts | 11 ++-- .../onchain_recursive_verification.test.ts | 10 +-- .../test/node/prove_and_verify.test.ts | 34 +++-------- .../test/node/smart_contract_verifier.test.ts | 9 +-- docs/docs/how_to/how-to-oracles.md | 2 +- docs/docs/how_to/how-to-recursion.md | 9 +-- docs/docs/tutorials/noirjs_app.md | 5 +- tooling/noir_js/src/index.ts | 4 +- tooling/noir_js/src/program.ts | 61 +------------------ tooling/noir_js/test/node/execute.test.ts | 2 +- 18 files changed, 95 insertions(+), 109 deletions(-) create mode 100644 compiler/integration-tests/circuits/assert_lt/Nargo.toml create mode 100644 compiler/integration-tests/circuits/assert_lt/src/main.nr create mode 100644 compiler/integration-tests/circuits/fold_fibonacci/Nargo.toml create mode 100644 compiler/integration-tests/circuits/fold_fibonacci/src/main.nr mode change 100644 => 100755 compiler/integration-tests/scripts/codegen-verifiers.sh create mode 100755 compiler/integration-tests/scripts/compile-programs.sh create mode 100755 compiler/integration-tests/scripts/setup.sh rename tooling/noir_js/test/node/e2e.test.ts => compiler/integration-tests/test/node/prove_and_verify.test.ts (82%) diff --git a/compiler/integration-tests/circuits/assert_lt/Nargo.toml b/compiler/integration-tests/circuits/assert_lt/Nargo.toml new file mode 100644 index 00000000000..f32ec18cae7 --- /dev/null +++ b/compiler/integration-tests/circuits/assert_lt/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "assert_lt" +type = "bin" +authors = [""] +[dependencies] diff --git a/compiler/integration-tests/circuits/assert_lt/src/main.nr b/compiler/integration-tests/circuits/assert_lt/src/main.nr new file mode 100644 index 00000000000..b8e255ca492 --- /dev/null +++ b/compiler/integration-tests/circuits/assert_lt/src/main.nr @@ -0,0 +1,10 @@ +use dep::std; + +fn main(x: u64, y: pub u64) -> pub u64 { + // We include a println statement to show that noirJS will ignore this and continue execution + std::println("foo"); + + + assert(x < y); + x + y +} diff --git a/compiler/integration-tests/circuits/fold_fibonacci/Nargo.toml b/compiler/integration-tests/circuits/fold_fibonacci/Nargo.toml new file mode 100644 index 00000000000..6d8214689b0 --- /dev/null +++ b/compiler/integration-tests/circuits/fold_fibonacci/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "fold_fibonacci" +type = "bin" +authors = [""] +compiler_version = ">=0.28.0" + +[dependencies] \ No newline at end of file diff --git a/compiler/integration-tests/circuits/fold_fibonacci/src/main.nr b/compiler/integration-tests/circuits/fold_fibonacci/src/main.nr new file mode 100644 index 00000000000..e150a586086 --- /dev/null +++ b/compiler/integration-tests/circuits/fold_fibonacci/src/main.nr @@ -0,0 +1,12 @@ +fn main(x: u32) { + assert(fibonacci(x) == 55); +} + +#[fold] +fn fibonacci(x: u32) -> u32 { + if x <= 1 { + x + } else { + fibonacci(x - 1) + fibonacci(x - 2) + } +} diff --git a/compiler/integration-tests/package.json b/compiler/integration-tests/package.json index 431fe408a8b..b789f1f05ce 100644 --- a/compiler/integration-tests/package.json +++ b/compiler/integration-tests/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "echo Integration Test build step", "test": "yarn test:browser && yarn test:node", - "test:node": "bash ./scripts/codegen-verifiers.sh && hardhat test test/node/**/*", + "test:node": "bash ./scripts/setup.sh && hardhat test test/node/**/*", "test:browser": "web-test-runner", "test:integration:browser": "web-test-runner test/browser/**/*.test.ts", "test:integration:browser:watch": "web-test-runner test/browser/**/*.test.ts --watch", diff --git a/compiler/integration-tests/scripts/codegen-verifiers.sh b/compiler/integration-tests/scripts/codegen-verifiers.sh old mode 100644 new mode 100755 index abc26c4c465..bec59eb6889 --- a/compiler/integration-tests/scripts/codegen-verifiers.sh +++ b/compiler/integration-tests/scripts/codegen-verifiers.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +set -e NARGO_BACKEND_PATH=${NARGO_BACKEND_PATH:-bb} diff --git a/compiler/integration-tests/scripts/compile-programs.sh b/compiler/integration-tests/scripts/compile-programs.sh new file mode 100755 index 00000000000..a7071acc401 --- /dev/null +++ b/compiler/integration-tests/scripts/compile-programs.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -e + +self_path=$(dirname "$(readlink -f "$0")") + +package_root=$self_path/../ + +assert_lt_dir=$package_root/circuits/assert_lt/ +nargo --program-dir $assert_lt_dir compile + +fold_fibonacci_dir=$package_root/circuits/fold_fibonacci/ +nargo --program-dir $fold_fibonacci_dir compile diff --git a/compiler/integration-tests/scripts/setup.sh b/compiler/integration-tests/scripts/setup.sh new file mode 100755 index 00000000000..37db38474c0 --- /dev/null +++ b/compiler/integration-tests/scripts/setup.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +self_path=$(dirname "$(readlink -f "$0")") + + +$self_path/compile-programs.sh +$self_path/codegen-verifiers.sh \ No newline at end of file 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 dba51895bb8..f18ceb85276 100644 --- a/compiler/integration-tests/test/browser/compile_prove_verify.test.ts +++ b/compiler/integration-tests/test/browser/compile_prove_verify.test.ts @@ -51,19 +51,20 @@ test_cases.forEach((testInfo) => { throw e; } - const backend = new BarretenbergBackend(noir_program); - const program = new Noir(noir_program, backend); - const prover_toml = await new Response(await getFile(`${base_relative_path}/${test_case}/Prover.toml`)).text(); const inputs: InputMap = TOML.parse(prover_toml) as InputMap; // JS Proving - const proofWithPublicInputs = await program.generateProof(inputs); + const program = new Noir(noir_program); + const { witness } = await program.execute(inputs); + + const backend = new BarretenbergBackend(noir_program); + const proof = await backend.generateProof(witness); // JS verification - const verified = await program.verifyProof(proofWithPublicInputs); + const verified = await backend.verifyProof(proof); expect(verified, 'Proof fails verification in JS').to.be.true; }); 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 ca7f19309d6..f4647c478f1 100644 --- a/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts +++ b/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts @@ -56,8 +56,7 @@ it.skip(`smart contract can verify a recursive proof`, async () => { // Final proof - const recursion_backend = new BarretenbergBackend(recursionProgram); - const recursion = new Noir(recursionProgram, recursion_backend); + const recursion = new Noir(recursionProgram); const recursion_inputs: InputMap = { verification_key: vkAsFields, @@ -66,8 +65,11 @@ it.skip(`smart contract can verify a recursive proof`, async () => { key_hash: vkHash, }; - const recursion_proof = await recursion.generateProof(recursion_inputs); - expect(await recursion.verifyProof(recursion_proof)).to.be.true; + const { witness: recursionWitness } = await recursion.execute(recursion_inputs); + + const recursion_backend = new BarretenbergBackend(recursionProgram); + const recursion_proof = await recursion_backend.generateProof(recursionWitness); + expect(await recursion_backend.verifyProof(recursion_proof)).to.be.true; // Smart contract verification diff --git a/tooling/noir_js/test/node/e2e.test.ts b/compiler/integration-tests/test/node/prove_and_verify.test.ts similarity index 82% rename from tooling/noir_js/test/node/e2e.test.ts rename to compiler/integration-tests/test/node/prove_and_verify.test.ts index dbb9abcc964..1cfe36f31e9 100644 --- a/tooling/noir_js/test/node/e2e.test.ts +++ b/compiler/integration-tests/test/node/prove_and_verify.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; -import assert_lt_json from '../noir_compiled_examples/assert_lt/target/assert_lt.json' assert { type: 'json' }; -import fold_fibonacci_json from '../noir_compiled_examples/fold_fibonacci/target/fold_fibonacci.json' assert { type: 'json' }; +import assert_lt_json from '../../circuits/assert_lt/target/assert_lt.json' assert { type: 'json' }; +import fold_fibonacci_json from '../../circuits/fold_fibonacci/target/fold_fibonacci.json' assert { type: 'json' }; import { Noir } from '@noir-lang/noir_js'; import { BarretenbergBackend as Backend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; import { CompiledCircuit } from '@noir-lang/types'; @@ -30,25 +30,6 @@ it('end-to-end proof creation and verification (outer)', async () => { expect(isValid).to.be.true; }); -it('end-to-end proof creation and verification (outer) -- Program API', async () => { - // Noir.Js part - const inputs = { - x: '2', - y: '3', - }; - - // Initialize backend - const backend = new Backend(assert_lt_program); - // Initialize program - const program = new Noir(assert_lt_program, backend); - // Generate proof - const proof = await program.generateProof(inputs); - - // Proof verification - const isValid = await program.verifyProof(proof); - expect(isValid).to.be.true; -}); - it('end-to-end proof creation and verification (outer) -- Verifier API', async () => { // Noir.Js part const inputs = { @@ -56,12 +37,13 @@ it('end-to-end proof creation and verification (outer) -- Verifier API', async ( y: '3', }; - // Initialize backend - const backend = new Backend(assert_lt_program); - // Initialize program - const program = new Noir(assert_lt_program, backend); + // Execute program + const program = new Noir(assert_lt_program); + const { witness } = await program.execute(inputs); + // Generate proof - const proof = await program.generateProof(inputs); + const backend = new Backend(assert_lt_program); + const proof = await backend.generateProof(witness); const verificationKey = await backend.getVerificationKey(); 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 79a0520da32..e109cbcab6a 100644 --- a/compiler/integration-tests/test/node/smart_contract_verifier.test.ts +++ b/compiler/integration-tests/test/node/smart_contract_verifier.test.ts @@ -38,19 +38,20 @@ test_cases.forEach((testInfo) => { const noir_program = compileResult.program; - const backend = new BarretenbergBackend(noir_program); - const program = new Noir(noir_program, backend); + const program = new Noir(noir_program); // JS Proving const prover_toml = readFileSync(resolve(`${base_relative_path}/${test_case}/Prover.toml`)).toString(); const inputs = toml.parse(prover_toml); + const { witness } = await program.execute(inputs); - const proofData = await program.generateProof(inputs); + const backend = new BarretenbergBackend(noir_program); + const proofData = await backend.generateProof(witness); // JS verification - const verified = await program.verifyProof(proofData); + const verified = await backend.verifyProof(proofData); expect(verified, 'Proof fails verification in JS').to.be.true; // Smart contract verification diff --git a/docs/docs/how_to/how-to-oracles.md b/docs/docs/how_to/how-to-oracles.md index 5f427f1e23f..2811968c634 100644 --- a/docs/docs/how_to/how-to-oracles.md +++ b/docs/docs/how_to/how-to-oracles.md @@ -194,7 +194,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateProof(inputs, foreignCallHandler) +await noir.execute(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/docs/how_to/how-to-recursion.md b/docs/docs/how_to/how-to-recursion.md index 4c45bb87ae2..aac84e29fac 100644 --- a/docs/docs/how_to/how-to-recursion.md +++ b/docs/docs/how_to/how-to-recursion.md @@ -70,7 +70,7 @@ You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in ` After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. ```js -const noir = new Noir(circuit, backend) +const noir = new Noir(circuit) const { witness } = noir.execute(input) ``` @@ -155,8 +155,8 @@ const backends = { recursive: new BarretenbergBackend(circuits.recursive) } const noir_programs = { - main: new Noir(circuits.main, backends.main), - recursive: new Noir(circuits.recursive, backends.recursive) + main: new Noir(circuits.main), + recursive: new Noir(circuits.recursive) } ``` @@ -173,7 +173,8 @@ const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecurs proof, numPublicInputs, ); -const recursiveProof = await noir_programs.recursive.generateProof(recursiveInputs) +const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) +const recursiveProof = await backends.recursive.generateProof(recursiveWitness); ``` ::: diff --git a/docs/docs/tutorials/noirjs_app.md b/docs/docs/tutorials/noirjs_app.md index 3dd9fe7d2b0..cbb1938a5c6 100644 --- a/docs/docs/tutorials/noirjs_app.md +++ b/docs/docs/tutorials/noirjs_app.md @@ -263,7 +263,7 @@ And instantiate them inside our try-catch block: ```ts // try { const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit, backend); +const noir = new Noir(circuit); // } ``` @@ -288,7 +288,8 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateProof(input); +const { witness } = await noir.execute(input); +const proof = await backend.generateProof(witness); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` diff --git a/tooling/noir_js/src/index.ts b/tooling/noir_js/src/index.ts index e7f356a582e..1feca8fa275 100644 --- a/tooling/noir_js/src/index.ts +++ b/tooling/noir_js/src/index.ts @@ -1,6 +1,6 @@ import * as acvm from '@noir-lang/acvm_js'; import * as abi from '@noir-lang/noirc_abi'; -import { CompiledCircuit, ProofData } from '@noir-lang/types'; +import { CompiledCircuit } from '@noir-lang/types'; export { ecdsa_secp256r1_verify, @@ -22,4 +22,4 @@ export { ErrorWithPayload } from './witness_generation.js'; export { acvm, abi }; // type exports for typedoc -export { CompiledCircuit, ProofData }; +export { CompiledCircuit }; diff --git a/tooling/noir_js/src/program.ts b/tooling/noir_js/src/program.ts index 86aa0f60ddf..accd89dc385 100644 --- a/tooling/noir_js/src/program.ts +++ b/tooling/noir_js/src/program.ts @@ -1,14 +1,10 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { Backend, CompiledCircuit, ProofData } from '@noir-lang/types'; +import { CompiledCircuit } from '@noir-lang/types'; import { generateWitness } from './witness_generation.js'; import initAbi, { abiDecode, InputMap, InputValue } from '@noir-lang/noirc_abi'; import initACVM, { compressWitnessStack, ForeignCallHandler } from '@noir-lang/acvm_js'; export class Noir { - constructor( - private circuit: CompiledCircuit, - private backend?: Backend, - ) {} + constructor(private circuit: CompiledCircuit) {} /** @ignore */ async init(): Promise { @@ -20,27 +16,6 @@ export class Noir { } } - /** - * - * @description - * Destroys the underlying backend instance. - * - * @example - * ```typescript - * await noir.destroy(); - * ``` - * - */ - async destroy(): Promise { - await this.backend?.destroy(); - } - - private getBackend(): Backend { - if (this.backend === undefined) throw new Error('Operation requires a backend but none was provided'); - return this.backend; - } - - // Initial inputs to your program /** * @description * Allows to execute a circuit to get its witness and return value. @@ -60,36 +35,4 @@ export class Noir { const { return_value: returnValue } = abiDecode(this.circuit.abi, main_witness); return { witness: compressWitnessStack(witness_stack), returnValue }; } - - /** - * - * @description - * Generates a witness and a proof given an object as input. - * - * @example - * ```typescript - * async generateProof(input) - * ``` - * - */ - async generateProof(inputs: InputMap, foreignCallHandler?: ForeignCallHandler): Promise { - const { witness } = await this.execute(inputs, foreignCallHandler); - return this.getBackend().generateProof(witness); - } - - /** - * - * @description - * Instantiates the verification key and verifies a proof. - * - * - * @example - * ```typescript - * async verifyProof(proof) - * ``` - * - */ - async verifyProof(proofData: ProofData): Promise { - return this.getBackend().verifyProof(proofData); - } } diff --git a/tooling/noir_js/test/node/execute.test.ts b/tooling/noir_js/test/node/execute.test.ts index d047e35035f..7c93a911042 100644 --- a/tooling/noir_js/test/node/execute.test.ts +++ b/tooling/noir_js/test/node/execute.test.ts @@ -11,7 +11,7 @@ const assert_lt_program = assert_lt_json as CompiledCircuit; const assert_msg_runtime = assert_msg_json as CompiledCircuit; const fold_fibonacci_program = fold_fibonacci_json as CompiledCircuit; -it('returns the return value of the circuit', async () => { +it('executes a single-ACIR program correctly', async () => { const inputs = { x: '2', y: '3',