Introduction

On June 8th, 2026, zkSecurity started a security audit of the Zcash Orchard and Halo2 repositories on behalf of the Tachyon Foundation. The audit lasted one week with two consultants, and was a focused, high-priority review aimed at reducing tail risk in the Orchard and Halo2 code.

Scope

The audit targeted the following release tags:

A primary focus was constraint soundness: the large Orchard Action circuit, together with the Halo2 gadgets it depends on. The review also covered the Halo2 proof system verifier and the non-circuit, verifier-facing surface of the Orchard crate. In concrete terms, the review spanned four areas:

  • The Orchard Action circuit and its sub-circuits: note commitment (NoteCommit), incoming-viewing-key commitment (CommitIvk), value commitment, nullifier derivation, and Sinsemilla Merkle-path verification.
  • The reusable Halo2 gadgets it builds on: the halo2_gadgets elliptic-curve, Sinsemilla, and utility gadgets, and the halo2_poseidon Poseidon circuits.
  • The halo2_proofs PLONK verifier, transcript, and polynomial-commitment verification paths.
  • The Orchard crate’s non-circuit surface: bundle, action, and public-instance handling; value, nullifier, and note-commitment binding; key, signature, and address parsing; and the PCZT partial-transaction verification and extraction flow.

The Halo2 crates in scope also contain gadgets that Orchard does not use, most notably the SHA-256 circuit. At the client’s direction, these components were excluded from the review. This report therefore makes no claim about their correctness; the SHA-256 gadgets are in fact known to contain serious soundness issues, outside the scope of this report.

Given the one-week timeframe relative to the size of the scope, the review was deliberately prioritized: effort was directed at the areas of highest risk and the most likely sources of soundness bugs, rather than at exhaustive, uniform coverage of every component. The findings in this report reflect the issues surfaced within that window, and the absence of findings in a given area should not be read as a guarantee that it is free of issues.

Summary

A substantial part of the engagement went into an MVP formal verification of the entire Orchard Action circuit in Lean, built with Clean, zkSecurity’s framework for formally verifying zero-knowledge circuits. This formalization, described below, is the most significant outcome of the audit: it gives us high confidence about circuit soundness against the Zcash protocol spec.

The manual review and accompanying proofs of concept surfaced no issue affecting the security of the deployed Orchard protocol. The circuit-level findings concentrate on gadget-usage paths that are hard to maintain and dangerous to change, where a future modification could silently break a soundness assumption; rather than on live bugs in the current code.

zkao, zkSecurity’s AI security scanner for cryptographic code, was used during the engagement and flagged the empty-Sinsemilla and fixed-base-multiplication findings.

A note on Halo2 gadget usage

Several observations during the review stem from general characteristics of the Halo2 constraint system rather than from defects specific to a single gadget. These four recur as footguns for anyone integrating the halo2_gadgets primitives:

  • Values are not cells. A Value<F> passed into a gadget is used only during witness generation; it is never part of the constraint system. Unless the gadget copies it into an assigned cell and constrains that cell, nothing binds the value a caller supplies to the value the gates actually enforce. An honest prover always makes the two agree, so a missing binding is invisible on the happy path and only a malicious prover diverges.
  • Constraints apply only where the selector is enabled. A gate constrains a row only when its selector is enabled on that row. Result cells written on rows where the selector is not enabled are assigned but unconstrained. A single missed enablement, for instance an off-by-one loop bound, silently leaves those cells free for a malicious prover to choose.
  • Semantically same cells are sometimes implicitly referred to by offset. Similarly, a cell that’s constrained on the chip configuration step is sometimes referred to in the synthesis step only by the documented offset. An offsetting error, such as an off-by-one, can miss those cells.
  • Risky APIs that are only used by tests are not gated. Some APIs, such as witness_decompose are only used by tests. While their naming and usages are clear, they increase the risk surface.

The audited code uses the gadgets correctly.

Formal verification of the Orchard Action circuit

To rule out correctness gaps in the Orchard circuits systematically, we formalized them in Clean, see https://github.com/zksecurity/clean-orchard. The work comes with end-to-end soundness and completeness proofs for the top-level Action circuit, covering all of its gadget dependencies. Proofs depend on zero sorries and a single benign axiom that establishes the Pallas curve order.

Limitations. The formalization was done within the circuit model of Clean, using raw constraints and a linear witness tape, which is a more primitive representation than Halo2 produces with its regions, custom gates and row-column layout. Because of that mismatch, an “approximate” rather than fully faithful style of porting circuits was chosen. The approach is documented in detail in the repository.

The main limitation, which follows from that approximation, is that our formalization does not mechanically connect the ported circuits to their Rust source, in any way. The translation of circuit code was done “by hand”, in an AI-assisted manner, with strong instructions to use source-conformant circuits but no waterproof mechanism to enforce such conformance. Therefore, we could accidentally have formalized different circuits than Orchard actually uses.

A second limitation is that not all circuit specs and interfaces were human-written or reviewed, and such a review is essential for guaranteeing the proof gives us the properties we want. We did review and co-author the top-level Action circuit spec and have reasonable confidence of its appropiateness, but this does not rule out gaps in lower-level gadgets that the Action circuit does not happen to exercise.

As concrete evidence of how this can hide soundness bugs, consider our finding showing that the Sinsemilla hash circuit is unsound for empty input messages. The ported Clean circuit that LLMs produced works around this issue (which is standing in the way of the required soundness proof) by simply requiring the input message to be a Vector of length n + 1. The Action circuit only uses inputs of length greater than zero.

Despite these limitations, we believe that the process we used would have exposed any end-to-end soundness gap with very high probability. As part of ongoing collaboration with Tachyon Foundation, zkSecurity is working on lifting both limitations, producing a circuit formalization that is mechanically checked to match the Halo2 constraint system, and where specs and circuit input signatures are carefully reviewed at each level.