Skip to content

Commit

Permalink
feat: add execute method to Noir class (#3081)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench authored Oct 10, 2023
1 parent 3b51f4d commit 17bdd7e
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as TOML from 'smol-toml';
import { initializeResolver } from '@noir-lang/source-resolver';
import newCompiler, { compile, init_log_level as compilerLogLevel } from '@noir-lang/noir_wasm';
import { Noir } from '@noir-lang/noir_js';
import { InputMap } from '@noir-lang/noirc_abi';
import { BarretenbergBackend } from '@noir-lang/backend_barretenberg';

import { getFile } from './utils.js';
Expand Down Expand Up @@ -64,7 +65,7 @@ test_cases.forEach((testInfo) => {
const program = new Noir(noir_program, backend);

const prover_toml = await getFile(`${base_relative_path}/${test_case}/Prover.toml`);
const inputs = TOML.parse(prover_toml);
const inputs: InputMap = TOML.parse(prover_toml) as InputMap;

// JS Proving

Expand Down
6 changes: 3 additions & 3 deletions compiler/integration-tests/test/browser/recursion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { TEST_LOG_LEVEL } from '../environment.js';
import { Logger } from 'tslog';
import { initializeResolver } from '@noir-lang/source-resolver';
import newCompiler, { compile, init_log_level as compilerLogLevel } from '@noir-lang/noir_wasm';
import { acvm, abi, generateWitness } from '@noir-lang/noir_js';
import { acvm, abi, Noir } from '@noir-lang/noir_js';

import * as TOML from 'smol-toml';
import { BarretenbergBackend } from '@noir-lang/backend_barretenberg';
Expand Down Expand Up @@ -55,7 +55,7 @@ describe('It compiles noir program code, receiving circuit bytes and abi object.

const main_backend = new BarretenbergBackend(main_program);

const main_witnessUint8Array = await generateWitness(main_program, main_inputs);
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);
Expand Down Expand Up @@ -84,7 +84,7 @@ describe('It compiles noir program code, receiving circuit bytes and abi object.

const recursion_backend = new BarretenbergBackend(recursion_program);

const recursion_witnessUint8Array = await generateWitness(recursion_program, recursion_inputs);
const { witness: recursion_witnessUint8Array } = await new Noir(recursion_program).execute(recursion_inputs);

const recursion_proof = await recursion_backend.generateFinalProof(recursion_witnessUint8Array);

Expand Down
1 change: 0 additions & 1 deletion tooling/noir_js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as abi from '@noir-lang/noirc_abi';

export { acvm, abi };

export { generateWitness } from './witness_generation.js';
export { acirToUint8Array, witnessMapToUint8Array } from './serialize.js';

export { Noir } from './program.js';
25 changes: 19 additions & 6 deletions tooling/noir_js/src/program.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Backend, CompiledCircuit, ProofData } from '@noir-lang/types';
import { generateWitness } from './witness_generation.js';
import initAbi from '@noir-lang/noirc_abi';
import initAbi, { abiDecode, InputMap, InputValue } from '@noir-lang/noirc_abi';
import initACVM from '@noir-lang/acvm_js';
import { witnessMapToUint8Array } from './serialize.js';

export class Noir {
constructor(
private circuit: CompiledCircuit,
private backend: Backend,
private backend?: Backend,
) {}

async init(): Promise<void> {
Expand All @@ -19,14 +20,26 @@ export class Noir {
}
}

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
async generateFinalProof(inputs: any): Promise<ProofData> {
async execute(inputs: InputMap): Promise<{ witness: Uint8Array; returnValue: InputValue }> {
await this.init();
const serializedWitness = await generateWitness(this.circuit, inputs);
return this.backend.generateFinalProof(serializedWitness);
const witness = await generateWitness(this.circuit, inputs);
const { return_value: returnValue } = abiDecode(this.circuit.abi, witness);
return { witness: witnessMapToUint8Array(witness), returnValue };
}

// Initial inputs to your program
async generateFinalProof(inputs: InputMap): Promise<ProofData> {
const { witness } = await this.execute(inputs);
return this.getBackend().generateFinalProof(witness);
}

async verifyFinalProof(proofData: ProofData): Promise<boolean> {
return this.backend.verifyFinalProof(proofData);
return this.getBackend().verifyFinalProof(proofData);
}
}
7 changes: 3 additions & 4 deletions tooling/noir_js/src/witness_generation.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { abiEncode, InputMap } from '@noir-lang/noirc_abi';
import { base64Decode } from './base64_decode.js';
import { executeCircuit } from '@noir-lang/acvm_js';
import { witnessMapToUint8Array } from './serialize.js';
import { executeCircuit, WitnessMap } from '@noir-lang/acvm_js';
import { CompiledCircuit } from '@noir-lang/types';

// Generates the witnesses needed to feed into the chosen proving system
export async function generateWitness(compiledProgram: CompiledCircuit, inputs: InputMap): Promise<Uint8Array> {
export async function generateWitness(compiledProgram: CompiledCircuit, inputs: InputMap): Promise<WitnessMap> {
// Throws on ABI encoding error
const witnessMap = abiEncode(compiledProgram.abi, inputs);

Expand All @@ -15,7 +14,7 @@ export async function generateWitness(compiledProgram: CompiledCircuit, inputs:
const solvedWitness = await executeCircuit(base64Decode(compiledProgram.bytecode), witnessMap, () => {
throw Error('unexpected oracle during execution');
});
return witnessMapToUint8Array(solvedWitness);
return solvedWitness;
} catch (err) {
throw new Error(`Circuit execution failed: ${err}`);
}
Expand Down
16 changes: 8 additions & 8 deletions tooling/noir_js/test/node/cjs.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const chai = require('chai');
const assert_lt_json = require('../noir_compiled_examples/assert_lt/target/assert_lt.json');
const noirjs = require('@noir-lang/noir_js');
const { Noir } = require('@noir-lang/noir_js');

it('generates witnesses successfully', async () => {
const inputs = {
x: '2',
y: '3',
};
const _solvedWitness = await noirjs.generateWitness(assert_lt_json, inputs);
const _solvedWitness = await new Noir(assert_lt_json).execute(inputs);
});

it('string input and number input are the same', async () => {
Expand All @@ -21,8 +21,8 @@ it('string input and number input are the same', async () => {
x: 2,
y: 3,
};
const solvedWitnessString = await noirjs.generateWitness(assert_lt_json, inputsString);
const solvedWitnessNumber = await noirjs.generateWitness(assert_lt_json, inputsNumber);
const solvedWitnessString = await new Noir(assert_lt_json).execute(inputsString);
const solvedWitnessNumber = await new Noir(assert_lt_json).execute(inputsNumber);
chai.expect(solvedWitnessString).to.deep.equal(solvedWitnessNumber);
});

Expand All @@ -36,8 +36,8 @@ it('string input and number input are the same', async () => {
y: 3,
};

const solvedWitnessString = await noirjs.generateWitness(assert_lt_json, inputsString);
const solvedWitnessNumber = await noirjs.generateWitness(assert_lt_json, inputsNumber);
const solvedWitnessString = await new Noir(assert_lt_json).execute(inputsString);
const solvedWitnessNumber = await new Noir(assert_lt_json).execute(inputsNumber);
chai.expect(solvedWitnessString).to.deep.equal(solvedWitnessNumber);
});

Expand All @@ -48,7 +48,7 @@ it('0x prefixed string input for inputs will throw', async () => {
};

try {
await noirjs.generateWitness(assert_lt_json, inputsHexPrefix);
await new Noir(assert_lt_json).execute(inputsHexPrefix);
chai.expect.fail(
'Expected generatedWitness to throw, due to inputs being prefixed with 0x. Currently not supported',
);
Expand All @@ -66,7 +66,7 @@ describe('input validation', () => {
};

try {
await noirjs.generateWitness(assert_lt_json, inputs);
await new Noir(assert_lt_json).execute(inputs);
chai.expect.fail('Expected generatedWitness to throw, due to x not being convertible to a uint64');
} catch (error) {
const knownError = error;
Expand Down
32 changes: 22 additions & 10 deletions tooling/noir_js/test/node/e2e.test.ts
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 { Noir, generateWitness } from '@noir-lang/noir_js';
import { Noir } from '@noir-lang/noir_js';
import { BarretenbergBackend as Backend } from '@noir-lang/backend_barretenberg';
import { CompiledCircuit } from '@noir-lang/types';

Expand All @@ -12,13 +12,16 @@ it('end-to-end proof creation and verification (outer)', async () => {
x: '2',
y: '3',
};
const serializedWitness = await generateWitness(assert_lt_program, inputs);

const program = new Noir(assert_lt_program);

const { witness } = await program.execute(inputs);

// bb.js part
//
// Proof creation
const prover = new Backend(assert_lt_program);
const proof = await prover.generateFinalProof(serializedWitness);
const proof = await prover.generateFinalProof(witness);

// Proof verification
const isValid = await prover.verifyFinalProof(proof);
Expand Down Expand Up @@ -50,13 +53,16 @@ it('end-to-end proof creation and verification (inner)', async () => {
x: '2',
y: '3',
};
const serializedWitness = await generateWitness(assert_lt_program, inputs);

const program = new Noir(assert_lt_program);

const { witness } = await program.execute(inputs);

// bb.js part
//
// Proof creation
const prover = new Backend(assert_lt_program);
const proof = await prover.generateIntermediateProof(serializedWitness);
const proof = await prover.generateIntermediateProof(witness);

// Proof verification
const isValid = await prover.verifyIntermediateProof(proof);
Expand All @@ -81,12 +87,15 @@ it('[BUG] -- bb.js null function or function signature mismatch (different insta
x: '2',
y: '3',
};
const serializedWitness = await generateWitness(assert_lt_program, inputs);

const program = new Noir(assert_lt_program);

const { witness } = await program.execute(inputs);

// bb.js part
const prover = new Backend(assert_lt_program);

const proof = await prover.generateFinalProof(serializedWitness);
const proof = await prover.generateFinalProof(witness);

try {
const verifier = new Backend(assert_lt_program);
Expand All @@ -113,7 +122,10 @@ it('[BUG] -- bb.js null function or function signature mismatch (outer-inner) ',
x: '2',
y: '3',
};
const serializedWitness = await generateWitness(assert_lt_program, inputs);

const program = new Noir(assert_lt_program);

const { witness } = await program.execute(inputs);

// bb.js part
//
Expand All @@ -122,8 +134,8 @@ 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(serializedWitness);
const _proofInner = await prover.generateIntermediateProof(serializedWitness);
const proofOuter = await prover.generateFinalProof(witness);
const _proofInner = await prover.generateIntermediateProof(witness);

// Proof verification
//
Expand Down
16 changes: 16 additions & 0 deletions tooling/noir_js/test/node/execute.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import assert_lt_json from '../noir_compiled_examples/assert_lt/target/assert_lt.json' assert { type: 'json' };
import { Noir } from '@noir-lang/noir_js';
import { CompiledCircuit } from '@noir-lang/types';
import { expect } from 'chai';

const assert_lt_program = assert_lt_json as CompiledCircuit;

it('returns the return value of the circuit', async () => {
const inputs = {
x: '2',
y: '3',
};
const { returnValue } = await new Noir(assert_lt_program).execute(inputs);

expect(returnValue).to.be.eq('0x05');
});
16 changes: 8 additions & 8 deletions tooling/noir_js/test/node/smoke.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from 'chai';
import assert_lt_json from '../noir_compiled_examples/assert_lt/target/assert_lt.json' assert { type: 'json' };
import { generateWitness } from '@noir-lang/noir_js';
import { CompiledCircuit } from '@noir-lang/types';
import { Noir } from '@noir-lang/noir_js';

const assert_lt_program = assert_lt_json as CompiledCircuit;

Expand All @@ -10,7 +10,7 @@ it('generates witnesses successfully', async () => {
x: '2',
y: '3',
};
expect(() => generateWitness(assert_lt_program, inputs)).to.not.throw;
expect(() => new Noir(assert_lt_program).execute(inputs)).to.not.throw;
});

it('string input and number input are the same', async () => {
Expand All @@ -22,8 +22,8 @@ it('string input and number input are the same', async () => {
x: 2,
y: 3,
};
const solvedWitnessString = await generateWitness(assert_lt_program, inputsString);
const solvedWitnessNumber = await generateWitness(assert_lt_program, inputsNumber);
const solvedWitnessString = await new Noir(assert_lt_program).execute(inputsString);
const solvedWitnessNumber = await new Noir(assert_lt_program).execute(inputsNumber);
expect(solvedWitnessString).to.deep.equal(solvedWitnessNumber);
});

Expand All @@ -37,8 +37,8 @@ it('string input and number input are the same', async () => {
y: 3,
};

const solvedWitnessString = await generateWitness(assert_lt_program, inputsString);
const solvedWitnessNumber = await generateWitness(assert_lt_program, inputsNumber);
const solvedWitnessString = await new Noir(assert_lt_program).execute(inputsString);
const solvedWitnessNumber = await new Noir(assert_lt_program).execute(inputsNumber);
expect(solvedWitnessString).to.deep.equal(solvedWitnessNumber);
});

Expand All @@ -49,7 +49,7 @@ it('0x prefixed string input for inputs will throw', async () => {
};

try {
await generateWitness(assert_lt_program, inputsHexPrefix);
await new Noir(assert_lt_program).execute(inputsHexPrefix);
expect.fail('Expected generatedWitness to throw, due to inputs being prefixed with 0x. Currently not supported');
} catch (error) {
// Successfully errored due to 0x not being supported. Update this test once/if we choose
Expand All @@ -65,7 +65,7 @@ describe('input validation', () => {
};

try {
await generateWitness(assert_lt_program, inputs);
await new Noir(assert_lt_program).execute(inputs);
expect.fail('Expected generatedWitness to throw, due to x not being convertible to a uint64');
} catch (error) {
const knownError = error as Error;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
fn main(x : u64, y : pub u64) {
fn main(x : u64, y : pub u64) -> pub u64 {
assert(x < y);
}
x + y
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[1],"y":[2]},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/81WXW7DIAw20BJl0noWE6CBt6k3WbT0/kfYooJm0bQPxZZqKXKw4PPPB5Y/AOATbqL+Pl30F1nrsqaiiq52j+cQ1nlanXffOOUlRQxxOSeXXEzxZ0rerymkOS95xuyCX901Zn/Fm5jXsbDBcobE9yxm7BNn+LCQxnvY+dfEZoq2AjlB46et42nHxupcgqSDAO4R+C6/VN5Hfo6QQIrWFDvFwP9DoaKZY1aM90C/HpdrDTupvyP2nS/a6GzRA7GNhEfd7Nu4qJxvb/5CzimiFcG4kDN7e9QDnJHY6vkTiQX4aoICjR5FG3mdHCwp5rYe4H6SMAK+Kxbng+zFGkGmCbf1652eLGPOAyOvHfV72sC4J1nLyPOjptXKL8ADXjPGCwAA"}
{"hash":7965745029883743051,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[1],"y":[2]},"return_type":{"kind":"integer","sign":"unsigned","width":64},"return_witnesses":[12]},"bytecode":"H4sIAAAAAAAA/+1WXW6DMAw2oVAGqNqedg2HQAlv1W4yNHr/I2yoieRmlIfGZpNaS8jBCl/8F/O9AcA7XCT5eZTTJ/Ku3DuVxGlvN3hs26lvJm30JzbDaDtsu/FotdWd7b4aa8xkW9sP49DjoFsz6XM3mDNeJL0fCwMsnRL/1nzGONEpHxZSf3cLa0VsqdO5QEwQnBPm8bBgYz1cokg7AdwM+JpfKu6Mv0ZIIEVzipEyx08vjRfF7HPC2Afqfr90aFgInQ2b5jV3ek9sGekPv3e+gx/wu3cSslZuj1rZk9zAeSE2/73osPLB5cRB5RJxCs5MBc72WJxNF4t1AJlBE+YvliHkjDHvGesakb/VAcDN1nLY5ocS62fB2DNbscqCD+uKVdIB+WSVkZiFSyg3bgn/m1XOcZf8NRJllSWjn3/BKjFOtAKZOkX2wCqrrJyuie2hWGUF16yyhm1ZJcaJ5mSVryDTwNyssmKMuWasBWf+bl0wv6YX9htJ8OlsXBQAAA=="}

0 comments on commit 17bdd7e

Please sign in to comment.