Skip to content

Commit

Permalink
feat!: separate proving from noir_js (noir-lang#5072)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves <!-- Link to GitHub Issue -->

## 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 <[email protected]>
  • Loading branch information
TomAFrench and vezenovm authored May 31, 2024
1 parent 1252b5f commit c93c738
Show file tree
Hide file tree
Showing 18 changed files with 95 additions and 109 deletions.
5 changes: 5 additions & 0 deletions compiler/integration-tests/circuits/assert_lt/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "assert_lt"
type = "bin"
authors = [""]
[dependencies]
10 changes: 10 additions & 0 deletions compiler/integration-tests/circuits/assert_lt/src/main.nr
Original file line number Diff line number Diff line change
@@ -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
}
7 changes: 7 additions & 0 deletions compiler/integration-tests/circuits/fold_fibonacci/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "fold_fibonacci"
type = "bin"
authors = [""]
compiler_version = ">=0.28.0"

[dependencies]
12 changes: 12 additions & 0 deletions compiler/integration-tests/circuits/fold_fibonacci/src/main.nr
Original file line number Diff line number Diff line change
@@ -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)
}
}
2 changes: 1 addition & 1 deletion compiler/integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions compiler/integration-tests/scripts/codegen-verifiers.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env bash
set -e

NARGO_BACKEND_PATH=${NARGO_BACKEND_PATH:-bb}

Expand Down
12 changes: 12 additions & 0 deletions compiler/integration-tests/scripts/compile-programs.sh
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions compiler/integration-tests/scripts/setup.sh
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -30,38 +30,20 @@ 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 = {
x: '2',
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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/how_to/how-to-oracles.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
9 changes: 5 additions & 4 deletions docs/docs/how_to/how-to-recursion.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```

Expand Down Expand Up @@ -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)
}
```

Expand All @@ -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);
```

:::
5 changes: 3 additions & 2 deletions docs/docs/tutorials/noirjs_app.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
// }
```
Expand All @@ -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);
```
Expand Down
4 changes: 2 additions & 2 deletions tooling/noir_js/src/index.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -22,4 +22,4 @@ export { ErrorWithPayload } from './witness_generation.js';
export { acvm, abi };

// type exports for typedoc
export { CompiledCircuit, ProofData };
export { CompiledCircuit };
61 changes: 2 additions & 59 deletions tooling/noir_js/src/program.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
Expand All @@ -20,27 +16,6 @@ export class Noir {
}
}

/**
*
* @description
* Destroys the underlying backend instance.
*
* @example
* ```typescript
* await noir.destroy();
* ```
*
*/
async destroy(): Promise<void> {
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.
Expand All @@ -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<ProofData> {
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<boolean> {
return this.getBackend().verifyProof(proofData);
}
}
2 changes: 1 addition & 1 deletion tooling/noir_js/test/node/execute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down

0 comments on commit c93c738

Please sign in to comment.