Skip to main content

Test Vectors and Cross-Implementation Checks

1. Why This Chapter Exists

zcash/orchard shares its consensus surface with zcashd, zebra, and librustzcash. A divergence in any of the cryptographic primitives is a fork-causing bug. The crate defends against this with three layers of tests: external JSON test vectors, a pinned proof binary, and an end-to-end integration test. After this chapter the reader can locate the test for any consensus-relevant claim and reproduce it.

2. Definitions

Definition 2.1 (Test Vector)

A tuple of inputs and expected outputs, generated by an independent implementation, serialised as JSON, and consumed by the crate's tests via src/test_vectors.rs.

Definition 2.2 (Pinned Proof)

A serialised Halo 2 proof produced by the crate at a fixed version. The pinned proof is checked into the repository at src/circuit_proof_test_case.bin and re-verified by a unit test on every build.

Definition 2.3 (Circuit Description)

A textual snapshot of the circuit shape (column counts, gates, lookups, KK), stored under src/circuit_description/. Any change to the shape must update both the description and the pinned proof.

3. The Code

3.1 JSON Wiring

src/test_vectors.rs
loading...

The module loads each JSON file and exposes typed accessors used by the unit tests in src/keys.rs, src/note.rs, src/note/nullifier.rs, src/note_encryption.rs, and src/zip32.rs.

3.2 Source of the Vectors

The vectors are generated by the zcash-hackworks/zcash-test-vectors repository. A new test vector is added there first, then mirrored into this crate via a PR. Open issue #191 tracks the gap for ZIP 32 derivation.

3.3 The Pinned Proof Test

The test in src/circuit.rs deserialises src/circuit_proof_test_case.bin and verifies it with SingleVerifier. The test catches any change in the transcript byte layout (a common consequence of bumping halo2_proofs).

3.4 The Integration Scenario

tests/builder.rs runs the full happy path: build a bundle, prove, sign, verify, trial-decrypt the output, advance the tree, and chain a second bundle that spends the new note. It is the single best entry point for end-to-end debugging.

4. Failure Modes

  • Stale JSON vectors. A librustzcash PR introduces a new vector; the Orchard crate must mirror it within the next release. The mirror is manual.
  • Pinned proof regeneration without review. Regenerating the pinned proof bytes is sometimes necessary (a halo2_proofs bump), but the PR must explain why and the reviewer must re-run a known-good prover. Silently regenerating defeats the purpose of the pin.
  • Circuit description drift. If the description changes but the proof does not (or vice versa), the test still passes but the audit-tracked shape no longer matches reality. The reviewer must update both.

5. Spec Pointers

6. Exercises

  1. Run cargo test --release --features circuit and identify the longest single test. State its name and its file.
  2. Read the integration test in tests/builder.rs. Which output of the first bundle is fed into the second bundle's Builder? Identify the line.
  3. Code task. Flip one byte of src/circuit_proof_test_case.bin in your working copy (do not commit). Run the relevant test and observe the failure mode. Restore the file with git checkout.

7. Further Reading