From aa419cb3467cc4c6742f1d6ffa31e32d1d13d4a1 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 22 Oct 2024 19:47:27 +0100 Subject: [PATCH 01/11] chore: remove noir_js_backend_barretenberg --- barretenberg/ts/package.json | 1 + barretenberg/ts/src/barretenberg/backend.ts | 103 +++++++++++-- barretenberg/ts/src/barretenberg/verifier.ts | 7 +- barretenberg/ts/src/index.ts | 2 +- barretenberg/ts/src/proof/index.ts | 58 ++++++- barretenberg/ts/yarn.lock | 8 + .../scripts/backend-barretenberg-build.sh | 4 - .../scripts/backend-barretenberg-test.sh | 4 - .../.github/workflows/test-js-packages.yml | 11 +- .../compiler/integration-tests/package.json | 64 ++++---- .../test/browser/compile_prove_verify.test.ts | 4 +- .../test/browser/recursion.test.ts | 4 +- .../onchain_recursive_verification.test.ts | 6 +- .../test/node/prove_and_verify.test.ts | 21 +-- .../test/node/smart_contract_verifier.test.ts | 4 +- .../docs/docs/how_to/how-to-recursion.md | 12 +- noir/noir-repo/package.json | 1 - noir/noir-repo/scripts/bump-bb.sh | 4 +- .../.eslintignore | 1 - .../.eslintrc.cjs | 3 - .../noir_js_backend_barretenberg/.gitignore | 2 - .../.mocharc.json | 11 -- .../noir_js_backend_barretenberg/fixup.sh | 18 --- .../noir_js_backend_barretenberg/package.json | 57 ------- .../src/backend.ts | 144 ------------------ .../src/base64_decode.ts | 13 -- .../noir_js_backend_barretenberg/src/index.ts | 6 - .../src/public_inputs.ts | 68 --------- .../src/serialize.ts | 8 - .../src/verifier.ts | 67 -------- .../tsconfig.cjs.json | 8 - .../tsconfig.json | 21 --- noir/noir-repo/yarn.lock | 28 +--- 33 files changed, 228 insertions(+), 545 deletions(-) delete mode 100755 noir/noir-repo/.github/scripts/backend-barretenberg-build.sh delete mode 100755 noir/noir-repo/.github/scripts/backend-barretenberg-test.sh delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/.eslintignore delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/.eslintrc.cjs delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/.gitignore delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/.mocharc.json delete mode 100755 noir/noir-repo/tooling/noir_js_backend_barretenberg/fixup.sh delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/src/base64_decode.ts delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/src/index.ts delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/src/public_inputs.ts delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/src/serialize.ts delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/src/verifier.ts delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json delete mode 100644 noir/noir-repo/tooling/noir_js_backend_barretenberg/tsconfig.json diff --git a/barretenberg/ts/package.json b/barretenberg/ts/package.json index 10fdc17b85d..c619210c457 100644 --- a/barretenberg/ts/package.json +++ b/barretenberg/ts/package.json @@ -55,6 +55,7 @@ "comlink": "^4.4.1", "commander": "^10.0.1", "debug": "^4.3.4", + "fflate": "^0.8.0", "tslib": "^2.4.0" }, "devDependencies": { diff --git a/barretenberg/ts/src/barretenberg/backend.ts b/barretenberg/ts/src/barretenberg/backend.ts index 243ee8dc1b3..a79fe6ceae7 100644 --- a/barretenberg/ts/src/barretenberg/backend.ts +++ b/barretenberg/ts/src/barretenberg/backend.ts @@ -1,5 +1,13 @@ import { BackendOptions, Barretenberg } from './index.js'; import { RawBuffer } from '../types/raw_buffer.js'; +import { decompressSync as gunzip } from 'fflate'; +import { + deflattenFields, + flattenFieldsAsArray, + ProofData, + reconstructHonkProof, + reconstructUltraPlonkProof, +} from '../proof/index.js'; export class UltraPlonkBackend { // These type assertions are used so that we don't @@ -11,7 +19,11 @@ export class UltraPlonkBackend { // eslint-disable-next-line @typescript-eslint/no-explicit-any protected acirComposer: any; - constructor(protected acirUncompressedBytecode: Uint8Array, protected options: BackendOptions = { threads: 1 }) {} + protected acirUncompressedBytecode: Uint8Array; + + constructor(acirBytecode: string, protected options: BackendOptions = { threads: 1 }) { + this.acirUncompressedBytecode = acirToUint8Array(acirBytecode); + } /** @ignore */ async instantiate(): Promise { @@ -30,9 +42,25 @@ export class UltraPlonkBackend { } /** @description Generates a proof */ - async generateProof(uncompressedWitness: Uint8Array): Promise { + async generateProof(compressedWitness: Uint8Array): Promise { await this.instantiate(); - return this.api.acirCreateProof(this.acirComposer, this.acirUncompressedBytecode, uncompressedWitness); + const proofWithPublicInputs = await this.api.acirCreateProof( + this.acirComposer, + this.acirUncompressedBytecode, + gunzip(compressedWitness), + ); + + // This is the number of bytes in a UltraPlonk proof + // minus the public inputs. + const numBytesInProofWithoutPublicInputs = 2144; + + const splitIndex = proofWithPublicInputs.length - numBytesInProofWithoutPublicInputs; + + const publicInputsConcatenated = proofWithPublicInputs.slice(0, splitIndex); + const proof = proofWithPublicInputs.slice(splitIndex); + const publicInputs = deflattenFields(publicInputsConcatenated); + + return { proof, publicInputs }; } /** @@ -52,7 +80,7 @@ export class UltraPlonkBackend { * ``` */ async generateRecursiveProofArtifacts( - proof: Uint8Array, + proofData: ProofData, numOfPublicInputs = 0, ): Promise<{ proofAsFields: string[]; @@ -60,6 +88,8 @@ export class UltraPlonkBackend { vkHash: string; }> { await this.instantiate(); + + const proof = reconstructUltraPlonkProof(proofData); const proofAsFields = ( await this.api.acirSerializeProofIntoFields(this.acirComposer, proof, numOfPublicInputs) ).slice(numOfPublicInputs); @@ -79,9 +109,10 @@ export class UltraPlonkBackend { } /** @description Verifies a proof */ - async verifyProof(proof: Uint8Array): Promise { + async verifyProof(proofData: ProofData): Promise { await this.instantiate(); await this.api.acirInitVerificationKey(this.acirComposer); + const proof = reconstructUltraPlonkProof(proofData); return await this.api.acirVerifyProof(this.acirComposer, proof); } @@ -99,6 +130,12 @@ export class UltraPlonkBackend { } } +// Buffers are prepended with their size. The size takes 4 bytes. +const serializedBufferSize = 4; +const fieldByteSize = 32; +const publicInputOffset = 3; +const publicInputsOffsetBytes = publicInputOffset * fieldByteSize; + export class UltraHonkBackend { // These type assertions are used so that we don't // have to initialize `api` in the constructor. @@ -106,9 +143,11 @@ export class UltraHonkBackend { // constructors cannot be asynchronous which is why we do this. protected api!: Barretenberg; + protected acirUncompressedBytecode: Uint8Array; - constructor(protected acirUncompressedBytecode: Uint8Array, protected options: BackendOptions = { threads: 1 }) {} - + constructor(acirBytecode: string, protected options: BackendOptions = { threads: 1 }) { + this.acirUncompressedBytecode = acirToUint8Array(acirBytecode); + } /** @ignore */ async instantiate(): Promise { if (!this.api) { @@ -122,13 +161,36 @@ export class UltraHonkBackend { } } - async generateProof(uncompressedWitness: Uint8Array): Promise { + async generateProof(uncompressedWitness: Uint8Array): Promise { await this.instantiate(); - return this.api.acirProveUltraHonk(this.acirUncompressedBytecode, uncompressedWitness); + const proofWithPublicInputs = await this.api.acirProveUltraHonk(this.acirUncompressedBytecode, uncompressedWitness); + + const proofAsStrings = deflattenFields(proofWithPublicInputs.slice(4)); + + const numPublicInputs = Number(proofAsStrings[1]); + + // Account for the serialized buffer size at start + const publicInputsOffset = publicInputsOffsetBytes + serializedBufferSize; + // Get the part before and after the public inputs + const proofStart = proofWithPublicInputs.slice(0, publicInputsOffset); + const publicInputsSplitIndex = numPublicInputs * fieldByteSize; + const proofEnd = proofWithPublicInputs.slice(publicInputsOffset + publicInputsSplitIndex); + // Construct the proof without the public inputs + const proof = new Uint8Array([...proofStart, ...proofEnd]); + + // Fetch the number of public inputs out of the proof string + const publicInputsConcatenated = proofWithPublicInputs.slice( + publicInputsOffset, + publicInputsOffset + publicInputsSplitIndex, + ); + const publicInputs = deflattenFields(publicInputsConcatenated); + + return { proof, publicInputs }; } - async verifyProof(proof: Uint8Array): Promise { + async verifyProof(proofData: ProofData): Promise { await this.instantiate(); + const proof = reconstructHonkProof(flattenFieldsAsArray(proofData.publicInputs), proofData.proof); const vkBuf = await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode); return await this.api.acirVerifyUltraHonk(proof, new RawBuffer(vkBuf)); @@ -178,3 +240,24 @@ export class UltraHonkBackend { await this.api.destroy(); } } + +// Converts bytecode from a base64 string to a Uint8Array +function acirToUint8Array(base64EncodedBytecode: string): Uint8Array { + const compressedByteCode = base64Decode(base64EncodedBytecode); + return gunzip(compressedByteCode); +} + +// Since this is a simple function, we can use feature detection to +// see if we are in the nodeJs environment or the browser environment. +function base64Decode(input: string): Uint8Array { + if (typeof Buffer !== 'undefined') { + // Node.js environment + const b = Buffer.from(input, 'base64'); + return new Uint8Array(b.buffer, b.byteOffset, b.byteLength); + } 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/barretenberg/ts/src/barretenberg/verifier.ts b/barretenberg/ts/src/barretenberg/verifier.ts index c6bffc5109b..0c76c2885da 100644 --- a/barretenberg/ts/src/barretenberg/verifier.ts +++ b/barretenberg/ts/src/barretenberg/verifier.ts @@ -1,5 +1,6 @@ import { BackendOptions, Barretenberg } from './index.js'; import { RawBuffer } from '../types/raw_buffer.js'; +import { flattenFieldsAsArray, ProofData, reconstructHonkProof, reconstructUltraPlonkProof } from '../proof/index.js'; // TODO: once UP is removed we can just roll this into the bas `Barretenberg` class. @@ -27,19 +28,21 @@ export class BarretenbergVerifier { } /** @description Verifies a proof */ - async verifyUltraplonkProof(proof: Uint8Array, verificationKey: Uint8Array): Promise { + async verifyUltraplonkProof(proofData: ProofData, verificationKey: Uint8Array): Promise { await this.instantiate(); // The verifier can be used for a variety of ACIR programs so we should not assume that it // is preloaded with the correct verification key. await this.api.acirLoadVerificationKey(this.acirComposer, new RawBuffer(verificationKey)); + const proof = reconstructUltraPlonkProof(proofData); return await this.api.acirVerifyProof(this.acirComposer, proof); } /** @description Verifies a proof */ - async verifyUltrahonkProof(proof: Uint8Array, verificationKey: Uint8Array): Promise { + async verifyUltrahonkProof(proofData: ProofData, verificationKey: Uint8Array): Promise { await this.instantiate(); + const proof = reconstructHonkProof(flattenFieldsAsArray(proofData.publicInputs), proofData.proof); return await this.api.acirVerifyUltraHonk(proof, new RawBuffer(verificationKey)); } diff --git a/barretenberg/ts/src/index.ts b/barretenberg/ts/src/index.ts index d67bad794af..dc3cee34937 100644 --- a/barretenberg/ts/src/index.ts +++ b/barretenberg/ts/src/index.ts @@ -8,4 +8,4 @@ export { UltraHonkBackend, } from './barretenberg/index.js'; export { RawBuffer, Fr } from './types/index.js'; -export { splitHonkProof, reconstructHonkProof } from './proof/index.js'; +export { splitHonkProof, reconstructHonkProof, ProofData } from './proof/index.js'; diff --git a/barretenberg/ts/src/proof/index.ts b/barretenberg/ts/src/proof/index.ts index 4219f167fc9..9d099c8a932 100644 --- a/barretenberg/ts/src/proof/index.ts +++ b/barretenberg/ts/src/proof/index.ts @@ -1,3 +1,14 @@ +/** + * @description + * The representation of a proof + * */ +export type ProofData = { + /** @description Public inputs of a proof */ + publicInputs: string[]; + /** @description An byte array representing the proof */ + proof: Uint8Array; +}; + // Buffers are prepended with their size. The size takes 4 bytes. const serializedBufferSize = 4; const fieldByteSize = 32; @@ -37,7 +48,17 @@ export function reconstructHonkProof(publicInputs: Uint8Array, proof: Uint8Array return proofWithPublicInputs; } -function deflattenFields(flattenedFields: Uint8Array): string[] { +export function reconstructUltraPlonkProof(proofData: ProofData): Uint8Array { + // Flatten publicInputs + const publicInputsConcatenated = flattenFieldsAsArray(proofData.publicInputs); + + // Concatenate publicInputs and proof + const proofWithPublicInputs = Uint8Array.from([...publicInputsConcatenated, ...proofData.proof]); + + return proofWithPublicInputs; +} + +export function deflattenFields(flattenedFields: Uint8Array): string[] { const publicInputSize = 32; const chunkedFlattenedPublicInputs: Uint8Array[] = []; @@ -49,6 +70,24 @@ function deflattenFields(flattenedFields: Uint8Array): string[] { return chunkedFlattenedPublicInputs.map(uint8ArrayToHex); } +export function flattenFieldsAsArray(fields: string[]): Uint8Array { + const flattenedPublicInputs = fields.map(hexToUint8Array); + return flattenUint8Arrays(flattenedPublicInputs); +} + +function flattenUint8Arrays(arrays: Uint8Array[]): Uint8Array { + const totalLength = arrays.reduce((acc, val) => acc + val.length, 0); + const result = new Uint8Array(totalLength); + + let offset = 0; + for (const arr of arrays) { + result.set(arr, offset); + offset += arr.length; + } + + return result; +} + function uint8ArrayToHex(buffer: Uint8Array): string { const hex: string[] = []; @@ -62,3 +101,20 @@ function uint8ArrayToHex(buffer: Uint8Array): string { return '0x' + hex.join(''); } + +function hexToUint8Array(hex: string): Uint8Array { + const sanitisedHex = BigInt(hex).toString(16).padStart(64, '0'); + + const len = sanitisedHex.length / 2; + const u8 = new Uint8Array(len); + + let i = 0; + let j = 0; + while (i < len) { + u8[i] = parseInt(sanitisedHex.slice(j, j + 2), 16); + i += 1; + j += 2; + } + + return u8; +} diff --git a/barretenberg/ts/yarn.lock b/barretenberg/ts/yarn.lock index 2f0f56bf747..27108df1eec 100644 --- a/barretenberg/ts/yarn.lock +++ b/barretenberg/ts/yarn.lock @@ -41,6 +41,7 @@ __metadata: debug: ^4.3.4 eslint: ^8.35.0 eslint-config-prettier: ^8.8.0 + fflate: ^0.8.0 html-webpack-plugin: ^5.5.1 idb-keyval: ^6.2.1 jest: ^29.5.0 @@ -3271,6 +3272,13 @@ __metadata: languageName: node linkType: hard +"fflate@npm:^0.8.0": + version: 0.8.2 + resolution: "fflate@npm:0.8.2" + checksum: 29470337b85d3831826758e78f370e15cda3169c5cd4477c9b5eea2402261a74b2975bae816afabe1c15d21d98591e0d30a574f7103aa117bff60756fa3035d4 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" diff --git a/noir/noir-repo/.github/scripts/backend-barretenberg-build.sh b/noir/noir-repo/.github/scripts/backend-barretenberg-build.sh deleted file mode 100755 index d90995397d8..00000000000 --- a/noir/noir-repo/.github/scripts/backend-barretenberg-build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -eu - -yarn workspace @noir-lang/backend_barretenberg build diff --git a/noir/noir-repo/.github/scripts/backend-barretenberg-test.sh b/noir/noir-repo/.github/scripts/backend-barretenberg-test.sh deleted file mode 100755 index 1bd6f8e410d..00000000000 --- a/noir/noir-repo/.github/scripts/backend-barretenberg-test.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -eu - -yarn workspace @noir-lang/backend_barretenberg test diff --git a/noir/noir-repo/.github/workflows/test-js-packages.yml b/noir/noir-repo/.github/workflows/test-js-packages.yml index ff894f061ee..0aa971a21f0 100644 --- a/noir/noir-repo/.github/workflows/test-js-packages.yml +++ b/noir/noir-repo/.github/workflows/test-js-packages.yml @@ -73,7 +73,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: noirc_abi_wasm - path: | + path: | ./tooling/noirc_abi_wasm/nodejs ./tooling/noirc_abi_wasm/web retention-days: 10 @@ -263,9 +263,6 @@ jobs: - name: Build noir_js_types run: yarn workspace @noir-lang/types build - - name: Build barretenberg wrapper - run: yarn workspace @noir-lang/backend_barretenberg build - - name: Run noir_js tests run: | yarn workspace @noir-lang/noir_js build @@ -416,7 +413,7 @@ jobs: - name: Setup `integration-tests` run: | # Note the lack of spaces between package names. - PACKAGES_TO_BUILD="@noir-lang/types,@noir-lang/backend_barretenberg,@noir-lang/noir_js" + PACKAGES_TO_BUILD="@noir-lang/types,@noir-lang/noir_js" yarn workspaces foreach -vtp --from "{$PACKAGES_TO_BUILD}" run build - name: Run `integration-tests` @@ -461,7 +458,7 @@ jobs: - name: Setup `integration-tests` run: | # Note the lack of spaces between package names. - PACKAGES_TO_BUILD="@noir-lang/types,@noir-lang/backend_barretenberg,@noir-lang/noir_js" + PACKAGES_TO_BUILD="@noir-lang/types,@noir-lang/noir_js" yarn workspaces foreach -vtp --from "{$PACKAGES_TO_BUILD}" run build - name: Run `integration-tests` @@ -565,7 +562,7 @@ jobs: runs-on: ubuntu-latest # We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping. if: ${{ always() }} - needs: + needs: - test-acvm_js-node - test-acvm_js-browser - test-noirc-abi diff --git a/noir/noir-repo/compiler/integration-tests/package.json b/noir/noir-repo/compiler/integration-tests/package.json index 64a638539d5..e33179f31e7 100644 --- a/noir/noir-repo/compiler/integration-tests/package.json +++ b/noir/noir-repo/compiler/integration-tests/package.json @@ -1,34 +1,34 @@ { - "name": "integration-tests", - "license": "(MIT OR Apache-2.0)", - "main": "index.js", - "private": true, - "scripts": { - "build": "echo Integration Test build step", - "test": "yarn test:browser && yarn test:node", - "test:node": "bash ./scripts/setup.sh && hardhat test test/node/prove_and_verify.test.ts && hardhat test test/node/smart_contract_verifier.test.ts && hardhat test test/node/onchain_recursive_verification.test.ts", - "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", - "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" - }, - "dependencies": { - "@noir-lang/backend_barretenberg": "workspace:*", - "@noir-lang/noir_js": "workspace:*", - "@noir-lang/noir_wasm": "workspace:*", - "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", - "@nomicfoundation/hardhat-ethers": "^3.0.0", - "@web/dev-server-esbuild": "^0.3.6", - "@web/dev-server-import-maps": "^0.2.0", - "@web/test-runner": "^0.18.1", - "@web/test-runner-playwright": "^0.11.0", - "eslint": "^8.57.0", - "eslint-plugin-prettier": "^5.1.3", - "ethers": "^6.7.1", - "hardhat": "^2.22.6", - "prettier": "3.2.5", - "smol-toml": "^1.1.2", - "toml": "^3.0.0", - "tslog": "^4.9.2" - } + "name": "integration-tests", + "license": "(MIT OR Apache-2.0)", + "main": "index.js", + "private": true, + "scripts": { + "build": "echo Integration Test build step", + "test": "yarn test:browser && yarn test:node", + "test:node": "bash ./scripts/setup.sh && hardhat test test/node/prove_and_verify.test.ts && hardhat test test/node/smart_contract_verifier.test.ts && hardhat test test/node/onchain_recursive_verification.test.ts", + "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", + "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" + }, + "dependencies": { + "@aztec/bb.js": "portal:../../../../barretenberg/ts", + "@noir-lang/noir_js": "workspace:*", + "@noir-lang/noir_wasm": "workspace:*", + "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@web/dev-server-esbuild": "^0.3.6", + "@web/dev-server-import-maps": "^0.2.0", + "@web/test-runner": "^0.18.1", + "@web/test-runner-playwright": "^0.11.0", + "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.1.3", + "ethers": "^6.7.1", + "hardhat": "^2.22.6", + "prettier": "3.2.5", + "smol-toml": "^1.1.2", + "toml": "^3.0.0", + "tslog": "^4.9.2" + } } diff --git a/noir/noir-repo/compiler/integration-tests/test/browser/compile_prove_verify.test.ts b/noir/noir-repo/compiler/integration-tests/test/browser/compile_prove_verify.test.ts index f18ceb85276..8d24b27ac25 100644 --- a/noir/noir-repo/compiler/integration-tests/test/browser/compile_prove_verify.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/browser/compile_prove_verify.test.ts @@ -4,7 +4,7 @@ import * as TOML from 'smol-toml'; import { compile, createFileManager } 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 { UltraPlonkBackend } from '@aztec/bb.js'; import { getFile } from './utils.js'; @@ -59,7 +59,7 @@ test_cases.forEach((testInfo) => { const program = new Noir(noir_program); const { witness } = await program.execute(inputs); - const backend = new BarretenbergBackend(noir_program); + const backend = new UltraPlonkBackend(noir_program.bytecode); const proof = await backend.generateProof(witness); // JS verification diff --git a/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts b/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts index abbee7b96ad..510952ff15f 100644 --- a/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts @@ -5,7 +5,7 @@ import { Logger } from 'tslog'; import { acvm, abi, Noir } from '@noir-lang/noir_js'; import * as TOML from 'smol-toml'; -import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { UltraPlonkBackend } from '@aztec/bb.js'; import { getFile } from './utils.js'; import { Field, InputMap } from '@noir-lang/noirc_abi'; import { createFileManager, compile } from '@noir-lang/noir_wasm'; @@ -73,7 +73,7 @@ describe('It compiles noir program code, receiving circuit bytes and abi object. const recursion_program = await getCircuit(`${base_relative_path}/${circuit_recursion}`); - const recursion_backend = new BarretenbergBackend(recursion_program); + const recursion_backend = new UltraPlonkBackend(recursion_program.bytecode); const { witness: recursion_witnessUint8Array } = await new Noir(recursion_program).execute(recursion_inputs); diff --git a/noir/noir-repo/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts b/noir/noir-repo/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts index f4647c478f1..b08d52e1604 100644 --- a/noir/noir-repo/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts @@ -6,7 +6,7 @@ import { resolve, join } from 'path'; import toml from 'toml'; import { Noir } from '@noir-lang/noir_js'; -import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { UltraPlonkBackend } from '@aztec/bb.js'; import { Field, InputMap } from '@noir-lang/noirc_abi'; import { compile, createFileManager } from '@noir-lang/noir_wasm'; @@ -35,7 +35,7 @@ it.skip(`smart contract can verify a recursive proof`, async () => { // Intermediate proof - const inner_backend = new BarretenbergBackend(innerProgram); + const inner_backend = new UltraPlonkBackend(innerProgram.bytecode); const inner = new Noir(innerProgram); const inner_prover_toml = readFileSync( @@ -67,7 +67,7 @@ it.skip(`smart contract can verify a recursive proof`, async () => { const { witness: recursionWitness } = await recursion.execute(recursion_inputs); - const recursion_backend = new BarretenbergBackend(recursionProgram); + const recursion_backend = new UltraPlonkBackend(recursionProgram.bytecode); const recursion_proof = await recursion_backend.generateProof(recursionWitness); expect(await recursion_backend.verifyProof(recursion_proof)).to.be.true; diff --git a/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts b/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts index babc8ca5bb8..7c79c169a33 100644 --- a/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts @@ -2,18 +2,13 @@ import { expect } from 'chai'; 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, - UltraHonkBackend, - UltraHonkVerifier, -} from '@noir-lang/backend_barretenberg'; +import { BarretenbergVerifier, UltraPlonkBackend, UltraHonkBackend } from '@aztec/bb.js'; import { CompiledCircuit } from '@noir-lang/types'; const assert_lt_program = assert_lt_json as CompiledCircuit; const fold_fibonacci_program = fold_fibonacci_json as CompiledCircuit; -const backend = new Backend(assert_lt_program); +const backend = new UltraPlonkBackend(assert_lt_program.bytecode); it('end-to-end proof creation and verification (outer)', async () => { // Noir.Js part @@ -53,7 +48,7 @@ it('end-to-end proof creation and verification (outer) -- Verifier API', async ( const verificationKey = await backend.getVerificationKey(); // Proof verification - const verifier = new Verifier(); + const verifier = new BarretenbergVerifier(); const isValid = await verifier.verifyProof(proof, verificationKey); expect(isValid).to.be.true; }); @@ -94,7 +89,7 @@ it('end-to-end proving and verification with different instances', async () => { // bb.js part const proof = await backend.generateProof(witness); - const verifier = new Backend(assert_lt_program); + const verifier = new UltraPlonkBackend(assert_lt_program.bytecode); const proof_is_valid = await verifier.verifyProof(proof); expect(proof_is_valid).to.be.true; }); @@ -148,7 +143,7 @@ it('end-to-end proof creation and verification for multiple ACIR circuits (inner // bb.js part // // Proof creation - const backend = new Backend(fold_fibonacci_program); + const backend = new UltraPlonkBackend(fold_fibonacci_program.bytecode); const proof = await backend.generateProof(witness); // Proof verification @@ -156,7 +151,7 @@ it('end-to-end proof creation and verification for multiple ACIR circuits (inner expect(isValid).to.be.true; }); -const honkBackend = new UltraHonkBackend(assert_lt_program); +const honkBackend = new UltraHonkBackend(assert_lt_program.bytecode); it('UltraHonk end-to-end proof creation and verification (outer)', async () => { // Noir.Js part @@ -196,7 +191,7 @@ it('UltraHonk end-to-end proof creation and verification (outer) -- Verifier API const verificationKey = await honkBackend.getVerificationKey(); // Proof verification - const verifier = new UltraHonkVerifier(); + const verifier = new BarretenbergVerifier(); const isValid = await verifier.verifyProof(proof, verificationKey); expect(isValid).to.be.true; }); @@ -283,7 +278,7 @@ it('UltraHonk end-to-end proof creation and verification for multiple ACIR circu // bb.js part // // Proof creation - const honkBackend = new UltraHonkBackend(fold_fibonacci_program); + const honkBackend = new UltraHonkBackend(fold_fibonacci_program.bytecode); const proof = await honkBackend.generateProof(witness); // Proof verification diff --git a/noir/noir-repo/compiler/integration-tests/test/node/smart_contract_verifier.test.ts b/noir/noir-repo/compiler/integration-tests/test/node/smart_contract_verifier.test.ts index e109cbcab6a..c43fba01424 100644 --- a/noir/noir-repo/compiler/integration-tests/test/node/smart_contract_verifier.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/node/smart_contract_verifier.test.ts @@ -6,7 +6,7 @@ import { resolve } from 'path'; import toml from 'toml'; import { Noir } from '@noir-lang/noir_js'; -import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { UltraPlonkBackend } from '@aztec/bb.js'; import { compile, createFileManager } from '@noir-lang/noir_wasm'; @@ -46,7 +46,7 @@ test_cases.forEach((testInfo) => { const inputs = toml.parse(prover_toml); const { witness } = await program.execute(inputs); - const backend = new BarretenbergBackend(noir_program); + const backend = new UltraPlonkBackend(noir_program.bytecode); const proofData = await backend.generateProof(witness); // JS verification diff --git a/noir/noir-repo/docs/docs/how_to/how-to-recursion.md b/noir/noir-repo/docs/docs/how_to/how-to-recursion.md index c8c4dc9f5b4..fac79a9cf49 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-recursion.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-recursion.md @@ -1,6 +1,6 @@ --- title: How to use recursion on NoirJS -description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `bb.js`. keywords: [ "NoirJS", @@ -10,7 +10,6 @@ keywords: "solidity verifiers", "Barretenberg backend", "noir_js", - "backend_barretenberg", "intermediate proofs", "final proofs", "nargo compile", @@ -33,13 +32,6 @@ It is also assumed that you're not using `noir_wasm` for compilation, and instea As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. -While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. - -In short: - -- `noir_js` generates *only* final proofs -- `backend_barretenberg` generates both types of proofs - ::: In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: @@ -147,7 +139,7 @@ Managing circuits and "who does what" can be confusing. To make sure your naming ```js const circuits = { - main: mainJSON, + main: mainJSON, recursive: recursiveJSON } const backends = { diff --git a/noir/noir-repo/package.json b/noir/noir-repo/package.json index 8abaced7bdd..00036838f9b 100644 --- a/noir/noir-repo/package.json +++ b/noir/noir-repo/package.json @@ -8,7 +8,6 @@ "tooling/noirc_abi_wasm", "tooling/noir_js", "tooling/noir_codegen", - "tooling/noir_js_backend_barretenberg", "acvm-repo/acvm_js", "docs" ], diff --git a/noir/noir-repo/scripts/bump-bb.sh b/noir/noir-repo/scripts/bump-bb.sh index 36c1f78ca05..fb1a2b1d31e 100755 --- a/noir/noir-repo/scripts/bump-bb.sh +++ b/noir/noir-repo/scripts/bump-bb.sh @@ -5,7 +5,7 @@ BB_VERSION=$1 sed -i.bak "s/^VERSION=.*/VERSION=\"$BB_VERSION\"/" ./scripts/install_bb.sh && rm ./scripts/install_bb.sh.bak tmp=$(mktemp) -BACKEND_BARRETENBERG_PACKAGE_JSON=./tooling/noir_js_backend_barretenberg/package.json -jq --arg v $BB_VERSION '.dependencies."@aztec/bb.js" = $v' $BACKEND_BARRETENBERG_PACKAGE_JSON > $tmp && mv $tmp $BACKEND_BARRETENBERG_PACKAGE_JSON +INTEGRATION_TESTS_PACKAGE_JSON=./compiler/integration_tests/package.json +jq --arg v $BB_VERSION '.dependencies."@aztec/bb.js" = $v' $INTEGRATION_TESTS_PACKAGE_JSON > $tmp && mv $tmp $INTEGRATION_TESTS_PACKAGE_JSON YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/.eslintignore b/noir/noir-repo/tooling/noir_js_backend_barretenberg/.eslintignore deleted file mode 100644 index b512c09d476..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -node_modules \ No newline at end of file diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/.eslintrc.cjs b/noir/noir-repo/tooling/noir_js_backend_barretenberg/.eslintrc.cjs deleted file mode 100644 index 33335c2a877..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/.eslintrc.cjs +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["../../.eslintrc.js"], -}; diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/.gitignore b/noir/noir-repo/tooling/noir_js_backend_barretenberg/.gitignore deleted file mode 100644 index 689b3ca0701..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -crs -lib diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/.mocharc.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/.mocharc.json deleted file mode 100644 index e1023f56327..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/.mocharc.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "require": "ts-node/register", - "loader": "ts-node/esm", - "extensions": [ - "ts", - "cjs" - ], - "spec": [ - "test/**/*.test.ts*" - ] -} diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/fixup.sh b/noir/noir-repo/tooling/noir_js_backend_barretenberg/fixup.sh deleted file mode 100755 index 80bd7ec71a5..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/fixup.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -# Put these package.json files in the cjs and -# mjs directory respectively, so that -# tools can recognise that the .js files are either -# commonjs or ESM files. -self_path=$(dirname "$(readlink -f "$0")") - -cjs_package='{ - "type": "commonjs" -}' - -esm_package='{ - "type": "module" -}' - -echo "$cjs_package" > $self_path/lib/cjs/package.json -echo "$esm_package" > $self_path/lib/esm/package.json \ No newline at end of file diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json deleted file mode 100644 index 42c15ae799e..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "@noir-lang/backend_barretenberg", - "contributors": [ - "The Noir Team " - ], - "version": "0.35.0", - "packageManager": "yarn@3.5.1", - "license": "(MIT OR Apache-2.0)", - "type": "module", - "homepage": "https://noir-lang.org/", - "repository": { - "url": "https://github.com/noir-lang/noir.git", - "directory": "tooling/noir_js_backend_barretenberg", - "type": "git" - }, - "bugs": { - "url": "https://github.com/noir-lang/noir/issues" - }, - "source": "src/index.ts", - "main": "lib/cjs/index.js", - "module": "lib/esm/index.js", - "exports": { - "require": "./lib/cjs/index.js", - "types": "./lib/esm/index.d.ts", - "default": "./lib/esm/index.js" - }, - "types": "lib/esm/index.d.ts", - "files": [ - "lib", - "package.json" - ], - "scripts": { - "dev": "tsc --watch", - "generate:package": "bash ./fixup.sh", - "build": "yarn clean && tsc && tsc -p ./tsconfig.cjs.json && yarn generate:package", - "clean": "rm -rf ./lib", - "prettier": "prettier 'src/**/*.ts'", - "prettier:fix": "prettier --write 'src/**/*.ts' 'test/**/*.ts'", - "nightly:version": "jq --arg new_version \"-$(git rev-parse --short HEAD)$1\" '.version = .version + $new_version' package.json > package-tmp.json && mv package-tmp.json package.json", - "publish": "echo 📡 publishing `$npm_package_name` && yarn npm publish", - "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" - }, - "dependencies": { - "@aztec/bb.js": "portal:../../../../barretenberg/ts", - "@noir-lang/types": "workspace:*", - "fflate": "^0.8.0" - }, - "devDependencies": { - "@types/node": "^20.6.2", - "@types/prettier": "^3", - "eslint": "^8.57.0", - "eslint-plugin-prettier": "^5.1.3", - "prettier": "3.2.5", - "ts-node": "^10.9.1", - "typescript": "5.4.2" - } -} diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts deleted file mode 100644 index 785b0c73421..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { acirToUint8Array } from './serialize.js'; -import { Backend, CompiledCircuit, ProofData, VerifierBackend } from '@noir-lang/types'; -import { deflattenFields } from './public_inputs.js'; -import { reconstructProofWithPublicInputs, reconstructProofWithPublicInputsHonk } from './verifier.js'; -import { BackendOptions, UltraPlonkBackend, UltraHonkBackend as UltraHonkBackendInternal } from '@aztec/bb.js'; -import { decompressSync as gunzip } from 'fflate'; - -// This is the number of bytes in a UltraPlonk proof -// minus the public inputs. -const numBytesInProofWithoutPublicInputs: number = 2144; - -export class BarretenbergBackend implements Backend, VerifierBackend { - protected backend!: UltraPlonkBackend; - - constructor(acirCircuit: CompiledCircuit, options: BackendOptions = { threads: 1 }) { - const acirBytecodeBase64 = acirCircuit.bytecode; - const acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); - this.backend = new UltraPlonkBackend(acirUncompressedBytecode, options); - } - - /** @description Generates a proof */ - async generateProof(compressedWitness: Uint8Array): Promise { - const proofWithPublicInputs = await this.backend.generateProof(gunzip(compressedWitness)); - - const splitIndex = proofWithPublicInputs.length - numBytesInProofWithoutPublicInputs; - - const publicInputsConcatenated = proofWithPublicInputs.slice(0, splitIndex); - const proof = proofWithPublicInputs.slice(splitIndex); - const publicInputs = deflattenFields(publicInputsConcatenated); - - return { proof, publicInputs }; - } - - /** - * Generates artifacts that will be passed to a circuit that will verify this proof. - * - * Instead of passing the proof and verification key as a byte array, we pass them - * as fields which makes it cheaper to verify in a circuit. - * - * The proof that is passed here will have been created using a circuit - * that has the #[recursive] attribute on its `main` method. - * - * The number of public inputs denotes how many public inputs are in the inner proof. - * - * @example - * ```typescript - * const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); - * ``` - */ - async generateRecursiveProofArtifacts( - proofData: ProofData, - numOfPublicInputs = 0, - ): Promise<{ - proofAsFields: string[]; - vkAsFields: string[]; - vkHash: string; - }> { - const proof = reconstructProofWithPublicInputs(proofData); - return this.backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); - } - - /** @description Verifies a proof */ - async verifyProof(proofData: ProofData): Promise { - const proof = reconstructProofWithPublicInputs(proofData); - return this.backend.verifyProof(proof); - } - - async getVerificationKey(): Promise { - return this.backend.getVerificationKey(); - } - - async destroy(): Promise { - await this.backend.destroy(); - } -} - -// Buffers are prepended with their size. The size takes 4 bytes. -const serializedBufferSize = 4; -const fieldByteSize = 32; -const publicInputOffset = 3; -const publicInputsOffsetBytes = publicInputOffset * fieldByteSize; - -export class UltraHonkBackend implements Backend, VerifierBackend { - // These type assertions are used so that we don't - // have to initialize `api` in the constructor. - // These are initialized asynchronously in the `init` function, - // constructors cannot be asynchronous which is why we do this. - - protected backend!: UltraHonkBackendInternal; - - constructor(acirCircuit: CompiledCircuit, options: BackendOptions = { threads: 1 }) { - const acirBytecodeBase64 = acirCircuit.bytecode; - const acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); - this.backend = new UltraHonkBackendInternal(acirUncompressedBytecode, options); - } - - async generateProof(compressedWitness: Uint8Array): Promise { - const proofWithPublicInputs = await this.backend.generateProof(gunzip(compressedWitness)); - - const proofAsStrings = deflattenFields(proofWithPublicInputs.slice(4)); - - const numPublicInputs = Number(proofAsStrings[1]); - - // Account for the serialized buffer size at start - const publicInputsOffset = publicInputsOffsetBytes + serializedBufferSize; - // Get the part before and after the public inputs - const proofStart = proofWithPublicInputs.slice(0, publicInputsOffset); - const publicInputsSplitIndex = numPublicInputs * fieldByteSize; - const proofEnd = proofWithPublicInputs.slice(publicInputsOffset + publicInputsSplitIndex); - // Construct the proof without the public inputs - const proof = new Uint8Array([...proofStart, ...proofEnd]); - - // Fetch the number of public inputs out of the proof string - const publicInputsConcatenated = proofWithPublicInputs.slice( - publicInputsOffset, - publicInputsOffset + publicInputsSplitIndex, - ); - const publicInputs = deflattenFields(publicInputsConcatenated); - - return { proof, publicInputs }; - } - - async verifyProof(proofData: ProofData): Promise { - const proof = reconstructProofWithPublicInputsHonk(proofData); - return this.backend.verifyProof(proof); - } - - async getVerificationKey(): Promise { - return this.backend.getVerificationKey(); - } - - // TODO(https://github.com/noir-lang/noir/issues/5661): Update this to handle Honk recursive aggregation in the browser once it is ready in the backend itself - async generateRecursiveProofArtifacts( - proofData: ProofData, - numOfPublicInputs: number, - ): Promise<{ proofAsFields: string[]; vkAsFields: string[]; vkHash: string }> { - const proof = reconstructProofWithPublicInputsHonk(proofData); - return this.backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); - } - - async destroy(): Promise { - await this.backend.destroy(); - } -} diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/base64_decode.ts b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/base64_decode.ts deleted file mode 100644 index d53aed187c7..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/base64_decode.ts +++ /dev/null @@ -1,13 +0,0 @@ -// 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/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/index.ts b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/index.ts deleted file mode 100644 index f1786396a2a..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { BarretenbergBackend, UltraHonkBackend } from './backend.js'; -export { BarretenbergVerifier, UltraHonkVerifier } from './verifier.js'; - -// typedoc exports -export { Backend, CompiledCircuit, ProofData } from '@noir-lang/types'; -export { BackendOptions } from '@aztec/bb.js'; diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/public_inputs.ts b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/public_inputs.ts deleted file mode 100644 index 10b4ee6ab32..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/public_inputs.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { WitnessMap } from '@noir-lang/types'; - -export function flattenFieldsAsArray(fields: string[]): Uint8Array { - const flattenedPublicInputs = fields.map(hexToUint8Array); - return flattenUint8Arrays(flattenedPublicInputs); -} - -export function deflattenFields(flattenedFields: Uint8Array): string[] { - const publicInputSize = 32; - const chunkedFlattenedPublicInputs: Uint8Array[] = []; - - for (let i = 0; i < flattenedFields.length; i += publicInputSize) { - const publicInput = flattenedFields.slice(i, i + publicInputSize); - chunkedFlattenedPublicInputs.push(publicInput); - } - - return chunkedFlattenedPublicInputs.map(uint8ArrayToHex); -} - -export function witnessMapToPublicInputs(publicInputs: WitnessMap): string[] { - const publicInputIndices = [...publicInputs.keys()].sort((a, b) => a - b); - const flattenedPublicInputs = publicInputIndices.map((index) => publicInputs.get(index) as string); - return flattenedPublicInputs; -} - -function flattenUint8Arrays(arrays: Uint8Array[]): Uint8Array { - const totalLength = arrays.reduce((acc, val) => acc + val.length, 0); - const result = new Uint8Array(totalLength); - - let offset = 0; - for (const arr of arrays) { - result.set(arr, offset); - offset += arr.length; - } - - return result; -} - -function uint8ArrayToHex(buffer: Uint8Array): string { - const hex: string[] = []; - - buffer.forEach(function (i) { - let h = i.toString(16); - if (h.length % 2) { - h = '0' + h; - } - hex.push(h); - }); - - return '0x' + hex.join(''); -} - -function hexToUint8Array(hex: string): Uint8Array { - const sanitised_hex = BigInt(hex).toString(16).padStart(64, '0'); - - const len = sanitised_hex.length / 2; - const u8 = new Uint8Array(len); - - let i = 0; - let j = 0; - while (i < len) { - u8[i] = parseInt(sanitised_hex.slice(j, j + 2), 16); - i += 1; - j += 2; - } - - return u8; -} diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/serialize.ts b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/serialize.ts deleted file mode 100644 index b15931848a0..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/serialize.ts +++ /dev/null @@ -1,8 +0,0 @@ -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: string): Uint8Array { - const compressedByteCode = base64Decode(base64EncodedBytecode); - return gunzip(compressedByteCode); -} diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/verifier.ts b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/verifier.ts deleted file mode 100644 index 885ec80caa8..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/verifier.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { ProofData } from '@noir-lang/types'; -import { flattenFieldsAsArray } from './public_inputs.js'; -import { BackendOptions, BarretenbergVerifier as BarretenbergVerifierInternal } from '@aztec/bb.js'; - -export class BarretenbergVerifier { - private verifier!: BarretenbergVerifierInternal; - - constructor(options: BackendOptions = { threads: 1 }) { - this.verifier = new BarretenbergVerifierInternal(options); - } - - /** @description Verifies a proof */ - async verifyProof(proofData: ProofData, verificationKey: Uint8Array): Promise { - const proof = reconstructProofWithPublicInputs(proofData); - return this.verifier.verifyUltraplonkProof(proof, verificationKey); - } - - async destroy(): Promise { - await this.verifier.destroy(); - } -} - -export function reconstructProofWithPublicInputs(proofData: ProofData): Uint8Array { - // Flatten publicInputs - const publicInputsConcatenated = flattenFieldsAsArray(proofData.publicInputs); - - // Concatenate publicInputs and proof - const proofWithPublicInputs = Uint8Array.from([...publicInputsConcatenated, ...proofData.proof]); - - return proofWithPublicInputs; -} - -export class UltraHonkVerifier { - private verifier!: BarretenbergVerifierInternal; - - constructor(options: BackendOptions = { threads: 1 }) { - this.verifier = new BarretenbergVerifierInternal(options); - } - - /** @description Verifies a proof */ - async verifyProof(proofData: ProofData, verificationKey: Uint8Array): Promise { - const proof = reconstructProofWithPublicInputsHonk(proofData); - return this.verifier.verifyUltrahonkProof(proof, verificationKey); - } - - async destroy(): Promise { - await this.verifier.destroy(); - } -} - -const serializedBufferSize = 4; -const fieldByteSize = 32; -const publicInputOffset = 3; -const publicInputsOffsetBytes = publicInputOffset * fieldByteSize; - -export function reconstructProofWithPublicInputsHonk(proofData: ProofData): Uint8Array { - // Flatten publicInputs - const publicInputsConcatenated = flattenFieldsAsArray(proofData.publicInputs); - - const proofStart = proofData.proof.slice(0, publicInputsOffsetBytes + serializedBufferSize); - const proofEnd = proofData.proof.slice(publicInputsOffsetBytes + serializedBufferSize); - - // Concatenate publicInputs and proof - const proofWithPublicInputs = Uint8Array.from([...proofStart, ...publicInputsConcatenated, ...proofEnd]); - - return proofWithPublicInputs; -} diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json deleted file mode 100644 index ac1e3784480..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "CommonJS", - "moduleResolution": "Node", - "outDir": "./lib/cjs" - }, -} diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/tsconfig.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/tsconfig.json deleted file mode 100644 index 1e28c044bba..00000000000 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "target": "esnext", - "declaration": true, - "emitDeclarationOnly": false, - "module": "NodeNext", - "moduleResolution": "NodeNext", - "outDir": "./lib/esm", - "esModuleInterop": true, - "resolveJsonModule": true, - "strict": true, - "noImplicitAny": false, - }, - "include": ["src/**/*.ts"], - "exclude": ["node_modules"], - "references": [ - { - "path": "../noir_js_types" - } - ] -} diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index 77708cc9b21..9df981f1af1 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,13 +221,14 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg": +"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests": version: 0.0.0-use.local - resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg" + resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 + fflate: ^0.8.0 tslib: ^2.4.0 bin: bb.js: ./dest/node/main.js @@ -4156,23 +4157,6 @@ __metadata: languageName: unknown linkType: soft -"@noir-lang/backend_barretenberg@workspace:*, @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": "portal:../../../../barretenberg/ts" - "@noir-lang/types": "workspace:*" - "@types/node": ^20.6.2 - "@types/prettier": ^3 - eslint: ^8.57.0 - eslint-plugin-prettier: ^5.1.3 - fflate: ^0.8.0 - prettier: 3.2.5 - ts-node: ^10.9.1 - typescript: 5.4.2 - languageName: unknown - linkType: soft - "@noir-lang/noir_codegen@workspace:tooling/noir_codegen": version: 0.0.0-use.local resolution: "@noir-lang/noir_codegen@workspace:tooling/noir_codegen" @@ -12570,7 +12554,7 @@ __metadata: version: 0.0.0-use.local resolution: "integration-tests@workspace:compiler/integration-tests" dependencies: - "@noir-lang/backend_barretenberg": "workspace:*" + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/noir_js": "workspace:*" "@noir-lang/noir_wasm": "workspace:*" "@nomicfoundation/hardhat-chai-matchers": ^2.0.0 @@ -19749,7 +19733,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.4.2, typescript@npm:^5.4.2": +"typescript@npm:^5.4.2": version: 5.4.2 resolution: "typescript@npm:5.4.2" bin: @@ -19759,7 +19743,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@5.4.2#~builtin, typescript@patch:typescript@^5.4.2#~builtin": +"typescript@patch:typescript@^5.4.2#~builtin": version: 5.4.2 resolution: "typescript@patch:typescript@npm%3A5.4.2#~builtin::version=5.4.2&hash=f3b441" bin: From db854d52d675570a8f3fb4e0c59564083d4b0dad Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 22 Oct 2024 19:49:54 +0100 Subject: [PATCH 02/11] . --- barretenberg/ts/src/barretenberg/backend.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/barretenberg/ts/src/barretenberg/backend.ts b/barretenberg/ts/src/barretenberg/backend.ts index a79fe6ceae7..0919f030e5c 100644 --- a/barretenberg/ts/src/barretenberg/backend.ts +++ b/barretenberg/ts/src/barretenberg/backend.ts @@ -161,9 +161,12 @@ export class UltraHonkBackend { } } - async generateProof(uncompressedWitness: Uint8Array): Promise { + async generateProof(compressedWitness: Uint8Array): Promise { await this.instantiate(); - const proofWithPublicInputs = await this.api.acirProveUltraHonk(this.acirUncompressedBytecode, uncompressedWitness); + const proofWithPublicInputs = await this.api.acirProveUltraHonk( + this.acirUncompressedBytecode, + gunzip(compressedWitness), + ); const proofAsStrings = deflattenFields(proofWithPublicInputs.slice(4)); From 15f5ecf24fff1e1fc4a4e4a1ad3951252adc440d Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 22 Oct 2024 20:02:38 +0100 Subject: [PATCH 03/11] . --- yarn-project/yarn.lock | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index ecd52a2eb69..ccecdbd1b24 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -321,6 +321,7 @@ __metadata: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 + fflate: ^0.8.0 tslib: ^2.4.0 bin: bb.js: ./dest/node/main.js @@ -8835,6 +8836,13 @@ __metadata: languageName: node linkType: hard +"fflate@npm:^0.8.0": + version: 0.8.2 + resolution: "fflate@npm:0.8.2" + checksum: 29470337b85d3831826758e78f370e15cda3169c5cd4477c9b5eea2402261a74b2975bae816afabe1c15d21d98591e0d30a574f7103aa117bff60756fa3035d4 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" From 282ea37ea282183dcd1f9fc89cde7ae36cca4aea Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 22 Oct 2024 20:33:07 +0100 Subject: [PATCH 04/11] . --- .../acir_tests/browser-test-app/src/index.ts | 12 +++------ .../acir_tests/headless-test/src/index.ts | 26 +++++-------------- .../test/node/prove_and_verify.test.ts | 6 ++--- 3 files changed, 14 insertions(+), 30 deletions(-) diff --git a/barretenberg/acir_tests/browser-test-app/src/index.ts b/barretenberg/acir_tests/browser-test-app/src/index.ts index a3080453677..4cf000a41a5 100644 --- a/barretenberg/acir_tests/browser-test-app/src/index.ts +++ b/barretenberg/acir_tests/browser-test-app/src/index.ts @@ -1,11 +1,10 @@ import createDebug from "debug"; -import { inflate } from "pako"; createDebug.enable("*"); const debug = createDebug("browser-test-app"); async function runTest( - bytecode: Uint8Array, + bytecode: string, witness: Uint8Array, threads?: number ) { @@ -48,16 +47,13 @@ function base64ToUint8Array(base64: string) { // Update by extracting from ../acir_tests/verify_honk_proof. Specifically: // - The base64 representation of the ACIR is the bytecode section of program.json // - The base64 representation of the witness is obtained by encoding witness.gz -const acir = inflate( - base64ToUint8Array( +const acir = "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" - ) -); +; -const witness = inflate( +const witness = base64ToUint8Array( "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" - ) ); document.addEventListener("DOMContentLoaded", function () { diff --git a/barretenberg/acir_tests/headless-test/src/index.ts b/barretenberg/acir_tests/headless-test/src/index.ts index a318010c427..373f7350111 100644 --- a/barretenberg/acir_tests/headless-test/src/index.ts +++ b/barretenberg/acir_tests/headless-test/src/index.ts @@ -37,20 +37,9 @@ function formatAndPrintLog(message: string): void { console.log(formattedMessage); } -const readBytecodeFile = (path: string): Uint8Array => { - const extension = path.substring(path.lastIndexOf(".") + 1); - - if (extension == "json") { - const encodedCircuit = JSON.parse(fs.readFileSync(path, "utf8")); - const decompressed = gunzipSync( - Uint8Array.from(atob(encodedCircuit.bytecode), (c) => c.charCodeAt(0)) - ); - return decompressed; - } - - const encodedCircuit = fs.readFileSync(path); - const decompressed = gunzipSync(encodedCircuit); - return decompressed; +const readBytecodeFile = (path: string): string => { + const encodedCircuit = JSON.parse(fs.readFileSync(path, "utf8")); + return encodedCircuit.bytecode; }; const readWitnessFile = (path: string): Uint8Array => { @@ -70,8 +59,8 @@ program ) .option( "-b, --bytecode-path ", - "Specify the path to the gzip encoded ACIR bytecode", - "./target/acir.gz" + "Specify the path to the ACIR artifact json file", + "./target/acir.json" ) .option( "-w, --witness-path ", @@ -102,14 +91,13 @@ program await page.goto("http://localhost:8080"); const result: boolean = await page.evaluate( - ([acirData, witnessData, threads]) => { + ([acir, witnessData, threads]) => { // Convert the input data to Uint8Arrays within the browser context - const acirUint8Array = new Uint8Array(acirData as number[]); const witnessUint8Array = new Uint8Array(witnessData as number[]); // Call the desired function and return the result return (window as any).runTest( - acirUint8Array, + acir, witnessUint8Array, threads ); diff --git a/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts b/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts index 7c79c169a33..497a291ba80 100644 --- a/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts @@ -49,7 +49,7 @@ it('end-to-end proof creation and verification (outer) -- Verifier API', async ( // Proof verification const verifier = new BarretenbergVerifier(); - const isValid = await verifier.verifyProof(proof, verificationKey); + const isValid = await verifier.verifyUltraplonkProof(proof, verificationKey); expect(isValid).to.be.true; }); @@ -192,7 +192,7 @@ it('UltraHonk end-to-end proof creation and verification (outer) -- Verifier API // Proof verification const verifier = new BarretenbergVerifier(); - const isValid = await verifier.verifyProof(proof, verificationKey); + const isValid = await verifier.verifyUltrahonkProof(proof, verificationKey); expect(isValid).to.be.true; }); @@ -231,7 +231,7 @@ it('UltraHonk end-to-end proving and verification with different instances', asy // bb.js part const proof = await honkBackend.generateProof(witness); - const verifier = new UltraHonkBackend(assert_lt_program); + const verifier = new UltraHonkBackend(assert_lt_program.bytecode); const proof_is_valid = await verifier.verifyProof(proof); expect(proof_is_valid).to.be.true; }); From 5b54e1554f762c113d1d33c51d6805a2efe066f2 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 22 Oct 2024 21:04:46 +0100 Subject: [PATCH 05/11] . --- .../compiler/integration-tests/test/browser/recursion.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts b/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts index 510952ff15f..4ee92d5b795 100644 --- a/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts @@ -45,7 +45,7 @@ describe('It compiles noir program code, receiving circuit bytes and abi object. const main_program = await getCircuit(`${base_relative_path}/${circuit_main}`); const main_inputs: InputMap = TOML.parse(circuit_main_toml) as InputMap; - const main_backend = new BarretenbergBackend(main_program); + const main_backend = new UltraPlonkBackend(main_program.bytecode); const { witness: main_witnessUint8Array } = await new Noir(main_program).execute(main_inputs); From 12f2488b5cb8f2bcbeed0f9c33effbbab1714d58 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 22 Oct 2024 21:06:00 +0100 Subject: [PATCH 06/11] . --- barretenberg/acir_tests/browser-test-app/src/index.ts | 2 +- .../integration-tests/test/node/prove_and_verify.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/acir_tests/browser-test-app/src/index.ts b/barretenberg/acir_tests/browser-test-app/src/index.ts index 4cf000a41a5..06370f5a82a 100644 --- a/barretenberg/acir_tests/browser-test-app/src/index.ts +++ b/barretenberg/acir_tests/browser-test-app/src/index.ts @@ -21,7 +21,7 @@ async function runTest( debug(`verifying...`); const verifier = new BarretenbergVerifier({ threads }); - const verified = await verifier.verifyUltrahonkProof(proof, verificationKey); + const verified = await verifier.verifyUltraHonkProof(proof, verificationKey); debug(`verified: ${verified}`); await verifier.destroy(); diff --git a/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts b/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts index 497a291ba80..df937b96d15 100644 --- a/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts @@ -192,7 +192,7 @@ it('UltraHonk end-to-end proof creation and verification (outer) -- Verifier API // Proof verification const verifier = new BarretenbergVerifier(); - const isValid = await verifier.verifyUltrahonkProof(proof, verificationKey); + const isValid = await verifier.verifyUltraHonkProof(proof, verificationKey); expect(isValid).to.be.true; }); From aa3082ab9fbca1ef1e2379154d3150732f9a3170 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 22 Oct 2024 21:42:37 +0100 Subject: [PATCH 07/11] Update verifier.ts --- barretenberg/ts/src/barretenberg/verifier.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/ts/src/barretenberg/verifier.ts b/barretenberg/ts/src/barretenberg/verifier.ts index 0c76c2885da..e0c59f64039 100644 --- a/barretenberg/ts/src/barretenberg/verifier.ts +++ b/barretenberg/ts/src/barretenberg/verifier.ts @@ -28,7 +28,7 @@ export class BarretenbergVerifier { } /** @description Verifies a proof */ - async verifyUltraplonkProof(proofData: ProofData, verificationKey: Uint8Array): Promise { + async verifyUltraPlonkProof(proofData: ProofData, verificationKey: Uint8Array): Promise { await this.instantiate(); // The verifier can be used for a variety of ACIR programs so we should not assume that it // is preloaded with the correct verification key. @@ -39,7 +39,7 @@ export class BarretenbergVerifier { } /** @description Verifies a proof */ - async verifyUltrahonkProof(proofData: ProofData, verificationKey: Uint8Array): Promise { + async verifyUltraHonkProof(proofData: ProofData, verificationKey: Uint8Array): Promise { await this.instantiate(); const proof = reconstructHonkProof(flattenFieldsAsArray(proofData.publicInputs), proofData.proof); From 4c218be3da3faf3fd30480a80c4818bbf127117a Mon Sep 17 00:00:00 2001 From: TomAFrench Date: Tue, 22 Oct 2024 21:14:40 +0000 Subject: [PATCH 08/11] . --- .../integration-tests/test/node/prove_and_verify.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts b/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts index df937b96d15..4353bccd90d 100644 --- a/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/node/prove_and_verify.test.ts @@ -49,7 +49,7 @@ it('end-to-end proof creation and verification (outer) -- Verifier API', async ( // Proof verification const verifier = new BarretenbergVerifier(); - const isValid = await verifier.verifyUltraplonkProof(proof, verificationKey); + const isValid = await verifier.verifyUltraPlonkProof(proof, verificationKey); expect(isValid).to.be.true; }); From 156138662f96efaf45ad3ca3a4b8cd8c269cd1e4 Mon Sep 17 00:00:00 2001 From: TomAFrench Date: Tue, 22 Oct 2024 21:27:40 +0000 Subject: [PATCH 09/11] . --- barretenberg/acir_tests/headless-test/src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/barretenberg/acir_tests/headless-test/src/index.ts b/barretenberg/acir_tests/headless-test/src/index.ts index 373f7350111..0304b9c4ab2 100644 --- a/barretenberg/acir_tests/headless-test/src/index.ts +++ b/barretenberg/acir_tests/headless-test/src/index.ts @@ -91,9 +91,9 @@ program await page.goto("http://localhost:8080"); const result: boolean = await page.evaluate( - ([acir, witnessData, threads]) => { + ([acir, witnessData, threads]: [string, number[], number]) => { // Convert the input data to Uint8Arrays within the browser context - const witnessUint8Array = new Uint8Array(witnessData as number[]); + const witnessUint8Array = new Uint8Array(witnessData); // Call the desired function and return the result return (window as any).runTest( @@ -102,7 +102,7 @@ program threads ); }, - [Array.from(acir), Array.from(witness), threads] + [acir, Array.from(witness), threads] ); await browser.close(); From 160eb7525d63fda2aefbe7775aaca6202fe07f5e Mon Sep 17 00:00:00 2001 From: TomAFrench Date: Tue, 22 Oct 2024 21:52:35 +0000 Subject: [PATCH 10/11] . --- barretenberg/acir_tests/headless-test/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/acir_tests/headless-test/src/index.ts b/barretenberg/acir_tests/headless-test/src/index.ts index 0304b9c4ab2..4dc3f353500 100644 --- a/barretenberg/acir_tests/headless-test/src/index.ts +++ b/barretenberg/acir_tests/headless-test/src/index.ts @@ -44,7 +44,7 @@ const readBytecodeFile = (path: string): string => { const readWitnessFile = (path: string): Uint8Array => { const buffer = fs.readFileSync(path); - return gunzipSync(buffer); + return buffer; }; // Set up the command-line interface From 9808ae83f630c45cb824ac1f976215d22c2924e1 Mon Sep 17 00:00:00 2001 From: TomAFrench Date: Tue, 22 Oct 2024 21:52:54 +0000 Subject: [PATCH 11/11] . --- barretenberg/acir_tests/headless-test/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/barretenberg/acir_tests/headless-test/src/index.ts b/barretenberg/acir_tests/headless-test/src/index.ts index 4dc3f353500..cffd98997dc 100644 --- a/barretenberg/acir_tests/headless-test/src/index.ts +++ b/barretenberg/acir_tests/headless-test/src/index.ts @@ -1,7 +1,6 @@ import { chromium, firefox, webkit } from "playwright"; import fs from "fs"; import { Command } from "commander"; -import { gunzipSync } from "zlib"; import chalk from "chalk"; import os from "os";