Skip to content

Commit

Permalink
feat: Add bb interface implementation (noir-lang#2902)
Browse files Browse the repository at this point in the history
Co-authored-by: Koby Hall <102518238+kobyhallx@users.noreply.github.com>
Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
Co-authored-by: Tom French <tom@tomfren.ch>
  • Loading branch information
4 people authored and Sakapoi committed Oct 19, 2023
1 parent 792d9f8 commit 0dcf22b
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 41 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/test-noir-js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
],
Expand Down
14 changes: 7 additions & 7 deletions tooling/noir_js/test/node/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.',
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions tooling/noir_js_backend_barretenberg/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
3 changes: 3 additions & 0 deletions tooling/noir_js_backend_barretenberg/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
extends: ["../../.eslintrc.js"],
};
2 changes: 2 additions & 0 deletions tooling/noir_js_backend_barretenberg/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
crs
lib
43 changes: 43 additions & 0 deletions tooling/noir_js_backend_barretenberg/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@noir-lang/backend_barretenberg",
"collaborators": [
"The Noir Team <team@noir-lang.org>"
],
"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"
}
}
13 changes: 13 additions & 0 deletions tooling/noir_js_backend_barretenberg/src/base64_decode.ts
Original file line number Diff line number Diff line change
@@ -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.');
}
}
Original file line number Diff line number Diff line change
@@ -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<BarretenbergBackend> {
const backend = new BarretenbergBackend(acirCircuit);
await backend.init();
return backend;
}

private async init(): Promise<void> {
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<void> {
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
Expand Down Expand Up @@ -73,6 +62,7 @@ export class BarretenbergBackend implements Backend {
}

async generateProof(decompressedWitness: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise<Uint8Array> {
await this.instantiate();
const proof = await this.api.acirCreateProof(
this.acirComposer,
this.acirUncompressedBytecode,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -128,11 +119,15 @@ export class BarretenbergBackend implements Backend {
}

async verifyProof(proof: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise<boolean> {
await this.instantiate();
await this.api.acirInitVerificationKey(this.acirComposer);
return await this.api.acirVerifyProof(this.acirComposer, proof, makeEasyToVerifyInCircuit);
}

async destroy(): Promise<void> {
if (!this.api) {
return;
}
await this.api.destroy();
}
}
8 changes: 8 additions & 0 deletions tooling/noir_js_backend_barretenberg/src/serialize.ts
Original file line number Diff line number Diff line change
@@ -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);
}
7 changes: 7 additions & 0 deletions tooling/noir_js_backend_barretenberg/tsconfig.cjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "CommonJS",
"outDir": "./lib/cjs"
},
}
16 changes: 16 additions & 0 deletions tooling/noir_js_backend_barretenberg/tsconfig.json
Original file line number Diff line number Diff line change
@@ -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"]
}
52 changes: 52 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand All @@ -7569,6 +7611,16 @@ __metadata:
languageName: node
linkType: hard

"typescript@patch:typescript@5.1.5#~builtin<compat/typescript>":
version: 5.1.5
resolution: "typescript@patch:typescript@npm%3A5.1.5#~builtin<compat/typescript>::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<compat/typescript>, typescript@patch:typescript@^5.2.2#~builtin<compat/typescript>":
version: 5.2.2
resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin<compat/typescript>::version=5.2.2&hash=f3b441"
Expand Down

0 comments on commit 0dcf22b

Please sign in to comment.