This is Cli tool for generation of aggregated proof for multiple inputs.
This implementation includes Approximate Sum-Check Protocol based on the paper "Sum-Check Protocol for Approximate Computations" (2025).
In this version, it supports circom circuit.
"Sum-Check Protocol for Approximate Computations" (2025)
The standard sum-check protocol requires exact polynomial evaluations, which limits its applicability to computations with inherent approximation errors (e.g., floating-point arithmetic, machine learning inference). This work introduces an approximate sum-check protocol that tolerates bounded errors while maintaining soundness guarantees.
- Approximate Sum-Check Protocol: Relaxes exact equality to bounded error tolerance
- Error Accumulation Analysis: Proves that errors accumulate linearly across rounds
- Applications to FP Arithmetic: Enables ZK proofs for IEEE 754 floating-point computations
- GKR Integration: Extends the GKR protocol with approximate verification
-
Standard Sum-Check:
$g_i(0) + g_i(1) = c_i$ (exact equality) -
Approximate Sum-Check:
$|g_i(0) + g_i(1) - c_i| \leq \epsilon_i$ (bounded error) -
Soundness: If the prover cheats by more than
$\sum_i \epsilon_i$ , the verifier detects with high probability
The standard sum-check protocol requires exact equality:
- Standard:
g_i(0) + g_i(1) = c_i
The approximate sum-check allows bounded error tolerance:
- Approximate:
|g_i(0) + g_i(1) - c_i| <= epsilon_i
- Per-round error bounds (delta_i): The prover sends error bounds along with each polynomial
- Error accumulation tracking: Total error is bounded by sum of per-round deltas
- Backward compatibility: Setting epsilon=0 gives exact verification
from sumcheck import prove_approx_sumcheck, verify_approx_sumcheck, ApproxSumcheckProof
from gkr import prove_approx, verify_approx
# Approximate sumcheck with error tolerance
epsilon = field.FQ(100) # Maximum error per round
approx_proof = prove_approx_sumcheck(g, num_vars, start_idx, max_delta=epsilon)
is_valid, accumulated_error = verify_approx_sumcheck(claim, approx_proof, num_vars, base_delta=epsilon)
# Approximate GKR
proof = prove_approx(circuit, D, max_delta=epsilon)
is_valid, total_error = verify_approx(proof, max_delta=epsilon)
# Standard exact verification (backward compatible)
proof = prove(circuit, D)
is_valid = verify(proof)use gkr::sumcheck::{prove_approx_sumcheck, verify_approx_sumcheck, ApproxSumcheckProof};
// Approximate sumcheck
let max_delta = S::from(100u64);
let approx_proof = prove_approx_sumcheck(&g, v, max_delta);
let result = verify_approx_sumcheck(claim, &approx_proof, v, None);
// Standard exact verification
let (proof, r) = prove_sumcheck(&g, v);
let valid = verify_sumcheck(claim, &proof, &r, v);// Standard exact sumcheck verification
component verify = SumcheckVerify(v, nTerms);
// Approximate sumcheck verification
// NOTE: epsilonBits is a range-check parameter for delta/error/slack (e.g., 32 or 64)
component approxVerify = ApproxSumcheckVerify(v, nTerms, epsilonBits);
approxVerify.deltas <== deltas; // Per-round error bounds
approxVerify.errorSigns <== signs; // Sign hints for absolute valueThis implementation includes IEEE 754 Floating Point Arithmetization using GKR with approximate sumcheck. This is ideal for verifying floating point computations where rounding errors are inherent.
- IEEE 754 Single & Double Precision representation
- FP Operations: Add, Sub, Mul, Div, Sqrt with error bounds
- ULP-based error computation for accurate tolerance
- Automatic error accumulation across circuit layers
- Circuit builders for common operations (dot product, sum, matrix multiply)
from fp_arithmetic import IEEE754Float, FPAddGate, FPMulGate
from fp_gkr import fp_prove_computation, verify_fp_gkr
# Prove a dot product computation
a = [1.0, 2.0, 3.0, 4.0]
b = [0.5, 1.5, 2.5, 3.5]
expected = sum(x * y for x, y in zip(a, b)) # 35.0
proof = fp_prove_computation('dot_product', a + b)
valid, info = verify_fp_gkr(proof, [expected])
print(f"Valid: {valid}, Max ULP error: {info['max_ulp_error']}")
# Other operations
sum_proof = fp_prove_computation('sum', [1.0, 2.0, 3.0, 4.0])
product_proof = fp_prove_computation('product', [1.1, 2.2, 3.3])use fp_arithmetic::{IEEE754Float, fp_add, fp_mul, create_dot_product_circuit};
// Create and evaluate a dot product circuit
let mut circuit = create_dot_product_circuit::<Fr>(4);
circuit.set_inputs(&[1.0, 2.0, 3.0, 4.0, 0.5, 1.5, 2.5, 3.5]);
let outputs = circuit.evaluate();
println!("Result: {}", outputs[0].to_f32());
println!("Error bound: {:?}", circuit.total_epsilon);# Build the CLI (requires Rust nightly)
cd rust
rustup run nightly cargo build --release
# Prove floating point sum
./target/release/gkr-aggregator fp-sum --input "1.0,2.0,3.0,4.0"
# Output: sum=10.0, epsilon=12582912, proof saved to fp_proof.json
# Prove floating point product
./target/release/gkr-aggregator fp-product --input "1.5,2.0,3.0,4.0"
# Output: product=36.0, epsilon=12582912
# Prove dot product of two vectors
./target/release/gkr-aggregator fp-dot-product --a "1.0,2.0,3.0,4.0" --b "5.0,6.0,7.0,8.0"
# Output: dot_product=70.0, epsilon=29360128
# Run benchmark
./target/release/gkr-aggregator fp-benchmark --operation sum --size 256
# Output: throughput ~127K proofs/s (release build)
# Verify a proof
./target/release/gkr-aggregator fp-verify --proof fp_proof.jsoninclude "fp/ieee754.circom";
// Verify FP dot product
component dotProduct = FPDotProductVerify(4, 5);
dotProduct.a_signs <== a_signs;
dotProduct.a_exps <== a_exps;
dotProduct.a_mants <== a_mants;
// ... etcThe error tolerance is computed based on IEEE 754 ULP (Unit in Last Place):
- Addition/Subtraction: 0.5 ULP per operation
- Multiplication/Division: 0.5 ULP per operation
- Total Error: Accumulated across all operations
The approximate sumcheck accepts proofs where:
|computed - expected| <= total_epsilon
# Setup Python environment
cd python
python3 -m venv ../.venv
source ../.venv/bin/activate
pip install -r requirements.txt # or just run the scripts (uses local field module)
# Run comprehensive FP GKR benchmark
python benchmark_fp_gkr.py
# Run aggregated proof benchmark
python benchmark_aggregated.py
# Run tests
python test_fp_gkr.py
python test_gkr.py| Operation | Inputs | Prove Time | Verify Time | Proof Size |
|---|---|---|---|---|
| sum | 16 | ~5ms | ~5ms | 1.5KB |
| sum | 64 | ~16ms | ~10ms | 3.2KB |
| sum | 256 | ~110ms | ~19ms | 6.0KB |
| dot_product | 32 | ~9ms | ~7ms | 2.3KB |
| dot_product | 128 | ~38ms | ~15ms | 4.4KB |
| dot_product | 512 | ~396ms | ~24ms | 8.3KB |
| Operation | Inputs | Prove Time | Throughput |
|---|---|---|---|
| sum | 64 | ~0.02ms | ~51K proofs/s |
| sum | 256 | ~0.01ms | ~127K proofs/s |
| product | 64 | ~0.02ms | ~50K proofs/s |
| dot_product | 64 | ~0.02ms | ~49K proofs/s |
| Batch Size | Input Size | Total Prove | Throughput |
|---|---|---|---|
| 100 | 16 | ~560ms | ~92 proofs/s |
| 100 | 32 | ~925ms | ~59 proofs/s |
| 100 | 64 | ~1.7s | ~36 proofs/s |
| 200 | 32 | ~1.9s | ~58 proofs/s |
| Scenario | Proofs | Total Time | Size | Throughput |
|---|---|---|---|---|
| ML Inference (100 dot products) | 100 | ~2.8s | 319KB | 36 p/s |
| Financial (200 sums) | 200 | ~3.4s | 451KB | 59 p/s |
| IoT Sensors (100 mixed) | 100 | ~1.1s | 152KB | 92 p/s |
Comprehensive benchmark covering:
- Single operation scaling (4-512 inputs)
- Batch proof aggregation
- Large-scale stress tests (up to 1024 inputs)
- Numerical accuracy analysis
Real-world aggregated proof scenarios:
- Mixed operation batches
- ML inference simulation (neural network layers)
- Financial computation batches
- IoT sensor data aggregation
from fp_gkr import fp_prove_computation, verify_fp_gkr
import time
# Benchmark custom computation
inputs = [float(i) for i in range(64)]
start = time.perf_counter()
proof = fp_prove_computation('sum', inputs)
prove_time = time.perf_counter() - start
start = time.perf_counter()
valid, info = verify_fp_gkr(proof, [sum(inputs)])
verify_time = time.perf_counter() - start
print(f"Prove: {prove_time*1000:.1f}ms, Verify: {verify_time*1000:.1f}ms")circom and snarkjs should be installed already.
You can check that by this command:
snarkjs --helpcircom --helpcargo install --path ./rustcd rustYou can give inputs by commands:
gkr-aggregator prove -c t.circom -i ./example/input1.json ./example/input2.json ./example/input3.jsonYou can get a message from cli:
Proving by groth16 can be doneYou should prepare an appropriate ptau file.
snarkjs groth16 setup aggregated.r1cs pot.ptau c0.zkey
snarkjs zkey contribute c0.zkey c1.zkey --name=“mock” -vGive random string for contribution, and then
snarkjs zkey beacon c1.zkey c.zkey 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon phase2"gkr-aggregator mock-groth -z c.zkeyYou can get proof.json and public.json.
Get input from input.json, make d in proof with it.
Parse r1cs file and convert it to GKRCircuit. (Let's call this
Make proof d and GKRCircuit.
There are two circuit
- input (for
$C_i$ ) - proof
$\pi_{i - 1}$
Make integrated circuit
Use those inputs, make proof
Also there are two circuit snarkjs.
Integrated circuit snarkjs also.
So final proof
gkr-approx-sumcheck/
├── python/
│ ├── sumcheck.py # Approximate sumcheck protocol
│ ├── gkr.py # GKR protocol with approx support
│ ├── fp_arithmetic.py # IEEE 754 FP arithmetization
│ ├── fp_gkr.py # FP GKR prover/verifier
│ ├── benchmark_fp_gkr.py # Comprehensive benchmarks
│ ├── benchmark_aggregated.py # Aggregated proof benchmarks
│ ├── test_fp_gkr.py # FP GKR tests
│ └── test_gkr.py # Original GKR tests
├── rust/
│ └── src/
│ ├── gkr/
│ │ ├── sumcheck.rs # Rust approx sumcheck
│ │ └── prover.rs # GKR prover
│ └── fp_arithmetic.rs # Rust FP implementation
├── gkr-verifier-circuits/
│ └── circom/
│ └── circom/
│ ├── sumcheck/
│ │ └── sumcheckVerify.circom # Approx sumcheck circuit
│ └── fp/
│ └── ieee754.circom # FP verification circuits
└── README.md
MIT