@izi-noir/sdk 0.1.10 → 0.1.11

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