diff --git a/package.json b/package.json index 84d44a3838..07ca6b4689 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,9 @@ "workspaces": [ "compiler/wasm", "compiler/source-resolver", - "tooling/noirc_abi_wasm", "compiler/integration-tests", + "tooling/noir_js_types", + "tooling/noirc_abi_wasm", "tooling/noir_js", "acvm-repo/acvm_js", "release-tests" diff --git a/tooling/noir_js/package.json b/tooling/noir_js/package.json index a79c4437d6..de9e2831f0 100644 --- a/tooling/noir_js/package.json +++ b/tooling/noir_js/package.json @@ -10,6 +10,7 @@ "dependencies": { "@noir-lang/acvm_js": "workspace:*", "@noir-lang/noirc_abi": "workspace:*", + "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, "files": [ @@ -51,4 +52,4 @@ "tsc-multi": "^1.1.0", "typescript": "^5.2.2" } -} \ No newline at end of file +} diff --git a/tooling/noir_js/src/base64_decode.ts b/tooling/noir_js/src/base64_decode.ts index d53aed187c..fddbc2640c 100644 --- a/tooling/noir_js/src/base64_decode.ts +++ b/tooling/noir_js/src/base64_decode.ts @@ -1,13 +1,13 @@ // Since this is a simple function, we can use feature detection to // see if we are in the nodeJs environment or the browser environment. export function base64Decode(input: string): Uint8Array { - if (typeof Buffer !== 'undefined') { + if (typeof Buffer !== "undefined") { // Node.js environment - return Buffer.from(input, 'base64'); - } else if (typeof atob === 'function') { + return Buffer.from(input, "base64"); + } else if (typeof atob === "function") { // Browser environment return Uint8Array.from(atob(input), (c) => c.charCodeAt(0)); } else { - throw new Error('No implementation found for base64 decoding.'); + throw new Error("No implementation found for base64 decoding."); } } diff --git a/tooling/noir_js/src/index.ts b/tooling/noir_js/src/index.ts index 3672db7663..2d3801bf2c 100644 --- a/tooling/noir_js/src/index.ts +++ b/tooling/noir_js/src/index.ts @@ -1,7 +1,9 @@ -import * as acvm from '@noir-lang/acvm_js'; -import * as abi from '@noir-lang/noirc_abi'; +import * as acvm from "@noir-lang/acvm_js"; +import * as abi from "@noir-lang/noirc_abi"; export { acvm, abi }; -export { generateWitness } from './witness_generation.js'; -export { acirToUint8Array, witnessMapToUint8Array } from './serialize.js'; +export { generateWitness } from "./witness_generation.js"; +export { acirToUint8Array, witnessMapToUint8Array } from "./serialize.js"; + +export { Noir } from "./program.js"; diff --git a/tooling/noir_js/src/program.ts b/tooling/noir_js/src/program.ts index 851f79cb35..1aea14644c 100644 --- a/tooling/noir_js/src/program.ts +++ b/tooling/noir_js/src/program.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Backend } from './backend/backend_interface.js'; -import { generateWitness } from './witness_generation.js'; +import { Backend, CompiledCircuit } from "@noir-lang/types"; +import { generateWitness } from "./witness_generation.js"; export class Noir { constructor( - private circuit: { bytecode: string; abi: any }, + private circuit: CompiledCircuit, private backend: Backend, ) {} diff --git a/tooling/noir_js/src/serialize.ts b/tooling/noir_js/src/serialize.ts index df01769c06..0193f46509 100644 --- a/tooling/noir_js/src/serialize.ts +++ b/tooling/noir_js/src/serialize.ts @@ -1,6 +1,6 @@ -import { WitnessMap, compressWitness } from '@noir-lang/acvm_js'; -import { decompressSync as gunzip } from 'fflate'; -import { base64Decode } from './base64_decode.js'; +import { WitnessMap, compressWitness } from "@noir-lang/acvm_js"; +import { decompressSync as gunzip } from "fflate"; +import { base64Decode } from "./base64_decode.js"; // After solving the witness, to pass it a backend, we need to serialize it to a Uint8Array export function witnessMapToUint8Array(solvedWitness: WitnessMap): Uint8Array { diff --git a/tooling/noir_js/src/witness_generation.ts b/tooling/noir_js/src/witness_generation.ts index c4f129dd2a..f3b31e0eb7 100644 --- a/tooling/noir_js/src/witness_generation.ts +++ b/tooling/noir_js/src/witness_generation.ts @@ -1,11 +1,12 @@ -import { abiEncode } from '@noir-lang/noirc_abi'; -import { base64Decode } from './base64_decode.js'; -import { executeCircuit } from '@noir-lang/acvm_js'; -import { witnessMapToUint8Array } from './serialize.js'; +import { abiEncode } from "@noir-lang/noirc_abi"; +import { base64Decode } from "./base64_decode.js"; +import { executeCircuit } from "@noir-lang/acvm_js"; +import { witnessMapToUint8Array } from "./serialize.js"; +import { CompiledCircuit } from "@noir-lang/types"; // Generates the witnesses needed to feed into the chosen proving system export async function generateWitness( - compiledProgram: { bytecode: string; abi: unknown }, + compiledProgram: CompiledCircuit, inputs: unknown, ): Promise { // Throws on ABI encoding error @@ -14,9 +15,13 @@ export async function generateWitness( // Execute the circuit to generate the rest of the witnesses and serialize // them into a Uint8Array. try { - const solvedWitness = await executeCircuit(base64Decode(compiledProgram.bytecode), witnessMap, () => { - throw Error('unexpected oracle during execution'); - }); + const solvedWitness = await executeCircuit( + base64Decode(compiledProgram.bytecode), + witnessMap, + () => { + throw Error("unexpected oracle during execution"); + }, + ); return witnessMapToUint8Array(solvedWitness); } catch (err) { throw new Error(`Circuit execution failed: ${err}`); diff --git a/tooling/noir_js/test/backend/barretenberg.ts b/tooling/noir_js/test/backend/barretenberg.ts index 55b7bbf2b7..557f2b3a74 100644 --- a/tooling/noir_js/test/backend/barretenberg.ts +++ b/tooling/noir_js/test/backend/barretenberg.ts @@ -3,7 +3,7 @@ // @ts-ignore import { Barretenberg, Crs, RawBuffer } from '@aztec/bb.js'; import { acirToUint8Array } from '../../src/index.js'; -import { Backend } from '../../src/backend/backend_interface.js'; +import { Backend } from '@noir-lang/types'; export class BarretenbergBackend implements Backend { // These type assertions are used so that we don't diff --git a/tooling/noir_js/test/node/e2e.test.ts b/tooling/noir_js/test/node/e2e.test.ts index 1120f08c81..3d13119e42 100644 --- a/tooling/noir_js/test/node/e2e.test.ts +++ b/tooling/noir_js/test/node/e2e.test.ts @@ -1,14 +1,14 @@ -import { expect } from 'chai'; -import assert_lt_json from '../noir_compiled_examples/assert_lt/target/assert_lt.json' assert { type: 'json' }; -import { generateWitness } from '../../src/index.js'; -import { Noir } from '../../src/program.js'; -import { BarretenbergBackend as Backend } from '../backend/barretenberg.js'; +import { expect } from "chai"; +import assert_lt_json from "../noir_compiled_examples/assert_lt/target/assert_lt.json" assert { type: "json" }; +import { generateWitness } from "../../src/index.js"; +import { Noir } from "../../src/program.js"; +import { BarretenbergBackend as Backend } from "../backend/barretenberg.js"; -it('end-to-end proof creation and verification (outer)', async () => { +it("end-to-end proof creation and verification (outer)", async () => { // Noir.Js part const inputs = { - x: '2', - y: '3', + x: "2", + y: "3", }; const serializedWitness = await generateWitness(assert_lt_json, inputs); @@ -23,11 +23,11 @@ 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 () => { +it("end-to-end proof creation and verification (outer) -- Program API", async () => { // Noir.Js part const inputs = { - x: '2', - y: '3', + x: "2", + y: "3", }; // Initialize backend @@ -42,11 +42,11 @@ it('end-to-end proof creation and verification (outer) -- Program API', async () expect(isValid).to.be.true; }); -it('end-to-end proof creation and verification (inner)', async () => { +it("end-to-end proof creation and verification (inner)", async () => { // Noir.Js part const inputs = { - x: '2', - y: '3', + x: "2", + y: "3", }; const serializedWitness = await generateWitness(assert_lt_json, inputs); @@ -73,11 +73,11 @@ it('end-to-end proof creation and verification (inner)', async () => { // a prover and verifier class to more accurately reflect what happens in production. // // If its not fixable, we can leave it in as documentation of this behavior. -it('[BUG] -- bb.js null function or function signature mismatch (different instance) ', async () => { +it("[BUG] -- bb.js null function or function signature mismatch (different instance) ", async () => { // Noir.Js part const inputs = { - x: '2', - y: '3', + x: "2", + y: "3", }; const serializedWitness = await generateWitness(assert_lt_json, inputs); @@ -90,11 +90,13 @@ it('[BUG] -- bb.js null function or function signature mismatch (different insta const verifier = await Backend.initialize(assert_lt_json); await verifier.verifyFinalProof(proof); expect.fail( - 'bb.js currently returns a bug when we try to verify a proof with a different Barretenberg instance that created it.', + "bb.js currently returns a bug when we try to verify a proof with a different Barretenberg instance that created it.", ); } catch (error) { const knownError = error as Error; - expect(knownError.message).to.contain('null function or function signature mismatch'); + expect(knownError.message).to.contain( + "null function or function signature mismatch", + ); } }); @@ -105,11 +107,11 @@ it('[BUG] -- bb.js null function or function signature mismatch (different insta // If we only create one type of proof, then this works as expected. // // If we do not create an inner proof, then this will work as expected. -it('[BUG] -- bb.js null function or function signature mismatch (outer-inner) ', async () => { +it("[BUG] -- bb.js null function or function signature mismatch (outer-inner) ", async () => { // Noir.Js part const inputs = { - x: '2', - y: '3', + x: "2", + y: "3", }; const serializedWitness = await generateWitness(assert_lt_json, inputs); @@ -131,9 +133,13 @@ it('[BUG] -- bb.js null function or function signature mismatch (outer-inner) ', // We can also try verifying an inner proof and it will fail. // const isValidInner = await prover.verifyInnerProof(_proofInner); // expect(isValidInner).to.be.true; - expect.fail('bb.js currently returns a bug when we try to verify an inner and outer proof with the same backend'); + expect.fail( + "bb.js currently returns a bug when we try to verify an inner and outer proof with the same backend", + ); } catch (error) { const knownError = error as Error; - expect(knownError.message).to.contain('null function or function signature mismatch'); + expect(knownError.message).to.contain( + "null function or function signature mismatch", + ); } }); diff --git a/tooling/noir_js/test/node/smoke.test.ts b/tooling/noir_js/test/node/smoke.test.ts index 4b0291c0f4..c78b297f4b 100644 --- a/tooling/noir_js/test/node/smoke.test.ts +++ b/tooling/noir_js/test/node/smoke.test.ts @@ -1,73 +1,89 @@ -import { expect } from 'chai'; -import assert_lt_json from '../noir_compiled_examples/assert_lt/target/assert_lt.json' assert { type: 'json' }; -import { generateWitness } from '../../src/index.js'; +import { expect } from "chai"; +import assert_lt_json from "../noir_compiled_examples/assert_lt/target/assert_lt.json" assert { type: "json" }; +import { generateWitness } from "../../src/index.js"; -it('generates witnesses successfully', async () => { +it("generates witnesses successfully", async () => { const inputs = { - x: '2', - y: '3', + x: "2", + y: "3", }; expect(() => generateWitness(assert_lt_json, inputs)).to.not.throw; }); -it('string input and number input are the same', async () => { +it("string input and number input are the same", async () => { const inputsString = { - x: '2', - y: '3', + x: "2", + y: "3", }; const inputsNumber = { x: 2, y: 3, }; - const solvedWitnessString = await generateWitness(assert_lt_json, inputsString); - const solvedWitnessNumber = await generateWitness(assert_lt_json, inputsNumber); + const solvedWitnessString = await generateWitness( + assert_lt_json, + inputsString, + ); + const solvedWitnessNumber = await generateWitness( + assert_lt_json, + inputsNumber, + ); expect(solvedWitnessString).to.deep.equal(solvedWitnessNumber); }); -it('string input and number input are the same', async () => { +it("string input and number input are the same", async () => { const inputsString = { - x: '2', - y: '3', + x: "2", + y: "3", }; const inputsNumber = { x: 2, y: 3, }; - const solvedWitnessString = await generateWitness(assert_lt_json, inputsString); - const solvedWitnessNumber = await generateWitness(assert_lt_json, inputsNumber); + const solvedWitnessString = await generateWitness( + assert_lt_json, + inputsString, + ); + const solvedWitnessNumber = await generateWitness( + assert_lt_json, + inputsNumber, + ); expect(solvedWitnessString).to.deep.equal(solvedWitnessNumber); }); -it('0x prefixed string input for inputs will throw', async () => { +it("0x prefixed string input for inputs will throw", async () => { const inputsHexPrefix = { - x: '0x2', - y: '0x3', + x: "0x2", + y: "0x3", }; try { await generateWitness(assert_lt_json, inputsHexPrefix); - expect.fail('Expected generatedWitness to throw, due to inputs being prefixed with 0x. Currently not supported'); + 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 // to support 0x prefixed inputs. } }); -describe('input validation', () => { - it('x should be a uint64 not a string', async () => { +describe("input validation", () => { + it("x should be a uint64 not a string", async () => { const inputs = { - x: 'foo', - y: '3', + x: "foo", + y: "3", }; try { await generateWitness(assert_lt_json, inputs); - expect.fail('Expected generatedWitness to throw, due to x not being convertible to a uint64'); + expect.fail( + "Expected generatedWitness to throw, due to x not being convertible to a uint64", + ); } catch (error) { const knownError = error as Error; expect(knownError.message).to.equal( - 'Expected witness values to be integers, provided value causes `invalid digit found in string` error', + "Expected witness values to be integers, provided value causes `invalid digit found in string` error", ); } }); diff --git a/tooling/noir_js_types/.eslintignore b/tooling/noir_js_types/.eslintignore new file mode 100644 index 0000000000..3c3629e647 --- /dev/null +++ b/tooling/noir_js_types/.eslintignore @@ -0,0 +1 @@ +node_modules diff --git a/tooling/noir_js_types/.eslintrc.cjs b/tooling/noir_js_types/.eslintrc.cjs new file mode 100644 index 0000000000..5a2cc7f1ec --- /dev/null +++ b/tooling/noir_js_types/.eslintrc.cjs @@ -0,0 +1,3 @@ +module.exports = { + extends: ['../../.eslintrc.js'], +}; diff --git a/tooling/noir_js_types/.gitignore b/tooling/noir_js_types/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tooling/noir_js/.prettierrc b/tooling/noir_js_types/.prettierrc similarity index 100% rename from tooling/noir_js/.prettierrc rename to tooling/noir_js_types/.prettierrc diff --git a/tooling/noir_js/src/backend/backend_interface.ts b/tooling/noir_js_types/lib/types.ts similarity index 88% rename from tooling/noir_js/src/backend/backend_interface.ts rename to tooling/noir_js_types/lib/types.ts index c3b8e30cdb..2b6ea8ad4b 100644 --- a/tooling/noir_js/src/backend/backend_interface.ts +++ b/tooling/noir_js_types/lib/types.ts @@ -11,3 +11,8 @@ export interface Backend { verifyIntermediateProof(proof: Uint8Array): Promise; } + +export type CompiledCircuit = { + bytecode: string; + abi: object; +}; diff --git a/tooling/noir_js_types/package.json b/tooling/noir_js_types/package.json new file mode 100644 index 0000000000..188b052273 --- /dev/null +++ b/tooling/noir_js_types/package.json @@ -0,0 +1,14 @@ +{ + "name": "@noir-lang/types", + "collaborators": [ + "The Noir Team " + ], + "version": "0.14.1", + "packageManager": "yarn@3.5.1", + "license": "(MIT OR Apache-2.0)", + "files": [ + "lib", + "package.json" + ], + "types": "lib/types.ts" +} diff --git a/yarn.lock b/yarn.lock index 2ec26866e9..5998147de8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -447,6 +447,7 @@ __metadata: "@aztec/bb.js": 0.7.2 "@noir-lang/acvm_js": "workspace:*" "@noir-lang/noirc_abi": "workspace:*" + "@noir-lang/types": "workspace:*" "@types/chai": ^4 "@types/mocha": ^10.0.1 "@types/node": ^20.6.2 @@ -519,6 +520,12 @@ __metadata: languageName: unknown linkType: soft +"@noir-lang/types@workspace:*, @noir-lang/types@workspace:tooling/noir_js_types": + version: 0.0.0-use.local + resolution: "@noir-lang/types@workspace:tooling/noir_js_types" + languageName: unknown + linkType: soft + "@npmcli/fs@npm:^3.1.0": version: 3.1.0 resolution: "@npmcli/fs@npm:3.1.0"