1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import { decompressSync } from 'fflate';
// @ts-ignore
import { Barretenberg, Crs, RawBuffer, Fr } from '@aztec/bb.js';
import { executeCircuit, compressWitness } from '@noir-lang/acvm_js';
import { ethers } from 'ethers'; // I'm lazy so I'm using ethers to pad my input
import { Ptr } from '@aztec/bb.js/dest/browser/types';
export class Noir {
circuit: any;
acir: string = '';
acirBufferCompressed: Uint8Array = Uint8Array.from([]);
acirBufferUncompressed: Uint8Array = Uint8Array.from([]);
api = {} as Barretenberg;
acirComposer = {} as Ptr;
constructor(circuit: Object) {
this.circuit = circuit;
}
async init() {
const isBrowser = typeof window !== 'undefined';
if (isBrowser) {
const { default: initACVM } = await import('@noir-lang/acvm_js');
await initACVM();
}
this.acirBufferCompressed = Buffer.from(this.circuit.bytecode, 'base64');
this.acirBufferUncompressed = decompressSync(this.acirBufferCompressed);
this.api = await Barretenberg.new(4);
const [exact, total, subgroup] = await this.api.acirGetCircuitSizes(
this.acirBufferUncompressed,
);
const subgroupSize = Math.pow(2, Math.ceil(Math.log2(total)));
const crs = await Crs.new(subgroupSize + 1);
await this.api.commonInitSlabAllocator(subgroupSize);
await this.api.srsInitSrs(
new RawBuffer(crs.getG1Data()),
crs.numPoints,
new RawBuffer(crs.getG2Data()),
);
this.acirComposer = await this.api.acirNewAcirComposer(subgroupSize);
}
// Generates the intermediate witnesses by using `input`
// as the initial set of witnesses and executing these
// against the circuit.
async generateWitness(input: any): Promise<Uint8Array> {
const initialWitness = new Map<number, string>();
for (let i = 1; i <= input.length; i++) {
initialWitness.set(i, ethers.utils.hexZeroPad(input[i - 1], 32));
}
const witnessMap = await executeCircuit(this.acirBufferCompressed, initialWitness, () => {
throw Error('unexpected oracle');
});
const witnessBuff = compressWitness(witnessMap);
return witnessBuff;
}
// Generates an inner proof. This is the proof that will be verified
// in another circuit.
//
// We set isRecursive to true, which will tell the backend to
// generate the proof using components that will make the proof
// easier to verify in a circuit.
async generateInnerProof(witness: Uint8Array) {
const makeEasyToVerifyInCircuit = true;
return this.generateProof(witness, makeEasyToVerifyInCircuit);
}
// Generates artifacts that will be passed to the 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 number of public inputs denotes how many public inputs are in the inner proof.
async generateInnerProofArtifacts(proof: Uint8Array, numOfPublicInputs: number = 0) {
console.log('serializing proof');
const proofAsFields = await this.api.acirSerializeProofIntoFields(
this.acirComposer,
proof,
numOfPublicInputs,
);
console.log('proof serialized');
console.log('serializing vk');
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);
console.log('vk serialized');
return {
proofAsFields: proofAsFields.map(p => p.toString()),
vkAsFields: vk[0].map(vk => vk.toString()),
vkHash: vk[1].toString(),
};
}
// Generate an outer proof. This is the proof for the circuit which will verify
// inner proofs.
//
// The settings for this proof are the same as the settings for a "normal" proof
// ie one that is not in the recursive setting.
async generateOuterProof(witness: Uint8Array) {
const makeEasyToVerifyInCircuit = false;
return this.generateProof(witness, makeEasyToVerifyInCircuit);
}
async generateProof(witness: Uint8Array, makeEasyToVerifyInCircuit: boolean) {
console.log('Creating outer proof');
const decompressedWitness = decompressSync(witness);
const proof = await this.api.acirCreateProof(
this.acirComposer,
this.acirBufferUncompressed,
decompressedWitness,
makeEasyToVerifyInCircuit,
);
return proof;
}
async verifyInnerProof(proof: Uint8Array) {
const makeEasyToVerifyInCircuit = true;
return this.verifyProof(proof, makeEasyToVerifyInCircuit);
}
async verifyOuterProof(proof: Uint8Array) {
const makeEasyToVerifyInCircuit = false;
console.log('verifying outer proof');
const verified = await this.verifyProof(proof, makeEasyToVerifyInCircuit);
console.log(verified);
return verified;
}
async verifyProof(proof: Uint8Array, makeEasyToVerifyInCircuit: boolean) {
await this.api.acirInitVerificationKey(this.acirComposer);
const verified = await this.api.acirVerifyProof(
this.acirComposer,
proof,
makeEasyToVerifyInCircuit,
);
return verified;
}
async destroy() {
await this.api.destroy();
}
}