Skip to main content

On This Page

Cross-Validating ZK Systems: From Python Physics to Rust BigInt Proofs

3 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

Python to Rust to Proof: Cross-Validating a ZK System

The zk-physics project maintains three distinct implementations of the Rayleigh-Plesset simulation to ensure mathematical correctness across different number representations. A ZK proof is only as valid as the reference implementation it was validated against, requiring bit-for-bit agreement between the witness generator and the reference model.

Why This Matters

In ZK systems, the gap between theoretical physics models using floating-point arithmetic and circuit implementation using finite fields creates critical failure points. Without a multi-layered validation pyramid, discrepancies between truncating integer division and modular field inversion can lead to intermittent proof failures that are nearly impossible to debug in production environments. This technical reality necessitates a bridge between float-based ground truth and field-based proof constraints to ensure the circuit actually proves the intended physics.

Key Insights

  • Python Float Reference: Uses NumPy float64 with a 53-bit mantissa as the ground truth for physics to establish the baseline simulation behavior.
  • Python Scaled-Integer Bridge: Implements arbitrary-precision integers with SCALE = 10**30 to bridge the gap between float physics and field arithmetic.
  • Rust BigInt Witness: Employs the num-bigint crate to generate witnesses that must match the Python scaled-integer implementation exactly.
  • Field Replay Necessity: The compute_public_inputs function replays simulation steps using modular field arithmetic to resolve the divergence between integer division and modular inversion.
  • Deduplication Strategy: A single simulate_inner function in Rust handles both collapse and acoustic presets to eliminate the risk of logic drift between simulation types.

Working Examples

Python scaled-integer arithmetic implementation used as the bridge between float and field arithmetic.

SCALE = 10**30
def smul(a, b):
    return (a * b) // SCALE
def sdiv(a, b):
    return (a * SCALE) // b

Rust BigInt implementation for witness generation using the num-bigint crate.

fn smul(a: &BigInt, b: &BigInt) -> BigInt {
    (a * b) / scale()
}
fn sdiv(a: &BigInt, b: &BigInt) -> BigInt {
    (a * scale()) / b
}

Validation test ensuring Python float and scaled-integer implementations agree within a 10^-10 relative error tolerance.

def test_collapse_float_vs_scaled():
    params_float = CollapsePreset.float_params()
    params_scaled = CollapsePreset.scaled_params()
    trace_float = simulate_float(params_float, n_steps=100)
    trace_scaled = simulate_scaled(params_scaled, n_steps=100)
    for i in range(len(trace_float)):
        r_float = trace_float[i]
        r_scaled = trace_scaled[i] / SCALE
        assert abs(r_float - r_scaled) / r_float < 1e-10

Practical Applications

  • Use Case: Rayleigh-Plesset simulation deduplication using simulate_inner to ensure both collapse and acoustic presets share identical physics logic. Pitfall: Maintaining separate simulation loops for different presets leads to correctness hazards where bug fixes are applied inconsistently.
  • Use Case: Cross-language trace validation using exported JSON files to ensure Rust BigInt witnesses match Python scaled-integer references bit-for-bit. Pitfall: Using approximate comparisons for integer-based implementations which can mask logic errors in the witness generator.
  • Use Case: Public input generation via compute_public_inputs to ensure verifier targets match circuit field arithmetic. Pitfall: Computing public inputs using integer arithmetic rather than field replay, causing proof failures during modular inversion steps.

References:

Continue reading

Next article

The Hidden Cost of Self-Hosted Uptime Monitoring in 2026

Related Content