@izi-noir/sdk 0.1.14 → 0.1.15

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/dist/index.cjs CHANGED
@@ -142,6 +142,7 @@ var init_R1csBuilder = __esm({
142
142
  // w_0 = 1 is reserved
143
143
  publicIndices = [];
144
144
  privateIndices = [];
145
+ auxWitnessComputations = [];
145
146
  // BN254 scalar field modulus - 1 (for representing -1)
146
147
  // Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
147
148
  // -1 mod Fr = Fr - 1
@@ -160,7 +161,8 @@ var init_R1csBuilder = __esm({
160
161
  num_witnesses: this.nextWitnessIdx,
161
162
  public_inputs: this.publicIndices,
162
163
  private_inputs: this.privateIndices,
163
- constraints: this.constraints
164
+ constraints: this.constraints,
165
+ auxWitnessComputations: this.auxWitnessComputations.length > 0 ? this.auxWitnessComputations : void 0
164
166
  };
165
167
  }
166
168
  /**
@@ -391,6 +393,12 @@ var init_R1csBuilder = __esm({
391
393
  const leftIdx = this.getOrCreateWitness(left);
392
394
  const rightIdx = this.getOrCreateWitness(right);
393
395
  const diffIdx = this.nextWitnessIdx++;
396
+ this.auxWitnessComputations.push({
397
+ type: "subtract",
398
+ targetIdx: diffIdx,
399
+ leftIdx,
400
+ rightIdx
401
+ });
394
402
  this.constraints.push({
395
403
  a: [
396
404
  ["0x1", leftIdx],
@@ -410,6 +418,14 @@ var init_R1csBuilder = __esm({
410
418
  const leftIdx = this.getOrCreateWitness(left);
411
419
  const rightIdx = this.getOrCreateWitness(right);
412
420
  const diffIdx = this.nextWitnessIdx++;
421
+ this.auxWitnessComputations.push({
422
+ type: "subtract",
423
+ targetIdx: diffIdx,
424
+ leftIdx,
425
+ rightIdx,
426
+ offset: -1
427
+ // For > operator: diff = left - right - 1
428
+ });
413
429
  this.constraints.push({
414
430
  a: [
415
431
  ["0x1", leftIdx],
@@ -440,6 +456,14 @@ var init_R1csBuilder = __esm({
440
456
  c: [["0x1", bitIdx]]
441
457
  });
442
458
  }
459
+ this.auxWitnessComputations.push({
460
+ type: "bit_decompose",
461
+ targetIdx: bitIndices[0],
462
+ // First bit index (for reference)
463
+ sourceIdx: valueIdx,
464
+ bitIndices,
465
+ numBits: this.COMPARISON_BITS
466
+ });
443
467
  const sumTerms = [];
444
468
  for (let i = 0; i < this.COMPARISON_BITS; i++) {
445
469
  const coeff = (1n << BigInt(i)).toString(16);
@@ -873,6 +897,15 @@ authors = [""]
873
897
  const strVal = String(value);
874
898
  witnessMap[r1csIndex.toString()] = strVal;
875
899
  }
900
+ const r1cs = JSON.parse(circuit.r1csJson);
901
+ if (r1cs.auxWitnessComputations && r1cs.auxWitnessComputations.length > 0) {
902
+ console.log("=== AUXILIARY WITNESS COMPUTATION ===");
903
+ console.log("Input witnesses:", JSON.stringify(witnessMap, null, 2));
904
+ console.log("Computations:", JSON.stringify(r1cs.auxWitnessComputations, null, 2));
905
+ this.computeAuxiliaryWitnesses(witnessMap, r1cs.auxWitnessComputations);
906
+ console.log("After aux computation:", JSON.stringify(witnessMap, null, 2));
907
+ console.log("=====================================");
908
+ }
876
909
  const witnessJson = JSON.stringify(witnessMap);
877
910
  let provingKey = circuit.provingKey;
878
911
  if (!provingKey) {
@@ -925,6 +958,63 @@ authors = [""]
925
958
  publicInputs.length
926
959
  );
927
960
  }
961
+ /**
962
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
963
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
964
+ */
965
+ computeAuxiliaryWitnesses(witnessMap, computations) {
966
+ const FR_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001");
967
+ const getWitnessValue = (idx) => {
968
+ if (idx === 0) return 1n;
969
+ const val = witnessMap[idx.toString()];
970
+ if (val === void 0) {
971
+ throw new Error(`Witness w_${idx} not found`);
972
+ }
973
+ if (val.startsWith("0x")) {
974
+ return BigInt(val);
975
+ }
976
+ return BigInt(val);
977
+ };
978
+ const setWitnessValue = (idx, val) => {
979
+ const normalized = (val % FR_MODULUS + FR_MODULUS) % FR_MODULUS;
980
+ witnessMap[idx.toString()] = "0x" + normalized.toString(16);
981
+ };
982
+ for (const comp of computations) {
983
+ switch (comp.type) {
984
+ case "subtract": {
985
+ const left = getWitnessValue(comp.leftIdx);
986
+ const right = getWitnessValue(comp.rightIdx);
987
+ const offset = BigInt(comp.offset ?? 0);
988
+ const result = left - right + offset;
989
+ setWitnessValue(comp.targetIdx, result);
990
+ break;
991
+ }
992
+ case "bit_decompose": {
993
+ const source = getWitnessValue(comp.sourceIdx);
994
+ const bitIndices = comp.bitIndices;
995
+ const numBits = comp.numBits;
996
+ if (source < 0n) {
997
+ throw new Error(
998
+ `Bit decomposition failed: value ${source} is negative. This means the comparison constraint is not satisfied.`
999
+ );
1000
+ }
1001
+ const maxVal = (1n << BigInt(numBits)) - 1n;
1002
+ if (source > maxVal) {
1003
+ throw new Error(
1004
+ `Bit decomposition failed: value ${source} exceeds ${numBits} bits (max: ${maxVal}). Consider using a larger bit width or smaller values.`
1005
+ );
1006
+ }
1007
+ for (let i = 0; i < numBits; i++) {
1008
+ const bit = source >> BigInt(i) & 1n;
1009
+ setWitnessValue(bitIndices[i], bit);
1010
+ }
1011
+ break;
1012
+ }
1013
+ default:
1014
+ throw new Error(`Unknown auxiliary witness computation type: ${comp.type}`);
1015
+ }
1016
+ }
1017
+ }
928
1018
  /**
929
1019
  * Get the verifying key in gnark format for on-chain deployment
930
1020
  */
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@ import { I as IChainFormatter } from './wasmInit-Wyynuk6r.cjs';
2
2
  export { C as ChainProofDataFor, D as DeployResult, a as IziNoir, S as SolanaDeployData, V as VerifyOnChainResult, W as WalletAdapter, i as initNoirWasm, b as isWasmInitialized, m as markWasmInitialized } from './wasmInit-Wyynuk6r.cjs';
3
3
  import { P as ProofData, S as SolanaProofData, a as CircuitFunction, I as InputValue, b as ProofResult } from './types-CxkI04bP.cjs';
4
4
  export { C as CompileResult, c as ProofTimings, d as ProverOptions, e as VerifierOptions, V as VerifyingKeyData } from './types-CxkI04bP.cjs';
5
- import { c as CircuitMetadata, S as SolanaChainMetadata, g as ParsedCircuit, I as IProvingSystem } from './IProvingSystem-SfzgcbqH.cjs';
5
+ import { g as ParsedCircuit, c as CircuitMetadata, S as SolanaChainMetadata, I as IProvingSystem } from './IProvingSystem-SfzgcbqH.cjs';
6
6
  export { A as AssertStatement, B as BinaryExpr, o as BinaryOperator, e as Chain, b as ChainId, h as ChainMetadata, d as ChainMetadataFor, l as CircuitParam, C as CircuitPaths, f as CompileOptions, E as EthereumChainMetadata, m as Expr, i as ICompiler, j as IProver, k as IVerifier, n as IdentifierExpr, a as IziNoirConfig, L as LiteralExpr, M as MemberExpr, q as NETWORK_CONFIG, N as Network, t as NetworkConfig, P as Provider, p as Statement, s as getExplorerAccountUrl, r as getExplorerTxUrl } from './IProvingSystem-SfzgcbqH.cjs';
7
7
  import { ArkworksWasm, ArkworksWasmConfig } from './providers/arkworks.cjs';
8
8
  export { ArkworksCompiledCircuit, ArkworksProofResult, ArkworksSetupResult, ArkworksWasmModule, isArkworksCircuit } from './providers/arkworks.cjs';
@@ -11,60 +11,6 @@ export { CompiledCircuit, InputMap } from '@noir-lang/types';
11
11
  export { Barretenberg } from './providers/barretenberg.cjs';
12
12
  export { IZI_NOIR_PROGRAM_ID, buildInitVkFromBytesData, buildVerifyProofData, calculateVkAccountRent, calculateVkAccountSize, parseProof, parsePublicInputs, parseVerifyingKey } from './providers/solana.cjs';
13
13
 
14
- /**
15
- * Formatter for Solana-compatible proof data.
16
- *
17
- * Converts generic ProofData into SolanaProofData format with:
18
- * - Verifying key in gnark format (compatible with gnark-verifier-solana)
19
- * - Proof bytes (256 bytes Groth16)
20
- * - Public inputs as 32-byte arrays
21
- * - VK account size and rent estimates
22
- *
23
- * @example
24
- * ```typescript
25
- * const formatter = new SolanaFormatter(arkworksProvider);
26
- * const solanaProof = await formatter.formatProof(proofData, circuit, metadata);
27
- * ```
28
- */
29
- declare class SolanaFormatter implements IChainFormatter<'solana'> {
30
- private arkworksProvider;
31
- readonly chainId: "solana";
32
- constructor(arkworksProvider: ArkworksWasm);
33
- /**
34
- * Format a generic proof for Solana on-chain verification.
35
- *
36
- * @param proofData - Generic proof data from Arkworks
37
- * @param circuit - The compiled circuit (must be Arkworks circuit)
38
- * @param metadata - Circuit metadata with public input count
39
- * @returns SolanaProofData ready for on-chain verification
40
- */
41
- formatProof(proofData: ProofData, circuit: CompiledCircuit, metadata: CircuitMetadata): Promise<SolanaProofData>;
42
- /**
43
- * Get Solana-specific metadata for a circuit.
44
- *
45
- * @param publicInputCount - Number of public inputs in the circuit
46
- * @returns Solana metadata with account size and rent estimates
47
- */
48
- getChainMetadata(publicInputCount: number): SolanaChainMetadata;
49
- /**
50
- * Calculate the size of a VK account for a given number of public inputs.
51
- * Matches the Rust `vk_account_size` function in the Solana program.
52
- */
53
- private calculateVkAccountSize;
54
- /**
55
- * Calculate the minimum rent for a VK account.
56
- */
57
- private calculateVkAccountRent;
58
- /**
59
- * Convert Uint8Array to base64 string.
60
- */
61
- private uint8ArrayToBase64;
62
- /**
63
- * Convert hex string to Uint8Array.
64
- */
65
- private hexToBytes;
66
- }
67
-
68
14
  /**
69
15
  * R1csBuilder - Generates R1CS constraints from ParsedCircuit
70
16
  *
@@ -88,6 +34,28 @@ interface R1csConstraint {
88
34
  b: [string, number][];
89
35
  c: [string, number][];
90
36
  }
37
+ /**
38
+ * Auxiliary witness computation instruction
39
+ * Tells the prover how to compute auxiliary witnesses from input witnesses
40
+ */
41
+ interface AuxWitnessComputation {
42
+ /** Type of computation */
43
+ type: 'subtract' | 'bit_decompose';
44
+ /** Witness index to compute */
45
+ targetIdx: number;
46
+ /** For 'subtract': left operand witness index */
47
+ leftIdx?: number;
48
+ /** For 'subtract': right operand witness index */
49
+ rightIdx?: number;
50
+ /** For 'subtract': additional constant offset to subtract (e.g., -1 for > operator) */
51
+ offset?: number;
52
+ /** For 'bit_decompose': source value witness index */
53
+ sourceIdx?: number;
54
+ /** For 'bit_decompose': array of bit witness indices (LSB first) */
55
+ bitIndices?: number[];
56
+ /** For 'bit_decompose': number of bits */
57
+ numBits?: number;
58
+ }
91
59
  /**
92
60
  * Complete R1CS definition for arkworks-groth16-wasm
93
61
  */
@@ -96,6 +64,8 @@ interface R1csDefinition {
96
64
  public_inputs: number[];
97
65
  private_inputs: number[];
98
66
  constraints: R1csConstraint[];
67
+ /** Instructions for computing auxiliary witnesses */
68
+ auxWitnessComputations?: AuxWitnessComputation[];
99
69
  }
100
70
  /**
101
71
  * Builds R1CS constraints from a ParsedCircuit
@@ -107,6 +77,7 @@ declare class R1csBuilder {
107
77
  private nextWitnessIdx;
108
78
  private publicIndices;
109
79
  private privateIndices;
80
+ private auxWitnessComputations;
110
81
  private readonly NEG_ONE;
111
82
  private readonly COMPARISON_BITS;
112
83
  constructor(parsedCircuit: ParsedCircuit);
@@ -202,6 +173,60 @@ declare class R1csBuilder {
202
173
  private getOrCreateWitness;
203
174
  }
204
175
 
176
+ /**
177
+ * Formatter for Solana-compatible proof data.
178
+ *
179
+ * Converts generic ProofData into SolanaProofData format with:
180
+ * - Verifying key in gnark format (compatible with gnark-verifier-solana)
181
+ * - Proof bytes (256 bytes Groth16)
182
+ * - Public inputs as 32-byte arrays
183
+ * - VK account size and rent estimates
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * const formatter = new SolanaFormatter(arkworksProvider);
188
+ * const solanaProof = await formatter.formatProof(proofData, circuit, metadata);
189
+ * ```
190
+ */
191
+ declare class SolanaFormatter implements IChainFormatter<'solana'> {
192
+ private arkworksProvider;
193
+ readonly chainId: "solana";
194
+ constructor(arkworksProvider: ArkworksWasm);
195
+ /**
196
+ * Format a generic proof for Solana on-chain verification.
197
+ *
198
+ * @param proofData - Generic proof data from Arkworks
199
+ * @param circuit - The compiled circuit (must be Arkworks circuit)
200
+ * @param metadata - Circuit metadata with public input count
201
+ * @returns SolanaProofData ready for on-chain verification
202
+ */
203
+ formatProof(proofData: ProofData, circuit: CompiledCircuit, metadata: CircuitMetadata): Promise<SolanaProofData>;
204
+ /**
205
+ * Get Solana-specific metadata for a circuit.
206
+ *
207
+ * @param publicInputCount - Number of public inputs in the circuit
208
+ * @returns Solana metadata with account size and rent estimates
209
+ */
210
+ getChainMetadata(publicInputCount: number): SolanaChainMetadata;
211
+ /**
212
+ * Calculate the size of a VK account for a given number of public inputs.
213
+ * Matches the Rust `vk_account_size` function in the Solana program.
214
+ */
215
+ private calculateVkAccountSize;
216
+ /**
217
+ * Calculate the minimum rent for a VK account.
218
+ */
219
+ private calculateVkAccountRent;
220
+ /**
221
+ * Convert Uint8Array to base64 string.
222
+ */
223
+ private uint8ArrayToBase64;
224
+ /**
225
+ * Convert hex string to Uint8Array.
226
+ */
227
+ private hexToBytes;
228
+ }
229
+
205
230
  interface IParser {
206
231
  parse(fn: CircuitFunction, publicInputs: InputValue[], privateInputs: InputValue[]): ParsedCircuit;
207
232
  }
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { I as IChainFormatter } from './wasmInit-D3RyRKIC.js';
2
2
  export { C as ChainProofDataFor, D as DeployResult, a as IziNoir, S as SolanaDeployData, V as VerifyOnChainResult, W as WalletAdapter, i as initNoirWasm, b as isWasmInitialized, m as markWasmInitialized } from './wasmInit-D3RyRKIC.js';
3
3
  import { P as ProofData, S as SolanaProofData, a as CircuitFunction, I as InputValue, b as ProofResult } from './types-CxkI04bP.js';
4
4
  export { C as CompileResult, c as ProofTimings, d as ProverOptions, e as VerifierOptions, V as VerifyingKeyData } from './types-CxkI04bP.js';
5
- import { c as CircuitMetadata, S as SolanaChainMetadata, g as ParsedCircuit, I as IProvingSystem } from './IProvingSystem-BKgFRl27.js';
5
+ import { g as ParsedCircuit, c as CircuitMetadata, S as SolanaChainMetadata, I as IProvingSystem } from './IProvingSystem-BKgFRl27.js';
6
6
  export { A as AssertStatement, B as BinaryExpr, o as BinaryOperator, e as Chain, b as ChainId, h as ChainMetadata, d as ChainMetadataFor, l as CircuitParam, C as CircuitPaths, f as CompileOptions, E as EthereumChainMetadata, m as Expr, i as ICompiler, j as IProver, k as IVerifier, n as IdentifierExpr, a as IziNoirConfig, L as LiteralExpr, M as MemberExpr, q as NETWORK_CONFIG, N as Network, t as NetworkConfig, P as Provider, p as Statement, s as getExplorerAccountUrl, r as getExplorerTxUrl } from './IProvingSystem-BKgFRl27.js';
7
7
  import { ArkworksWasm, ArkworksWasmConfig } from './providers/arkworks.js';
8
8
  export { ArkworksCompiledCircuit, ArkworksProofResult, ArkworksSetupResult, ArkworksWasmModule, isArkworksCircuit } from './providers/arkworks.js';
@@ -11,60 +11,6 @@ export { CompiledCircuit, InputMap } from '@noir-lang/types';
11
11
  export { Barretenberg } from './providers/barretenberg.js';
12
12
  export { IZI_NOIR_PROGRAM_ID, buildInitVkFromBytesData, buildVerifyProofData, calculateVkAccountRent, calculateVkAccountSize, parseProof, parsePublicInputs, parseVerifyingKey } from './providers/solana.js';
13
13
 
14
- /**
15
- * Formatter for Solana-compatible proof data.
16
- *
17
- * Converts generic ProofData into SolanaProofData format with:
18
- * - Verifying key in gnark format (compatible with gnark-verifier-solana)
19
- * - Proof bytes (256 bytes Groth16)
20
- * - Public inputs as 32-byte arrays
21
- * - VK account size and rent estimates
22
- *
23
- * @example
24
- * ```typescript
25
- * const formatter = new SolanaFormatter(arkworksProvider);
26
- * const solanaProof = await formatter.formatProof(proofData, circuit, metadata);
27
- * ```
28
- */
29
- declare class SolanaFormatter implements IChainFormatter<'solana'> {
30
- private arkworksProvider;
31
- readonly chainId: "solana";
32
- constructor(arkworksProvider: ArkworksWasm);
33
- /**
34
- * Format a generic proof for Solana on-chain verification.
35
- *
36
- * @param proofData - Generic proof data from Arkworks
37
- * @param circuit - The compiled circuit (must be Arkworks circuit)
38
- * @param metadata - Circuit metadata with public input count
39
- * @returns SolanaProofData ready for on-chain verification
40
- */
41
- formatProof(proofData: ProofData, circuit: CompiledCircuit, metadata: CircuitMetadata): Promise<SolanaProofData>;
42
- /**
43
- * Get Solana-specific metadata for a circuit.
44
- *
45
- * @param publicInputCount - Number of public inputs in the circuit
46
- * @returns Solana metadata with account size and rent estimates
47
- */
48
- getChainMetadata(publicInputCount: number): SolanaChainMetadata;
49
- /**
50
- * Calculate the size of a VK account for a given number of public inputs.
51
- * Matches the Rust `vk_account_size` function in the Solana program.
52
- */
53
- private calculateVkAccountSize;
54
- /**
55
- * Calculate the minimum rent for a VK account.
56
- */
57
- private calculateVkAccountRent;
58
- /**
59
- * Convert Uint8Array to base64 string.
60
- */
61
- private uint8ArrayToBase64;
62
- /**
63
- * Convert hex string to Uint8Array.
64
- */
65
- private hexToBytes;
66
- }
67
-
68
14
  /**
69
15
  * R1csBuilder - Generates R1CS constraints from ParsedCircuit
70
16
  *
@@ -88,6 +34,28 @@ interface R1csConstraint {
88
34
  b: [string, number][];
89
35
  c: [string, number][];
90
36
  }
37
+ /**
38
+ * Auxiliary witness computation instruction
39
+ * Tells the prover how to compute auxiliary witnesses from input witnesses
40
+ */
41
+ interface AuxWitnessComputation {
42
+ /** Type of computation */
43
+ type: 'subtract' | 'bit_decompose';
44
+ /** Witness index to compute */
45
+ targetIdx: number;
46
+ /** For 'subtract': left operand witness index */
47
+ leftIdx?: number;
48
+ /** For 'subtract': right operand witness index */
49
+ rightIdx?: number;
50
+ /** For 'subtract': additional constant offset to subtract (e.g., -1 for > operator) */
51
+ offset?: number;
52
+ /** For 'bit_decompose': source value witness index */
53
+ sourceIdx?: number;
54
+ /** For 'bit_decompose': array of bit witness indices (LSB first) */
55
+ bitIndices?: number[];
56
+ /** For 'bit_decompose': number of bits */
57
+ numBits?: number;
58
+ }
91
59
  /**
92
60
  * Complete R1CS definition for arkworks-groth16-wasm
93
61
  */
@@ -96,6 +64,8 @@ interface R1csDefinition {
96
64
  public_inputs: number[];
97
65
  private_inputs: number[];
98
66
  constraints: R1csConstraint[];
67
+ /** Instructions for computing auxiliary witnesses */
68
+ auxWitnessComputations?: AuxWitnessComputation[];
99
69
  }
100
70
  /**
101
71
  * Builds R1CS constraints from a ParsedCircuit
@@ -107,6 +77,7 @@ declare class R1csBuilder {
107
77
  private nextWitnessIdx;
108
78
  private publicIndices;
109
79
  private privateIndices;
80
+ private auxWitnessComputations;
110
81
  private readonly NEG_ONE;
111
82
  private readonly COMPARISON_BITS;
112
83
  constructor(parsedCircuit: ParsedCircuit);
@@ -202,6 +173,60 @@ declare class R1csBuilder {
202
173
  private getOrCreateWitness;
203
174
  }
204
175
 
176
+ /**
177
+ * Formatter for Solana-compatible proof data.
178
+ *
179
+ * Converts generic ProofData into SolanaProofData format with:
180
+ * - Verifying key in gnark format (compatible with gnark-verifier-solana)
181
+ * - Proof bytes (256 bytes Groth16)
182
+ * - Public inputs as 32-byte arrays
183
+ * - VK account size and rent estimates
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * const formatter = new SolanaFormatter(arkworksProvider);
188
+ * const solanaProof = await formatter.formatProof(proofData, circuit, metadata);
189
+ * ```
190
+ */
191
+ declare class SolanaFormatter implements IChainFormatter<'solana'> {
192
+ private arkworksProvider;
193
+ readonly chainId: "solana";
194
+ constructor(arkworksProvider: ArkworksWasm);
195
+ /**
196
+ * Format a generic proof for Solana on-chain verification.
197
+ *
198
+ * @param proofData - Generic proof data from Arkworks
199
+ * @param circuit - The compiled circuit (must be Arkworks circuit)
200
+ * @param metadata - Circuit metadata with public input count
201
+ * @returns SolanaProofData ready for on-chain verification
202
+ */
203
+ formatProof(proofData: ProofData, circuit: CompiledCircuit, metadata: CircuitMetadata): Promise<SolanaProofData>;
204
+ /**
205
+ * Get Solana-specific metadata for a circuit.
206
+ *
207
+ * @param publicInputCount - Number of public inputs in the circuit
208
+ * @returns Solana metadata with account size and rent estimates
209
+ */
210
+ getChainMetadata(publicInputCount: number): SolanaChainMetadata;
211
+ /**
212
+ * Calculate the size of a VK account for a given number of public inputs.
213
+ * Matches the Rust `vk_account_size` function in the Solana program.
214
+ */
215
+ private calculateVkAccountSize;
216
+ /**
217
+ * Calculate the minimum rent for a VK account.
218
+ */
219
+ private calculateVkAccountRent;
220
+ /**
221
+ * Convert Uint8Array to base64 string.
222
+ */
223
+ private uint8ArrayToBase64;
224
+ /**
225
+ * Convert hex string to Uint8Array.
226
+ */
227
+ private hexToBytes;
228
+ }
229
+
205
230
  interface IParser {
206
231
  parse(fn: CircuitFunction, publicInputs: InputValue[], privateInputs: InputValue[]): ParsedCircuit;
207
232
  }
package/dist/index.js CHANGED
@@ -120,6 +120,7 @@ var init_R1csBuilder = __esm({
120
120
  // w_0 = 1 is reserved
121
121
  publicIndices = [];
122
122
  privateIndices = [];
123
+ auxWitnessComputations = [];
123
124
  // BN254 scalar field modulus - 1 (for representing -1)
124
125
  // Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
125
126
  // -1 mod Fr = Fr - 1
@@ -138,7 +139,8 @@ var init_R1csBuilder = __esm({
138
139
  num_witnesses: this.nextWitnessIdx,
139
140
  public_inputs: this.publicIndices,
140
141
  private_inputs: this.privateIndices,
141
- constraints: this.constraints
142
+ constraints: this.constraints,
143
+ auxWitnessComputations: this.auxWitnessComputations.length > 0 ? this.auxWitnessComputations : void 0
142
144
  };
143
145
  }
144
146
  /**
@@ -369,6 +371,12 @@ var init_R1csBuilder = __esm({
369
371
  const leftIdx = this.getOrCreateWitness(left);
370
372
  const rightIdx = this.getOrCreateWitness(right);
371
373
  const diffIdx = this.nextWitnessIdx++;
374
+ this.auxWitnessComputations.push({
375
+ type: "subtract",
376
+ targetIdx: diffIdx,
377
+ leftIdx,
378
+ rightIdx
379
+ });
372
380
  this.constraints.push({
373
381
  a: [
374
382
  ["0x1", leftIdx],
@@ -388,6 +396,14 @@ var init_R1csBuilder = __esm({
388
396
  const leftIdx = this.getOrCreateWitness(left);
389
397
  const rightIdx = this.getOrCreateWitness(right);
390
398
  const diffIdx = this.nextWitnessIdx++;
399
+ this.auxWitnessComputations.push({
400
+ type: "subtract",
401
+ targetIdx: diffIdx,
402
+ leftIdx,
403
+ rightIdx,
404
+ offset: -1
405
+ // For > operator: diff = left - right - 1
406
+ });
391
407
  this.constraints.push({
392
408
  a: [
393
409
  ["0x1", leftIdx],
@@ -418,6 +434,14 @@ var init_R1csBuilder = __esm({
418
434
  c: [["0x1", bitIdx]]
419
435
  });
420
436
  }
437
+ this.auxWitnessComputations.push({
438
+ type: "bit_decompose",
439
+ targetIdx: bitIndices[0],
440
+ // First bit index (for reference)
441
+ sourceIdx: valueIdx,
442
+ bitIndices,
443
+ numBits: this.COMPARISON_BITS
444
+ });
421
445
  const sumTerms = [];
422
446
  for (let i = 0; i < this.COMPARISON_BITS; i++) {
423
447
  const coeff = (1n << BigInt(i)).toString(16);
@@ -850,6 +874,15 @@ authors = [""]
850
874
  const strVal = String(value);
851
875
  witnessMap[r1csIndex.toString()] = strVal;
852
876
  }
877
+ const r1cs = JSON.parse(circuit.r1csJson);
878
+ if (r1cs.auxWitnessComputations && r1cs.auxWitnessComputations.length > 0) {
879
+ console.log("=== AUXILIARY WITNESS COMPUTATION ===");
880
+ console.log("Input witnesses:", JSON.stringify(witnessMap, null, 2));
881
+ console.log("Computations:", JSON.stringify(r1cs.auxWitnessComputations, null, 2));
882
+ this.computeAuxiliaryWitnesses(witnessMap, r1cs.auxWitnessComputations);
883
+ console.log("After aux computation:", JSON.stringify(witnessMap, null, 2));
884
+ console.log("=====================================");
885
+ }
853
886
  const witnessJson = JSON.stringify(witnessMap);
854
887
  let provingKey = circuit.provingKey;
855
888
  if (!provingKey) {
@@ -902,6 +935,63 @@ authors = [""]
902
935
  publicInputs.length
903
936
  );
904
937
  }
938
+ /**
939
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
940
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
941
+ */
942
+ computeAuxiliaryWitnesses(witnessMap, computations) {
943
+ const FR_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001");
944
+ const getWitnessValue = (idx) => {
945
+ if (idx === 0) return 1n;
946
+ const val = witnessMap[idx.toString()];
947
+ if (val === void 0) {
948
+ throw new Error(`Witness w_${idx} not found`);
949
+ }
950
+ if (val.startsWith("0x")) {
951
+ return BigInt(val);
952
+ }
953
+ return BigInt(val);
954
+ };
955
+ const setWitnessValue = (idx, val) => {
956
+ const normalized = (val % FR_MODULUS + FR_MODULUS) % FR_MODULUS;
957
+ witnessMap[idx.toString()] = "0x" + normalized.toString(16);
958
+ };
959
+ for (const comp of computations) {
960
+ switch (comp.type) {
961
+ case "subtract": {
962
+ const left = getWitnessValue(comp.leftIdx);
963
+ const right = getWitnessValue(comp.rightIdx);
964
+ const offset = BigInt(comp.offset ?? 0);
965
+ const result = left - right + offset;
966
+ setWitnessValue(comp.targetIdx, result);
967
+ break;
968
+ }
969
+ case "bit_decompose": {
970
+ const source = getWitnessValue(comp.sourceIdx);
971
+ const bitIndices = comp.bitIndices;
972
+ const numBits = comp.numBits;
973
+ if (source < 0n) {
974
+ throw new Error(
975
+ `Bit decomposition failed: value ${source} is negative. This means the comparison constraint is not satisfied.`
976
+ );
977
+ }
978
+ const maxVal = (1n << BigInt(numBits)) - 1n;
979
+ if (source > maxVal) {
980
+ throw new Error(
981
+ `Bit decomposition failed: value ${source} exceeds ${numBits} bits (max: ${maxVal}). Consider using a larger bit width or smaller values.`
982
+ );
983
+ }
984
+ for (let i = 0; i < numBits; i++) {
985
+ const bit = source >> BigInt(i) & 1n;
986
+ setWitnessValue(bitIndices[i], bit);
987
+ }
988
+ break;
989
+ }
990
+ default:
991
+ throw new Error(`Unknown auxiliary witness computation type: ${comp.type}`);
992
+ }
993
+ }
994
+ }
905
995
  /**
906
996
  * Get the verifying key in gnark format for on-chain deployment
907
997
  */
@@ -45,6 +45,7 @@ var init_R1csBuilder = __esm({
45
45
  // w_0 = 1 is reserved
46
46
  publicIndices = [];
47
47
  privateIndices = [];
48
+ auxWitnessComputations = [];
48
49
  // BN254 scalar field modulus - 1 (for representing -1)
49
50
  // Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
50
51
  // -1 mod Fr = Fr - 1
@@ -63,7 +64,8 @@ var init_R1csBuilder = __esm({
63
64
  num_witnesses: this.nextWitnessIdx,
64
65
  public_inputs: this.publicIndices,
65
66
  private_inputs: this.privateIndices,
66
- constraints: this.constraints
67
+ constraints: this.constraints,
68
+ auxWitnessComputations: this.auxWitnessComputations.length > 0 ? this.auxWitnessComputations : void 0
67
69
  };
68
70
  }
69
71
  /**
@@ -294,6 +296,12 @@ var init_R1csBuilder = __esm({
294
296
  const leftIdx = this.getOrCreateWitness(left);
295
297
  const rightIdx = this.getOrCreateWitness(right);
296
298
  const diffIdx = this.nextWitnessIdx++;
299
+ this.auxWitnessComputations.push({
300
+ type: "subtract",
301
+ targetIdx: diffIdx,
302
+ leftIdx,
303
+ rightIdx
304
+ });
297
305
  this.constraints.push({
298
306
  a: [
299
307
  ["0x1", leftIdx],
@@ -313,6 +321,14 @@ var init_R1csBuilder = __esm({
313
321
  const leftIdx = this.getOrCreateWitness(left);
314
322
  const rightIdx = this.getOrCreateWitness(right);
315
323
  const diffIdx = this.nextWitnessIdx++;
324
+ this.auxWitnessComputations.push({
325
+ type: "subtract",
326
+ targetIdx: diffIdx,
327
+ leftIdx,
328
+ rightIdx,
329
+ offset: -1
330
+ // For > operator: diff = left - right - 1
331
+ });
316
332
  this.constraints.push({
317
333
  a: [
318
334
  ["0x1", leftIdx],
@@ -343,6 +359,14 @@ var init_R1csBuilder = __esm({
343
359
  c: [["0x1", bitIdx]]
344
360
  });
345
361
  }
362
+ this.auxWitnessComputations.push({
363
+ type: "bit_decompose",
364
+ targetIdx: bitIndices[0],
365
+ // First bit index (for reference)
366
+ sourceIdx: valueIdx,
367
+ bitIndices,
368
+ numBits: this.COMPARISON_BITS
369
+ });
346
370
  const sumTerms = [];
347
371
  for (let i = 0; i < this.COMPARISON_BITS; i++) {
348
372
  const coeff = (1n << BigInt(i)).toString(16);
@@ -776,6 +800,15 @@ authors = [""]
776
800
  const strVal = String(value);
777
801
  witnessMap[r1csIndex.toString()] = strVal;
778
802
  }
803
+ const r1cs = JSON.parse(circuit.r1csJson);
804
+ if (r1cs.auxWitnessComputations && r1cs.auxWitnessComputations.length > 0) {
805
+ console.log("=== AUXILIARY WITNESS COMPUTATION ===");
806
+ console.log("Input witnesses:", JSON.stringify(witnessMap, null, 2));
807
+ console.log("Computations:", JSON.stringify(r1cs.auxWitnessComputations, null, 2));
808
+ this.computeAuxiliaryWitnesses(witnessMap, r1cs.auxWitnessComputations);
809
+ console.log("After aux computation:", JSON.stringify(witnessMap, null, 2));
810
+ console.log("=====================================");
811
+ }
779
812
  const witnessJson = JSON.stringify(witnessMap);
780
813
  let provingKey = circuit.provingKey;
781
814
  if (!provingKey) {
@@ -828,6 +861,63 @@ authors = [""]
828
861
  publicInputs.length
829
862
  );
830
863
  }
864
+ /**
865
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
866
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
867
+ */
868
+ computeAuxiliaryWitnesses(witnessMap, computations) {
869
+ const FR_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001");
870
+ const getWitnessValue = (idx) => {
871
+ if (idx === 0) return 1n;
872
+ const val = witnessMap[idx.toString()];
873
+ if (val === void 0) {
874
+ throw new Error(`Witness w_${idx} not found`);
875
+ }
876
+ if (val.startsWith("0x")) {
877
+ return BigInt(val);
878
+ }
879
+ return BigInt(val);
880
+ };
881
+ const setWitnessValue = (idx, val) => {
882
+ const normalized = (val % FR_MODULUS + FR_MODULUS) % FR_MODULUS;
883
+ witnessMap[idx.toString()] = "0x" + normalized.toString(16);
884
+ };
885
+ for (const comp of computations) {
886
+ switch (comp.type) {
887
+ case "subtract": {
888
+ const left = getWitnessValue(comp.leftIdx);
889
+ const right = getWitnessValue(comp.rightIdx);
890
+ const offset = BigInt(comp.offset ?? 0);
891
+ const result = left - right + offset;
892
+ setWitnessValue(comp.targetIdx, result);
893
+ break;
894
+ }
895
+ case "bit_decompose": {
896
+ const source = getWitnessValue(comp.sourceIdx);
897
+ const bitIndices = comp.bitIndices;
898
+ const numBits = comp.numBits;
899
+ if (source < 0n) {
900
+ throw new Error(
901
+ `Bit decomposition failed: value ${source} is negative. This means the comparison constraint is not satisfied.`
902
+ );
903
+ }
904
+ const maxVal = (1n << BigInt(numBits)) - 1n;
905
+ if (source > maxVal) {
906
+ throw new Error(
907
+ `Bit decomposition failed: value ${source} exceeds ${numBits} bits (max: ${maxVal}). Consider using a larger bit width or smaller values.`
908
+ );
909
+ }
910
+ for (let i = 0; i < numBits; i++) {
911
+ const bit = source >> BigInt(i) & 1n;
912
+ setWitnessValue(bitIndices[i], bit);
913
+ }
914
+ break;
915
+ }
916
+ default:
917
+ throw new Error(`Unknown auxiliary witness computation type: ${comp.type}`);
918
+ }
919
+ }
920
+ }
831
921
  /**
832
922
  * Get the verifying key in gnark format for on-chain deployment
833
923
  */
@@ -70,6 +70,7 @@ interface ArkworksWasmConfig {
70
70
  /** Cache proving/verifying keys for repeated proofs */
71
71
  cacheKeys?: boolean;
72
72
  }
73
+
73
74
  /**
74
75
  * Extended CompiledCircuit for ArkworksWasm backend
75
76
  */
@@ -121,6 +122,11 @@ declare class ArkworksWasm implements IProvingSystem {
121
122
  * Verify a Groth16 proof
122
123
  */
123
124
  verifyProof(circuit: CompiledCircuit, proof: Uint8Array, publicInputs: string[]): Promise<boolean>;
125
+ /**
126
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
127
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
128
+ */
129
+ private computeAuxiliaryWitnesses;
124
130
  /**
125
131
  * Get the verifying key in gnark format for on-chain deployment
126
132
  */
@@ -70,6 +70,7 @@ interface ArkworksWasmConfig {
70
70
  /** Cache proving/verifying keys for repeated proofs */
71
71
  cacheKeys?: boolean;
72
72
  }
73
+
73
74
  /**
74
75
  * Extended CompiledCircuit for ArkworksWasm backend
75
76
  */
@@ -121,6 +122,11 @@ declare class ArkworksWasm implements IProvingSystem {
121
122
  * Verify a Groth16 proof
122
123
  */
123
124
  verifyProof(circuit: CompiledCircuit, proof: Uint8Array, publicInputs: string[]): Promise<boolean>;
125
+ /**
126
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
127
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
128
+ */
129
+ private computeAuxiliaryWitnesses;
124
130
  /**
125
131
  * Get the verifying key in gnark format for on-chain deployment
126
132
  */
@@ -23,6 +23,7 @@ var init_R1csBuilder = __esm({
23
23
  // w_0 = 1 is reserved
24
24
  publicIndices = [];
25
25
  privateIndices = [];
26
+ auxWitnessComputations = [];
26
27
  // BN254 scalar field modulus - 1 (for representing -1)
27
28
  // Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
28
29
  // -1 mod Fr = Fr - 1
@@ -41,7 +42,8 @@ var init_R1csBuilder = __esm({
41
42
  num_witnesses: this.nextWitnessIdx,
42
43
  public_inputs: this.publicIndices,
43
44
  private_inputs: this.privateIndices,
44
- constraints: this.constraints
45
+ constraints: this.constraints,
46
+ auxWitnessComputations: this.auxWitnessComputations.length > 0 ? this.auxWitnessComputations : void 0
45
47
  };
46
48
  }
47
49
  /**
@@ -272,6 +274,12 @@ var init_R1csBuilder = __esm({
272
274
  const leftIdx = this.getOrCreateWitness(left);
273
275
  const rightIdx = this.getOrCreateWitness(right);
274
276
  const diffIdx = this.nextWitnessIdx++;
277
+ this.auxWitnessComputations.push({
278
+ type: "subtract",
279
+ targetIdx: diffIdx,
280
+ leftIdx,
281
+ rightIdx
282
+ });
275
283
  this.constraints.push({
276
284
  a: [
277
285
  ["0x1", leftIdx],
@@ -291,6 +299,14 @@ var init_R1csBuilder = __esm({
291
299
  const leftIdx = this.getOrCreateWitness(left);
292
300
  const rightIdx = this.getOrCreateWitness(right);
293
301
  const diffIdx = this.nextWitnessIdx++;
302
+ this.auxWitnessComputations.push({
303
+ type: "subtract",
304
+ targetIdx: diffIdx,
305
+ leftIdx,
306
+ rightIdx,
307
+ offset: -1
308
+ // For > operator: diff = left - right - 1
309
+ });
294
310
  this.constraints.push({
295
311
  a: [
296
312
  ["0x1", leftIdx],
@@ -321,6 +337,14 @@ var init_R1csBuilder = __esm({
321
337
  c: [["0x1", bitIdx]]
322
338
  });
323
339
  }
340
+ this.auxWitnessComputations.push({
341
+ type: "bit_decompose",
342
+ targetIdx: bitIndices[0],
343
+ // First bit index (for reference)
344
+ sourceIdx: valueIdx,
345
+ bitIndices,
346
+ numBits: this.COMPARISON_BITS
347
+ });
324
348
  const sumTerms = [];
325
349
  for (let i = 0; i < this.COMPARISON_BITS; i++) {
326
350
  const coeff = (1n << BigInt(i)).toString(16);
@@ -753,6 +777,15 @@ authors = [""]
753
777
  const strVal = String(value);
754
778
  witnessMap[r1csIndex.toString()] = strVal;
755
779
  }
780
+ const r1cs = JSON.parse(circuit.r1csJson);
781
+ if (r1cs.auxWitnessComputations && r1cs.auxWitnessComputations.length > 0) {
782
+ console.log("=== AUXILIARY WITNESS COMPUTATION ===");
783
+ console.log("Input witnesses:", JSON.stringify(witnessMap, null, 2));
784
+ console.log("Computations:", JSON.stringify(r1cs.auxWitnessComputations, null, 2));
785
+ this.computeAuxiliaryWitnesses(witnessMap, r1cs.auxWitnessComputations);
786
+ console.log("After aux computation:", JSON.stringify(witnessMap, null, 2));
787
+ console.log("=====================================");
788
+ }
756
789
  const witnessJson = JSON.stringify(witnessMap);
757
790
  let provingKey = circuit.provingKey;
758
791
  if (!provingKey) {
@@ -805,6 +838,63 @@ authors = [""]
805
838
  publicInputs.length
806
839
  );
807
840
  }
841
+ /**
842
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
843
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
844
+ */
845
+ computeAuxiliaryWitnesses(witnessMap, computations) {
846
+ const FR_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001");
847
+ const getWitnessValue = (idx) => {
848
+ if (idx === 0) return 1n;
849
+ const val = witnessMap[idx.toString()];
850
+ if (val === void 0) {
851
+ throw new Error(`Witness w_${idx} not found`);
852
+ }
853
+ if (val.startsWith("0x")) {
854
+ return BigInt(val);
855
+ }
856
+ return BigInt(val);
857
+ };
858
+ const setWitnessValue = (idx, val) => {
859
+ const normalized = (val % FR_MODULUS + FR_MODULUS) % FR_MODULUS;
860
+ witnessMap[idx.toString()] = "0x" + normalized.toString(16);
861
+ };
862
+ for (const comp of computations) {
863
+ switch (comp.type) {
864
+ case "subtract": {
865
+ const left = getWitnessValue(comp.leftIdx);
866
+ const right = getWitnessValue(comp.rightIdx);
867
+ const offset = BigInt(comp.offset ?? 0);
868
+ const result = left - right + offset;
869
+ setWitnessValue(comp.targetIdx, result);
870
+ break;
871
+ }
872
+ case "bit_decompose": {
873
+ const source = getWitnessValue(comp.sourceIdx);
874
+ const bitIndices = comp.bitIndices;
875
+ const numBits = comp.numBits;
876
+ if (source < 0n) {
877
+ throw new Error(
878
+ `Bit decomposition failed: value ${source} is negative. This means the comparison constraint is not satisfied.`
879
+ );
880
+ }
881
+ const maxVal = (1n << BigInt(numBits)) - 1n;
882
+ if (source > maxVal) {
883
+ throw new Error(
884
+ `Bit decomposition failed: value ${source} exceeds ${numBits} bits (max: ${maxVal}). Consider using a larger bit width or smaller values.`
885
+ );
886
+ }
887
+ for (let i = 0; i < numBits; i++) {
888
+ const bit = source >> BigInt(i) & 1n;
889
+ setWitnessValue(bitIndices[i], bit);
890
+ }
891
+ break;
892
+ }
893
+ default:
894
+ throw new Error(`Unknown auxiliary witness computation type: ${comp.type}`);
895
+ }
896
+ }
897
+ }
808
898
  /**
809
899
  * Get the verifying key in gnark format for on-chain deployment
810
900
  */
@@ -142,6 +142,7 @@ var init_R1csBuilder = __esm({
142
142
  // w_0 = 1 is reserved
143
143
  publicIndices = [];
144
144
  privateIndices = [];
145
+ auxWitnessComputations = [];
145
146
  // BN254 scalar field modulus - 1 (for representing -1)
146
147
  // Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
147
148
  // -1 mod Fr = Fr - 1
@@ -160,7 +161,8 @@ var init_R1csBuilder = __esm({
160
161
  num_witnesses: this.nextWitnessIdx,
161
162
  public_inputs: this.publicIndices,
162
163
  private_inputs: this.privateIndices,
163
- constraints: this.constraints
164
+ constraints: this.constraints,
165
+ auxWitnessComputations: this.auxWitnessComputations.length > 0 ? this.auxWitnessComputations : void 0
164
166
  };
165
167
  }
166
168
  /**
@@ -391,6 +393,12 @@ var init_R1csBuilder = __esm({
391
393
  const leftIdx = this.getOrCreateWitness(left);
392
394
  const rightIdx = this.getOrCreateWitness(right);
393
395
  const diffIdx = this.nextWitnessIdx++;
396
+ this.auxWitnessComputations.push({
397
+ type: "subtract",
398
+ targetIdx: diffIdx,
399
+ leftIdx,
400
+ rightIdx
401
+ });
394
402
  this.constraints.push({
395
403
  a: [
396
404
  ["0x1", leftIdx],
@@ -410,6 +418,14 @@ var init_R1csBuilder = __esm({
410
418
  const leftIdx = this.getOrCreateWitness(left);
411
419
  const rightIdx = this.getOrCreateWitness(right);
412
420
  const diffIdx = this.nextWitnessIdx++;
421
+ this.auxWitnessComputations.push({
422
+ type: "subtract",
423
+ targetIdx: diffIdx,
424
+ leftIdx,
425
+ rightIdx,
426
+ offset: -1
427
+ // For > operator: diff = left - right - 1
428
+ });
413
429
  this.constraints.push({
414
430
  a: [
415
431
  ["0x1", leftIdx],
@@ -440,6 +456,14 @@ var init_R1csBuilder = __esm({
440
456
  c: [["0x1", bitIdx]]
441
457
  });
442
458
  }
459
+ this.auxWitnessComputations.push({
460
+ type: "bit_decompose",
461
+ targetIdx: bitIndices[0],
462
+ // First bit index (for reference)
463
+ sourceIdx: valueIdx,
464
+ bitIndices,
465
+ numBits: this.COMPARISON_BITS
466
+ });
443
467
  const sumTerms = [];
444
468
  for (let i = 0; i < this.COMPARISON_BITS; i++) {
445
469
  const coeff = (1n << BigInt(i)).toString(16);
@@ -873,6 +897,15 @@ authors = [""]
873
897
  const strVal = String(value);
874
898
  witnessMap[r1csIndex.toString()] = strVal;
875
899
  }
900
+ const r1cs = JSON.parse(circuit.r1csJson);
901
+ if (r1cs.auxWitnessComputations && r1cs.auxWitnessComputations.length > 0) {
902
+ console.log("=== AUXILIARY WITNESS COMPUTATION ===");
903
+ console.log("Input witnesses:", JSON.stringify(witnessMap, null, 2));
904
+ console.log("Computations:", JSON.stringify(r1cs.auxWitnessComputations, null, 2));
905
+ this.computeAuxiliaryWitnesses(witnessMap, r1cs.auxWitnessComputations);
906
+ console.log("After aux computation:", JSON.stringify(witnessMap, null, 2));
907
+ console.log("=====================================");
908
+ }
876
909
  const witnessJson = JSON.stringify(witnessMap);
877
910
  let provingKey = circuit.provingKey;
878
911
  if (!provingKey) {
@@ -925,6 +958,63 @@ authors = [""]
925
958
  publicInputs.length
926
959
  );
927
960
  }
961
+ /**
962
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
963
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
964
+ */
965
+ computeAuxiliaryWitnesses(witnessMap, computations) {
966
+ const FR_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001");
967
+ const getWitnessValue = (idx) => {
968
+ if (idx === 0) return 1n;
969
+ const val = witnessMap[idx.toString()];
970
+ if (val === void 0) {
971
+ throw new Error(`Witness w_${idx} not found`);
972
+ }
973
+ if (val.startsWith("0x")) {
974
+ return BigInt(val);
975
+ }
976
+ return BigInt(val);
977
+ };
978
+ const setWitnessValue = (idx, val) => {
979
+ const normalized = (val % FR_MODULUS + FR_MODULUS) % FR_MODULUS;
980
+ witnessMap[idx.toString()] = "0x" + normalized.toString(16);
981
+ };
982
+ for (const comp of computations) {
983
+ switch (comp.type) {
984
+ case "subtract": {
985
+ const left = getWitnessValue(comp.leftIdx);
986
+ const right = getWitnessValue(comp.rightIdx);
987
+ const offset = BigInt(comp.offset ?? 0);
988
+ const result = left - right + offset;
989
+ setWitnessValue(comp.targetIdx, result);
990
+ break;
991
+ }
992
+ case "bit_decompose": {
993
+ const source = getWitnessValue(comp.sourceIdx);
994
+ const bitIndices = comp.bitIndices;
995
+ const numBits = comp.numBits;
996
+ if (source < 0n) {
997
+ throw new Error(
998
+ `Bit decomposition failed: value ${source} is negative. This means the comparison constraint is not satisfied.`
999
+ );
1000
+ }
1001
+ const maxVal = (1n << BigInt(numBits)) - 1n;
1002
+ if (source > maxVal) {
1003
+ throw new Error(
1004
+ `Bit decomposition failed: value ${source} exceeds ${numBits} bits (max: ${maxVal}). Consider using a larger bit width or smaller values.`
1005
+ );
1006
+ }
1007
+ for (let i = 0; i < numBits; i++) {
1008
+ const bit = source >> BigInt(i) & 1n;
1009
+ setWitnessValue(bitIndices[i], bit);
1010
+ }
1011
+ break;
1012
+ }
1013
+ default:
1014
+ throw new Error(`Unknown auxiliary witness computation type: ${comp.type}`);
1015
+ }
1016
+ }
1017
+ }
928
1018
  /**
929
1019
  * Get the verifying key in gnark format for on-chain deployment
930
1020
  */
@@ -120,6 +120,7 @@ var init_R1csBuilder = __esm({
120
120
  // w_0 = 1 is reserved
121
121
  publicIndices = [];
122
122
  privateIndices = [];
123
+ auxWitnessComputations = [];
123
124
  // BN254 scalar field modulus - 1 (for representing -1)
124
125
  // Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
125
126
  // -1 mod Fr = Fr - 1
@@ -138,7 +139,8 @@ var init_R1csBuilder = __esm({
138
139
  num_witnesses: this.nextWitnessIdx,
139
140
  public_inputs: this.publicIndices,
140
141
  private_inputs: this.privateIndices,
141
- constraints: this.constraints
142
+ constraints: this.constraints,
143
+ auxWitnessComputations: this.auxWitnessComputations.length > 0 ? this.auxWitnessComputations : void 0
142
144
  };
143
145
  }
144
146
  /**
@@ -369,6 +371,12 @@ var init_R1csBuilder = __esm({
369
371
  const leftIdx = this.getOrCreateWitness(left);
370
372
  const rightIdx = this.getOrCreateWitness(right);
371
373
  const diffIdx = this.nextWitnessIdx++;
374
+ this.auxWitnessComputations.push({
375
+ type: "subtract",
376
+ targetIdx: diffIdx,
377
+ leftIdx,
378
+ rightIdx
379
+ });
372
380
  this.constraints.push({
373
381
  a: [
374
382
  ["0x1", leftIdx],
@@ -388,6 +396,14 @@ var init_R1csBuilder = __esm({
388
396
  const leftIdx = this.getOrCreateWitness(left);
389
397
  const rightIdx = this.getOrCreateWitness(right);
390
398
  const diffIdx = this.nextWitnessIdx++;
399
+ this.auxWitnessComputations.push({
400
+ type: "subtract",
401
+ targetIdx: diffIdx,
402
+ leftIdx,
403
+ rightIdx,
404
+ offset: -1
405
+ // For > operator: diff = left - right - 1
406
+ });
391
407
  this.constraints.push({
392
408
  a: [
393
409
  ["0x1", leftIdx],
@@ -418,6 +434,14 @@ var init_R1csBuilder = __esm({
418
434
  c: [["0x1", bitIdx]]
419
435
  });
420
436
  }
437
+ this.auxWitnessComputations.push({
438
+ type: "bit_decompose",
439
+ targetIdx: bitIndices[0],
440
+ // First bit index (for reference)
441
+ sourceIdx: valueIdx,
442
+ bitIndices,
443
+ numBits: this.COMPARISON_BITS
444
+ });
421
445
  const sumTerms = [];
422
446
  for (let i = 0; i < this.COMPARISON_BITS; i++) {
423
447
  const coeff = (1n << BigInt(i)).toString(16);
@@ -850,6 +874,15 @@ authors = [""]
850
874
  const strVal = String(value);
851
875
  witnessMap[r1csIndex.toString()] = strVal;
852
876
  }
877
+ const r1cs = JSON.parse(circuit.r1csJson);
878
+ if (r1cs.auxWitnessComputations && r1cs.auxWitnessComputations.length > 0) {
879
+ console.log("=== AUXILIARY WITNESS COMPUTATION ===");
880
+ console.log("Input witnesses:", JSON.stringify(witnessMap, null, 2));
881
+ console.log("Computations:", JSON.stringify(r1cs.auxWitnessComputations, null, 2));
882
+ this.computeAuxiliaryWitnesses(witnessMap, r1cs.auxWitnessComputations);
883
+ console.log("After aux computation:", JSON.stringify(witnessMap, null, 2));
884
+ console.log("=====================================");
885
+ }
853
886
  const witnessJson = JSON.stringify(witnessMap);
854
887
  let provingKey = circuit.provingKey;
855
888
  if (!provingKey) {
@@ -902,6 +935,63 @@ authors = [""]
902
935
  publicInputs.length
903
936
  );
904
937
  }
938
+ /**
939
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
940
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
941
+ */
942
+ computeAuxiliaryWitnesses(witnessMap, computations) {
943
+ const FR_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001");
944
+ const getWitnessValue = (idx) => {
945
+ if (idx === 0) return 1n;
946
+ const val = witnessMap[idx.toString()];
947
+ if (val === void 0) {
948
+ throw new Error(`Witness w_${idx} not found`);
949
+ }
950
+ if (val.startsWith("0x")) {
951
+ return BigInt(val);
952
+ }
953
+ return BigInt(val);
954
+ };
955
+ const setWitnessValue = (idx, val) => {
956
+ const normalized = (val % FR_MODULUS + FR_MODULUS) % FR_MODULUS;
957
+ witnessMap[idx.toString()] = "0x" + normalized.toString(16);
958
+ };
959
+ for (const comp of computations) {
960
+ switch (comp.type) {
961
+ case "subtract": {
962
+ const left = getWitnessValue(comp.leftIdx);
963
+ const right = getWitnessValue(comp.rightIdx);
964
+ const offset = BigInt(comp.offset ?? 0);
965
+ const result = left - right + offset;
966
+ setWitnessValue(comp.targetIdx, result);
967
+ break;
968
+ }
969
+ case "bit_decompose": {
970
+ const source = getWitnessValue(comp.sourceIdx);
971
+ const bitIndices = comp.bitIndices;
972
+ const numBits = comp.numBits;
973
+ if (source < 0n) {
974
+ throw new Error(
975
+ `Bit decomposition failed: value ${source} is negative. This means the comparison constraint is not satisfied.`
976
+ );
977
+ }
978
+ const maxVal = (1n << BigInt(numBits)) - 1n;
979
+ if (source > maxVal) {
980
+ throw new Error(
981
+ `Bit decomposition failed: value ${source} exceeds ${numBits} bits (max: ${maxVal}). Consider using a larger bit width or smaller values.`
982
+ );
983
+ }
984
+ for (let i = 0; i < numBits; i++) {
985
+ const bit = source >> BigInt(i) & 1n;
986
+ setWitnessValue(bitIndices[i], bit);
987
+ }
988
+ break;
989
+ }
990
+ default:
991
+ throw new Error(`Unknown auxiliary witness computation type: ${comp.type}`);
992
+ }
993
+ }
994
+ }
905
995
  /**
906
996
  * Get the verifying key in gnark format for on-chain deployment
907
997
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izi-noir/sdk",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Write ZK circuits in JavaScript/TypeScript, generate Noir code and proofs automatically",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",