Skip to content

feat(wasm-utxo): add Zcash NU7 consensus branch id - #420

Open
hrishikeshjain wants to merge 2 commits into
masterfrom
SPT-483-zcash-nu7-support
Open

hrishikeshjain wants to merge 2 commits into
masterfrom
SPT-483-zcash-nu7-support

Conversation

@hrishikeshjain

Copy link
Copy Markdown

Summary

Zcash testnet activated NU7 at block 4,465,026 (ZIP-259), introducing consensus branch id 0x77190ad9. Per ZIP-2003, version 4 transactions become invalid once NU7 activates — only v5 (ZIP-225) and v6 (ZIP-229) remain valid.

NetworkUpgrade stopped at Nu6_3, so branch_id_for_height resolved post-NU7 testnet heights to the NU6.3 branch id, and new_at_height went on to build a version 4 transaction regardless. Those signed and queued normally, then failed every broadcast with transaction version 4 not supported by the network upgrade Nu7 and retried indefinitely — observed in sendq: 5 stuck TZEC sends, one retried ~750 times over ~7h.

This is the wasm-utxo counterpart of BitGoJS#9896 (approved, then closed — utxo-lib is deprecated, per Veetrag in thread).

Changes

  • src/zcash/mod.rs — Nu7 variant (branch id 0x77190ad9, testnet height 4465026), wired into ALL, params(), and both zebra parity maps.
  • mainnet_activation_height → Option<u32> — NU7's mainnet height is TBD in ZIP-259. An upgrade with no assigned height on a network is never active there, so mainnet stays on NU6.3 and is unaffected by this PR.
  • new_at_height rejects v4 at/after NU7 — this path only ever builds v4/Sapling, so at those heights it has no valid output to produce. Failing at build time beats signing something that can never broadcast. v6 (Ironwood) builds are untouched and remain the valid path.
  • zebra-chain 11.2 → 14.0 — 11.2 carries Nu7 only as a test-only placeholder (0xfffffffe); 14.0 is the first release with the real constants, so the parity tests check our entry against upstream rather than against a value I typed in. Also adjusts the v6 zebra-oracle test for 14's API, which exposes ironwood actions as its own orchard release's Action.

Verification

  • cargo test --lib — 635 passed, 0 failed, 94 ignored.
  • All four parity_with_zebra_chain tests pass against zebra 14's real NU7 constants — branch id and testnet activation height are cross-checked against upstream, not hardcoded here.
  • test_mainnet_heights_match_zebra now compares as Option, so it starts failing the moment zebra publishes a mainnet NU7 height instead of silently drifting.
  • New regression test test_new_zcash_at_height_rejects_v4_after_nu7_on_testnet covers the exact cutover: nu7 - 1 still builds, nu7 and beyond are refused, mainnet unaffected.
  • Guard test proven non-vacuous: stubbed the condition to false and the test failed with v4 must be refused at or after NU7 activation; restored, passes.
  • cargo clippy --all-targets -- -D warnings clean against this diff. One pre-existing chunks_exact lint in src/p2mr/mod.rs (untouched here) fires on local rustc 1.99 but not on CI's pinned toolchain.

Scope — please read before assuming TZEC sends resume

This stops the bleeding; it does not by itself make TZEC sends work again.

ZcashBitGoPsbt only builds v4/Sapling, and there is no v5 (ZIP-225) codec in this repo — BitGoJS#9896 got v5 for free from utxo-lib, which is why it was a ~30-line change there and is not here. After this PR, post-NU7 testnet sends fail loudly at build time instead of silently producing unbroadcastable transactions.

For sends to actually succeed, one of:

  1. implement the v5 (ZIP-225) wire format + ZIP-244 transparent sighash here, or
  2. route transparent testnet sends through the existing v6 (Ironwood) path.

@veetragjain — which did you have in mind? Happy to take the follow-up either way.

Also note: the 5 TZEC sendq entries stuck since the bug were signed as v4 and can never validate, even with this fix — they need purging and re-sending.

Refs: SPT-483

Zcash testnet activated NU7 at block 4,465,026 (ZIP-259), introducing
consensus branch id 0x77190ad9. Per ZIP-2003, version 4 transactions
become invalid once NU7 activates; only v5 (ZIP-225) and v6 (ZIP-229)
remain valid.

`NetworkUpgrade` stopped at Nu6_3, so `branch_id_for_height` resolved
post-NU7 testnet heights to the NU6.3 branch id, and `new_at_height`
went on to build a version 4 transaction regardless. Those signed and
queued normally, then failed every broadcast with "transaction version
4 not supported by the network upgrade Nu7" and retried indefinitely
(observed in sendq: 5 stuck TZEC sends, one retried ~750 times over
~7h).

Adds the Nu7 entry and rejects a v4 build at or after NU7 activation,
so the failure surfaces at build time instead of after signing. v6
(Ironwood) builds are unaffected and remain the valid path.

NU7 has no assigned mainnet activation height yet (TBD in ZIP-259), so
`UpgradeParams::mainnet_activation_height` becomes `Option<u32>`: an
upgrade with no height on a network is never active there, leaving
mainnet on NU6.3. The zebra parity test compares the field as an
`Option`, so it starts failing the moment zebra publishes a height
rather than silently drifting.

Bumps the zebra-chain dev-dependency 11.2 -> 14.0: 11.2 carries Nu7
only as a test-only placeholder (0xfffffffe), so the parity tests can
only check the real constants against 14. Adjusts the v6 zebra-oracle
test for 14's API, which exposes ironwood actions as its own orchard
release's `Action` type.

Refs: SPT-483
@hrishikeshjain
hrishikeshjain requested review from a team as code owners October 6, 2026 11:56
@linear-code

linear-code Bot commented Oct 6, 2026

Copy link
Copy Markdown

CSHLD-1902

The zebra-chain 11.2 -> 14 bump broke `cargo clippy --all-features`:
halo2_proofs 0.3.2 does not compile with `multicore` off, and nothing
was turning it on any more.

orchard takes halo2 with default features disabled, so `multicore`
never came from our own dependency edge. It was supplied by accident:
zebra-chain 11.2 resolved to the same orchard 0.15 we use, and cargo
unified its feature set with ours. zebra-chain 14 moved to orchard
0.16, so the two graphs no longer unify and our halo2 was left without
rayon.

Declare halo2_proofs as an optional dependency and have the
`orchard-proving` feature enable `halo2_proofs/multicore` explicitly,
so the proving build states the requirement itself instead of relying
on a dev-dependency's resolution.

Refs: SPT-483

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant