soft3/mudra/specs/bridge-proof.md

bridge-proof — verifying a claim in the stack (Phase 2)

goal

Phase 1 verifies a migration bridge claim natively (Rust k256). A verifier is trusted to run claim::verify honestly. Phase 2 removes that trust: re-express the verification — secp256k1 ECDSA + sha256 + ripemd160 — as a computation over the Goldilocks field, run it on nox, and emit a zheng proof. Then a light client checks the migration with no trusted operator.

The Phase-1 Rust is the reference; the Phase-2 gadget must match it bit-for-bit.

the substrate (what the stack gives us)

nox reduces over Goldilocks p = 2^64 − 2^32 + 1 only. Its patterns: add/sub/mul/inv (field, tags 5–8), xor/and/not/shl (32-bit words, tags 11–14), eq/lt, hash (Poseidon2). A run emits a VecTrace (rows of 16 Goldilocks columns); zheng::commit(trace, …) folds the rows by pattern and produces the proof. secp256k1 lives in a different, 256-bit field q = 2^256 − 2^32 − 977, so it must be emulated as limbs of Goldilocks. rune and nox can't express it directly — Phase 2 is a Rust gadget over nebu::Goldilocks whose every operation is one nox pattern.

limb representation (the load-bearing choice)

A secp256k1 field element is 16 limbs of 16 bits, little-endian, each limb a Goldilocks element in [0, 2^16). This is chosen so nothing ever wraps the native field:

  • a limb product is < 2^32
  • a schoolbook column sums ≤ 16 such products: < 16·2^32 = 2^36 ≪ p ≈ 2^64

So every limb multiply and every column add is exact inside one Goldilocks element — i.e. a legal mul/add nox step. Carry extraction (digit = v & 0xFFFF, carry = v >> 16) is a range-check gadget in-circuit (and/shl + a bound proof); in the reference it is a native shift.

Reduction mod q uses the Solinas structure 2^256 ≡ 2^32 + 977 (mod q): a 512-bit product folds by multiplying its high half by the small constant R = 2^32 + 977 and adding the low half, twice, then a final conditional subtract of q. Every step is limb multiply/add — Goldilocks-native.

milestones

# milestone proves status
2a secp256k1 base field Fp over Goldilocks limbs (add/sub/mul/inv/sqrt) field arithmetic is exact and Goldilocks-native DONE
2b secp256k1 curve: point add/double, scalar mul, decompress (Jacobian) EC group law over the limb field DONE
2c ECDSA verify equation: R.x == r from (r,s,z,Q) the signature check itself DONE
2d sha256 + ripemd160 as bit-op circuits (nox and/xor/not/shl) address derivation + ADR-036 digest DONE
2e nox program → VecTracezheng::commit → verify (pipeline) a real, checkable zheng proof pipeline DONE; full-claim assembly remains

2a–2d express the full claim check (ECDSA + both hashes) in Goldilocks-limb / word arithmetic — the reference gadget, every operation a nox pattern by construction. Validated against k256, num-bigint, sha2, ripemd, and a real Phase-1 claim (in-stack verify agrees with native on 40+ random signatures).

2e closes the proving mechanism: proof::prove (feature prove) builds a nox program, runs nox::reduce to a VecTrace, zheng::commits it, and zheng::verifys the proof — demonstrated on the Goldilocks a·b + c that every limb decomposes into, with a soundness check (understated focus_bound rejected). What remains: emitting the full verify_claim as one nox program is an arithmetization effort — the same proven operations at scale (~10⁷ rows), a compiler/tracing task, not a new capability.

Each milestone is validated against the Phase-1 native path on shared inputs.

milestone 2a — the base field

proof::field::Fe — an element of 𝔽_q, q = 2^256 − 2^32 − 977:

Fe::from_bytes(&[u8;32]) -> Fe        // big-endian, reduced mod q
Fe::to_bytes(&self)      -> [u8;32]
Fe::add / sub / mul / sqr / inv       // field ops, all via Goldilocks limbs
Fe::ZERO / ONE / is_zero / ==

mul accumulates its 16×16 schoolbook columns with nebu::Goldilocks operations, asserts each column < Goldilocks::P (the no-wraparound soundness condition), carry-normalizes to a 512-bit limb vector, then Solinas-reduces mod q. Verified against num-bigint (BigUint) modular arithmetic on thousands of pseudo-random inputs plus edge cases (0, 1, q−1), and a·a⁻¹ = 1.

constraint budget (estimate)

ECDSA verify is ~1 scalar-mul-heavy double-scalar multiplication: ~256 point ops, each ~16 field muls, each 256 limb muls → order 10^6–10^7 Goldilocks steps, plus sha256/ripemd160 bit circuits. ~10–20M trace rows per claim — a one-time, per-neuron cost paid at mainnet migration, not at the spacepussy rehearsal.

Graph