Skip to content

Hazync v0.6.0 — round-8 audit: anchor-bound chain proofs + gated witness commitment

Choose a tag to compare

@defenwycke defenwycke released this 22 Jul 20:21

Round-8 soundness audit — a five-reviewer completeness + verifier pass over the guest, the C++ VerifyScript boundary, the Utreexo accumulator, enforcement gating, and the end-to-end trust scope. Four dimensions came back sound; one verifier-hole and five completeness deviations were found and fixed (bar the one rule that is mathematically unprovable in a zkVM). Full write-up: AUDIT_2026-07.md.

The guest changed, so METHOD_ID is re-pinned c029cee4…601d7ca298f32c54137fdb395d0debd31e95e4f084615fa710e5db5c785f05e6. Proofs made against the old id no longer verify and are being re-proven from genesis. The reproducible-build mechanism is unchanged (pinned toolchain, Bitcoin Core v28.0, secp256k1 v0.5.1, Cargo.lock, reproduce/Dockerfile) — the id is simply a new value. NB: the guest ELF embeds assert!/panic! line numbers, so its exact (compact) formatting is load-bearing for the id — do not cargo fmt the guest.

What changed

  • Anchor-bound chain proofs (S5) + verify-chain. ChainState now commits anchor_id (the double-SHA256 of the base anchor), set in the base step and carried forward, and the new verify-chain command pins it to the genesis anchor. A bare mode-2/5 chain receipt is now self-authenticating — a receipt built on a fabricated anchor is rejected — closing a verifier-side foot-gun (RangeState already had this via its genesis in-boundary pin).
  • Witness-commitment activation gate (G3) + unexpected-witness incl. coinbase (G2). The BIP141 commitment is now enforced only from segwit height 481824, matching Core; below activation any witness data is rejected. This removes a reject-valid stall on canonical pre-activation blocks (433k–481823).
  • Block-level MoneyRange(nFees) (G5). Explicit fee-total range check plus an i128-safe coinbase ≤ subsidy + fees bound.
  • BIP30 as a documented, gated, bounded invariant (G1). A Utreexo Stump cannot prove non-membership, so Core's general HaveCoin lookup is replaced by an in-code structural argument (BIP34 uniqueness ≥227931 + the two grandfathered overwrites), sound to ~height 1,983,702.
  • Hardening (N1/N2/N3). Removed the dead host-supplied BlockInput.flags (flags are guest-derived); length-prefixed the scriptPubKey in the UTXO-leaf commitment; tx_full_sigops now fails closed on a short prevouts blob.

Per-file breakdown

  • prover/methods/guest/src/main.rsChainState.anchor_id + carry logic in chain_step/aggregate; witness-commitment activation gate + coinbase-inclusive has_witness; block-level MoneyRange; BIP30 bounded-invariant documentation; removed dead BlockInput.flags.
  • prover/methods/guest/verify_input.cpp — length-prefixed scriptPubKey in coin_leaf and tx_out_leaves (N2, changes every leaf hash); tx_full_sigops returns a poison cost on a short prevouts blob (N3).
  • prover/host/src/main.rsverify-chain command; ChainState.anchor_id mirror + all anchor constructors; host coin_leaf length-prefix (kept byte-identical to the guest); removed BlockInput.flags/Spend.flags.
  • reproduce/METHOD_ID — re-pinned to 601d7ca2… with a note not to cargo fmt the guest.
  • AUDIT_2026-07.md (new), SECURITY.md (round-8 section + status table), docs/SOUNDNESS.md (§3c + completeness matrix), docs/PROVING.md (verify-chain), README.md — audit record and docs.

Verification

Verified on a 2×L40S box against real mainnet data: regress, the adversarial soundness suite (all holes reject), check-bip30, check-full 741000 (byte-exact tip f0e89160…), and a genesis-anchored range proof — all green on this guest.

Assets

  • hazync-host-x86_64-linux-gnu — CPU host (verify + prove on CPU).
  • hazync-host-x86_64-linux-gnu-cuda — CUDA host (GPU proving).

Both are the 601d7ca2… guest. SHA256SUMS.txt is PGP-signed (maintainer key 852C2B3190F5B928); verify per SECURITY.md.