diff --git a/.github/workflows/test-noir-js.yml b/.github/workflows/test-noir-js.yml index 1dac0200027..907f10f49dd 100644 --- a/.github/workflows/test-noir-js.yml +++ b/.github/workflows/test-noir-js.yml @@ -47,6 +47,9 @@ jobs: - name: Build noirc_abi run: yarn workspace @noir-lang/noirc_abi build + + - name: Build barretenberg wrapper + run: yarn workspace @noir-lang/backend_barretenberg build - name: Run noir_js tests run: | diff --git a/Cargo.lock b/Cargo.lock index 627ae7c1743..3b28ca0d846 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3019,9 +3019,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ "base64", "bytes", @@ -3206,9 +3206,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", "ring", @@ -4470,24 +4470,11 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "winapi" @@ -4649,11 +4636,12 @@ dependencies = [ [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] diff --git a/acvm-repo/barretenberg_blackbox_solver/Cargo.toml b/acvm-repo/barretenberg_blackbox_solver/Cargo.toml index bbbd6649c47..c595def2902 100644 --- a/acvm-repo/barretenberg_blackbox_solver/Cargo.toml +++ b/acvm-repo/barretenberg_blackbox_solver/Cargo.toml @@ -42,7 +42,7 @@ wasmer = "3.3" pkg-config = "0.3" tar = "~0.4.15" flate2 = "~1.0.1" -reqwest = { version = "0.11.16", default-features = false, features = [ +reqwest = { version = "0.11.20", default-features = false, features = [ "rustls-tls", "blocking", ] } diff --git a/deny.toml b/deny.toml index f29dc95a9d7..5d5b0e34d0f 100644 --- a/deny.toml +++ b/deny.toml @@ -56,6 +56,8 @@ allow = [ "LicenseRef-webpki", # https://github.com/rustls/webpki/blob/main/LICENSE ISC Style "LicenseRef-rustls-webpki", + # bitmaps 2.1.0, generational-arena 0.2.9,im 15.1.0 + "MPL-2.0", ] # Allow 1 or more licenses on a per-crate basis, so that particular licenses @@ -95,6 +97,4 @@ unknown-registry = "warn" # Lint level for what to happen when a crate from a git repository that is not # in the allow list is encountered unknown-git = "deny" -allow-git = [ - "https://github.com/jfecher/chumsky" -] +allow-git = ["https://github.com/jfecher/chumsky"] diff --git a/package.json b/package.json index 07ca6b4689a..0cbcc7d9770 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "tooling/noir_js_types", "tooling/noirc_abi_wasm", "tooling/noir_js", + "tooling/noir_js_backend_barretenberg", "acvm-repo/acvm_js", "release-tests" ], diff --git a/tooling/backend_interface/Cargo.toml b/tooling/backend_interface/Cargo.toml index 986cb502048..587dcf41005 100644 --- a/tooling/backend_interface/Cargo.toml +++ b/tooling/backend_interface/Cargo.toml @@ -22,7 +22,7 @@ tempfile = "3.6.0" ## bb binary downloading tar = "~0.4.15" flate2 = "~1.0.1" -reqwest = { version = "0.11.16", default-features = false, features = [ +reqwest = { version = "0.11.20", default-features = false, features = [ "rustls-tls", "blocking", ] } diff --git a/tooling/noir_js/test/node/e2e.test.ts b/tooling/noir_js/test/node/e2e.test.ts index 1120f08c81b..fe0d26c7e3b 100644 --- a/tooling/noir_js/test/node/e2e.test.ts +++ b/tooling/noir_js/test/node/e2e.test.ts @@ -2,7 +2,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 '../../src/index.js'; import { Noir } from '../../src/program.js'; -import { BarretenbergBackend as Backend } from '../backend/barretenberg.js'; +import { BarretenbergBackend as Backend } from '@noir-lang/backend_barretenberg'; it('end-to-end proof creation and verification (outer)', async () => { // Noir.Js part @@ -15,7 +15,7 @@ it('end-to-end proof creation and verification (outer)', async () => { // bb.js part // // Proof creation - const prover = await Backend.initialize(assert_lt_json); + const prover = new Backend(assert_lt_json); const proof = await prover.generateFinalProof(serializedWitness); // Proof verification @@ -31,7 +31,7 @@ it('end-to-end proof creation and verification (outer) -- Program API', async () }; // Initialize backend - const backend = await Backend.initialize(assert_lt_json); + const backend = new Backend(assert_lt_json); // Initialize program const program = new Noir(assert_lt_json, backend); // Generate proof @@ -53,7 +53,7 @@ it('end-to-end proof creation and verification (inner)', async () => { // bb.js part // // Proof creation - const prover = await Backend.initialize(assert_lt_json); + const prover = new Backend(assert_lt_json); const proof = await prover.generateIntermediateProof(serializedWitness); // Proof verification @@ -82,12 +82,12 @@ it('[BUG] -- bb.js null function or function signature mismatch (different insta const serializedWitness = await generateWitness(assert_lt_json, inputs); // bb.js part - const prover = await Backend.initialize(assert_lt_json); + const prover = new Backend(assert_lt_json); const proof = await prover.generateFinalProof(serializedWitness); try { - const verifier = await Backend.initialize(assert_lt_json); + const verifier = new Backend(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.', @@ -117,7 +117,7 @@ it('[BUG] -- bb.js null function or function signature mismatch (outer-inner) ', // // Proof creation // - const prover = await Backend.initialize(assert_lt_json); + const prover = new Backend(assert_lt_json); // Create a proof using both proving systems, the majority of the time // one would only use outer proofs. const proofOuter = await prover.generateFinalProof(serializedWitness); diff --git a/tooling/noir_js_backend_barretenberg/.eslintignore b/tooling/noir_js_backend_barretenberg/.eslintignore new file mode 100644 index 00000000000..b512c09d476 --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/.eslintignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/tooling/noir_js_backend_barretenberg/.eslintrc.cjs b/tooling/noir_js_backend_barretenberg/.eslintrc.cjs new file mode 100644 index 00000000000..33335c2a877 --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/.eslintrc.cjs @@ -0,0 +1,3 @@ +module.exports = { + extends: ["../../.eslintrc.js"], +}; diff --git a/tooling/noir_js_backend_barretenberg/.gitignore b/tooling/noir_js_backend_barretenberg/.gitignore new file mode 100644 index 00000000000..689b3ca0701 --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/.gitignore @@ -0,0 +1,2 @@ +crs +lib diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json new file mode 100644 index 00000000000..1ab6c7bb86a --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -0,0 +1,43 @@ +{ + "name": "@noir-lang/backend_barretenberg", + "collaborators": [ + "The Noir Team " + ], + "version": "0.7.10", + "packageManager": "yarn@3.5.1", + "license": "(MIT OR Apache-2.0)", + "type": "module", + "source": "src/index.ts", + "main": "lib/cjs/index.cjs", + "module": "lib/esm/index.js", + "exports": { + "require": "./lib/cjs/index.cjs", + "default": "./lib/esm/index.js", + "types": "./lib/esm/index.d.ts" + }, + "types": "lib/esm/index.d.ts", + "scripts": { + "dev": "tsc --watch", + "build": "yarn clean && tsc && tsc -p ./tsconfig.cjs.json && mv ./lib/cjs/index.js ./lib/cjs/index.cjs && mv ./lib/cjs/serialize.js ./lib/cjs/serialize.cjs && mv ./lib/cjs/base64_decode.js ./lib/cjs/base64_decode.cjs", + "clean": "rm -rf ./lib", + "prettier": "prettier 'src/**/*.ts'", + "prettier:fix": "prettier --write 'src/**/*.ts' 'test/**/*.ts'", + "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" + }, + "dependencies": { + "@aztec/bb.js": "0.7.10", + "@noir-lang/types": "workspace:*", + "fflate": "^0.8.0" + }, + "peerDependencies": { + "@noir-lang/backend_barretenberg": "workspace:*" + }, + "devDependencies": { + "@types/node": "^20.6.2", + "@types/prettier": "^3", + "eslint": "^8.40.0", + "eslint-plugin-prettier": "^5.0.0", + "prettier": "3.0.3", + "typescript": "5.1.5" + } +} diff --git a/tooling/noir_js_backend_barretenberg/src/base64_decode.ts b/tooling/noir_js_backend_barretenberg/src/base64_decode.ts new file mode 100644 index 00000000000..d53aed187c7 --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/src/base64_decode.ts @@ -0,0 +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') { + // Node.js environment + 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.'); + } +} diff --git a/tooling/noir_js/test/backend/barretenberg.ts b/tooling/noir_js_backend_barretenberg/src/index.ts similarity index 74% rename from tooling/noir_js/test/backend/barretenberg.ts rename to tooling/noir_js_backend_barretenberg/src/index.ts index 557f2b3a74a..763de88cf93 100644 --- a/tooling/noir_js/test/backend/barretenberg.ts +++ b/tooling/noir_js_backend_barretenberg/src/index.ts @@ -1,49 +1,38 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -import { Barretenberg, Crs, RawBuffer } from '@aztec/bb.js'; -import { acirToUint8Array } from '../../src/index.js'; -import { Backend } from '@noir-lang/types'; +import { acirToUint8Array } from './serialize.js'; +import { Backend, CompiledCircuit } from '@noir-lang/types'; export class BarretenbergBackend implements Backend { // These type assertions are used so that we don't // have to initialize `api` and `acirComposer` in the constructor. // These are initialized asynchronously in the `init` function, // constructors cannot be asynchronous which is why we do this. - api = {} as Barretenberg; - acirComposer = {} as any; - acirUncompressedBytecode: Uint8Array; + private api: any; + private acirComposer: any; + private acirUncompressedBytecode: Uint8Array; + private numberOfThreads = 1; - private constructor(acirCircuit: { bytecode: string }) { + constructor(acirCircuit: CompiledCircuit, numberOfThreads = 1) { const acirBytecodeBase64 = acirCircuit.bytecode; + this.numberOfThreads = numberOfThreads; this.acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); } - static async initialize(acirCircuit: { bytecode: string }): Promise { - const backend = new BarretenbergBackend(acirCircuit); - await backend.init(); - return backend; - } - - private async init(): Promise { - const numThreads = 4; - - const { api, composer } = await this.initBarretenberg(numThreads, this.acirUncompressedBytecode); - - this.api = api; - this.acirComposer = composer; - } - - private async initBarretenberg(numThreads: number, acirUncompressedBytecode: Uint8Array) { - const api = await Barretenberg.new(numThreads); - - const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(acirUncompressedBytecode); - const crs = await Crs.new(subgroupSize + 1); - await api.commonInitSlabAllocator(subgroupSize); - await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); - - const acirComposer = await api.acirNewAcirComposer(subgroupSize); - return { api: api, composer: acirComposer }; + private async instantiate(): Promise { + if (!this.api) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); + const api = await Barretenberg.new(this.numberOfThreads); + + const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode); + const crs = await Crs.new(subgroupSize + 1); + await api.commonInitSlabAllocator(subgroupSize); + await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); + + this.acirComposer = await api.acirNewAcirComposer(subgroupSize); + this.api = api; + } } // Generate an outer proof. This is the proof for the circuit which will verify @@ -73,6 +62,7 @@ export class BarretenbergBackend implements Backend { } async generateProof(decompressedWitness: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { + await this.instantiate(); const proof = await this.api.acirCreateProof( this.acirComposer, this.acirUncompressedBytecode, @@ -100,6 +90,7 @@ export class BarretenbergBackend implements Backend { vkAsFields: string[]; vkHash: string; }> { + await this.instantiate(); const proofAsFields = await this.api.acirSerializeProofIntoFields(this.acirComposer, proof, numOfPublicInputs); // TODO: perhaps we should put this in the init function. Need to benchmark @@ -128,11 +119,15 @@ export class BarretenbergBackend implements Backend { } async verifyProof(proof: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { + await this.instantiate(); await this.api.acirInitVerificationKey(this.acirComposer); return await this.api.acirVerifyProof(this.acirComposer, proof, makeEasyToVerifyInCircuit); } async destroy(): Promise { + if (!this.api) { + return; + } await this.api.destroy(); } } diff --git a/tooling/noir_js_backend_barretenberg/src/serialize.ts b/tooling/noir_js_backend_barretenberg/src/serialize.ts new file mode 100644 index 00000000000..af07913f3c6 --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/src/serialize.ts @@ -0,0 +1,8 @@ +import { decompressSync as gunzip } from 'fflate'; +import { base64Decode } from './base64_decode.js'; + +// Converts bytecode from a base64 string to a Uint8Array +export function acirToUint8Array(base64EncodedBytecode): Uint8Array { + const compressedByteCode = base64Decode(base64EncodedBytecode); + return gunzip(compressedByteCode); +} diff --git a/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json b/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json new file mode 100644 index 00000000000..15d273af62e --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "./lib/cjs" + }, +} diff --git a/tooling/noir_js_backend_barretenberg/tsconfig.json b/tooling/noir_js_backend_barretenberg/tsconfig.json new file mode 100644 index 00000000000..bd76f50af0f --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "esnext", + "declaration": true, + "emitDeclarationOnly": false, + "module": "ESNext", + "moduleResolution": "NodeNext", + "outDir": "./lib/esm", + "esModuleInterop": true, + "resolveJsonModule": true, + "strict": true, + "noImplicitAny": false, + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/yarn.lock b/yarn.lock index 5998147de80..52c5915c8b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,6 +29,20 @@ __metadata: languageName: node linkType: hard +"@aztec/bb.js@npm:0.7.10": + version: 0.7.10 + resolution: "@aztec/bb.js@npm:0.7.10" + dependencies: + comlink: ^4.4.1 + commander: ^10.0.1 + debug: ^4.3.4 + tslib: ^2.4.0 + bin: + bb.js: dest/node/main.js + checksum: 0410278e6ec2a6ecdcbaa58633b181ec1d91e1c267c76e7e587fb69c8f2fd394e79f65bd96cfcdb2a2b20fe5abeb86ababd45bd6364ba07555fc0643bf0e4307 + languageName: node + linkType: hard + "@aztec/bb.js@npm:0.7.2": version: 0.7.2 resolution: "@aztec/bb.js@npm:0.7.2" @@ -440,6 +454,24 @@ __metadata: languageName: unknown linkType: soft +"@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg": + version: 0.0.0-use.local + resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" + dependencies: + "@aztec/bb.js": 0.7.10 + "@noir-lang/types": "workspace:*" + "@types/node": ^20.6.2 + "@types/prettier": ^3 + eslint: ^8.40.0 + eslint-plugin-prettier: ^5.0.0 + fflate: ^0.8.0 + prettier: 3.0.3 + typescript: 5.1.5 + peerDependencies: + "@noir-lang/backend_barretenberg": "workspace:*" + languageName: unknown + linkType: soft + "@noir-lang/noir_js@workspace:*, @noir-lang/noir_js@workspace:tooling/noir_js": version: 0.0.0-use.local resolution: "@noir-lang/noir_js@workspace:tooling/noir_js" @@ -7549,6 +7581,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:5.1.5": + version: 5.1.5 + resolution: "typescript@npm:5.1.5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 0eef8699e05ae767096924dbed633c340b4d36e953bb8ed87fb12e9dd9dcea5055ceac7182c614a556dbd346a8a82df799d330e1e286ae66e17c84e1710f6a6f + languageName: node + linkType: hard + "typescript@npm:^5.0.4, typescript@npm:^5.2.2": version: 5.2.2 resolution: "typescript@npm:5.2.2" @@ -7569,6 +7611,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@5.1.5#~builtin": + version: 5.1.5 + resolution: "typescript@patch:typescript@npm%3A5.1.5#~builtin::version=5.1.5&hash=5da071" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 12ff5d14888805f24479e54bc8a3f83647107a6345f6c29dffcd429fb345be55f584a37e262cca58a0105203e41d4cb4e31b1b9096c9abeca0e2ace8eb00935e + languageName: node + linkType: hard + "typescript@patch:typescript@^5.0.4#~builtin, typescript@patch:typescript@^5.2.2#~builtin": version: 5.2.2 resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin::version=5.2.2&hash=f3b441"