@izi-noir/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +458 -0
- package/dist/IProvingSystem-D9TnEig0.d.ts +140 -0
- package/dist/IProvingSystem-TKNofoo8.d.cts +140 -0
- package/dist/index.cjs +2793 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1196 -0
- package/dist/index.d.ts +1196 -0
- package/dist/index.js +2730 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/arkworks.cjs +824 -0
- package/dist/providers/arkworks.cjs.map +1 -0
- package/dist/providers/arkworks.d.cts +121 -0
- package/dist/providers/arkworks.d.ts +121 -0
- package/dist/providers/arkworks.js +791 -0
- package/dist/providers/arkworks.js.map +1 -0
- package/dist/providers/barretenberg.cjs +822 -0
- package/dist/providers/barretenberg.cjs.map +1 -0
- package/dist/providers/barretenberg.d.cts +18 -0
- package/dist/providers/barretenberg.d.ts +18 -0
- package/dist/providers/barretenberg.js +790 -0
- package/dist/providers/barretenberg.js.map +1 -0
- package/dist/providers/solana.cjs +262 -0
- package/dist/providers/solana.cjs.map +1 -0
- package/dist/providers/solana.d.cts +223 -0
- package/dist/providers/solana.d.ts +223 -0
- package/dist/providers/solana.js +222 -0
- package/dist/providers/solana.js.map +1 -0
- package/dist/providers/sunspot.cjs +475 -0
- package/dist/providers/sunspot.cjs.map +1 -0
- package/dist/providers/sunspot.d.cts +210 -0
- package/dist/providers/sunspot.d.ts +210 -0
- package/dist/providers/sunspot.js +443 -0
- package/dist/providers/sunspot.js.map +1 -0
- package/dist/types-CaaigonG.d.cts +93 -0
- package/dist/types-CaaigonG.d.ts +93 -0
- package/dist/wasm/nodejs/arkworks_groth16_wasm.js +448 -0
- package/dist/wasm/nodejs/arkworks_groth16_wasm_bg.wasm +0 -0
- package/dist/wasm/web/arkworks_groth16_wasm.js +536 -0
- package/dist/wasm/web/arkworks_groth16_wasm_bg.wasm +0 -0
- package/dist/wasmInit-KV6DTj4J.d.ts +282 -0
- package/dist/wasmInit-iEYiiB8M.d.cts +282 -0
- package/package.json +87 -0
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/infra/provingSystems/ArkworksWasm.ts
|
|
12
|
+
var ArkworksWasm_exports = {};
|
|
13
|
+
__export(ArkworksWasm_exports, {
|
|
14
|
+
ArkworksWasm: () => ArkworksWasm,
|
|
15
|
+
isArkworksCircuit: () => isArkworksCircuit
|
|
16
|
+
});
|
|
17
|
+
import { compile, createFileManager } from "@noir-lang/noir_wasm";
|
|
18
|
+
import { Noir } from "@noir-lang/noir_js";
|
|
19
|
+
function stringToStream(content) {
|
|
20
|
+
return new ReadableStream({
|
|
21
|
+
start(controller) {
|
|
22
|
+
controller.enqueue(new TextEncoder().encode(content));
|
|
23
|
+
controller.close();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function isNodeJs() {
|
|
28
|
+
return typeof globalThis.process !== "undefined" && globalThis.process.versions != null && globalThis.process.versions.node != null;
|
|
29
|
+
}
|
|
30
|
+
async function createTempDir() {
|
|
31
|
+
if (!isNodeJs()) {
|
|
32
|
+
return { basePath: "/", cleanup: null };
|
|
33
|
+
}
|
|
34
|
+
const fs = await import("fs/promises");
|
|
35
|
+
const os = await import("os");
|
|
36
|
+
const path = await import("path");
|
|
37
|
+
const basePath = await fs.mkdtemp(path.join(os.tmpdir(), "arkworks-circuit-"));
|
|
38
|
+
const cleanup = async () => {
|
|
39
|
+
await fs.rm(basePath, { recursive: true, force: true });
|
|
40
|
+
};
|
|
41
|
+
return { basePath, cleanup };
|
|
42
|
+
}
|
|
43
|
+
function isArkworksCircuit(circuit) {
|
|
44
|
+
return "__arkworks" in circuit && circuit.__arkworks === true;
|
|
45
|
+
}
|
|
46
|
+
async function initWasm() {
|
|
47
|
+
if (wasmModule) {
|
|
48
|
+
return wasmModule;
|
|
49
|
+
}
|
|
50
|
+
if (wasmInitPromise) {
|
|
51
|
+
return wasmInitPromise;
|
|
52
|
+
}
|
|
53
|
+
wasmInitPromise = (async () => {
|
|
54
|
+
try {
|
|
55
|
+
if (isNodeJs()) {
|
|
56
|
+
const module = await import("../wasm/nodejs/arkworks_groth16_wasm.js");
|
|
57
|
+
wasmModule = module;
|
|
58
|
+
} else {
|
|
59
|
+
const module = await import("../wasm/web/arkworks_groth16_wasm.js");
|
|
60
|
+
if (typeof module.default === "function") {
|
|
61
|
+
await module.default();
|
|
62
|
+
}
|
|
63
|
+
wasmModule = module;
|
|
64
|
+
}
|
|
65
|
+
return wasmModule;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
wasmInitPromise = null;
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Failed to initialize arkworks-groth16-wasm: ${error instanceof Error ? error.message : String(error)}
|
|
70
|
+
Make sure the WASM module is built: cd packages/arkworks-groth16-wasm && npm run build`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
return wasmInitPromise;
|
|
75
|
+
}
|
|
76
|
+
function base64ToUint8Array(b64) {
|
|
77
|
+
const binaryString = atob(b64);
|
|
78
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
79
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
80
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
81
|
+
}
|
|
82
|
+
return bytes;
|
|
83
|
+
}
|
|
84
|
+
function uint8ArrayToBase64(bytes) {
|
|
85
|
+
let binary = "";
|
|
86
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
87
|
+
binary += String.fromCharCode(bytes[i]);
|
|
88
|
+
}
|
|
89
|
+
return btoa(binary);
|
|
90
|
+
}
|
|
91
|
+
function publicInputsToGnarkBase64(publicInputs) {
|
|
92
|
+
const FIELD_SIZE = 32;
|
|
93
|
+
const bytes = new Uint8Array(publicInputs.length * FIELD_SIZE);
|
|
94
|
+
for (let i = 0; i < publicInputs.length; i++) {
|
|
95
|
+
const input = publicInputs[i];
|
|
96
|
+
const hex = input.startsWith("0x") ? input.slice(2) : input;
|
|
97
|
+
const inputBytes = hexToBytes(hex.padStart(64, "0"));
|
|
98
|
+
bytes.set(inputBytes, i * FIELD_SIZE);
|
|
99
|
+
}
|
|
100
|
+
return uint8ArrayToBase64(bytes);
|
|
101
|
+
}
|
|
102
|
+
function hexToBytes(hex) {
|
|
103
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
104
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
105
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
106
|
+
}
|
|
107
|
+
return bytes;
|
|
108
|
+
}
|
|
109
|
+
var wasmModule, wasmInitPromise, ArkworksWasm;
|
|
110
|
+
var init_ArkworksWasm = __esm({
|
|
111
|
+
"src/infra/provingSystems/ArkworksWasm.ts"() {
|
|
112
|
+
"use strict";
|
|
113
|
+
wasmModule = null;
|
|
114
|
+
wasmInitPromise = null;
|
|
115
|
+
ArkworksWasm = class {
|
|
116
|
+
config;
|
|
117
|
+
constructor(config = {}) {
|
|
118
|
+
this.config = {
|
|
119
|
+
keepArtifacts: false,
|
|
120
|
+
cacheKeys: true,
|
|
121
|
+
...config
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Compile Noir code to a circuit with ACIR for Groth16 proving
|
|
126
|
+
*/
|
|
127
|
+
async compile(noirCode) {
|
|
128
|
+
const wasm = await initWasm();
|
|
129
|
+
const { basePath, cleanup } = await createTempDir();
|
|
130
|
+
const fm = createFileManager(basePath);
|
|
131
|
+
const nargoToml = `[package]
|
|
132
|
+
name = "circuit"
|
|
133
|
+
type = "bin"
|
|
134
|
+
authors = [""]
|
|
135
|
+
|
|
136
|
+
[dependencies]
|
|
137
|
+
`;
|
|
138
|
+
try {
|
|
139
|
+
if (isNodeJs()) {
|
|
140
|
+
await fm.writeFile("./src/main.nr", stringToStream(noirCode));
|
|
141
|
+
await fm.writeFile("./Nargo.toml", stringToStream(nargoToml));
|
|
142
|
+
} else {
|
|
143
|
+
fm.writeFile("./src/main.nr", stringToStream(noirCode));
|
|
144
|
+
fm.writeFile("./Nargo.toml", stringToStream(nargoToml));
|
|
145
|
+
}
|
|
146
|
+
const result = await compile(fm);
|
|
147
|
+
const compiled = result.program;
|
|
148
|
+
if (!compiled || !compiled.bytecode) {
|
|
149
|
+
throw new Error("Compilation failed: no bytecode generated");
|
|
150
|
+
}
|
|
151
|
+
const acirJson = JSON.stringify({
|
|
152
|
+
functions: [
|
|
153
|
+
{
|
|
154
|
+
current_witness_index: compiled.abi.parameters.length + 1,
|
|
155
|
+
opcodes: [],
|
|
156
|
+
// Will be extracted from bytecode during prove
|
|
157
|
+
private_parameters: compiled.abi.parameters.filter((p) => p.visibility === "private").map((_, i) => i + 1),
|
|
158
|
+
public_parameters: {
|
|
159
|
+
witnesses: compiled.abi.parameters.filter((p) => p.visibility === "public").map((_, i) => i + 1)
|
|
160
|
+
},
|
|
161
|
+
return_values: { witnesses: [] }
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
});
|
|
165
|
+
let provingKey;
|
|
166
|
+
let verifyingKey;
|
|
167
|
+
let verifyingKeyGnark;
|
|
168
|
+
if (this.config.cacheKeys) {
|
|
169
|
+
try {
|
|
170
|
+
const setupResult = wasm.setup(acirJson);
|
|
171
|
+
provingKey = setupResult.proving_key;
|
|
172
|
+
verifyingKey = setupResult.verifying_key;
|
|
173
|
+
verifyingKeyGnark = setupResult.verifying_key_gnark;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.warn("Deferred setup: will run during proof generation");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const arkworksCircuit = {
|
|
179
|
+
...compiled,
|
|
180
|
+
__arkworks: true,
|
|
181
|
+
acirJson,
|
|
182
|
+
provingKey,
|
|
183
|
+
verifyingKey,
|
|
184
|
+
verifyingKeyGnark
|
|
185
|
+
};
|
|
186
|
+
return arkworksCircuit;
|
|
187
|
+
} finally {
|
|
188
|
+
if (cleanup) {
|
|
189
|
+
await cleanup();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Generate a Groth16 proof
|
|
195
|
+
*/
|
|
196
|
+
async generateProof(circuit, inputs) {
|
|
197
|
+
const wasm = await initWasm();
|
|
198
|
+
if (!isArkworksCircuit(circuit)) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
"ArkworksWasm.generateProof requires an ArkworksCompiledCircuit. Use ArkworksWasm.compile() first."
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const noir = new Noir(circuit);
|
|
204
|
+
const { witness } = await noir.execute(inputs);
|
|
205
|
+
const witnessMap = {};
|
|
206
|
+
for (const [index, value] of witness.entries()) {
|
|
207
|
+
witnessMap[index.toString()] = String(value);
|
|
208
|
+
}
|
|
209
|
+
const witnessJson = JSON.stringify(witnessMap);
|
|
210
|
+
let provingKey = circuit.provingKey;
|
|
211
|
+
if (!provingKey) {
|
|
212
|
+
const setupResult = wasm.setup(circuit.acirJson);
|
|
213
|
+
provingKey = setupResult.proving_key;
|
|
214
|
+
circuit.provingKey = provingKey;
|
|
215
|
+
circuit.verifyingKey = setupResult.verifying_key;
|
|
216
|
+
circuit.verifyingKeyGnark = setupResult.verifying_key_gnark;
|
|
217
|
+
}
|
|
218
|
+
const proofResult = wasm.prove(provingKey, circuit.acirJson, witnessJson);
|
|
219
|
+
const proofBytes = base64ToUint8Array(proofResult.proof_gnark);
|
|
220
|
+
return {
|
|
221
|
+
proof: proofBytes,
|
|
222
|
+
publicInputs: proofResult.public_inputs
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Verify a Groth16 proof
|
|
227
|
+
*/
|
|
228
|
+
async verifyProof(circuit, proof, publicInputs) {
|
|
229
|
+
const wasm = await initWasm();
|
|
230
|
+
if (!isArkworksCircuit(circuit)) {
|
|
231
|
+
throw new Error(
|
|
232
|
+
"ArkworksWasm.verifyProof requires an ArkworksCompiledCircuit. Use ArkworksWasm.compile() first."
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
let verifyingKeyGnark = circuit.verifyingKeyGnark;
|
|
236
|
+
if (!verifyingKeyGnark) {
|
|
237
|
+
const setupResult = wasm.setup(circuit.acirJson);
|
|
238
|
+
circuit.provingKey = setupResult.proving_key;
|
|
239
|
+
circuit.verifyingKey = setupResult.verifying_key;
|
|
240
|
+
verifyingKeyGnark = setupResult.verifying_key_gnark;
|
|
241
|
+
circuit.verifyingKeyGnark = verifyingKeyGnark;
|
|
242
|
+
}
|
|
243
|
+
const proofB64 = uint8ArrayToBase64(proof);
|
|
244
|
+
const publicInputsGnarkB64 = publicInputsToGnarkBase64(publicInputs);
|
|
245
|
+
return wasm.verify_gnark(
|
|
246
|
+
verifyingKeyGnark,
|
|
247
|
+
proofB64,
|
|
248
|
+
publicInputsGnarkB64,
|
|
249
|
+
publicInputs.length
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get the verifying key in gnark format for on-chain deployment
|
|
254
|
+
*/
|
|
255
|
+
async getVerifyingKeyGnark(circuit) {
|
|
256
|
+
const wasm = await initWasm();
|
|
257
|
+
if (!isArkworksCircuit(circuit)) {
|
|
258
|
+
throw new Error("getVerifyingKeyGnark requires an ArkworksCompiledCircuit");
|
|
259
|
+
}
|
|
260
|
+
if (!circuit.verifyingKeyGnark) {
|
|
261
|
+
const setupResult = wasm.setup(circuit.acirJson);
|
|
262
|
+
circuit.provingKey = setupResult.proving_key;
|
|
263
|
+
circuit.verifyingKey = setupResult.verifying_key;
|
|
264
|
+
circuit.verifyingKeyGnark = setupResult.verifying_key_gnark;
|
|
265
|
+
}
|
|
266
|
+
return base64ToUint8Array(circuit.verifyingKeyGnark);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// src/infra/provingSystems/Barretenberg.ts
|
|
273
|
+
var Barretenberg_exports = {};
|
|
274
|
+
__export(Barretenberg_exports, {
|
|
275
|
+
Barretenberg: () => Barretenberg
|
|
276
|
+
});
|
|
277
|
+
import { compile as compile2, createFileManager as createFileManager2 } from "@noir-lang/noir_wasm";
|
|
278
|
+
import { Noir as Noir2 } from "@noir-lang/noir_js";
|
|
279
|
+
import { Barretenberg as BarretenbergBackend, UltraHonkBackend } from "@aztec/bb.js";
|
|
280
|
+
function stringToStream2(content) {
|
|
281
|
+
return new ReadableStream({
|
|
282
|
+
start(controller) {
|
|
283
|
+
controller.enqueue(new TextEncoder().encode(content));
|
|
284
|
+
controller.close();
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
function isNodeJs3() {
|
|
289
|
+
return typeof globalThis.process !== "undefined" && globalThis.process.versions != null && globalThis.process.versions.node != null;
|
|
290
|
+
}
|
|
291
|
+
async function createTempDir2() {
|
|
292
|
+
if (!isNodeJs3()) {
|
|
293
|
+
return { basePath: "/", cleanup: null };
|
|
294
|
+
}
|
|
295
|
+
const fs = await import("fs/promises");
|
|
296
|
+
const os = await import("os");
|
|
297
|
+
const path = await import("path");
|
|
298
|
+
const basePath = await fs.mkdtemp(path.join(os.tmpdir(), "noir-circuit-"));
|
|
299
|
+
const cleanup = async () => {
|
|
300
|
+
await fs.rm(basePath, { recursive: true, force: true });
|
|
301
|
+
};
|
|
302
|
+
return { basePath, cleanup };
|
|
303
|
+
}
|
|
304
|
+
var Barretenberg;
|
|
305
|
+
var init_Barretenberg = __esm({
|
|
306
|
+
"src/infra/provingSystems/Barretenberg.ts"() {
|
|
307
|
+
"use strict";
|
|
308
|
+
Barretenberg = class {
|
|
309
|
+
async compile(noirCode) {
|
|
310
|
+
const { basePath, cleanup } = await createTempDir2();
|
|
311
|
+
const fm = createFileManager2(basePath);
|
|
312
|
+
const nargoToml = `[package]
|
|
313
|
+
name = "circuit"
|
|
314
|
+
type = "bin"
|
|
315
|
+
authors = [""]
|
|
316
|
+
|
|
317
|
+
[dependencies]
|
|
318
|
+
`;
|
|
319
|
+
try {
|
|
320
|
+
if (isNodeJs3()) {
|
|
321
|
+
await fm.writeFile("./src/main.nr", stringToStream2(noirCode));
|
|
322
|
+
await fm.writeFile("./Nargo.toml", stringToStream2(nargoToml));
|
|
323
|
+
} else {
|
|
324
|
+
fm.writeFile("./src/main.nr", stringToStream2(noirCode));
|
|
325
|
+
fm.writeFile("./Nargo.toml", stringToStream2(nargoToml));
|
|
326
|
+
}
|
|
327
|
+
const result = await compile2(fm);
|
|
328
|
+
const compiled = result.program;
|
|
329
|
+
if (!compiled || !compiled.bytecode) {
|
|
330
|
+
throw new Error("Compilation failed: no bytecode generated");
|
|
331
|
+
}
|
|
332
|
+
return compiled;
|
|
333
|
+
} finally {
|
|
334
|
+
if (cleanup) {
|
|
335
|
+
await cleanup();
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async generateProof(circuit, inputs) {
|
|
340
|
+
const noir = new Noir2(circuit);
|
|
341
|
+
const { witness } = await noir.execute(inputs);
|
|
342
|
+
const barretenberg = await BarretenbergBackend.new({ threads: 1 });
|
|
343
|
+
const backend = new UltraHonkBackend(circuit.bytecode, barretenberg);
|
|
344
|
+
try {
|
|
345
|
+
const proofData = await backend.generateProof(witness);
|
|
346
|
+
return {
|
|
347
|
+
proof: proofData.proof,
|
|
348
|
+
publicInputs: proofData.publicInputs || []
|
|
349
|
+
};
|
|
350
|
+
} finally {
|
|
351
|
+
await barretenberg.destroy();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async verifyProof(circuit, proof, publicInputs) {
|
|
355
|
+
const barretenberg = await BarretenbergBackend.new({ threads: 1 });
|
|
356
|
+
const backend = new UltraHonkBackend(circuit.bytecode, barretenberg);
|
|
357
|
+
try {
|
|
358
|
+
return await backend.verifyProof({ proof, publicInputs });
|
|
359
|
+
} finally {
|
|
360
|
+
await barretenberg.destroy();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// src/infra/chainFormatters/SolanaFormatter.ts
|
|
368
|
+
var SolanaFormatter_exports = {};
|
|
369
|
+
__export(SolanaFormatter_exports, {
|
|
370
|
+
SolanaFormatter: () => SolanaFormatter
|
|
371
|
+
});
|
|
372
|
+
var G1_SIZE, G2_SIZE, SolanaFormatter;
|
|
373
|
+
var init_SolanaFormatter = __esm({
|
|
374
|
+
"src/infra/chainFormatters/SolanaFormatter.ts"() {
|
|
375
|
+
"use strict";
|
|
376
|
+
G1_SIZE = 64;
|
|
377
|
+
G2_SIZE = 128;
|
|
378
|
+
SolanaFormatter = class {
|
|
379
|
+
constructor(arkworksProvider) {
|
|
380
|
+
this.arkworksProvider = arkworksProvider;
|
|
381
|
+
}
|
|
382
|
+
chainId = "solana";
|
|
383
|
+
/**
|
|
384
|
+
* Format a generic proof for Solana on-chain verification.
|
|
385
|
+
*
|
|
386
|
+
* @param proofData - Generic proof data from Arkworks
|
|
387
|
+
* @param circuit - The compiled circuit (must be Arkworks circuit)
|
|
388
|
+
* @param metadata - Circuit metadata with public input count
|
|
389
|
+
* @returns SolanaProofData ready for on-chain verification
|
|
390
|
+
*/
|
|
391
|
+
async formatProof(proofData, circuit, metadata) {
|
|
392
|
+
const vkBytes = await this.arkworksProvider.getVerifyingKeyGnark(circuit);
|
|
393
|
+
const vkBase64 = this.uint8ArrayToBase64(vkBytes);
|
|
394
|
+
const nrPublicInputs = metadata.numPublicInputs;
|
|
395
|
+
const publicInputsBytes = proofData.publicInputs.map((input) => {
|
|
396
|
+
const hex = input.startsWith("0x") ? input.slice(2) : input;
|
|
397
|
+
return this.hexToBytes(hex.padStart(64, "0"));
|
|
398
|
+
});
|
|
399
|
+
const { accountSize, estimatedRent } = this.getChainMetadata(nrPublicInputs);
|
|
400
|
+
return {
|
|
401
|
+
verifyingKey: {
|
|
402
|
+
base64: vkBase64,
|
|
403
|
+
bytes: vkBytes,
|
|
404
|
+
nrPublicInputs
|
|
405
|
+
},
|
|
406
|
+
proof: {
|
|
407
|
+
base64: this.uint8ArrayToBase64(proofData.proof),
|
|
408
|
+
bytes: proofData.proof
|
|
409
|
+
},
|
|
410
|
+
publicInputs: {
|
|
411
|
+
hex: proofData.publicInputs,
|
|
412
|
+
bytes: publicInputsBytes
|
|
413
|
+
},
|
|
414
|
+
accountSize,
|
|
415
|
+
estimatedRent
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Get Solana-specific metadata for a circuit.
|
|
420
|
+
*
|
|
421
|
+
* @param publicInputCount - Number of public inputs in the circuit
|
|
422
|
+
* @returns Solana metadata with account size and rent estimates
|
|
423
|
+
*/
|
|
424
|
+
getChainMetadata(publicInputCount) {
|
|
425
|
+
const accountSize = this.calculateVkAccountSize(publicInputCount);
|
|
426
|
+
const estimatedRent = this.calculateVkAccountRent(publicInputCount);
|
|
427
|
+
return {
|
|
428
|
+
chainId: "solana",
|
|
429
|
+
accountSize,
|
|
430
|
+
estimatedRent
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Calculate the size of a VK account for a given number of public inputs.
|
|
435
|
+
* Matches the Rust `vk_account_size` function in the Solana program.
|
|
436
|
+
*/
|
|
437
|
+
calculateVkAccountSize(nrPublicInputs) {
|
|
438
|
+
const fixedSize = 8 + 32 + 1 + G1_SIZE + G2_SIZE * 3 + 4;
|
|
439
|
+
return fixedSize + (nrPublicInputs + 1) * G1_SIZE;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Calculate the minimum rent for a VK account.
|
|
443
|
+
*/
|
|
444
|
+
calculateVkAccountRent(nrPublicInputs, rentExemptionPerByte = 6960) {
|
|
445
|
+
const size = this.calculateVkAccountSize(nrPublicInputs);
|
|
446
|
+
return size * rentExemptionPerByte;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Convert Uint8Array to base64 string.
|
|
450
|
+
*/
|
|
451
|
+
uint8ArrayToBase64(bytes) {
|
|
452
|
+
if (typeof btoa === "function") {
|
|
453
|
+
let binary = "";
|
|
454
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
455
|
+
binary += String.fromCharCode(bytes[i]);
|
|
456
|
+
}
|
|
457
|
+
return btoa(binary);
|
|
458
|
+
}
|
|
459
|
+
return Buffer.from(bytes).toString("base64");
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Convert hex string to Uint8Array.
|
|
463
|
+
*/
|
|
464
|
+
hexToBytes(hex) {
|
|
465
|
+
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
466
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
467
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
468
|
+
bytes[i] = parseInt(cleanHex.substring(i * 2, i * 2 + 2), 16);
|
|
469
|
+
}
|
|
470
|
+
return bytes;
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// src/providers/arkworks.ts
|
|
477
|
+
init_ArkworksWasm();
|
|
478
|
+
|
|
479
|
+
// src/domain/types/provider.ts
|
|
480
|
+
var Provider = /* @__PURE__ */ ((Provider2) => {
|
|
481
|
+
Provider2["Barretenberg"] = "barretenberg";
|
|
482
|
+
Provider2["Arkworks"] = "arkworks";
|
|
483
|
+
Provider2["Sunspot"] = "sunspot";
|
|
484
|
+
return Provider2;
|
|
485
|
+
})(Provider || {});
|
|
486
|
+
|
|
487
|
+
// src/infra/wasm/wasmInit.ts
|
|
488
|
+
var wasmInitPromise2 = null;
|
|
489
|
+
var wasmInitialized = false;
|
|
490
|
+
function isNodeJs2() {
|
|
491
|
+
return typeof globalThis.process !== "undefined" && globalThis.process.versions != null && globalThis.process.versions.node != null;
|
|
492
|
+
}
|
|
493
|
+
async function initNoirWasm() {
|
|
494
|
+
if (wasmInitialized) return;
|
|
495
|
+
if (!wasmInitPromise2) {
|
|
496
|
+
wasmInitPromise2 = initWasmInternal();
|
|
497
|
+
}
|
|
498
|
+
await wasmInitPromise2;
|
|
499
|
+
wasmInitialized = true;
|
|
500
|
+
}
|
|
501
|
+
async function initWasmInternal() {
|
|
502
|
+
if (isNodeJs2()) {
|
|
503
|
+
await import("@noir-lang/acvm_js/nodejs/acvm_js.js");
|
|
504
|
+
await import("@noir-lang/noirc_abi/nodejs/noirc_abi_wasm.js");
|
|
505
|
+
} else {
|
|
506
|
+
const [{ default: initACVM }, { default: initNoirC }] = await Promise.all([
|
|
507
|
+
import("@noir-lang/acvm_js"),
|
|
508
|
+
import("@noir-lang/noirc_abi")
|
|
509
|
+
]);
|
|
510
|
+
await Promise.all([initACVM(), initNoirC()]);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
function isWasmInitialized() {
|
|
514
|
+
return wasmInitialized;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// src/IziNoir.ts
|
|
518
|
+
var IziNoir = class _IziNoir {
|
|
519
|
+
provingSystem;
|
|
520
|
+
compiledCircuit = null;
|
|
521
|
+
chainFormatters = /* @__PURE__ */ new Map();
|
|
522
|
+
chain;
|
|
523
|
+
_verifyingKey;
|
|
524
|
+
_lastProof;
|
|
525
|
+
constructor(provingSystem, chain) {
|
|
526
|
+
this.provingSystem = provingSystem;
|
|
527
|
+
this.chain = chain;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Get the verifying key from the last proof generation.
|
|
531
|
+
* Only available after calling prove() with a chain configured.
|
|
532
|
+
*/
|
|
533
|
+
get vk() {
|
|
534
|
+
return this._verifyingKey;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Get the configured chain, if any.
|
|
538
|
+
*/
|
|
539
|
+
get targetChain() {
|
|
540
|
+
return this.chain;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Check if operating in offchain mode (no chain configured).
|
|
544
|
+
*/
|
|
545
|
+
get isOffchain() {
|
|
546
|
+
return this.chain === void 0;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Register a chain formatter for chain-specific proof formatting.
|
|
550
|
+
*
|
|
551
|
+
* @param formatter - The chain formatter to register
|
|
552
|
+
*/
|
|
553
|
+
registerChainFormatter(formatter) {
|
|
554
|
+
this.chainFormatters.set(formatter.chainId, formatter);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Get a registered chain formatter.
|
|
558
|
+
*
|
|
559
|
+
* @param chainId - The chain ID to get the formatter for
|
|
560
|
+
* @returns The formatter or undefined if not registered
|
|
561
|
+
*/
|
|
562
|
+
getChainFormatter(chainId) {
|
|
563
|
+
return this.chainFormatters.get(chainId);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Initialize IziNoir with the specified provider and optional chain.
|
|
567
|
+
*
|
|
568
|
+
* @param config - Configuration specifying the provider, chain, and optional circuit paths
|
|
569
|
+
* @returns Initialized IziNoir instance
|
|
570
|
+
*
|
|
571
|
+
* @example
|
|
572
|
+
* ```typescript
|
|
573
|
+
* // On-chain mode (Solana)
|
|
574
|
+
* const izi = await IziNoir.init({
|
|
575
|
+
* provider: Provider.Arkworks,
|
|
576
|
+
* chain: Chain.Solana
|
|
577
|
+
* });
|
|
578
|
+
*
|
|
579
|
+
* // Offchain mode (no chain formatting)
|
|
580
|
+
* const iziOffchain = await IziNoir.init({
|
|
581
|
+
* provider: Provider.Arkworks
|
|
582
|
+
* });
|
|
583
|
+
*
|
|
584
|
+
* // Barretenberg (browser-compatible, ~16KB proofs, offchain only)
|
|
585
|
+
* const bb = await IziNoir.init({ provider: Provider.Barretenberg });
|
|
586
|
+
* ```
|
|
587
|
+
*/
|
|
588
|
+
static async init(config) {
|
|
589
|
+
await initNoirWasm();
|
|
590
|
+
let provingSystem;
|
|
591
|
+
switch (config.provider) {
|
|
592
|
+
case "barretenberg" /* Barretenberg */: {
|
|
593
|
+
if (config.chain) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
"Barretenberg provider does not support chain formatting. Use Provider.Arkworks for on-chain proofs."
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const { Barretenberg: Barretenberg2 } = await Promise.resolve().then(() => (init_Barretenberg(), Barretenberg_exports));
|
|
599
|
+
provingSystem = new Barretenberg2();
|
|
600
|
+
return new _IziNoir(provingSystem);
|
|
601
|
+
}
|
|
602
|
+
case "arkworks" /* Arkworks */: {
|
|
603
|
+
const { ArkworksWasm: ArkworksWasm2 } = await Promise.resolve().then(() => (init_ArkworksWasm(), ArkworksWasm_exports));
|
|
604
|
+
const arkworksInstance = new ArkworksWasm2();
|
|
605
|
+
provingSystem = arkworksInstance;
|
|
606
|
+
const instance = new _IziNoir(provingSystem, config.chain);
|
|
607
|
+
if (config.chain === "solana" /* Solana */ || !config.chain) {
|
|
608
|
+
const { SolanaFormatter: SolanaFormatter2 } = await Promise.resolve().then(() => (init_SolanaFormatter(), SolanaFormatter_exports));
|
|
609
|
+
instance.registerChainFormatter(new SolanaFormatter2(arkworksInstance));
|
|
610
|
+
}
|
|
611
|
+
return instance;
|
|
612
|
+
}
|
|
613
|
+
case "sunspot" /* Sunspot */: {
|
|
614
|
+
throw new Error(
|
|
615
|
+
'Sunspot is not available in the main entry point. Import from "@izi-noir/sdk/sunspot" for Sunspot support.'
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
default:
|
|
619
|
+
throw new Error(`Unknown provider: ${config.provider}`);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Get the underlying proving system instance.
|
|
624
|
+
* Useful for advanced use cases.
|
|
625
|
+
*/
|
|
626
|
+
getProvingSystem() {
|
|
627
|
+
return this.provingSystem;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Get the currently compiled circuit, if any.
|
|
631
|
+
*/
|
|
632
|
+
getCompiledCircuit() {
|
|
633
|
+
return this.compiledCircuit;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Compile Noir code into a circuit.
|
|
637
|
+
*
|
|
638
|
+
* @param noirCode - The Noir source code to compile
|
|
639
|
+
* @returns The compiled circuit
|
|
640
|
+
*/
|
|
641
|
+
async compile(noirCode) {
|
|
642
|
+
this.compiledCircuit = await this.provingSystem.compile(noirCode);
|
|
643
|
+
return this.compiledCircuit;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Generate a proof for the given inputs.
|
|
647
|
+
*
|
|
648
|
+
* If a chain is configured, returns chain-formatted proof data and stores
|
|
649
|
+
* the verifying key in `this.vk`. Otherwise, returns raw proof data.
|
|
650
|
+
*
|
|
651
|
+
* @param inputs - The inputs (both public and private) for the circuit
|
|
652
|
+
* @param circuit - Optional circuit to use (defaults to last compiled circuit)
|
|
653
|
+
* @returns The proof data - type depends on configured chain
|
|
654
|
+
* @throws Error if no circuit is available
|
|
655
|
+
*
|
|
656
|
+
* @example
|
|
657
|
+
* ```typescript
|
|
658
|
+
* // With chain configured - returns SolanaProofData
|
|
659
|
+
* const izi = await IziNoir.init({ provider: Provider.Arkworks, chain: Chain.Solana });
|
|
660
|
+
* await izi.compile(noirCode);
|
|
661
|
+
* const proof = await izi.prove({ expected: '100', secret: '10' });
|
|
662
|
+
* // proof is SolanaProofData, izi.vk is available
|
|
663
|
+
*
|
|
664
|
+
* // Offchain mode - returns ProofData
|
|
665
|
+
* const iziOffchain = await IziNoir.init({ provider: Provider.Arkworks });
|
|
666
|
+
* await iziOffchain.compile(noirCode);
|
|
667
|
+
* const rawProof = await iziOffchain.prove({ expected: '100', secret: '10' });
|
|
668
|
+
* // rawProof is ProofData, iziOffchain.vk is undefined
|
|
669
|
+
* ```
|
|
670
|
+
*/
|
|
671
|
+
async prove(inputs, circuit) {
|
|
672
|
+
const circuitToUse = circuit || this.compiledCircuit;
|
|
673
|
+
if (!circuitToUse) {
|
|
674
|
+
throw new Error("No circuit available. Call compile() first or provide a circuit.");
|
|
675
|
+
}
|
|
676
|
+
const rawProof = await this.provingSystem.generateProof(circuitToUse, inputs);
|
|
677
|
+
if (!this.chain) {
|
|
678
|
+
this._lastProof = rawProof;
|
|
679
|
+
return rawProof;
|
|
680
|
+
}
|
|
681
|
+
const formatter = this.chainFormatters.get(this.chain);
|
|
682
|
+
if (!formatter) {
|
|
683
|
+
throw new Error(
|
|
684
|
+
`No formatter registered for chain: ${this.chain}. This is an internal error - please report it.`
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
const metadata = {
|
|
688
|
+
numPublicInputs: rawProof.publicInputs.length
|
|
689
|
+
};
|
|
690
|
+
const formattedProof = await formatter.formatProof(rawProof, circuitToUse, metadata);
|
|
691
|
+
const chainProof = formattedProof;
|
|
692
|
+
this._verifyingKey = chainProof.verifyingKey;
|
|
693
|
+
this._lastProof = chainProof;
|
|
694
|
+
return chainProof;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Verify a proof.
|
|
698
|
+
* Available in both on-chain and offchain modes.
|
|
699
|
+
*
|
|
700
|
+
* @param proof - The proof bytes to verify
|
|
701
|
+
* @param publicInputs - The public inputs that were used
|
|
702
|
+
* @param circuit - Optional circuit to use (defaults to last compiled circuit)
|
|
703
|
+
* @returns true if the proof is valid, false otherwise
|
|
704
|
+
* @throws Error if no circuit is available
|
|
705
|
+
*/
|
|
706
|
+
async verify(proof, publicInputs, circuit) {
|
|
707
|
+
const circuitToUse = circuit || this.compiledCircuit;
|
|
708
|
+
if (!circuitToUse) {
|
|
709
|
+
throw new Error("No circuit available. Call compile() first or provide a circuit.");
|
|
710
|
+
}
|
|
711
|
+
return this.provingSystem.verifyProof(circuitToUse, proof, publicInputs);
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Convenience method: compile, prove, and verify in one call.
|
|
715
|
+
*
|
|
716
|
+
* @param noirCode - The Noir source code to compile
|
|
717
|
+
* @param inputs - The inputs (both public and private) for the circuit
|
|
718
|
+
* @returns Object containing proof data and verification result
|
|
719
|
+
*
|
|
720
|
+
* @example
|
|
721
|
+
* ```typescript
|
|
722
|
+
* const { proof, verified } = await izi.createProof(noirCode, {
|
|
723
|
+
* x: '100',
|
|
724
|
+
* y: '10',
|
|
725
|
+
* });
|
|
726
|
+
* console.log(`Verified: ${verified}`);
|
|
727
|
+
* ```
|
|
728
|
+
*/
|
|
729
|
+
async createProof(noirCode, inputs) {
|
|
730
|
+
const circuit = await this.compile(noirCode);
|
|
731
|
+
const proof = await this.prove(inputs, circuit);
|
|
732
|
+
const proofBytes = "proof" in proof && proof.proof instanceof Uint8Array ? proof.proof : proof.proof.bytes;
|
|
733
|
+
const pubInputs = Array.isArray(proof.publicInputs) ? proof.publicInputs : proof.publicInputs.hex;
|
|
734
|
+
const verified = await this.verify(proofBytes, pubInputs, circuit);
|
|
735
|
+
return { proof, verified };
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Get deployment data for the verifying key.
|
|
739
|
+
* Returns the data needed to deploy to the configured blockchain.
|
|
740
|
+
* Use with SolanaTransactionBuilder to build and send the transaction.
|
|
741
|
+
*
|
|
742
|
+
* @param options - Optional configuration
|
|
743
|
+
* @returns Deployment data that can be used with SolanaTransactionBuilder
|
|
744
|
+
* @throws Error if no chain is configured (offchain mode)
|
|
745
|
+
* @throws Error if prove() hasn't been called yet
|
|
746
|
+
*
|
|
747
|
+
* @example
|
|
748
|
+
* ```typescript
|
|
749
|
+
* const izi = await IziNoir.init({ provider: Provider.Arkworks, chain: Chain.Solana });
|
|
750
|
+
* await izi.compile(noirCode);
|
|
751
|
+
* await izi.prove(inputs);
|
|
752
|
+
*
|
|
753
|
+
* // Get deployment data
|
|
754
|
+
* const deployData = izi.getDeployData();
|
|
755
|
+
*
|
|
756
|
+
* // Use with SolanaTransactionBuilder in your frontend
|
|
757
|
+
* const builder = new SolanaTransactionBuilder({ programId: deployData.programId });
|
|
758
|
+
* const { initVk, rentLamports, accountSize } = builder.buildInitAndVerifyInstructions(
|
|
759
|
+
* deployData.proofData,
|
|
760
|
+
* vkAccountPubkey,
|
|
761
|
+
* authority,
|
|
762
|
+
* payer
|
|
763
|
+
* );
|
|
764
|
+
* ```
|
|
765
|
+
*/
|
|
766
|
+
getDeployData(options) {
|
|
767
|
+
if (!this.chain) {
|
|
768
|
+
throw new Error("Cannot deploy in offchain mode. Initialize with a chain parameter.");
|
|
769
|
+
}
|
|
770
|
+
if (!this._verifyingKey || !this._lastProof) {
|
|
771
|
+
throw new Error("Must call prove() before getDeployData().");
|
|
772
|
+
}
|
|
773
|
+
if (this.chain !== "solana" /* Solana */) {
|
|
774
|
+
throw new Error(`Deployment for ${this.chain} is not yet supported.`);
|
|
775
|
+
}
|
|
776
|
+
return {
|
|
777
|
+
proofData: this._lastProof,
|
|
778
|
+
programId: options?.programId ?? "EYhRED7EuMyyVjx57aDXUD9h6ArnEKng64qtz8999KrS",
|
|
779
|
+
computeUnits: options?.computeUnits ?? 4e5
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
export {
|
|
784
|
+
ArkworksWasm,
|
|
785
|
+
IziNoir,
|
|
786
|
+
Provider,
|
|
787
|
+
initNoirWasm,
|
|
788
|
+
isArkworksCircuit,
|
|
789
|
+
isWasmInitialized
|
|
790
|
+
};
|
|
791
|
+
//# sourceMappingURL=arkworks.js.map
|