Troubleshooting

Common issues and solutions for ZKVAULT development and deployment.

Proof Generation Issues

Error: Constraint not satisfied

Symptom: Proof generation fails with constraint error

Error: Constraint not satisfied at line 42
Signal 'intermediate': Expected 12345, got 54321

Causes:

  • Incorrect input values
  • Circuit logic bug
  • Arithmetic overflow in circuit

Solutions:

// 1. Debug with trace logging
zkvault debug \
  --circuit circuit.circom \
  --witness witness.json \
  --trace-level verbose

// 2. Validate inputs before proving
function validateInputs(inputs) {
  if (inputs.a < 0 || inputs.a >= FIELD_MODULUS) {
    throw new Error('Input a out of range');
  }
  // Check all inputs...
}

// 3. Test circuit with known-good inputs
zkvault prove \
  --circuit circuit.circom \
  --witness test_witness.json \
  --verbose

Error: Out of memory during proving

Symptom: Process killed or OOM error during proof generation

Solutions:

// 1. Increase Node.js memory limit
export NODE_OPTIONS="--max-old-space-size=8192"

// 2. Use streaming witness generation
const witness = await circuit.calculateWitnessStreaming(input);

// 3. Optimize circuit (reduce constraints)
// Before: 500,000 constraints
// After: 250,000 constraints (2x memory reduction)

// 4. Use GPU proving for large circuits
zkvault prove \
  --circuit circuit.circom \
  --witness witness.json \
  --backend gpu

Error: Proving takes too long

Symptom: Proof generation exceeds acceptable time

Solutions:

// 1. Profile circuit to find bottlenecks
zkvault analyze constraints --circuit circuit.circom

// 2. Use optimized proving backend
zkvault prove --backend gpu  // 3-5x faster

// 3. Optimize circuit structure
// Bad: Many small constraints
for (var i = 0; i < 1000; i++) {
    component hash[i] = Poseidon(2);
}

// Good: Batched operations
component hashTree = PoseidonTree(1000);

// 4. Cache compiled circuits
zkvault config set proving.cache_circuits true

// 5. Pre-compute witness generation
const witness = await precomputeWitness(input);

Verification Issues

Error: Pairing check failed

Symptom: Verification fails with pairing check error

Error: Proof verification failed - pairing check failed

Causes:

  • Proof generated with wrong verification key
  • Public inputs mismatch
  • Corrupted proof data
  • Wrong proof protocol (Groth16 vs PLONK)

Solutions:

// 1. Verify proof format
zkvault proof validate proof.json

// 2. Check VK matches circuit
zkvault vk check-compatibility \
  --circuit circuit.r1cs \
  --vk vk.json

// 3. Validate public inputs
const expectedInputs = [123, 456, 789];
const actualInputs = proof.publicInputs;
assert.deepEqual(actualInputs, expectedInputs);

// 4. Re-generate proof with correct VK
zkvault prove \
  --circuit circuit.circom \
  --witness witness.json \
  --vk vk.json  // Specify correct VK

// 5. Check proof hasn't been corrupted
const proofHash = computeHash(proof);
console.log('Proof hash:', proofHash);

Error: Compute unit limit exceeded

Symptom: On-chain verification fails due to CU limit

Error: Transaction exceeded compute unit limit (400000)

Solutions:

// 1. Estimate CU before submission
const estimate = await zkvault.estimateComputeUnits(proof);
console.log(`Estimated CU: ${estimate}`);

// 2. Request additional compute units
const tx = await program.methods
  .verifyProof(proof)
  .preInstructions([
    ComputeBudgetProgram.setComputeUnitLimit({
      units: 500_000,
    }),
  ])
  .rpc();

// 3. Use proof compression
const compressed = await zkvault.compress(proof, 'high');
// Verification is faster with compressed proofs

// 4. Split verification into multiple transactions
// For very large proofs, use incremental verification
await verifyProofIncremental(proof, {
  chunkSize: 100_000,  // CU per transaction
});

// 5. Optimize circuit for on-chain verification
// Reduce number of public inputs
// Use pairing-friendly curve operations

Integration Issues

Error: Wallet not connected

Symptom: Cannot submit transactions

Solutions:

// 1. Check wallet connection
import { useWallet } from '@solana/wallet-adapter-react';

function MyComponent() {
  const { connected, connect } = useWallet();
  
  useEffect(() => {
    if (!connected) {
      connect().catch(console.error);
    }
  }, [connected]);
}

// 2. Handle connection errors
try {
  await wallet.connect();
} catch (error) {
  if (error.name === 'WalletNotReadyError') {
    alert('Please install a Solana wallet');
  }
}

// 3. Use configured RPC connection
const connection = new Connection('https://api.devnet.solana.com');

Error: Transaction simulation failed

Symptom: Transaction fails before submission

Solutions:

// 1. Check account states
const vaultAccount = await program.account.vault.fetch(vaultPda);
console.log('Vault state:', vaultAccount);

// 2. Verify account ownership
require(vaultAccount.authority.equals(wallet.publicKey));

// 3. Check for frozen vault
if (vaultAccount.isFrozen) {
  throw new Error('Vault is frozen');
}

// 4. Validate PDAs
const [expectedPda, bump] = PublicKey.findProgramAddressSync(
  [Buffer.from('vault'), authority.toBuffer()],
  programId
);

// 5. Add simulation logging
const { value: simulationResult } = await connection.simulateTransaction(tx);
console.log('Simulation logs:', simulationResult.logs);

Error: RPC rate limit exceeded

Symptom: Too many requests error from RPC

Solutions:

// 1. Implement request throttling
import pThrottle from 'p-throttle';

const throttled = pThrottle({
  limit: 10,  // 10 requests
  interval: 1000,  // per second
});

// 2. Use connection pooling
const connections = [
  new Connection(RPC_URL_1),
  new Connection(RPC_URL_2),
  new Connection(RPC_URL_3),
];

let currentIndex = 0;
function getConnection() {
  const conn = connections[currentIndex];
  currentIndex = (currentIndex + 1) % connections.length;
  return conn;
}

// 3. Implement exponential backoff
async function sendWithRetry(tx, maxRetries = 5) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await connection.sendTransaction(tx);
    } catch (error) {
      if (error.message.includes('rate limit')) {
        const delay = Math.pow(2, i) * 1000;
        await sleep(delay);
        continue;
      }
      throw error;
    }
  }
}

// 4. Use premium RPC providers
// - Helius, QuickNode, Triton for higher limits

Circuit Issues

Error: Witness generation failed

Symptom: Cannot generate witness from inputs

Solutions:

// 1. Validate input JSON format
const inputs = JSON.parse(inputFile);
assert(inputs.a !== undefined, 'Missing input: a');

// 2. Check for arithmetic errors
// Cause: Division by zero in circuit
signal output quotient <== numerator / denominator;
// Fix: Add zero check
assert(denominator != 0);

// 3. Verify input types
// Circuit expects: signal input a
// Input must be: number or string representing field element
{
  "a": "123",  // ✓ Correct
  "a": 123,    // ✓ Correct
  "a": "abc",  // ✗ Wrong
}

// 4. Check for missing signals
zkvault circuit info circuit.circom
// Lists all required input signals

Error: Too many constraints

Symptom: Circuit compilation or proving fails due to size

Solutions:

// 1. Analyze constraint usage
zkvault circuit info circuit.circom
// Output: Constraints: 2,450,123 (too many!)

// 2. Optimize using lookup tables
// Before: 50,000 constraints
for (var i = 0; i < 1000; i++) {
    component range = Num2Bits(252);
    range.in <== values[i];
}

// After: 5,000 constraints
component lookupTable = RangeLookup(1000);
lookupTable.values <== values;

// 3. Use recursive proofs to split work
// Generate proofs for sub-circuits, then aggregate

// 4. Switch to PLONK (more efficient for some circuits)
zkvault setup --protocol plonk

// 5. Simplify circuit logic
// Remove unnecessary computations
// Combine similar operations

Deployment Issues

Error: Program deployment failed

Symptom: Cannot deploy Solana program

Solutions:

// 1. Check SOL balance
solana balance
// Need ~2 SOL for program deployment

// 2. Verify program size
ls -lh target/deploy/program.so
// Must be < 400KB for standard deployment

// 3. Use verifiable build
anchor build --verifiable

// 4. Deploy with sufficient compute
solana program deploy target/deploy/program.so \
  --max-len 400000 \
  --with-compute-unit-price 1

// 5. For large programs, use write buffer
solana program write-buffer target/deploy/program.so
solana program deploy <BUFFER_ADDRESS>

Error: Verification key upload failed

Symptom: Cannot upload VK to vault

Solutions:

// 1. Check VK file size
ls -lh verification_key.json
// If > 10KB, may need multiple transactions

// 2. Validate VK format
zkvault vk validate verification_key.json

// 3. Chunk large VKs
zkvault vk upload \
  --vault $VAULT \
  --vk verification_key.json \
  --chunked  // Automatically splits

// 4. Verify authority
// Only vault authority can upload VK
solana address
// Must match vault.authority

// 5. Check vault state
zkvault vault info $VAULT
// Ensure vault is active, not frozen

Performance Issues

Slow proof generation

Diagnosis:

// Profile proving performance
zkvault benchmark prove \
  --circuit circuit.circom \
  --witness witness.json \
  --iterations 10

// Output:
// Breakdown:
//   Witness generation: 8.2s (40%)
//   FFT operations: 7.1s (35%)
//   Scalar multiplication: 5.1s (25%)

Optimizations:

// 1. Use GPU proving
zkvault config set proving.backend gpu
// 3-5x speedup

// 2. Cache witness generation
const witnessCache = new Map();
function getCachedWitness(input) {
  const key = hashInput(input);
  if (witnessCache.has(key)) {
    return witnessCache.get(key);
  }
  const witness = circuit.calculateWitness(input);
  witnessCache.set(key, witness);
  return witness;
}

// 3. Parallelize batch proving
await Promise.all(
  inputs.map(input => zkvault.prove(circuit, input))
);

// 4. Use WASM for browser proving
// Faster than pure JS implementation
zkvault config set proving.backend wasm

Debugging Tools

Enable Debug Logging

// Set log level in code
zkvault.setLogLevel('debug');

// View detailed logs
zkvault prove \
  --circuit circuit.circom \
  --witness witness.json \
  --verbose

// Logs show:
// [DEBUG] Loading circuit...
// [DEBUG] Generating witness...
// [DEBUG] FFT transform...
// [DEBUG] Multiexponentiation...
// [INFO] Proof generated in 12.3s

Circuit Debugger

// Interactive circuit debugging
zkvault debug-interactive \
  --circuit circuit.circom \
  --witness witness.json

// Commands:
// > step          - Execute next constraint
// > break 42      - Set breakpoint at line 42
// > print signal  - Show signal value
// > trace         - Show execution trace
// > continue      - Run to next breakpoint

Getting Help

Support Channels

Bug Report Template

**Describe the bug**
Clear and concise description.

**To Reproduce**
Steps to reproduce:
1. Load circuit 'example.circom'
2. Generate witness with input {...}
3. Run `zkvault prove ...`
4. See error

**Expected behavior**
What you expected to happen.

**System Information**
- OS: [e.g. Ubuntu 22.04]
- Node version: [e.g. v18.16.0]
- ZKVAULT version: [e.g. 1.2.3]
- Circuit size: [e.g. 50K constraints]

**Logs**
```
Paste relevant logs here
```

**Additional context**
Any other information.