@izi-noir/sdk 0.1.14 → 0.1.16
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 +92 -2
- package/dist/index.d.cts +80 -55
- package/dist/index.d.ts +80 -55
- package/dist/index.js +92 -2
- package/dist/providers/arkworks.cjs +91 -1
- package/dist/providers/arkworks.d.cts +6 -0
- package/dist/providers/arkworks.d.ts +6 -0
- package/dist/providers/arkworks.js +91 -1
- package/dist/providers/barretenberg.cjs +91 -1
- package/dist/providers/barretenberg.js +91 -1
- package/package.json +1 -1
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
|
*/
|
|
@@ -2852,7 +2942,7 @@ var CreateProofUseCase = class {
|
|
|
2852
2942
|
const noirCode = generateNoir(parsed);
|
|
2853
2943
|
timings.generateMs = performance.now() - generateStart;
|
|
2854
2944
|
const compileStart = performance.now();
|
|
2855
|
-
const circuit = await this.deps.provingSystem.compile(noirCode);
|
|
2945
|
+
const circuit = await this.deps.provingSystem.compile(noirCode, { parsedCircuit: parsed });
|
|
2856
2946
|
timings.compileMs = performance.now() - compileStart;
|
|
2857
2947
|
const toNoirInput = (val) => typeof val === "bigint" ? val.toString() : val;
|
|
2858
2948
|
const inputs = {};
|
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 {
|
|
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 {
|
|
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
|
*/
|
|
@@ -2783,7 +2873,7 @@ var CreateProofUseCase = class {
|
|
|
2783
2873
|
const noirCode = generateNoir(parsed);
|
|
2784
2874
|
timings.generateMs = performance.now() - generateStart;
|
|
2785
2875
|
const compileStart = performance.now();
|
|
2786
|
-
const circuit = await this.deps.provingSystem.compile(noirCode);
|
|
2876
|
+
const circuit = await this.deps.provingSystem.compile(noirCode, { parsedCircuit: parsed });
|
|
2787
2877
|
timings.compileMs = performance.now() - compileStart;
|
|
2788
2878
|
const toNoirInput = (val) => typeof val === "bigint" ? val.toString() : val;
|
|
2789
2879
|
const inputs = {};
|
|
@@ -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
|
*/
|