@noir-lang/backend_barretenberg
Advanced tools
Comparing version 0.34.0-e74b4ae.nightly to 0.34.0-fd03ea0.nightly
import { Backend, CompiledCircuit, ProofData, VerifierBackend } from '@noir-lang/types'; | ||
import { BackendOptions } from './types.js'; | ||
import { type Barretenberg } from '@aztec/bb.js'; | ||
import { BackendOptions, UltraPlonkBackend, UltraHonkBackend as UltraHonkBackendInternal } from '@aztec/bb.js'; | ||
export declare class BarretenbergBackend implements Backend, VerifierBackend { | ||
protected options: BackendOptions; | ||
protected api: Barretenberg; | ||
protected acirComposer: any; | ||
protected acirUncompressedBytecode: Uint8Array; | ||
protected backend: UltraPlonkBackend; | ||
constructor(acirCircuit: CompiledCircuit, options?: BackendOptions); | ||
/** @ignore */ | ||
instantiate(): Promise<void>; | ||
/** @description Generates a proof */ | ||
@@ -41,12 +35,8 @@ generateProof(compressedWitness: Uint8Array): Promise<ProofData>; | ||
export declare class UltraHonkBackend implements Backend, VerifierBackend { | ||
protected options: BackendOptions; | ||
protected api: Barretenberg; | ||
protected acirUncompressedBytecode: Uint8Array; | ||
protected backend: UltraHonkBackendInternal; | ||
constructor(acirCircuit: CompiledCircuit, options?: BackendOptions); | ||
/** @ignore */ | ||
instantiate(): Promise<void>; | ||
generateProof(decompressedWitness: Uint8Array): Promise<ProofData>; | ||
generateProof(compressedWitness: Uint8Array): Promise<ProofData>; | ||
verifyProof(proofData: ProofData): Promise<boolean>; | ||
getVerificationKey(): Promise<Uint8Array>; | ||
generateRecursiveProofArtifacts(_proofData: ProofData, _numOfPublicInputs: number): Promise<{ | ||
generateRecursiveProofArtifacts(proofData: ProofData, numOfPublicInputs: number): Promise<{ | ||
proofAsFields: string[]; | ||
@@ -53,0 +43,0 @@ vkAsFields: string[]; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.UltraHonkBackend = exports.BarretenbergBackend = void 0; | ||
const fflate_1 = require("fflate"); | ||
const serialize_js_1 = require("./serialize.js"); | ||
const public_inputs_js_1 = require("./public_inputs.js"); | ||
const verifier_js_1 = require("./verifier.js"); | ||
const bb_js_1 = require("@aztec/bb.js"); | ||
const fflate_1 = require("fflate"); | ||
// This is the number of bytes in a UltraPlonk proof | ||
@@ -35,47 +13,11 @@ // minus the public inputs. | ||
class BarretenbergBackend { | ||
options; | ||
// 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; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
acirComposer; | ||
acirUncompressedBytecode; | ||
backend; | ||
constructor(acirCircuit, options = { threads: 1 }) { | ||
this.options = options; | ||
const acirBytecodeBase64 = acirCircuit.bytecode; | ||
this.acirUncompressedBytecode = (0, serialize_js_1.acirToUint8Array)(acirBytecodeBase64); | ||
const acirUncompressedBytecode = (0, serialize_js_1.acirToUint8Array)(acirBytecodeBase64); | ||
this.backend = new bb_js_1.UltraPlonkBackend(acirUncompressedBytecode, options); | ||
} | ||
/** @ignore */ | ||
async instantiate() { | ||
if (!this.api) { | ||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { | ||
this.options.threads = navigator.hardwareConcurrency; | ||
} | ||
else { | ||
try { | ||
const os = await Promise.resolve().then(() => __importStar(require('os'))); | ||
this.options.threads = os.cpus().length; | ||
} | ||
catch (e) { | ||
console.log('Could not detect environment. Falling back to one thread.', e); | ||
} | ||
} | ||
const { Barretenberg, RawBuffer, Crs } = await Promise.resolve().then(() => __importStar(require('@aztec/bb.js'))); | ||
const api = await Barretenberg.new(this.options); | ||
const honkRecursion = false; | ||
const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode, honkRecursion); | ||
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); | ||
await api.acirInitProvingKey(this.acirComposer, this.acirUncompressedBytecode); | ||
this.api = api; | ||
} | ||
} | ||
/** @description Generates a proof */ | ||
async generateProof(compressedWitness) { | ||
await this.instantiate(); | ||
const proofWithPublicInputs = await this.api.acirCreateProof(this.acirComposer, this.acirUncompressedBytecode, (0, fflate_1.decompressSync)(compressedWitness)); | ||
const proofWithPublicInputs = await this.backend.generateProof((0, fflate_1.decompressSync)(compressedWitness)); | ||
const splitIndex = proofWithPublicInputs.length - numBytesInProofWithoutPublicInputs; | ||
@@ -104,15 +46,4 @@ const publicInputsConcatenated = proofWithPublicInputs.slice(0, splitIndex); | ||
async generateRecursiveProofArtifacts(proofData, numOfPublicInputs = 0) { | ||
await this.instantiate(); | ||
const proof = (0, verifier_js_1.reconstructProofWithPublicInputs)(proofData); | ||
const proofAsFields = (await this.api.acirSerializeProofIntoFields(this.acirComposer, proof, numOfPublicInputs)).slice(numOfPublicInputs); | ||
// TODO: perhaps we should put this in the init function. Need to benchmark | ||
// TODO how long it takes. | ||
await this.api.acirInitVerificationKey(this.acirComposer); | ||
// Note: If you don't init verification key, `acirSerializeVerificationKeyIntoFields`` will just hang on serialization | ||
const vk = await this.api.acirSerializeVerificationKeyIntoFields(this.acirComposer); | ||
return { | ||
proofAsFields: proofAsFields.map((p) => p.toString()), | ||
vkAsFields: vk[0].map((vk) => vk.toString()), | ||
vkHash: vk[1].toString(), | ||
}; | ||
return this.backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); | ||
} | ||
@@ -122,16 +53,9 @@ /** @description Verifies a proof */ | ||
const proof = (0, verifier_js_1.reconstructProofWithPublicInputs)(proofData); | ||
await this.instantiate(); | ||
await this.api.acirInitVerificationKey(this.acirComposer); | ||
return await this.api.acirVerifyProof(this.acirComposer, proof); | ||
return this.backend.verifyProof(proof); | ||
} | ||
async getVerificationKey() { | ||
await this.instantiate(); | ||
await this.api.acirInitVerificationKey(this.acirComposer); | ||
return await this.api.acirGetVerificationKey(this.acirComposer); | ||
return this.backend.getVerificationKey(); | ||
} | ||
async destroy() { | ||
if (!this.api) { | ||
return; | ||
} | ||
await this.api.destroy(); | ||
await this.backend.destroy(); | ||
} | ||
@@ -146,3 +70,2 @@ } | ||
class UltraHonkBackend { | ||
options; | ||
// These type assertions are used so that we don't | ||
@@ -152,39 +75,10 @@ // have to initialize `api` in the constructor. | ||
// constructors cannot be asynchronous which is why we do this. | ||
api; | ||
acirUncompressedBytecode; | ||
backend; | ||
constructor(acirCircuit, options = { threads: 1 }) { | ||
this.options = options; | ||
const acirBytecodeBase64 = acirCircuit.bytecode; | ||
this.acirUncompressedBytecode = (0, serialize_js_1.acirToUint8Array)(acirBytecodeBase64); | ||
const acirUncompressedBytecode = (0, serialize_js_1.acirToUint8Array)(acirBytecodeBase64); | ||
this.backend = new bb_js_1.UltraHonkBackend(acirUncompressedBytecode, options); | ||
} | ||
/** @ignore */ | ||
async instantiate() { | ||
if (!this.api) { | ||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { | ||
this.options.threads = navigator.hardwareConcurrency; | ||
} | ||
else { | ||
try { | ||
const os = await Promise.resolve().then(() => __importStar(require('os'))); | ||
this.options.threads = os.cpus().length; | ||
} | ||
catch (e) { | ||
console.log('Could not detect environment. Falling back to one thread.', e); | ||
} | ||
} | ||
const { Barretenberg, RawBuffer, Crs } = await Promise.resolve().then(() => __importStar(require('@aztec/bb.js'))); | ||
const api = await Barretenberg.new(this.options); | ||
const honkRecursion = true; | ||
const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode, honkRecursion); | ||
const crs = await Crs.new(subgroupSize + 1); | ||
await api.commonInitSlabAllocator(subgroupSize); | ||
await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); | ||
// We don't init a proving key here in the Honk API | ||
// await api.acirInitProvingKey(this.acirComposer, this.acirUncompressedBytecode); | ||
this.api = api; | ||
} | ||
} | ||
async generateProof(decompressedWitness) { | ||
await this.instantiate(); | ||
const proofWithPublicInputs = await this.api.acirProveUltraHonk(this.acirUncompressedBytecode, (0, fflate_1.decompressSync)(decompressedWitness)); | ||
async generateProof(compressedWitness) { | ||
const proofWithPublicInputs = await this.backend.generateProof((0, fflate_1.decompressSync)(compressedWitness)); | ||
const proofAsStrings = (0, public_inputs_js_1.deflattenFields)(proofWithPublicInputs.slice(4)); | ||
@@ -206,43 +100,17 @@ const numPublicInputs = Number(proofAsStrings[1]); | ||
async verifyProof(proofData) { | ||
const { RawBuffer } = await Promise.resolve().then(() => __importStar(require('@aztec/bb.js'))); | ||
const proof = (0, verifier_js_1.reconstructProofWithPublicInputsHonk)(proofData); | ||
await this.instantiate(); | ||
const vkBuf = await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode); | ||
return await this.api.acirVerifyUltraHonk(proof, new RawBuffer(vkBuf)); | ||
return this.backend.verifyProof(proof); | ||
} | ||
async getVerificationKey() { | ||
await this.instantiate(); | ||
return await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode); | ||
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, _numOfPublicInputs) { | ||
await this.instantiate(); | ||
// TODO(https://github.com/noir-lang/noir/issues/5661): This needs to be updated to handle recursive aggregation. | ||
// There is still a proofAsFields method but we could consider getting rid of it as the proof itself | ||
// is a list of field elements. | ||
// UltraHonk also does not have public inputs directly prepended to the proof and they are still instead | ||
// inserted at an offset. | ||
// const proof = reconstructProofWithPublicInputs(proofData); | ||
// const proofAsFields = (await this.api.acirProofAsFieldsUltraHonk(proof)).slice(numOfPublicInputs); | ||
// TODO: perhaps we should put this in the init function. Need to benchmark | ||
// TODO how long it takes. | ||
const vkBuf = await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode); | ||
const vk = await this.api.acirVkAsFieldsUltraHonk(vkBuf); | ||
return { | ||
// TODO(https://github.com/noir-lang/noir/issues/5661) | ||
proofAsFields: [], | ||
vkAsFields: vk.map((vk) => vk.toString()), | ||
// We use an empty string for the vk hash here as it is unneeded as part of the recursive artifacts | ||
// The user can be expected to hash the vk inside their circuit to check whether the vk is the circuit | ||
// they expect | ||
vkHash: '', | ||
}; | ||
async generateRecursiveProofArtifacts(proofData, numOfPublicInputs) { | ||
const proof = (0, verifier_js_1.reconstructProofWithPublicInputsHonk)(proofData); | ||
return this.backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); | ||
} | ||
async destroy() { | ||
if (!this.api) { | ||
return; | ||
} | ||
await this.api.destroy(); | ||
await this.backend.destroy(); | ||
} | ||
} | ||
exports.UltraHonkBackend = UltraHonkBackend; |
export { BarretenbergBackend, UltraHonkBackend } from './backend.js'; | ||
export { BarretenbergVerifier, UltraHonkVerifier } from './verifier.js'; | ||
export { Backend, CompiledCircuit, ProofData } from '@noir-lang/types'; | ||
export { BackendOptions } from './types.js'; | ||
export { BackendOptions } from '@aztec/bb.js'; |
import { ProofData } from '@noir-lang/types'; | ||
import { BackendOptions } from './types.js'; | ||
import { BackendOptions } from '@aztec/bb.js'; | ||
export declare class BarretenbergVerifier { | ||
private options; | ||
private api; | ||
private acirComposer; | ||
private verifier; | ||
constructor(options?: BackendOptions); | ||
/** @ignore */ | ||
instantiate(): Promise<void>; | ||
/** @description Verifies a proof */ | ||
@@ -16,7 +12,4 @@ verifyProof(proofData: ProofData, verificationKey: Uint8Array): Promise<boolean>; | ||
export declare class UltraHonkVerifier { | ||
private options; | ||
private api; | ||
private verifier; | ||
constructor(options?: BackendOptions); | ||
/** @ignore */ | ||
instantiate(): Promise<void>; | ||
/** @description Verifies a proof */ | ||
@@ -23,0 +16,0 @@ verifyProof(proofData: ProofData, verificationKey: Uint8Array): Promise<boolean>; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.reconstructProofWithPublicInputsHonk = exports.UltraHonkVerifier = exports.reconstructProofWithPublicInputs = exports.BarretenbergVerifier = void 0; | ||
const public_inputs_js_1 = require("./public_inputs.js"); | ||
const bb_js_1 = require("@aztec/bb.js"); | ||
class BarretenbergVerifier { | ||
options; | ||
// 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; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
acirComposer; | ||
verifier; | ||
constructor(options = { threads: 1 }) { | ||
this.options = options; | ||
this.verifier = new bb_js_1.BarretenbergVerifier(options); | ||
} | ||
/** @ignore */ | ||
async instantiate() { | ||
if (!this.api) { | ||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { | ||
this.options.threads = navigator.hardwareConcurrency; | ||
} | ||
else { | ||
try { | ||
const os = await Promise.resolve().then(() => __importStar(require('os'))); | ||
this.options.threads = os.cpus().length; | ||
} | ||
catch (e) { | ||
console.log('Could not detect environment. Falling back to one thread.', e); | ||
} | ||
} | ||
const { Barretenberg, RawBuffer, Crs } = await Promise.resolve().then(() => __importStar(require('@aztec/bb.js'))); | ||
// This is the number of CRS points necessary to verify a Barretenberg proof. | ||
const NUM_CRS_POINTS_FOR_VERIFICATION = 0; | ||
const [api, crs] = await Promise.all([Barretenberg.new(this.options), Crs.new(NUM_CRS_POINTS_FOR_VERIFICATION)]); | ||
await api.commonInitSlabAllocator(NUM_CRS_POINTS_FOR_VERIFICATION); | ||
await api.srsInitSrs(new RawBuffer([] /* crs.getG1Data() */), NUM_CRS_POINTS_FOR_VERIFICATION, new RawBuffer(crs.getG2Data())); | ||
this.acirComposer = await api.acirNewAcirComposer(NUM_CRS_POINTS_FOR_VERIFICATION); | ||
this.api = api; | ||
} | ||
} | ||
/** @description Verifies a proof */ | ||
async verifyProof(proofData, verificationKey) { | ||
const { RawBuffer } = await Promise.resolve().then(() => __importStar(require('@aztec/bb.js'))); | ||
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 = reconstructProofWithPublicInputs(proofData); | ||
return await this.api.acirVerifyProof(this.acirComposer, proof); | ||
return this.verifier.verifyUltraplonkProof(proof, verificationKey); | ||
} | ||
async destroy() { | ||
if (!this.api) { | ||
return; | ||
} | ||
await this.api.destroy(); | ||
await this.verifier.destroy(); | ||
} | ||
@@ -92,47 +30,13 @@ } | ||
class UltraHonkVerifier { | ||
options; | ||
// 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. | ||
api; | ||
verifier; | ||
constructor(options = { threads: 1 }) { | ||
this.options = options; | ||
this.verifier = new bb_js_1.BarretenbergVerifier(options); | ||
} | ||
/** @ignore */ | ||
async instantiate() { | ||
if (!this.api) { | ||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { | ||
this.options.threads = navigator.hardwareConcurrency; | ||
} | ||
else { | ||
try { | ||
const os = await Promise.resolve().then(() => __importStar(require('os'))); | ||
this.options.threads = os.cpus().length; | ||
} | ||
catch (e) { | ||
console.log('Could not detect environment. Falling back to one thread.', e); | ||
} | ||
} | ||
const { Barretenberg, RawBuffer, Crs } = await Promise.resolve().then(() => __importStar(require('@aztec/bb.js'))); | ||
// This is the number of CRS points necessary to verify a Barretenberg proof. | ||
const NUM_CRS_POINTS_FOR_VERIFICATION = 0; | ||
const [api, crs] = await Promise.all([Barretenberg.new(this.options), Crs.new(NUM_CRS_POINTS_FOR_VERIFICATION)]); | ||
await api.commonInitSlabAllocator(NUM_CRS_POINTS_FOR_VERIFICATION); | ||
await api.srsInitSrs(new RawBuffer([] /* crs.getG1Data() */), NUM_CRS_POINTS_FOR_VERIFICATION, new RawBuffer(crs.getG2Data())); | ||
this.api = api; | ||
} | ||
} | ||
/** @description Verifies a proof */ | ||
async verifyProof(proofData, verificationKey) { | ||
const { RawBuffer } = await Promise.resolve().then(() => __importStar(require('@aztec/bb.js'))); | ||
await this.instantiate(); | ||
const proof = reconstructProofWithPublicInputsHonk(proofData); | ||
return await this.api.acirVerifyUltraHonk(proof, new RawBuffer(verificationKey)); | ||
return this.verifier.verifyUltrahonkProof(proof, verificationKey); | ||
} | ||
async destroy() { | ||
if (!this.api) { | ||
return; | ||
} | ||
await this.api.destroy(); | ||
await this.verifier.destroy(); | ||
} | ||
@@ -139,0 +43,0 @@ } |
import { Backend, CompiledCircuit, ProofData, VerifierBackend } from '@noir-lang/types'; | ||
import { BackendOptions } from './types.js'; | ||
import { type Barretenberg } from '@aztec/bb.js'; | ||
import { BackendOptions, UltraPlonkBackend, UltraHonkBackend as UltraHonkBackendInternal } from '@aztec/bb.js'; | ||
export declare class BarretenbergBackend implements Backend, VerifierBackend { | ||
protected options: BackendOptions; | ||
protected api: Barretenberg; | ||
protected acirComposer: any; | ||
protected acirUncompressedBytecode: Uint8Array; | ||
protected backend: UltraPlonkBackend; | ||
constructor(acirCircuit: CompiledCircuit, options?: BackendOptions); | ||
/** @ignore */ | ||
instantiate(): Promise<void>; | ||
/** @description Generates a proof */ | ||
@@ -41,12 +35,8 @@ generateProof(compressedWitness: Uint8Array): Promise<ProofData>; | ||
export declare class UltraHonkBackend implements Backend, VerifierBackend { | ||
protected options: BackendOptions; | ||
protected api: Barretenberg; | ||
protected acirUncompressedBytecode: Uint8Array; | ||
protected backend: UltraHonkBackendInternal; | ||
constructor(acirCircuit: CompiledCircuit, options?: BackendOptions); | ||
/** @ignore */ | ||
instantiate(): Promise<void>; | ||
generateProof(decompressedWitness: Uint8Array): Promise<ProofData>; | ||
generateProof(compressedWitness: Uint8Array): Promise<ProofData>; | ||
verifyProof(proofData: ProofData): Promise<boolean>; | ||
getVerificationKey(): Promise<Uint8Array>; | ||
generateRecursiveProofArtifacts(_proofData: ProofData, _numOfPublicInputs: number): Promise<{ | ||
generateRecursiveProofArtifacts(proofData: ProofData, numOfPublicInputs: number): Promise<{ | ||
proofAsFields: string[]; | ||
@@ -53,0 +43,0 @@ vkAsFields: string[]; |
@@ -1,5 +0,6 @@ | ||
import { decompressSync as gunzip } from 'fflate'; | ||
import { acirToUint8Array } from './serialize.js'; | ||
import { deflattenFields } from './public_inputs.js'; | ||
import { reconstructProofWithPublicInputs, reconstructProofWithPublicInputsHonk } from './verifier.js'; | ||
import { UltraPlonkBackend, UltraHonkBackend as UltraHonkBackendInternal } from '@aztec/bb.js'; | ||
import { decompressSync as gunzip } from 'fflate'; | ||
// This is the number of bytes in a UltraPlonk proof | ||
@@ -9,47 +10,11 @@ // minus the public inputs. | ||
export class BarretenbergBackend { | ||
options; | ||
// 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; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
acirComposer; | ||
acirUncompressedBytecode; | ||
backend; | ||
constructor(acirCircuit, options = { threads: 1 }) { | ||
this.options = options; | ||
const acirBytecodeBase64 = acirCircuit.bytecode; | ||
this.acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); | ||
const acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); | ||
this.backend = new UltraPlonkBackend(acirUncompressedBytecode, options); | ||
} | ||
/** @ignore */ | ||
async instantiate() { | ||
if (!this.api) { | ||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { | ||
this.options.threads = navigator.hardwareConcurrency; | ||
} | ||
else { | ||
try { | ||
const os = await import('os'); | ||
this.options.threads = os.cpus().length; | ||
} | ||
catch (e) { | ||
console.log('Could not detect environment. Falling back to one thread.', e); | ||
} | ||
} | ||
const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); | ||
const api = await Barretenberg.new(this.options); | ||
const honkRecursion = false; | ||
const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode, honkRecursion); | ||
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); | ||
await api.acirInitProvingKey(this.acirComposer, this.acirUncompressedBytecode); | ||
this.api = api; | ||
} | ||
} | ||
/** @description Generates a proof */ | ||
async generateProof(compressedWitness) { | ||
await this.instantiate(); | ||
const proofWithPublicInputs = await this.api.acirCreateProof(this.acirComposer, this.acirUncompressedBytecode, gunzip(compressedWitness)); | ||
const proofWithPublicInputs = await this.backend.generateProof(gunzip(compressedWitness)); | ||
const splitIndex = proofWithPublicInputs.length - numBytesInProofWithoutPublicInputs; | ||
@@ -78,15 +43,4 @@ const publicInputsConcatenated = proofWithPublicInputs.slice(0, splitIndex); | ||
async generateRecursiveProofArtifacts(proofData, numOfPublicInputs = 0) { | ||
await this.instantiate(); | ||
const proof = reconstructProofWithPublicInputs(proofData); | ||
const proofAsFields = (await this.api.acirSerializeProofIntoFields(this.acirComposer, proof, numOfPublicInputs)).slice(numOfPublicInputs); | ||
// TODO: perhaps we should put this in the init function. Need to benchmark | ||
// TODO how long it takes. | ||
await this.api.acirInitVerificationKey(this.acirComposer); | ||
// Note: If you don't init verification key, `acirSerializeVerificationKeyIntoFields`` will just hang on serialization | ||
const vk = await this.api.acirSerializeVerificationKeyIntoFields(this.acirComposer); | ||
return { | ||
proofAsFields: proofAsFields.map((p) => p.toString()), | ||
vkAsFields: vk[0].map((vk) => vk.toString()), | ||
vkHash: vk[1].toString(), | ||
}; | ||
return this.backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); | ||
} | ||
@@ -96,16 +50,9 @@ /** @description Verifies a proof */ | ||
const proof = reconstructProofWithPublicInputs(proofData); | ||
await this.instantiate(); | ||
await this.api.acirInitVerificationKey(this.acirComposer); | ||
return await this.api.acirVerifyProof(this.acirComposer, proof); | ||
return this.backend.verifyProof(proof); | ||
} | ||
async getVerificationKey() { | ||
await this.instantiate(); | ||
await this.api.acirInitVerificationKey(this.acirComposer); | ||
return await this.api.acirGetVerificationKey(this.acirComposer); | ||
return this.backend.getVerificationKey(); | ||
} | ||
async destroy() { | ||
if (!this.api) { | ||
return; | ||
} | ||
await this.api.destroy(); | ||
await this.backend.destroy(); | ||
} | ||
@@ -119,3 +66,2 @@ } | ||
export class UltraHonkBackend { | ||
options; | ||
// These type assertions are used so that we don't | ||
@@ -125,39 +71,10 @@ // have to initialize `api` in the constructor. | ||
// constructors cannot be asynchronous which is why we do this. | ||
api; | ||
acirUncompressedBytecode; | ||
backend; | ||
constructor(acirCircuit, options = { threads: 1 }) { | ||
this.options = options; | ||
const acirBytecodeBase64 = acirCircuit.bytecode; | ||
this.acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); | ||
const acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); | ||
this.backend = new UltraHonkBackendInternal(acirUncompressedBytecode, options); | ||
} | ||
/** @ignore */ | ||
async instantiate() { | ||
if (!this.api) { | ||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { | ||
this.options.threads = navigator.hardwareConcurrency; | ||
} | ||
else { | ||
try { | ||
const os = await import('os'); | ||
this.options.threads = os.cpus().length; | ||
} | ||
catch (e) { | ||
console.log('Could not detect environment. Falling back to one thread.', e); | ||
} | ||
} | ||
const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); | ||
const api = await Barretenberg.new(this.options); | ||
const honkRecursion = true; | ||
const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode, honkRecursion); | ||
const crs = await Crs.new(subgroupSize + 1); | ||
await api.commonInitSlabAllocator(subgroupSize); | ||
await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); | ||
// We don't init a proving key here in the Honk API | ||
// await api.acirInitProvingKey(this.acirComposer, this.acirUncompressedBytecode); | ||
this.api = api; | ||
} | ||
} | ||
async generateProof(decompressedWitness) { | ||
await this.instantiate(); | ||
const proofWithPublicInputs = await this.api.acirProveUltraHonk(this.acirUncompressedBytecode, gunzip(decompressedWitness)); | ||
async generateProof(compressedWitness) { | ||
const proofWithPublicInputs = await this.backend.generateProof(gunzip(compressedWitness)); | ||
const proofAsStrings = deflattenFields(proofWithPublicInputs.slice(4)); | ||
@@ -179,42 +96,16 @@ const numPublicInputs = Number(proofAsStrings[1]); | ||
async verifyProof(proofData) { | ||
const { RawBuffer } = await import('@aztec/bb.js'); | ||
const proof = reconstructProofWithPublicInputsHonk(proofData); | ||
await this.instantiate(); | ||
const vkBuf = await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode); | ||
return await this.api.acirVerifyUltraHonk(proof, new RawBuffer(vkBuf)); | ||
return this.backend.verifyProof(proof); | ||
} | ||
async getVerificationKey() { | ||
await this.instantiate(); | ||
return await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode); | ||
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, _numOfPublicInputs) { | ||
await this.instantiate(); | ||
// TODO(https://github.com/noir-lang/noir/issues/5661): This needs to be updated to handle recursive aggregation. | ||
// There is still a proofAsFields method but we could consider getting rid of it as the proof itself | ||
// is a list of field elements. | ||
// UltraHonk also does not have public inputs directly prepended to the proof and they are still instead | ||
// inserted at an offset. | ||
// const proof = reconstructProofWithPublicInputs(proofData); | ||
// const proofAsFields = (await this.api.acirProofAsFieldsUltraHonk(proof)).slice(numOfPublicInputs); | ||
// TODO: perhaps we should put this in the init function. Need to benchmark | ||
// TODO how long it takes. | ||
const vkBuf = await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode); | ||
const vk = await this.api.acirVkAsFieldsUltraHonk(vkBuf); | ||
return { | ||
// TODO(https://github.com/noir-lang/noir/issues/5661) | ||
proofAsFields: [], | ||
vkAsFields: vk.map((vk) => vk.toString()), | ||
// We use an empty string for the vk hash here as it is unneeded as part of the recursive artifacts | ||
// The user can be expected to hash the vk inside their circuit to check whether the vk is the circuit | ||
// they expect | ||
vkHash: '', | ||
}; | ||
async generateRecursiveProofArtifacts(proofData, numOfPublicInputs) { | ||
const proof = reconstructProofWithPublicInputsHonk(proofData); | ||
return this.backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); | ||
} | ||
async destroy() { | ||
if (!this.api) { | ||
return; | ||
} | ||
await this.api.destroy(); | ||
await this.backend.destroy(); | ||
} | ||
} |
export { BarretenbergBackend, UltraHonkBackend } from './backend.js'; | ||
export { BarretenbergVerifier, UltraHonkVerifier } from './verifier.js'; | ||
export { Backend, CompiledCircuit, ProofData } from '@noir-lang/types'; | ||
export { BackendOptions } from './types.js'; | ||
export { BackendOptions } from '@aztec/bb.js'; |
import { ProofData } from '@noir-lang/types'; | ||
import { BackendOptions } from './types.js'; | ||
import { BackendOptions } from '@aztec/bb.js'; | ||
export declare class BarretenbergVerifier { | ||
private options; | ||
private api; | ||
private acirComposer; | ||
private verifier; | ||
constructor(options?: BackendOptions); | ||
/** @ignore */ | ||
instantiate(): Promise<void>; | ||
/** @description Verifies a proof */ | ||
@@ -16,7 +12,4 @@ verifyProof(proofData: ProofData, verificationKey: Uint8Array): Promise<boolean>; | ||
export declare class UltraHonkVerifier { | ||
private options; | ||
private api; | ||
private verifier; | ||
constructor(options?: BackendOptions); | ||
/** @ignore */ | ||
instantiate(): Promise<void>; | ||
/** @description Verifies a proof */ | ||
@@ -23,0 +16,0 @@ verifyProof(proofData: ProofData, verificationKey: Uint8Array): Promise<boolean>; |
import { flattenFieldsAsArray } from './public_inputs.js'; | ||
import { BarretenbergVerifier as BarretenbergVerifierInternal } from '@aztec/bb.js'; | ||
export class BarretenbergVerifier { | ||
options; | ||
// 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; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
acirComposer; | ||
verifier; | ||
constructor(options = { threads: 1 }) { | ||
this.options = options; | ||
this.verifier = new BarretenbergVerifierInternal(options); | ||
} | ||
/** @ignore */ | ||
async instantiate() { | ||
if (!this.api) { | ||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { | ||
this.options.threads = navigator.hardwareConcurrency; | ||
} | ||
else { | ||
try { | ||
const os = await import('os'); | ||
this.options.threads = os.cpus().length; | ||
} | ||
catch (e) { | ||
console.log('Could not detect environment. Falling back to one thread.', e); | ||
} | ||
} | ||
const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); | ||
// This is the number of CRS points necessary to verify a Barretenberg proof. | ||
const NUM_CRS_POINTS_FOR_VERIFICATION = 0; | ||
const [api, crs] = await Promise.all([Barretenberg.new(this.options), Crs.new(NUM_CRS_POINTS_FOR_VERIFICATION)]); | ||
await api.commonInitSlabAllocator(NUM_CRS_POINTS_FOR_VERIFICATION); | ||
await api.srsInitSrs(new RawBuffer([] /* crs.getG1Data() */), NUM_CRS_POINTS_FOR_VERIFICATION, new RawBuffer(crs.getG2Data())); | ||
this.acirComposer = await api.acirNewAcirComposer(NUM_CRS_POINTS_FOR_VERIFICATION); | ||
this.api = api; | ||
} | ||
} | ||
/** @description Verifies a proof */ | ||
async verifyProof(proofData, verificationKey) { | ||
const { RawBuffer } = await import('@aztec/bb.js'); | ||
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 = reconstructProofWithPublicInputs(proofData); | ||
return await this.api.acirVerifyProof(this.acirComposer, proof); | ||
return this.verifier.verifyUltraplonkProof(proof, verificationKey); | ||
} | ||
async destroy() { | ||
if (!this.api) { | ||
return; | ||
} | ||
await this.api.destroy(); | ||
await this.verifier.destroy(); | ||
} | ||
@@ -64,47 +25,13 @@ } | ||
export class UltraHonkVerifier { | ||
options; | ||
// 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. | ||
api; | ||
verifier; | ||
constructor(options = { threads: 1 }) { | ||
this.options = options; | ||
this.verifier = new BarretenbergVerifierInternal(options); | ||
} | ||
/** @ignore */ | ||
async instantiate() { | ||
if (!this.api) { | ||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { | ||
this.options.threads = navigator.hardwareConcurrency; | ||
} | ||
else { | ||
try { | ||
const os = await import('os'); | ||
this.options.threads = os.cpus().length; | ||
} | ||
catch (e) { | ||
console.log('Could not detect environment. Falling back to one thread.', e); | ||
} | ||
} | ||
const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); | ||
// This is the number of CRS points necessary to verify a Barretenberg proof. | ||
const NUM_CRS_POINTS_FOR_VERIFICATION = 0; | ||
const [api, crs] = await Promise.all([Barretenberg.new(this.options), Crs.new(NUM_CRS_POINTS_FOR_VERIFICATION)]); | ||
await api.commonInitSlabAllocator(NUM_CRS_POINTS_FOR_VERIFICATION); | ||
await api.srsInitSrs(new RawBuffer([] /* crs.getG1Data() */), NUM_CRS_POINTS_FOR_VERIFICATION, new RawBuffer(crs.getG2Data())); | ||
this.api = api; | ||
} | ||
} | ||
/** @description Verifies a proof */ | ||
async verifyProof(proofData, verificationKey) { | ||
const { RawBuffer } = await import('@aztec/bb.js'); | ||
await this.instantiate(); | ||
const proof = reconstructProofWithPublicInputsHonk(proofData); | ||
return await this.api.acirVerifyUltraHonk(proof, new RawBuffer(verificationKey)); | ||
return this.verifier.verifyUltrahonkProof(proof, verificationKey); | ||
} | ||
async destroy() { | ||
if (!this.api) { | ||
return; | ||
} | ||
await this.api.destroy(); | ||
await this.verifier.destroy(); | ||
} | ||
@@ -111,0 +38,0 @@ } |
@@ -6,3 +6,3 @@ { | ||
], | ||
"version": "0.34.0-e74b4ae.nightly", | ||
"version": "0.34.0-fd03ea0.nightly", | ||
"packageManager": "yarn@3.5.1", | ||
@@ -45,4 +45,4 @@ "license": "(MIT OR Apache-2.0)", | ||
"dependencies": { | ||
"@aztec/bb.js": "0.55.0", | ||
"@noir-lang/types": "0.34.0-e74b4ae.nightly", | ||
"@aztec/bb.js": "0.56.0", | ||
"@noir-lang/types": "0.34.0-fd03ea0.nightly", | ||
"fflate": "^0.8.0" | ||
@@ -49,0 +49,0 @@ }, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
31764
27
643
+ Added@aztec/bb.js@0.56.0(transitive)
+ Added@noir-lang/types@0.34.0-fd03ea0.nightly(transitive)
- Removed@aztec/bb.js@0.55.0(transitive)
- Removed@noir-lang/types@0.34.0-e74b4ae.nightly(transitive)
Updated@aztec/bb.js@0.56.0