@izi-noir/sdk 0.1.10 → 0.1.12

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.
@@ -8,6 +8,378 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/infra/provingSystems/R1csBuilder.ts
12
+ var R1csBuilder;
13
+ var init_R1csBuilder = __esm({
14
+ "src/infra/provingSystems/R1csBuilder.ts"() {
15
+ "use strict";
16
+ R1csBuilder = class {
17
+ constructor(parsedCircuit) {
18
+ this.parsedCircuit = parsedCircuit;
19
+ }
20
+ constraints = [];
21
+ witnessMap = /* @__PURE__ */ new Map();
22
+ nextWitnessIdx = 1;
23
+ // w_0 = 1 is reserved
24
+ publicIndices = [];
25
+ privateIndices = [];
26
+ // BN254 scalar field modulus - 1 (for representing -1)
27
+ // Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
28
+ // -1 mod Fr = Fr - 1
29
+ NEG_ONE = "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000";
30
+ /**
31
+ * Build R1CS definition from the parsed circuit
32
+ */
33
+ build() {
34
+ this.registerInputs();
35
+ for (const stmt of this.parsedCircuit.statements) {
36
+ this.processStatement(stmt);
37
+ }
38
+ return {
39
+ num_witnesses: this.nextWitnessIdx,
40
+ public_inputs: this.publicIndices,
41
+ private_inputs: this.privateIndices,
42
+ constraints: this.constraints
43
+ };
44
+ }
45
+ /**
46
+ * Get the witness index for an input parameter name
47
+ */
48
+ getWitnessIndex(name) {
49
+ return this.witnessMap.get(name);
50
+ }
51
+ /**
52
+ * Register all circuit inputs as witnesses
53
+ * IMPORTANT: Noir orders witnesses as private first, then public
54
+ * We must match this order for the witness values to align correctly
55
+ */
56
+ registerInputs() {
57
+ for (const param of this.parsedCircuit.privateParams) {
58
+ const idx = this.nextWitnessIdx++;
59
+ this.witnessMap.set(param.name, idx);
60
+ this.privateIndices.push(idx);
61
+ }
62
+ for (const param of this.parsedCircuit.publicParams) {
63
+ const idx = this.nextWitnessIdx++;
64
+ this.witnessMap.set(param.name, idx);
65
+ this.publicIndices.push(idx);
66
+ }
67
+ }
68
+ /**
69
+ * Process a single statement and generate constraints
70
+ */
71
+ processStatement(stmt) {
72
+ switch (stmt.kind) {
73
+ case "assert":
74
+ this.processAssert(stmt.condition);
75
+ break;
76
+ case "variable_declaration":
77
+ this.processVariableDecl(stmt.name, stmt.initializer);
78
+ break;
79
+ case "assignment":
80
+ this.processAssignment(stmt.target, stmt.value);
81
+ break;
82
+ case "if_statement":
83
+ for (const s of stmt.consequent) {
84
+ this.processStatement(s);
85
+ }
86
+ if (stmt.alternate) {
87
+ for (const s of stmt.alternate) {
88
+ this.processStatement(s);
89
+ }
90
+ }
91
+ break;
92
+ case "for_statement":
93
+ throw new Error(
94
+ "For loops are not yet supported in R1CS generation. Use the Barretenberg backend for complex circuits."
95
+ );
96
+ }
97
+ }
98
+ /**
99
+ * Process an assert statement
100
+ * The condition must evaluate to true for a valid proof
101
+ */
102
+ processAssert(condition) {
103
+ if (condition.kind === "binary") {
104
+ this.processBinaryAssert(condition);
105
+ } else if (condition.kind === "identifier") {
106
+ const idx = this.getOrCreateWitness(condition);
107
+ this.constraints.push({
108
+ a: [["0x1", idx]],
109
+ b: [["0x1", 0]],
110
+ // * 1
111
+ c: [["0x1", 0]]
112
+ // = 1 (w_0)
113
+ });
114
+ } else {
115
+ throw new Error(`Unsupported assert condition kind: ${condition.kind}`);
116
+ }
117
+ }
118
+ /**
119
+ * Process a binary expression in an assert
120
+ */
121
+ processBinaryAssert(expr) {
122
+ const { left, operator, right } = expr;
123
+ switch (operator) {
124
+ case "==":
125
+ this.processEquality(left, right);
126
+ break;
127
+ case "!=":
128
+ throw new Error(
129
+ "Inequality (!=) is not yet supported in R1CS. Use equality (==) instead."
130
+ );
131
+ case "+":
132
+ case "-":
133
+ case "*":
134
+ case "/":
135
+ case "%":
136
+ throw new Error(
137
+ `Arithmetic operator ${operator} must be part of an equality assertion. Use: assert(a ${operator} b == c)`
138
+ );
139
+ case "<":
140
+ case ">":
141
+ case "<=":
142
+ case ">=":
143
+ throw new Error(
144
+ `Comparison operators (${operator}) require range proofs which are not yet supported. Use the Barretenberg backend for comparison operations.`
145
+ );
146
+ case "&":
147
+ case "|":
148
+ throw new Error(
149
+ `Logical operator ${operator} is not yet supported in R1CS.`
150
+ );
151
+ default:
152
+ throw new Error(`Unsupported operator in assert: ${operator}`);
153
+ }
154
+ }
155
+ /**
156
+ * Process an equality assertion: assert(left == right)
157
+ */
158
+ processEquality(left, right) {
159
+ if (left.kind === "binary" && left.operator === "*") {
160
+ this.processMultiplicationEquality(left.left, left.right, right);
161
+ return;
162
+ }
163
+ if (right.kind === "binary" && right.operator === "*") {
164
+ this.processMultiplicationEquality(right.left, right.right, left);
165
+ return;
166
+ }
167
+ if (left.kind === "binary" && left.operator === "+") {
168
+ this.processAdditionEquality(left.left, left.right, right);
169
+ return;
170
+ }
171
+ if (right.kind === "binary" && right.operator === "+") {
172
+ this.processAdditionEquality(right.left, right.right, left);
173
+ return;
174
+ }
175
+ if (left.kind === "binary" && left.operator === "-") {
176
+ this.processSubtractionEquality(left.left, left.right, right);
177
+ return;
178
+ }
179
+ if (right.kind === "binary" && right.operator === "-") {
180
+ this.processSubtractionEquality(right.left, right.right, left);
181
+ return;
182
+ }
183
+ const leftIdx = this.getOrCreateWitness(left);
184
+ const rightIdx = this.getOrCreateWitness(right);
185
+ this.constraints.push({
186
+ a: [
187
+ ["0x1", leftIdx],
188
+ [this.NEG_ONE, rightIdx]
189
+ ],
190
+ // a - b
191
+ b: [["0x1", 0]],
192
+ // * 1 (w_0 = 1)
193
+ c: []
194
+ // = 0
195
+ });
196
+ }
197
+ /**
198
+ * Process multiplication equality: assert(a * b == c)
199
+ * R1CS: a * b = c
200
+ */
201
+ processMultiplicationEquality(left, right, result) {
202
+ const leftIdx = this.getOrCreateWitness(left);
203
+ const rightIdx = this.getOrCreateWitness(right);
204
+ const resultIdx = this.getOrCreateWitness(result);
205
+ this.constraints.push({
206
+ a: [["0x1", leftIdx]],
207
+ b: [["0x1", rightIdx]],
208
+ c: [["0x1", resultIdx]]
209
+ });
210
+ }
211
+ /**
212
+ * Process addition equality: assert(a + b == c)
213
+ * R1CS: (a + b - c) * 1 = 0
214
+ * Which is: (a + b) * 1 = c
215
+ */
216
+ processAdditionEquality(left, right, result) {
217
+ const leftIdx = this.getOrCreateWitness(left);
218
+ const rightIdx = this.getOrCreateWitness(right);
219
+ const resultIdx = this.getOrCreateWitness(result);
220
+ this.constraints.push({
221
+ a: [
222
+ ["0x1", leftIdx],
223
+ ["0x1", rightIdx]
224
+ ],
225
+ // a + b
226
+ b: [["0x1", 0]],
227
+ // * 1
228
+ c: [["0x1", resultIdx]]
229
+ // = c
230
+ });
231
+ }
232
+ /**
233
+ * Process subtraction equality: assert(a - b == c)
234
+ * R1CS: (a - b) * 1 = c
235
+ */
236
+ processSubtractionEquality(left, right, result) {
237
+ const leftIdx = this.getOrCreateWitness(left);
238
+ const rightIdx = this.getOrCreateWitness(right);
239
+ const resultIdx = this.getOrCreateWitness(result);
240
+ this.constraints.push({
241
+ a: [
242
+ ["0x1", leftIdx],
243
+ [this.NEG_ONE, rightIdx]
244
+ ],
245
+ // a - b
246
+ b: [["0x1", 0]],
247
+ // * 1
248
+ c: [["0x1", resultIdx]]
249
+ // = c
250
+ });
251
+ }
252
+ /**
253
+ * Process a variable declaration: let x = expr
254
+ * Creates a new witness for x and adds constraint if needed
255
+ */
256
+ processVariableDecl(name, initializer) {
257
+ const varIdx = this.nextWitnessIdx++;
258
+ this.witnessMap.set(name, varIdx);
259
+ if (initializer.kind === "identifier") {
260
+ const initIdx = this.getOrCreateWitness(initializer);
261
+ this.constraints.push({
262
+ a: [
263
+ ["0x1", varIdx],
264
+ [this.NEG_ONE, initIdx]
265
+ ],
266
+ b: [["0x1", 0]],
267
+ c: []
268
+ });
269
+ } else if (initializer.kind === "literal") {
270
+ } else if (initializer.kind === "binary") {
271
+ this.processVariableInitBinary(varIdx, initializer);
272
+ } else {
273
+ throw new Error(
274
+ `Unsupported initializer kind for variable declaration: ${initializer.kind}`
275
+ );
276
+ }
277
+ }
278
+ /**
279
+ * Process a binary expression as variable initializer
280
+ */
281
+ processVariableInitBinary(varIdx, expr) {
282
+ const { left, operator, right } = expr;
283
+ const leftIdx = this.getOrCreateWitness(left);
284
+ const rightIdx = this.getOrCreateWitness(right);
285
+ switch (operator) {
286
+ case "*":
287
+ this.constraints.push({
288
+ a: [["0x1", leftIdx]],
289
+ b: [["0x1", rightIdx]],
290
+ c: [["0x1", varIdx]]
291
+ });
292
+ break;
293
+ case "+":
294
+ this.constraints.push({
295
+ a: [
296
+ ["0x1", leftIdx],
297
+ ["0x1", rightIdx]
298
+ ],
299
+ b: [["0x1", 0]],
300
+ c: [["0x1", varIdx]]
301
+ });
302
+ break;
303
+ case "-":
304
+ this.constraints.push({
305
+ a: [
306
+ ["0x1", leftIdx],
307
+ [this.NEG_ONE, rightIdx]
308
+ ],
309
+ b: [["0x1", 0]],
310
+ c: [["0x1", varIdx]]
311
+ });
312
+ break;
313
+ default:
314
+ throw new Error(
315
+ `Unsupported operator in variable initializer: ${operator}`
316
+ );
317
+ }
318
+ }
319
+ /**
320
+ * Process an assignment: x = expr
321
+ * Updates the witness mapping
322
+ */
323
+ processAssignment(target, value) {
324
+ const existingIdx = this.witnessMap.get(target);
325
+ if (existingIdx === void 0) {
326
+ throw new Error(`Assignment to undeclared variable: ${target}`);
327
+ }
328
+ if (value.kind === "identifier") {
329
+ const valueIdx = this.getOrCreateWitness(value);
330
+ this.constraints.push({
331
+ a: [
332
+ ["0x1", existingIdx],
333
+ [this.NEG_ONE, valueIdx]
334
+ ],
335
+ b: [["0x1", 0]],
336
+ c: []
337
+ });
338
+ } else if (value.kind === "binary") {
339
+ this.processVariableInitBinary(existingIdx, value);
340
+ }
341
+ }
342
+ /**
343
+ * Get or create a witness index for an expression
344
+ */
345
+ getOrCreateWitness(expr) {
346
+ if (expr.kind === "identifier") {
347
+ const existing = this.witnessMap.get(expr.name);
348
+ if (existing !== void 0) {
349
+ return existing;
350
+ }
351
+ const idx = this.nextWitnessIdx++;
352
+ this.witnessMap.set(expr.name, idx);
353
+ return idx;
354
+ }
355
+ if (expr.kind === "literal") {
356
+ const idx = this.nextWitnessIdx++;
357
+ return idx;
358
+ }
359
+ if (expr.kind === "binary") {
360
+ const idx = this.nextWitnessIdx++;
361
+ this.processVariableInitBinary(idx, expr);
362
+ return idx;
363
+ }
364
+ if (expr.kind === "unary") {
365
+ const operandIdx = this.getOrCreateWitness(expr.operand);
366
+ if (expr.operator === "-") {
367
+ const idx = this.nextWitnessIdx++;
368
+ this.constraints.push({
369
+ a: [[this.NEG_ONE, operandIdx]],
370
+ b: [["0x1", 0]],
371
+ c: [["0x1", idx]]
372
+ });
373
+ return idx;
374
+ }
375
+ throw new Error(`Unsupported unary operator: ${expr.operator}`);
376
+ }
377
+ throw new Error(`Unsupported expression kind: ${expr.kind}`);
378
+ }
379
+ };
380
+ }
381
+ });
382
+
11
383
  // src/infra/provingSystems/ArkworksWasm.ts
12
384
  var ArkworksWasm_exports = {};
13
385
  __export(ArkworksWasm_exports, {
@@ -141,6 +513,7 @@ var wasmModule, wasmInitPromise, ArkworksWasm;
141
513
  var init_ArkworksWasm = __esm({
142
514
  "src/infra/provingSystems/ArkworksWasm.ts"() {
143
515
  "use strict";
516
+ init_R1csBuilder();
144
517
  wasmModule = null;
145
518
  wasmInitPromise = null;
146
519
  ArkworksWasm = class {
@@ -154,8 +527,11 @@ var init_ArkworksWasm = __esm({
154
527
  }
155
528
  /**
156
529
  * Compile Noir code to a circuit with ACIR for Groth16 proving
530
+ *
531
+ * @param noirCode - The Noir source code to compile
532
+ * @param options - Optional compilation options including ParsedCircuit for dynamic R1CS
157
533
  */
158
- async compile(noirCode) {
534
+ async compile(noirCode, options) {
159
535
  const wasm = await initWasm();
160
536
  const { basePath, cleanup } = await createTempDir();
161
537
  const fm = createFileManager(basePath);
@@ -180,36 +556,52 @@ authors = [""]
180
556
  throw new Error("Compilation failed: no bytecode generated");
181
557
  }
182
558
  const parameters = compiled.abi.parameters;
183
- const publicR1csIndices = [];
184
- const privateR1csIndices = [];
559
+ let r1cs;
185
560
  const witnessIndexMapping = /* @__PURE__ */ new Map();
186
- parameters.forEach((p, noirIndex) => {
187
- const r1csIndex = noirIndex + 1;
188
- witnessIndexMapping.set(noirIndex, r1csIndex);
189
- if (p.visibility === "public") {
190
- publicR1csIndices.push(r1csIndex);
191
- } else if (p.visibility === "private") {
192
- privateR1csIndices.push(r1csIndex);
193
- }
194
- });
195
- const r1cs = {
196
- num_witnesses: parameters.length + 1,
197
- // +1 for w_0
198
- public_inputs: publicR1csIndices,
199
- private_inputs: privateR1csIndices,
200
- constraints: []
201
- };
202
- if (privateR1csIndices.length === 1 && publicR1csIndices.length === 1) {
203
- const privateIdx = privateR1csIndices[0];
204
- const publicIdx = publicR1csIndices[0];
205
- r1cs.constraints.push({
206
- a: [["0x1", privateIdx]],
207
- // secret
208
- b: [["0x1", privateIdx]],
209
- // secret
210
- c: [["0x1", publicIdx]]
211
- // expected
561
+ if (options?.parsedCircuit) {
562
+ const builder = new R1csBuilder(options.parsedCircuit);
563
+ r1cs = builder.build();
564
+ parameters.forEach((p, noirIndex) => {
565
+ const r1csIndex = noirIndex + 1;
566
+ witnessIndexMapping.set(noirIndex, r1csIndex);
567
+ });
568
+ console.log("=== R1CS BUILDER DEBUG ===");
569
+ console.log("ParsedCircuit publicParams:", options.parsedCircuit.publicParams);
570
+ console.log("ParsedCircuit privateParams:", options.parsedCircuit.privateParams);
571
+ console.log("ParsedCircuit statements:", JSON.stringify(options.parsedCircuit.statements, null, 2));
572
+ console.log("Generated R1CS:", JSON.stringify(r1cs, null, 2));
573
+ console.log("==========================");
574
+ } else {
575
+ const publicR1csIndices = [];
576
+ const privateR1csIndices = [];
577
+ parameters.forEach((p, noirIndex) => {
578
+ const r1csIndex = noirIndex + 1;
579
+ witnessIndexMapping.set(noirIndex, r1csIndex);
580
+ if (p.visibility === "public") {
581
+ publicR1csIndices.push(r1csIndex);
582
+ } else if (p.visibility === "private") {
583
+ privateR1csIndices.push(r1csIndex);
584
+ }
212
585
  });
586
+ r1cs = {
587
+ num_witnesses: parameters.length + 1,
588
+ public_inputs: publicR1csIndices,
589
+ private_inputs: privateR1csIndices,
590
+ constraints: []
591
+ };
592
+ if (privateR1csIndices.length === 1 && publicR1csIndices.length === 1) {
593
+ const privateIdx = privateR1csIndices[0];
594
+ const publicIdx = publicR1csIndices[0];
595
+ r1cs.constraints.push({
596
+ a: [["0x1", privateIdx]],
597
+ b: [["0x1", privateIdx]],
598
+ c: [["0x1", publicIdx]]
599
+ });
600
+ }
601
+ console.log("=== R1CS HARDCODED DEBUG ===");
602
+ console.log("Using hardcoded R1CS pattern (no ParsedCircuit provided)");
603
+ console.log("R1CS:", JSON.stringify(r1cs, null, 2));
604
+ console.log("============================");
213
605
  }
214
606
  const r1csJson = JSON.stringify(r1cs);
215
607
  let provingKey;
@@ -226,7 +618,7 @@ authors = [""]
226
618
  }
227
619
  }
228
620
  const acirJson = JSON.stringify({
229
- functions: [{ current_witness_index: parameters.length, opcodes: [], private_parameters: privateR1csIndices, public_parameters: { witnesses: publicR1csIndices }, return_values: { witnesses: [] } }]
621
+ functions: [{ current_witness_index: parameters.length, opcodes: [], private_parameters: r1cs.private_inputs, public_parameters: { witnesses: r1cs.public_inputs }, return_values: { witnesses: [] } }]
230
622
  });
231
623
  const arkworksCircuit = {
232
624
  ...compiled,
@@ -374,7 +766,9 @@ var init_Barretenberg = __esm({
374
766
  "src/infra/provingSystems/Barretenberg.ts"() {
375
767
  "use strict";
376
768
  Barretenberg = class {
377
- async compile(noirCode) {
769
+ // Note: options parameter is accepted but not used - Barretenberg uses Noir's ACIR directly
770
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
771
+ async compile(noirCode, _options) {
378
772
  const { basePath, cleanup } = await createTempDir2();
379
773
  const fm = createFileManager2(basePath);
380
774
  const nargoToml = `[package]
@@ -1038,16 +1432,20 @@ var IziNoir = class _IziNoir {
1038
1432
  * After compile(), the verifying key is available via `this.vk`.
1039
1433
  *
1040
1434
  * @param noirCode - The Noir source code to compile
1435
+ * @param options - Optional compilation options including ParsedCircuit for dynamic R1CS
1041
1436
  * @returns CompileResult with circuit and verifying key
1042
1437
  *
1043
1438
  * @example
1044
1439
  * ```typescript
1440
+ * // Basic usage
1045
1441
  * const { circuit, verifyingKey } = await izi.compile(noirCode);
1046
- * console.log('VK:', izi.vk); // Available immediately after compile
1442
+ *
1443
+ * // With ParsedCircuit for dynamic R1CS generation
1444
+ * const { circuit } = await izi.compile(noirCode, { parsedCircuit });
1047
1445
  * ```
1048
1446
  */
1049
- async compile(noirCode) {
1050
- this.compiledCircuit = await this.provingSystem.compile(noirCode);
1447
+ async compile(noirCode, options) {
1448
+ this.compiledCircuit = await this.provingSystem.compile(noirCode, options);
1051
1449
  const vk = await this.extractVerifyingKey(this.compiledCircuit);
1052
1450
  if (vk) {
1053
1451
  this._verifyingKey = vk;