Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: devex improvements to noirjs #3044

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ test_cases.forEach((testInfo) => {
}

const backend = new BarretenbergBackend(noir_program);
const program = new Noir(noir_program, backend);
const program = new Noir(backend);

const prover_toml = await getFile(`${base_relative_path}/${test_case}/Prover.toml`);
const inputs: InputMap = TOML.parse(prover_toml) as InputMap;
Expand Down
6 changes: 4 additions & 2 deletions compiler/integration-tests/test/browser/recursion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ describe('It compiles noir program code, receiving circuit bytes and abi object.
let circuit_main_toml;
let circuit_recursion_source;

const noir = new Noir(); // backendless noir

before(async () => {
circuit_main_source = await getFile(`${base_relative_path}/${circuit_main}/src/main.nr`);
circuit_main_toml = await getFile(`${base_relative_path}/${circuit_main}/Prover.toml`);
Expand All @@ -55,7 +57,7 @@ describe('It compiles noir program code, receiving circuit bytes and abi object.

const main_backend = new BarretenbergBackend(main_program);

const { witness: main_witnessUint8Array } = await new Noir(main_program).execute(main_inputs);
const { witness: main_witnessUint8Array } = await noir.execute(main_inputs, main_program);

const main_proof = await main_backend.generateIntermediateProof(main_witnessUint8Array);
const main_verification = await main_backend.verifyIntermediateProof(main_proof);
Expand Down Expand Up @@ -84,7 +86,7 @@ describe('It compiles noir program code, receiving circuit bytes and abi object.

const recursion_backend = new BarretenbergBackend(recursion_program);

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

const recursion_proof = await recursion_backend.generateFinalProof(recursion_witnessUint8Array);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ test_cases.forEach((testInfo) => {
const noir_program = compile(noir_source_path);

const backend = new BarretenbergBackend(noir_program);
const program = new Noir(noir_program, backend);
const program = new Noir(backend);

// JS Proving

Expand Down
61 changes: 56 additions & 5 deletions tooling/noir_js/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,59 @@
import * as acvm from '@noir-lang/acvm_js';
import * as abi from '@noir-lang/noirc_abi';
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Backend, CompiledCircuit, ProofData } from '@noir-lang/types';
import { generateWitness } from './witness_generation.js';
import initAbi, * as abi from '@noir-lang/noirc_abi';
import initACVM, * as acvm from '@noir-lang/acvm_js';
import { witnessMapToUint8Array } from './serialize.js';

export { acvm, abi };
const { abiDecode } = abi;

export { acirToUint8Array, witnessMapToUint8Array } from './serialize.js';
class Noir {
constructor(private backend?: Backend) {}

export { Noir } from './program.js';
async init(): Promise<void> {
// If these are available, then we are in the
// web environment. For the node environment, this
// is a no-op.
if (typeof initAbi === 'function') {
await Promise.all([initAbi(), initACVM()]);
}
}

async destroy(): Promise<void> {
if (!this.backend) throw new Error('No backend to destroy');
signorecello marked this conversation as resolved.
Show resolved Hide resolved

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
async execute(
inputs: abi.InputMap,
circuit?: CompiledCircuit,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a better API to instantiate the Noir class with a CompiledCircuit and use that in this function rather than having to pass in a circuit each time.

This is inconsistent when Noir "knows" about the circuit you're interacting with when you're proving/verifying but not for execution.

): Promise<{ witness: Uint8Array; returnValue: abi.InputValue }> {
if (!circuit && !this.backend) throw new Error('Operation requires a circuit or a backend, but none was provided');

await this.init();
const witness = await generateWitness(circuit!, inputs);
const { return_value: returnValue } = abiDecode(circuit!.abi, witness);
return { witness: witnessMapToUint8Array(witness), returnValue };
}

// Initial inputs to your program
async generateFinalProof(inputs: abi.InputMap): Promise<ProofData> {
if (!this.backend) throw new Error('Operation requires a backend but none was provided');

const { witness } = await this.execute(inputs, this.backend.circuit);
return this.getBackend().generateFinalProof(witness);
}

async verifyFinalProof(proofData: ProofData): Promise<boolean> {
return this.getBackend().verifyFinalProof(proofData);
}
}

export { Noir, acvm, abi };
45 changes: 0 additions & 45 deletions tooling/noir_js/src/program.ts

This file was deleted.

16 changes: 9 additions & 7 deletions tooling/noir_js/test/node/cjs.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ const chai = require('chai');
const assert_lt_json = require('../noir_compiled_examples/assert_lt/target/assert_lt.json');
const { Noir } = require('@noir-lang/noir_js');

const assert_lt_program = assert_lt_json;

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

it('string input and number input are the same', async () => {
Expand All @@ -21,8 +23,8 @@ it('string input and number input are the same', async () => {
x: 2,
y: 3,
};
const solvedWitnessString = await new Noir(assert_lt_json).execute(inputsString);
const solvedWitnessNumber = await new Noir(assert_lt_json).execute(inputsNumber);
const solvedWitnessString = await new Noir(assert_lt_json).execute(inputsString, assert_lt_program);
const solvedWitnessNumber = await new Noir(assert_lt_json).execute(inputsNumber, assert_lt_program);
Comment on lines +26 to +27
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are broken. You're passing the circuit in twice.

chai.expect(solvedWitnessString).to.deep.equal(solvedWitnessNumber);
});

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

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

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

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

try {
await new Noir(assert_lt_json).execute(inputs);
await new Noir(assert_lt_json).execute(inputs, assert_lt_program);
chai.expect.fail('Expected generatedWitness to throw, due to x not being convertible to a uint64');
} catch (error) {
const knownError = error;
Expand Down
Loading