Skip to content

chore(deps): pin librustzcash crates to NU6.3 testnet-supporting pre-release version. - #10789

Merged
conradoplg merged 1 commit into
ZcashFoundation:nu63-ironwoodfrom
nuttycom:chore/update_librustzcash
Jul 1, 2026
Merged

chore(deps): pin librustzcash crates to NU6.3 testnet-supporting pre-release version.#10789
conradoplg merged 1 commit into
ZcashFoundation:nu63-ironwoodfrom
nuttycom:chore/update_librustzcash

Conversation

@nuttycom

@nuttycom nuttycom commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Motivation

Track upstream librustzcash development by pinning Zebra's zcash_* dependencies to librustzcash release/nu6.3_testnet_support branch, ahead of the next round of crate releases.

The [patch.crates-io] entries are temporary — they point at a librustzcash git revision and must be
replaced with published crate versions before the target branch is released.

Solution

  • Add [patch.crates-io] directives pinning equihash, f4jumble,
    transparent (zcash_transparent), zcash_address, zcash_encoding,
    zcash_history, zcash_keys, zcash_primitives, zcash_proofs, and
    zcash_protocol to the librustzcash release/nu6.3_testnet_support branch
    (rev 703fe3e6608fab8be0dedbbcbf684f030a1ea451).
  • Update the miner-reward transaction builders in
    zebra-rpc/src/methods/types/transaction.rs for the removed lifetime
    parameter on zcash_primitives' transaction Builder — it is now
    Builder<P, U> instead of Builder<'a, P, U>, so the closure type
    annotations change from Builder<'_, _, _> to Builder<_, _>.
  • Remove the zcash_unstable=nu6.3 config gate.

Tests

  • CXXFLAGS="-include cstdint" cargo check --workspace --all-targets passes
    cleanly (0 errors).
  • Full cargo clippy -D warnings and cargo test / nextest CI have not
    yet been run — see Follow-up Work.

Specifications & References

Follow-up Work

  • Replace the temporary [patch.crates-io] git pins with published crate
    releases before taking this out of draft.
  • Run the full cargo clippy -D warnings and cargo test / nextest CI
    suites and add a CHANGELOG.md entry if the final version is user-visible.

AI Disclosure

  • No AI tools were used in this PR
  • AI tools were used: Claude Code — fixed the compilation error caused by
    the Builder lifetime change and drafted this PR description.

PR Checklist

  • The PR title follows conventional commits format: type(scope): description
  • The PR follows the contribution guidelines.
  • This change was discussed in an issue or with the team beforehand. (Opened as a draft; coordinating directly with maintainers, no tracking issue yet.)
  • The solution is tested. (cargo check only so far — see Tests / Follow-up.)
  • The documentation and changelogs are up to date. (Deferred until the dependencies are on published versions — see Follow-up.)

@nuttycom
nuttycom force-pushed the chore/update_librustzcash branch 2 times, most recently from d9e1f0b to 9f2fcf2 Compare June 24, 2026 15:32
dannywillems added a commit to zcash/zallet that referenced this pull request Jun 29, 2026
Bump the librustzcash pin from 4cdba554 to 4367ba26 (tracking upstream main),
and point the zebra dependencies at nuttycom's chore/update_librustzcash
branch (rev 9f2fcf29), which carries the build fixes for librustzcash main
(ZcashFoundation/zebra#10789). Crate versions are unchanged, so this is an
API-drift adaptation rather than a semver bump.

Two source changes were required:

- chain: migrate ChainReader::find_fork_point off the removed
  ReadRequest::FindForkPoint / ReadResponse::ForkPoint. The newer zebra-state
  has no dedicated fork-point request, so reconstruct it: walk the locator
  hashes (tip-first), find the most recent one on the best chain via
  ReadRequest::Depth, and derive its height from the tip height and that
  depth.

- json_rpc/payments: replace the per-step input_in_pool / output_in_pool /
  change_in_pool closures in enforce_privacy_policy and required_privacy_policy
  with the new public Step::input_in_pool / output_in_pool / change_in_pool
  methods, which are byte-for-byte equivalent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ions

Add [patch.crates-io] directives pinning equihash, f4jumble, transparent
(zcash_transparent), and the zcash_* crates to librustzcash main
(rev 703fe3e6608fab8be0dedbbcbf684f030a1ea451).

Removes the `zcash_unstable=nu6.3` config flag gates and updates the
miner-reward transaction builders in zebra-rpc to adapt to the removed
lifetime parameter on zcash_primitives' transaction `Builder` (now
`Builder<P, U>` instead of `Builder<'a, P, U>`).
Copilot AI review requested due to automatic review settings July 1, 2026 00:51
@nuttycom
nuttycom force-pushed the chore/update_librustzcash branch from 9f2fcf2 to 6ac219f Compare July 1, 2026 00:51
@nuttycom
nuttycom changed the base branch from main to nu63-ironwood July 1, 2026 00:52
@nuttycom
nuttycom marked this pull request as ready for review July 1, 2026 00:52
@nuttycom nuttycom changed the title chore(deps): pin librustzcash crates to upstream main chore(deps): pin librustzcash crates to NU6.3 testnet-supporting pre-release version. Jul 1, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is titled as a dependency-pinning chore — pointing Zebra's zcash_* dependencies at a librustzcash git revision ahead of the next crate releases — plus a small transaction Builder lifetime-annotation fix. In practice, the diff is far broader: it lands an experimental NU6.3 / "Ironwood" shielded pool and v6 transaction implementation across zebra-chain, zebra-consensus, zebra-state, zebra-rpc, zebra-network, and their tests/snapshots, while removing the tx_v6 feature gate (making Transaction::V6 unconditional) and dropping ZIP-233 handling. The PR is explicitly marked "Draft / not for merge as-is" with temporary [patch.crates-io] git pins.

Changes:

  • Add temporary [patch.crates-io] git pins for the zcash_*/equihash/f4jumble/transparent crates and bump workspace pre-release versions; update coinbase builders for the removed Builder lifetime parameter.
  • Introduce the Ironwood pool and v6 transaction support: new ironwood types, value-balance pool, DB column families + migration (DB format 27 → 28), consensus rules, three-era Halo2 verifier routing, and NU6.3 network upgrade.
  • Remove the tx_v6 feature and ZIP-233 (zip233_amount) handling from serialization, RPC coinbase construction, and fee/miner-reward paths; expand RPC valuePools from 5 to 6 entries.

Risk scan / holistic assessment:

  • BLOCKER (process/consensus): This is consensus-critical code (serialization, verification, DB format, network protocol versions). It uses placeholder consensus branch IDs and provisional protocol versions, temporary git patches, and per the description has not had cargo clippy -D warnings or the full test suite run. It is not mergeable as-is and needs full human review.
  • IMPORTANT (discrepancy): The PR description understates scope and cites a different librustzcash revision (8a0ae65…) than the one actually committed (703fe3e…, referencing PR #2509, not main). Flagged inline on Cargo.toml.
  • Positive: The Ironwood/NU6.3 logic I inspected is internally consistent — the transaction accessors, three-era Halo2 verifier routing, value-balance widening to 48 bytes, and the new consensus rules all line up, and the new consensus checks have unit tests in zebra-consensus/src/transaction/tests.rs.

Reviewed changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
Cargo.toml Adds temporary [patch.crates-io] git pins and pre-release version bumps; rev/scope differs from PR description.
zebrad/Cargo.toml, zebra-test/Cargo.toml Remove the tx_v6 feature definitions.
zebrad/tests/integration/regtest.rs Renames the nu7/tx_v6-gated block-template test to an unconditional nu6_3 test and drops the zip233_amount argument.
zebrad/src/components/mempool/tests/vector.rs Inserts an nu6_3 activation height into the mempool network-upgrade test.
zebrad/src/components/mempool/storage/tests/prop.rs Removes tx_v6 cfg gates; unwraps/rewraps ShieldedDataV6 to reuse the Orchard conflict filter.
zebra-state/src/tests.rs Removes the tx_v6 cfg gate from the Transaction::V6 match arm.
zebra-state/.../non_finalized_state/tests/{vectors,prop}.rs Thread an extra Default::default() Ironwood tree into Chain::new and include the Ironwood note-commitment tree in comparisons.
NU6.3 / Ironwood implementation (broader diff: zebra-chain, zebra-consensus, zebra-state, zebra-rpc, zebra-network) Adds the Ironwood pool, v6 transaction rules, DB migration/format bump, Halo2 verifier eras, and RPC value-pool expansion supporting the new upgrade.

Comment thread Cargo.toml
Comment on lines +383 to +384
# TEMPORARY: Pin librustzcash crates to https://github.com/zcash/librustzcash/pull/2509
equihash = { git = "https://github.com/zcash/librustzcash.git", rev = "703fe3e6608fab8be0dedbbcbf684f030a1ea451" }
@nuttycom
nuttycom marked this pull request as draft July 1, 2026 01:08
@natalieesk
natalieesk marked this pull request as ready for review July 1, 2026 10:01

@dannywillems dannywillems left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 6ac219f

dannywillems added a commit to zcash/zallet that referenced this pull request Jul 1, 2026
Pin the librustzcash crates to zcash/librustzcash `dw/merge-ironwood-branch`
(782b1da7), which merges the NU6.3 / ironwood 0.29-pre pre-release
(zcash/librustzcash#2509) into librustzcash main, so it carries both the
ironwood changes and the wallet's `spend-index` feature. Pin the zebra crates to
nuttycom's `chore/update_librustzcash` branch (ZcashFoundation/zebra#10789),
which targets the same librustzcash line, so the workspace and its zebra
dependencies resolve against a single librustzcash revision.

Bump the affected workspace version requirements to the 0.29-pre set
(zcash_protocol 0.10-pre, zcash_address 0.13-pre, zcash_primitives and
zcash_proofs 0.29-pre, zcash_keys 0.15-pre, zcash_transparent 0.9-pre, orchard
0.15-pre). These are temporary WIP pins; the librustzcash revision is isolated
in the [patch.crates-io] section so it can be bumped on its own as the branch
advances.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gustavovalverde gustavovalverde added the run-stateful-tests Allos to manually trigger a stateful tests run in GCP in PRs label Jul 1, 2026
@natalieesk natalieesk added the do-not-merge Tells Mergify not to merge this PR label Jul 1, 2026
dannywillems added a commit to zcash/zallet that referenced this pull request Jul 1, 2026
Bump the librustzcash pin from 4cdba554 to 4367ba26 (tracking upstream main),
and point the zebra dependencies at nuttycom's chore/update_librustzcash
branch (rev 9f2fcf29), which carries the build fixes for librustzcash main
(ZcashFoundation/zebra#10789). Crate versions are unchanged, so this is an
API-drift adaptation rather than a semver bump.

Two source changes were required:

- chain: migrate ChainReader::find_fork_point off the removed
  ReadRequest::FindForkPoint / ReadResponse::ForkPoint. The newer zebra-state
  has no dedicated fork-point request, so reconstruct it: walk the locator
  hashes (tip-first), find the most recent one on the best chain via
  ReadRequest::Depth, and derive its height from the tip height and that
  depth.

- json_rpc/payments: replace the per-step input_in_pool / output_in_pool /
  change_in_pool closures in enforce_privacy_policy and required_privacy_policy
  with the new public Step::input_in_pool / output_in_pool / change_in_pool
  methods, which are byte-for-byte equivalent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dannywillems added a commit to zcash/zallet that referenced this pull request Jul 1, 2026
Pin the librustzcash crates to zcash/librustzcash `dw/merge-ironwood-branch`
(782b1da7), which merges the NU6.3 / ironwood 0.29-pre pre-release
(zcash/librustzcash#2509) into librustzcash main, so it carries both the
ironwood changes and the wallet's `spend-index` feature. Pin the zebra crates to
nuttycom's `chore/update_librustzcash` branch (ZcashFoundation/zebra#10789),
which targets the same librustzcash line, so the workspace and its zebra
dependencies resolve against a single librustzcash revision.

Bump the affected workspace version requirements to the 0.29-pre set
(zcash_protocol 0.10-pre, zcash_address 0.13-pre, zcash_primitives and
zcash_proofs 0.29-pre, zcash_keys 0.15-pre, zcash_transparent 0.9-pre, orchard
0.15-pre). These are temporary WIP pins; the librustzcash revision is isolated
in the [patch.crates-io] section so it can be bumped on its own as the branch
advances.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dannywillems added a commit to zcash/zallet that referenced this pull request Jul 1, 2026
Bump the librustzcash pin from 4cdba554 to 4367ba26 (tracking upstream main),
and point the zebra dependencies at nuttycom's chore/update_librustzcash
branch (rev 9f2fcf29), which carries the build fixes for librustzcash main
(ZcashFoundation/zebra#10789). Crate versions are unchanged, so this is an
API-drift adaptation rather than a semver bump.

Two source changes were required:

- chain: migrate ChainReader::find_fork_point off the removed
  ReadRequest::FindForkPoint / ReadResponse::ForkPoint. The newer zebra-state
  has no dedicated fork-point request, so reconstruct it: walk the locator
  hashes (tip-first), find the most recent one on the best chain via
  ReadRequest::Depth, and derive its height from the tip height and that
  depth.

- json_rpc/payments: replace the per-step input_in_pool / output_in_pool /
  change_in_pool closures in enforce_privacy_policy and required_privacy_policy
  with the new public Step::input_in_pool / output_in_pool / change_in_pool
  methods, which are byte-for-byte equivalent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dannywillems added a commit to zcash/zallet that referenced this pull request Jul 1, 2026
Pin the librustzcash crates to zcash/librustzcash `dw/merge-ironwood-branch`
(782b1da7), which merges the NU6.3 / ironwood 0.29-pre pre-release
(zcash/librustzcash#2509) into librustzcash main, so it carries both the
ironwood changes and the wallet's `spend-index` feature. Pin the zebra crates to
nuttycom's `chore/update_librustzcash` branch (ZcashFoundation/zebra#10789),
which targets the same librustzcash line, so the workspace and its zebra
dependencies resolve against a single librustzcash revision.

Bump the affected workspace version requirements to the 0.29-pre set
(zcash_protocol 0.10-pre, zcash_address 0.13-pre, zcash_primitives and
zcash_proofs 0.29-pre, zcash_keys 0.15-pre, zcash_transparent 0.9-pre, orchard
0.15-pre). These are temporary WIP pins; the librustzcash revision is isolated
in the [patch.crates-io] section so it can be bumped on its own as the branch
advances.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@conradoplg

Copy link
Copy Markdown
Collaborator

Merging (into nu63-ironwood) per discussion to test this in CI

@conradoplg
conradoplg merged commit 1cde30a into ZcashFoundation:nu63-ironwood Jul 1, 2026
16 checks passed
@nuttycom
nuttycom deleted the chore/update_librustzcash branch July 1, 2026 15:15
mergify Bot pushed a commit that referenced this pull request Jul 2, 2026
* feat(chain): add NU6.3 (Ironwood) network upgrade scaffolding

Introduce the `NetworkUpgrade::Nu6_3` variant (Ironwood) between Nu6_2 and
Nu7, mirroring how Nu7 is wired as an unscheduled upgrade:

- enum variant with `serde` rename "NU6.3" and a test-gated placeholder
  consensus branch id (0xfffffffe); no mainnet/testnet activation height yet
- testnet `ConfiguredActivationHeights.nu6_3` field and all builder paths
- extend every exhaustive NetworkUpgrade match across zebra-chain,
  zebra-consensus, zebra-network, and zebra-rpc (history tree, commitment,
  tx version gating, halo2 verifier routing, peer protocol versions,
  block-template commitments)
- v4/v5 remain valid at NU6.3; NU6.3 routes to the post-NU6.2 Halo2 key
- update unscheduled-upgrade tests to account for two unscheduled upgrades

Scaffolding only: no v6/Ironwood bundle, value pool, or consensus rules yet
(see NU6.3-IRONWOOD-PLAN.md). Builds clean across the workspace with fmt and
clippy -D warnings; parameter, network, and halo2 tests pass.

* feat(chain): add NU6.3 ironwood chain value pool

Add `ironwood` as the sixth chain value pool (ZIP 209, NU6.3) alongside
transparent/sprout/sapling/orchard/deferred:

- `ValueBalance<C>` field plus `from_ironwood_amount`, `ironwood_amount`,
  `set_ironwood_value_balance`, and inclusion in zero/constrain/Add/Sub/Neg,
  the Arbitrary impl, and `remaining_transaction_value`
- serialization appends ironwood at bytes 40..48: `to_bytes` is now [u8; 48]
  and `from_bytes` accepts 32/40/48-byte records, so databases written by
  earlier Zebra versions still parse (ironwood defaults to zero)
- on-disk consumers updated: `IntoDisk for ValueBalance` ([u8; 48]) and
  `BlockInfo` (handles 44-byte old and 52-byte new layouts)
- bump database format version to 27.1.0 (minor: compatible read code)
- regenerate value-pool raw-data snapshots (ironwood-zero widening only;
  all existing pool values and block sizes preserved)

The pool is always zero until NU6.3 activates. Reporting ironwood in the
getblockchaininfo valuePools array is deferred to the RPC phase.

Workspace builds across all targets; fmt and clippy -D warnings clean;
value_balance and disk_format tests pass.

* feat(chain): add ironwood transaction accessors and value-balance wiring

Add the Ironwood (NU6.3) accessor surface on `Transaction`, mirroring the
orchard accessors: `ironwood_shielded_data`, `ironwood_actions`,
`ironwood_nullifiers`, `ironwood_note_commitments`, `ironwood_flags`,
`has_ironwood_shielded_data`, and `ironwood_value_balance`. The last is folded
into `value_balance_from_outputs` so the ironwood chain value pool is wired
end-to-end.

Ironwood reuses the Orchard `ShieldedData`/tree/nullifier types (identical
Pallas/Sinsemilla machinery), differing only by storage instance, so no separate
tree module is introduced. `ironwood_shielded_data` returns `None` for every
version until `Transaction::V6` gains the Ironwood bundle field (a later change),
so all derived accessors are empty and the value balance is zero today.

Builds clean; fmt and clippy -D warnings pass; transaction and value_balance
tests pass.

* feat(chain): reshape v6 transaction to the Ironwood format

Redefine the (gated) v6 transaction format for NU6.3 / Ironwood as "v5 plus an
Ironwood bundle", replacing the older NU7/ZIP-233 burn draft:

- `Transaction::V6`: drop `zip233_amount`, add
  `ironwood_shielded_data: Option<orchard::ShieldedData>`, serialized after the
  orchard bundle with the identical Orchard wire layout
- wire `ironwood_shielded_data()` to the v6 field; count ironwood actions in
  `has_shielded_inputs`/`has_shielded_outputs` so an ironwood-only v6 tx is
  considered to have inputs/outputs
- remove the `zip233_amount()`/`has_zip233_amount()` accessors and the burn
  subtraction in the transaction-fee path; simplify `has_inputs_and_outputs`
- neutralize the orphaned ZIP-235 NSM burn enforcement in block subsidy checks
  (burn treated as zero, with a TODO to re-source it) and remove the three
  obsolete zip233 NSM tests

All v6/Ironwood data-model changes are behind `zcash_unstable = "nu7"` +
`tx_v6`. The default build is unaffected.

Known upstream gaps: `to_librustzcash` is a serialize/parse roundtrip, so v6
txid/auth/sighash fail at runtime until zcash_primitives' v6 parser matches the
Ironwood format (Zebra's v6 wire (de)serialization is self-contained). The
zebra-rpc tx_v6 block-template path is pre-broken on main and left untouched.

Default workspace builds across all targets with fmt and clippy -D warnings;
zebra-chain/-consensus tests pass; nu7+tx_v6 compiles for chain/state/consensus.

* test(chain): cover the NU6.3 ironwood chain value pool rule

Add fixed-vector tests confirming the ironwood pool participates in the ZIP-209
chain value pool non-negativity rule (`add_chain_value_pool_change` rejects a
negative ironwood balance with an ironwood-specific error) and that the ironwood
value balance is included in `remaining_transaction_value`.

This locks in the ironwood ZIP-209 rule, which rides the generic `ValueBalance`
machinery added earlier. The remaining Phase 4 state storage (nullifier sets and
note-commitment trees) is deferred pending upstream finalization of the orchard
Ironwood pool types (see NU6.3-IRONWOOD-PLAN.md).

* build(deps): pin librustzcash + orchard to the Ironwood forks (spike)

Add a [patch.crates-io] block pointing the librustzcash family at the
valargroup/librustzcash `adam/ironwood-split-2-v6-txid` branch (which adds v6
Ironwood txid/sighash under `zcash_unstable="nu6.3"`) and `orchard` at the
matching `qr_orchard` rev. The fork declares the same crate versions as our
crates.io pins (zcash_primitives 0.28, zcash_protocol 0.9, orchard 0.14), so
this is a source-only patch with no version drift.

Adapt the Orchard Halo2 batch verifier to the new `qr_orchard` API:
- `VerifyingKey::build_for_version(v)`/`build()` -> `VerifyingKey::build(v)`
- `BatchValidator::default()` -> `BatchValidator::new(&vk)` (now borrows the key,
  so `Verifier.batch` is `BatchValidator<'static>`); `take` rebuilds it
- `validate(vk, rng)` -> `validate(rng)` (key is held by the validator)
- `add_bundle` now returns `Result`; a queued item whose cross-address
  restriction is unsupported by the era's key is rejected on its own without
  poisoning the batch
- add the `OrchardCircuitVersion::PostNu6_3` verifying key and route NU6.3+
  (and ZFuture) to a new `VERIFIER_POST_NU6_3`, leaving NU6.2 on the fixed key

Default workspace builds across all targets against the forks; fmt and clippy
-D warnings clean; halo2 and consensus transaction tests pass (historical
pre-NU6.2 Orchard proofs still verify).

This patch is experimental: the fork branch is force-pushed, so pin to a rev
before relying on it.

* feat(chain): gate v6/Ironwood on nu6.3 and wire it to the fork

Align Zebra's v6 transaction support with the librustzcash Ironwood fork, which
gates v6 txid/sighash under `zcash_unstable = "nu6.3"`:

- migrate every `cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))` gate to
  `"nu6.3"` across zebra-chain/-consensus/-state/-rpc/zebrad (the v6 format is an
  NU6.3 feature; the lone genuinely-NU7 `From<zcash_protocol::Nu7>` arm stays)
- add the `From<zcash_protocol::NetworkUpgrade::Nu6_3>` conversion (gated nu6.3)
- set the Nu6_3 consensus branch id to the fork's `BranchId::Nu6_3` placeholder
  (0xffff_ffff) so `to_librustzcash` resolves v6 transactions to the fork's NU6.3
  branch id; move the Nu7 test placeholder to 0xffff_fffe to keep ids unique
- register `nu6.3` in the workspace `unexpected_cfgs` check-cfg list
- add a v6 round-trip + txid test that drives the fork's v6 (ZIP-244) digest

With the fork patched in and built under `--cfg zcash_unstable="nu6.3"` plus the
`tx_v6` feature, v6 Ironwood transactions now compute a txid end-to-end. The
default build (no cfg) is unaffected; fmt and clippy -D warnings are clean in
both the default and the nu6.3+tx_v6 configurations.

* fix(chain): give ZFuture a distinct test branch-id placeholder

The NU6.3 branch-id placeholder is `0xffffffff` (matching the librustzcash
Ironwood fork), and the `Nu6_3` entry is gated on `test`/`zebra-test`
independently of `zcash_unstable`. The `ZFuture` placeholder was also
`0xffffffff`, so building tests with `--cfg zcash_unstable="zfuture"` put two
`0xffffffff` entries in `CONSENSUS_BRANCH_IDS` and failed `branch_id_bijective`.
(This collision pre-existed between `Nu7` and `ZFuture`; the NU6.3 branch-id swap
relocated it onto `Nu6_3`.)

Give `ZFuture` a distinct `0xfffffffd` placeholder so all three future-upgrade
placeholders are unique. `branch_id_bijective` now passes under the default,
`nu6.3`, and `zfuture` configurations.

* fix(consensus): route Orchard verifier by transaction version

A v5 transaction's Orchard bundle commits to the pre-NU6.3 circuit even when
mined at NU6.3 (the v5 format predates the NU6.3 cross-address circuit), so it
must verify under the fixed (post-NU6.2) key — not the NU6.3 key. The previous
`verifier_for(network_upgrade)` routed ALL NU6.3 Orchard bundles to the NU6.3
key, which would reject valid v5 Orchard bundles mined at NU6.3.

Split the routing by transaction version:
- `orchard_v5_verifier_for(nu)`: v5 Orchard bundle → InsecurePreNu6_2 (<NU6.2) or
  FixedPostNu6_2 (NU6.2 onward, incl. NU6.3/NU7).
- `orchard_v6_verifier()`: v6 Orchard + Ironwood bundles → PostNu6_3.

`verify_orchard_bundle` now takes the resolved verifier; `verify_v6_transaction`
is a real function (no longer a passthrough to v5) that routes its Orchard bundle
to the NU6.3 key and gates v6 on NU6.3+. (Ironwood bundle proof verification is
wired in a follow-up.) Regression test asserts v5 Orchard at NU6.3 routes to the
fixed key.

Default + nu6.3/tx_v6 builds, fmt, clippy -D warnings clean; halo2 tests pass.

* feat(chain): accept the NU6.3 enableCrossAddress flag in v6 bundles

Per the v6 transaction format (ZIP 1301), the Orchard flag byte gains bit 2
`enableCrossAddress` at NU6.3, shared by the v6 Orchard and Ironwood bundles;
before NU6.3 (v5 Orchard) that bit is reserved and MUST be zero.

- add `Flags::ENABLE_CROSS_ADDRESS` (bit 2) and a `FlagFormat { PreNu6_3, Nu6_3 }`
  selector, with `Flags::from_byte`/`zcash_deserialize_with_format` enforcing the
  per-format reserved bits (PreNu6_3 reserves bits 2..7; Nu6_3 reserves bits 3..7)
- the default `ZcashDeserialize for Flags` now uses the PreNu6_3 format, so v5
  Orchard bundles still reject bit 2 (unchanged consensus behavior, including in
  nu6.3 builds where the bit is defined)
- extract `deserialize_orchard_shielded_data(reader, flag_format)`; the v6
  transaction deserializer parses its Orchard and Ironwood bundles with the Nu6_3
  format so cross-address-enabled bundles round-trip

All v6 code is behind `zcash_unstable = "nu6.3"` + `tx_v6`; the default build is
unaffected. Unit tests cover both flag formats. Default + nu6.3 builds, fmt, and
clippy -D warnings clean.

* refactor(consensus): split Orchard bundle verification into v5/v6 methods

Encapsulate the verifying-key choice in the verify methods instead of threading a
`&VerifierService` through the transaction-verification path:

- `verify_orchard_bundle(bundle, sighash, network_upgrade)` verifies a v5 Orchard
  bundle, resolving its key from the upgrade via `orchard_v5_verifier_for`.
- new `verify_orchard_v6(bundle, sighash)` verifies a v6 Orchard bundle against the
  NU6.3 key via `orchard_v6_verifier`.

Also completes the verifying-key static rename (`VERIFYING_KEY_V5_PRE_NU6_2` /
`VERIFYING_KEY_V5_POST_NU6_2` / `VERIFYING_KEY_V6`) across the verifier statics,
tests, and bench, so default and nu6.3+tx_v6 builds compile again.

Default + nu6.3 builds, fmt, clippy -D warnings clean; halo2 tests pass.

* refactor(consensus): address PR review feedback

- rename the Orchard verifier statics to match the verifying keys:
  `VERIFIER_PRE_NU6_2`/`POST_NU6_2`/`POST_NU6_3` →
  `VERIFIER_V5_PRE_NU6_2`/`VERIFIER_V5_POST_NU6_2`/`VERIFIER_V6`.
- remove the always-zero `zip233_amount` term from the coinbase balance equation
  in `subsidy_is_valid` (the ZIP-233 burn field was removed from v6; the term added
  nothing). NSM will re-source the burn when it's specified.
- clarify the `BlockInfo::from_bytes` length match: records are exactly 44 (pre-NU6.3)
  or 52 (NU6.3) bytes; use an open `44..` range for the pre-NU6.3 arm to keep the
  original forward-compatible behavior now that the `52..` arm takes every NU6.3 record.

Default + nu6.3 builds, fmt, clippy -D warnings clean; halo2 tests pass.

* refactor(chain): add orchard::ShieldedDataV6 newtype for v6 bundle (de)serialization

Encode the v6/NU6.3 Orchard flag-byte format in the type system instead of
threading a `FlagFormat` through the v6 deserializer:

- add `orchard::ShieldedDataV6(orchard::ShieldedData)` with a `ZcashDeserialize`
  impl that parses the NU6.3 flag format (permitting `enableCrossAddress`).
- the v6 transaction parses its Orchard and Ironwood bundles as
  `Option<ShieldedDataV6>`, then unwraps to the in-memory `orchard::ShieldedData`,
  so the transaction fields, accessors, and downstream state code are unchanged
  (minimal diff — no field-type ripple).
- v6 bundles encode identically to v5 on the wire, so serialization reuses the
  existing `Option<orchard::ShieldedData>` serializer (no duplicate serialize impl).

Also print the ironwood action count after the orchard action count in the
`Transaction` Debug output (review feedback), replacing the ad-hoc
`has_ironwood_shielded_data` field.

Default + nu6.3 builds, fmt, clippy -D warnings clean; v6 round-trip, flag, and
zebra-chain lib tests pass.

* refactor(consensus): rename verify_orchard_v6 to verify_orchard_v6_bundle

Match the `verify_orchard_bundle` (v5) naming, per PR review feedback.

* feat(consensus): verify the v6 Ironwood bundle's Halo2 proof

The librustzcash Ironwood fork exposes the v6 transaction's Ironwood bundle, so
extract it (like the Orchard bundle) and verify it. The Ironwood bundle reuses the
Orchard Action proof system and commits to the NU6.3 circuit, so it verifies under
the same NU6.3 key as the v6 Orchard bundle (`verify_orchard_v6_bundle`).

- add `ironwood_bundle()` accessors to `PrecomputedTxData` and `SigHasher` (gated
  on `zcash_unstable = "nu6.3"` + `tx_v6`).
- `verify_v6_transaction` now verifies the Ironwood bundle's proof alongside the
  Orchard bundle, on the shared transaction-verification path (so it applies to
  both block validation and the mempool).

Default + nu6.3 builds, fmt, clippy -D warnings clean; v6 tests pass.

* feat(consensus): enforce NU6.3 coinbase, flag, and cross-address rules

Add the NU6.3 / Ironwood transaction consensus rules, on the shared
transaction-verification path (so they apply to both block validation and the
mempool):

- coinbase: the `enableSpendsIronwood` flag MUST be 0 (extends the existing
  `coinbase_tx_no_prevout_joinsplit_spend` check) → `CoinbaseHasEnableSpendsIronwood`.
- a transaction with Ironwood actions MUST set at least one Ironwood flag
  (`has_enough_ironwood_flags`, mirroring the Orchard rule) → `NotEnoughIronwoodFlags`.
- the Orchard pool MUST NOT enable cross-address transfers at NU6.3
  (`orchard_cross_address_disabled`): no new value may enter Orchard, so only the
  Ironwood pool may cross-address → `OrchardHasEnableCrossAddress`.

The Orchard/Ironwood chain value pool non-negativity rule (ZIP-209) is already
enforced generically via `ValueBalance`. The new flag checks are no-ops for pre-v6
transactions (no Ironwood actions, and v5 Orchard bundles can't set bit 2).

Default + nu6.3 builds, fmt, clippy -D warnings clean; consensus tests pass.

* docs(chain): note v6 Sapling reuses the v5 wire codec (digest changes are in librustzcash)

* refactor(chain): add orchard::FlagsV6 newtype for the v6 flag-byte format

Replace the runtime `FlagFormat` parameter with a `FlagsV6` newtype (parallel to
`ShieldedDataV6`), per PR review feedback:

- `orchard::FlagsV6(orchard::Flags)` with a `ZcashDeserialize` impl that uses the
  NU6.3 flag-byte format (bit 2 `enableCrossAddress` valid, bits 3..7 reserved);
  the bare `Flags` codec keeps the pre-NU6.3 format (bits 2..7 reserved).
- remove the `FlagFormat` enum and `Flags::zcash_deserialize_with_format`; the
  reserved-bit check is a private `Flags::from_byte(byte, reserved_mask)` helper.
- `deserialize_orchard_shielded_data` is now generic over the flag type
  (`F: ZcashDeserialize + Into<Flags>`): v5 bundles parse `Flags`, v6 Orchard and
  Ironwood bundles parse `FlagsV6`. The format is selected by the type, not a
  runtime arg.

Default + nu6.3 builds, fmt, clippy -D warnings clean; flag, prop, and v6
round-trip tests pass.

* feat(ironwood): type v6 shielded fields with ShieldedDataV6/ironwood newtypes

Make the v6 transaction's shielded-data fields carry their newtypes directly
instead of unwrapping to the bare orchard::ShieldedData:

- orchard_shielded_data: Option<orchard::ShieldedDataV6>
- ironwood_shielded_data: Option<ironwood::ShieldedData>

Add a new zebra-chain ironwood module housing ironwood::ShieldedData, a newtype
around orchard::ShieldedDataV6. The Ironwood bundle shares the v6 Orchard wire
format but commits into a separate note commitment tree and nullifier set, so a
distinct type keeps the two pools from being interchanged.

Dedup the (de)serialization: a single zcash_serialize_optional_orchard_bundle
helper backs the v5 Orchard, v6 Orchard, and Ironwood serializers; the v6 and
Ironwood deserializers delegate to the v5 Orchard codec, only wrapping their
newtypes. The transaction accessors still expose Option<&orchard::ShieldedData>,
so consensus/state callers are unchanged except the non-finalized orchard
nullifier UpdateWith, which now takes Option<&orchard::ShieldedData>.

All v6 code stays behind the zcash_unstable="nu6.3" + tx_v6 gates.

* refactor(ironwood): privatize v6 newtype fields; add ironwood::Nullifier

Make the inner fields of the v6 newtypes private and expose them through
methods instead of tuple access:

- orchard::FlagsV6(Flags): private; read via the existing From<FlagsV6> for Flags
- orchard::ShieldedDataV6: private; ShieldedDataV6::new / .data() / .data_mut()
- ironwood::ShieldedData: private; ShieldedData::new / .data()

Also lay the groundwork for the Ironwood state layer:

- The ironwood module and its Nullifier newtype are now always compiled (only
  the v6 ShieldedData bundle stays behind the nu6.3/tx_v6 gates), so the on-disk
  database format is stable across build flags — matching ValueBalance's
  always-present ironwood pool. ironwood::Nullifier wraps orchard::Nullifier and
  keeps the Ironwood nullifier set type-disjoint from Orchard's.
- Transaction::ironwood_nullifiers / Block::ironwood_nullifiers now yield owned
  ironwood::Nullifier values for the state layer.

* feat(state): add the Ironwood nullifier set (NU6.3)

Track Ironwood-pool nullifiers in their own set, disjoint from Orchard's, to
reject Ironwood double-spends:

- New finalized `ironwood_nullifiers` column family (always registered, empty
  until NU6.3) with contains_ironwood_nullifier / ironwood_revealing_tx_loc reads,
  the prepare_nullifier_batch write path, and an IntoDisk impl reusing the Orchard
  nullifier byte encoding.
- Non-finalized Chain gains an ironwood_nullifiers map. Because Ironwood reuses
  orchard::ShieldedData, its nullifiers can't go through a distinct UpdateWith impl
  (it would collide with Orchard's), so they are added/removed inline.
- check::nullifier rejects duplicate Ironwood nullifiers within the non-finalized
  and finalized chains, via a DuplicateNullifierError impl for ironwood::Nullifier
  (new ValidateContextError::DuplicateIronwoodNullifier).
- Spend::Ironwood + From for the indexer's spending-transaction lookups.

Database format bumped to 28.0.0: a major bump that is restorable from the
previous major version (NoMigration upgrade; the new column family is created
empty and the widened value-pool records are read in place — no resync, no data
migration). Snapshots regenerated.

The Ironwood note commitment tree, anchors, and subtrees follow in a subsequent
commit.

* fix(consensus): subtract the Ironwood value balance from coinbase output value

The coinbase output-value equation subtracted the Sapling and Orchard value
balances but not the Ironwood one. Ironwood is a shielded pool, so [NU6.3 onward]
`vbalanceIronwood` must be subtracted too (value shielded into Ironwood by a
coinbase counts toward its output value, exactly like Orchard). This is a no-op
before NU6.3, since pre-v6 coinbase transactions have no Ironwood bundle and the
balance is zero.

Addresses a PR review comment on block/check.rs.

* feat(state): add the Ironwood note commitment tree, anchors, and subtrees (NU6.3)

Maintain the Ironwood note commitment tree alongside Orchard's, so Ironwood
anchors can be validated against prior treestates. Ironwood reuses the Orchard
tree types (orchard::tree::{NoteCommitmentTree, Root, Node}) but commits into
separate column families and indexes.

- zebra-chain: NoteCommitmentTrees gains ironwood + ironwood_subtree fields and
  an update_ironwood_note_commitment_tree() spawned in update_trees_parallel;
  Block::ironwood_note_commitments(); a distinct NoteCommitmentTreeError::Ironwood.
- Finalized state: three new column families (ironwood_anchors,
  ironwood_note_commitment_tree, ironwood_note_commitment_subtree) with the tree/
  anchor/subtree reads, note_commitment_trees_for_tip, create_ironwood_tree /
  insert_ironwood_subtree, and prepare_trees_batch wiring; contains_ironwood_anchor.
- Non-finalized Chain: ironwood tree/anchor/subtree indexes with the matching
  accessors, add/remove_ironwood_tree_and_anchor, treestate, pop_root/pop_tip,
  parallel-update, and revert wiring; Treestate::new + Chain::new take the
  Ironwood tip tree.
- check::anchors rejects Ironwood actions whose anchor is unknown
  (ValidateContextError::UnknownIronwoodAnchor).

The new column families are registered under the existing 28.0.0 format bump and
hold only the genesis empty-tree root/tree until NU6.3 activates. Snapshots
regenerated (ironwood genesis anchor root is identical to Orchard's empty tree).

The IronwoodTree / IronwoodSubtrees read requests and RPC follow separately.

* fix(rpc): drop the abandoned zip233_amount block-template plumbing

ZIP-233 was dropped from the v6 transaction format, but a gated zip233_amount
parameter was left threaded through new_coinbase / new_internal /
select_mempool_transactions. No caller ever passed it, and its body referenced an
undefined miner_fee, so the nu6.3 + tx_v6 build of zebra-rpc/zebrad did not
compile.

Remove the parameter, the dead zip235 branch, and the set_zip233_amount call (the
coinbase builder no longer sets a burn amount; the Network Sustainability
Mechanism can re-plumb one when it is specified), plus the now-redundant gated
imports and the call-site arguments in tests. The default build is unchanged
(all removed code was gated); the experimental nu6.3 + tx_v6 build of zebrad now
compiles end-to-end.

* feat(chain): add the ZIP-221 V3 history node for Ironwood (NU6.3)

At NU6.3 the chain history MMR commits to the Ironwood note commitment tree, via
the V3 history node format (zcash_history::V3 / NodeDataV3, which extends V2 with
start/end Ironwood roots and an Ironwood transaction count).

- Thread an ironwood_root through the history-tree API: the Version trait's
  block_to_history_node, Entry::new_leaf, Tree::new_from_block / append_leaf, and
  both HistoryTree types' from_block / push / try_extend. V1 and V2 ignore it.
- Add a Version impl for zcash_history::V3 building NodeDataV3 from the V2 node
  data plus the Ironwood root and Block::ironwood_transactions_count(); the fork's
  zcash_history handles V3 hashing/combining.
- Select the tree version by network upgrade: Heartwood/Canopy -> V1, NU5..NU6.2
  -> V2, NU6.3 onward (and ZFuture) -> V3. Pre-NU6.3 behaviour is unchanged
  (existing V1/V2 history tests pass), so this only affects NU6.3 once activated.
- Wire the Ironwood tip root into the finalized and non-finalized history-tree
  updates, and into the proptest block generator so generated NU6.3 commitments
  match validation.

The V3 path is dormant until NU6.3 activates (no activation height yet).

* fix(state): backfill the genesis Ironwood tree on database upgrade

The 28.0.0 NoMigration was insufficient: a node upgrading a pre-28 database
ends up with an empty ironwood_note_commitment_tree / ironwood_anchors column
family (its genesis block was committed long ago, before Ironwood existed). That
makes ironwood_tree_for_tip() panic on the first block after restart, and leaves
the genesis Ironwood anchor missing for NU6.3 anchor validation — a consensus
split versus genesis-synced nodes once NU6.3 activates.

Replace NoMigration(28.0.0) with a real add_ironwood_tree::Upgrade that backfills
the empty Ironwood tree and its anchor at the genesis height, matching what a
genesis-synced v28 node writes. It is a no-op for genesis-synced and
already-upgraded databases, and remains a reusable major upgrade (restorable from
the previous major format version, no resync). Adds ironwood_tree_by_height_range
for the existence check and the upgrade's validate().

Addresses a code-review finding (confirmed against valargroup's add_ironwood_tree).

* feat(consensus): enforce Ironwood proof size, nullifier uniqueness, coinbase flags

Three NU6.3 consensus rules that were missing for the Ironwood bundle (found by
code review + spec-conformance against ZIP-1301):

- The canonical Halo2 proof-size rule was enforced only for the Orchard bundle.
  Apply it to the Ironwood bundle too (TransactionError::IronwoodProofSize).
  Ironwood only exists from NU6.3 onward, so it is enforced unconditionally — no
  legacy lenient period as there was for Orchard (GHSA-jfw5-j458-pfv6).
- spend_conflicts() now checks intra-transaction duplicate Ironwood nullifiers
  (TransactionError::DuplicateIronwoodNullifier), like the other pools.
- [NU6.3] A v6 coinbase transaction MUST have flagsOrchard == 0 (no new value may
  be shielded into the Orchard pool at NU6.3; new shielded value goes to Ironwood).
  Previously only enableSpends was rejected (TransactionError::CoinbaseHasNonZeroOrchardFlags).

Adds an Arbitrary impl for ironwood::Nullifier (used by the error enum's derived
Arbitrary under proptest).

* fix(chain): reject pre-NU6.3 consensus branch IDs when parsing v6 transactions

v6 transactions are only valid from NU6.3 onward, but the deserializer rejected
only pre-NU5 branch IDs. Tighten the wire-layer check to reject anything below
NU6.3 (the exact tx-vs-block network-upgrade match is still re-checked during
verification by consensus_branch_id).

Addresses a code-review / spec-conformance finding.

* refactor(state): apply Ironwood nullifiers via UpdateWith; drop per-tx Vec allocs

Update the non-finalized chain's Ironwood nullifier set through the UpdateWith
trait (keyed on the ironwood::ShieldedData newtype, so it doesn't collide with
the Orchard impl), exactly like the other shielded pools, instead of the previous
inline add/remove calls.

To enable this and remove the per-transaction Vec allocations (the accessor
yields owned ironwood::Nullifier), the shared nullifier helpers
(add_to_non_finalized_chain_unique, remove_from_non_finalized_chain,
find_duplicate_nullifier) now take owned Copy nullifiers instead of references.
Sprout/Sapling/Orchard callers pass .copied(); Ironwood passes its owned
nullifiers directly. No pool now allocates a throwaway Vec on the commit, revert,
or duplicate-check paths.

Addresses code-review finding #9 and the altitude/consistency note about Ironwood
not using UpdateWith.

* feat(rpc,chain): count Ironwood actions for ZIP-317 fees and expose its value pool

Two NU6.3 completeness gaps found by spec-conformance review against ZIP-1301:

- ZIP-317 conventional_actions() now counts Ironwood actions alongside Orchard
  actions (identical structure and cost). Previously v6 transactions with Ironwood
  actions were under-charged the conventional fee and under-counted unpaid actions
  for mempool/relay. Zero for pre-v6 transactions.
- getblockchaininfo / getblock 'valuePools' now include the Ironwood chain value
  pool (the spec defines it; it is zero before NU6.3 activates). The pool array
  grows from 5 to 6 entries; snapshots regenerated.

Both are no-ops before NU6.3.

* docs(changelog): note the NU6.3 DB format bump and experimental Ironwood support

* style(ironwood): align with existing codebase conventions

Code-review style-consistency pass against the orchard/sapling precedent:

- error.rs: rename CoinbaseHasNonZeroOrchardFlags -> CoinbaseHasOrchardFlags
  (matches the CoinbaseHas<Flag> sibling pattern); fix message casing
  (EnableSpendsIronwood, not enableSpendsIronwood) to match the Orchard sibling;
  drop the [NU6.3 onward] tag/parenthetical from the OrchardHasEnableCrossAddress
  message for terse-imperative consistency; move NotEnoughIronwoodFlags beside
  NotEnoughFlags (and out of the score=100 coinbase cluster, matching its
  unprefixed orchard sibling's score).
- check.rs / anchors.rs: restore the # Consensus + spec-link form on the Ironwood
  flag/cross-address/anchor checks to match the adjacent orchard/sapling blocks.
- nullifier.rs: update the disjoint-pools doc quote to include Ironwood.
- value_balance.rs: order the ironwood accessors last, matching the field order.
- transaction.rs: fold  into the existing use crate::{...} group.
- ironwood.rs: move the Nullifier Arbitrary impl into ironwood/arbitrary.rs, like
  every other pool's arbitrary submodule.
- drop a stray insta assertion_line header from one regenerated snapshot.

No behavior change.

* feat(consensus): freeze the Orchard pool at NU6.3 and drop the tx_v6 feature

Addresses review feedback on #10762.

- Remove the `tx_v6` cargo feature: NU6.3 code now compiles under
  `--cfg zcash_unstable="nu6.3"` alone. All 94 `cfg(all(zcash_unstable="nu6.3",
  feature="tx_v6"))` gates are simplified to `cfg(zcash_unstable="nu6.3")` and the
  feature is deleted from every crate's Cargo.toml (it was only ever used by these
  gates on this branch). New build command drops `--features tx_v6`.

- Enforce `[NU6.3 onward] valueBalanceOrchard >= 0`
  (`TransactionError::NegativeOrchardValueBalance`). The Orchard pool is frozen
  against new inflows from NU6.3, since newly shielded value is routed to Ironwood;
  spends (Orchard-to-transparent) and note management (Orchard-to-Orchard) stay
  valid. Applies to both v5 and v6 Orchard bundles.

- Keep v5 Orchard bundles valid after NU6.3 (no rejection), so non-upgraded
  hardware wallets can keep authorizing Orchard spends. The existing
  version-based verifier routing (v5 -> FixedPostNu6_2, v6 -> PostNu6_3) is
  already correct.

- Add `NotEnoughIronwoodFlags` to the misbehavior-score list.

- Drop the stale ZIP-233 `None` arguments to `new_coinbase`, fix the run-command
  doc comment, and rename `nu7_nsm_transactions` -> `nu6_3_block_template_proposal`.

- Reword two disk-format comments and fix comma spacing in the non-finalized
  state property tests.

- Fix a pre-existing NU6.3 compile error in the mempool property tests by adding
  `orchard::ShieldedDataV6::into_inner` and unwrapping/rewrapping the v6 Orchard
  bundle.

* docs(changelog): add NU6.3 Ironwood pool entries

* test(consensus): expand NU6.3/Ironwood v6 test coverage

Previously every v6 path was exercised only with empty bundles, because there was
no way to build a populated v6 Orchard/Ironwood bundle in tests. Add the missing
generators and cover the NU6.3 consensus rules and the v6 wire codec.

- Add `fake_v6_orchard_shielded_data` / `fake_v6_transaction` test helpers
  (zebra-chain) that build populated, structurally-valid-but-cryptographically-fake
  v6 Orchard and Ironwood bundles (mirroring `insert_fake_orchard_shielded_data`).
  These unblock all the structural v6 tests below; they must not be used where proof
  verification or a canonical proof size is required.

- Consensus unit tests for the NU6.3 rules (zebra-consensus):
  - `v6_transaction_with_ironwood_actions_must_have_flags` (NotEnoughIronwoodFlags)
  - `v6_orchard_bundle_must_not_enable_cross_address` (OrchardHasEnableCrossAddress)
  - `v6_transaction_with_duplicate_ironwood_nullifier_is_rejected` (spend_conflicts /
    DuplicateIronwoodNullifier, and Ironwood/Orchard nullifier sets stay disjoint)

- Round-trip a v6 transaction carrying populated Orchard-v6 and Ironwood bundles
  through Zebra's own v6 (de)serializer (zebra-chain). It does not compute a txid,
  which would drive the librustzcash fork's parser (it does not accept fake proof
  bytes); the empty-bundle txid path is already covered.

- Make the `nu6_3_block_template_proposal` integration test actually activate NU6.3
  (it was activating NU7), so it exercises the NU6.3 block-template -> proposal ->
  submit pipeline end-to-end.

A positive proof-validity end-to-end test (valid Ironwood proofs verified through
full block validation) is still deferred: it needs an Ironwood prover to generate
valid blocks, the same dependency QEDIT's OrchardZSA tests have on zcash_tx_tool.

* refactor(consensus): rename NotEnoughFlags to NotEnoughOrchardFlags

The variant is implicitly about Orchard flags; rename it for symmetry with
the new NotEnoughIronwoodFlags variant.

* docs(chain): document the reserved Orchard-flag consensus rule on the deserializers

The generic Flags::from_byte helper enforces whatever mask the caller passes,
so the reserved-bits consensus rule belongs on the ZcashDeserialize impls for
Flags (v5, pre-NU6.3 format) and FlagsV6 (NU6.3 format) where the specific
mask is chosen, not on the helper.

* refactor(consensus): surface the halo2 batch-queue error instead of discarding it

Match the known BatchError::RestrictionUnsupportedByKey variant explicitly with
an unreachable! documenting why routing makes it impossible (v5 verifiers only
ever see v5 bundles, which always have cross_address_enabled = true; v6 bundles
go to VERIFIER_V6 whose key supports the restriction), and keep a graceful arm
for any future variant of the #[non_exhaustive] enum.

* feat(consensus): require an empty Orchard component in NU6.3 coinbase transactions

From NU6.3, newly shielded coinbase value is routed to the Ironwood pool, so a
coinbase transaction must have an empty Orchard component (ZIP-229). This applies
to every transaction version, so a v5 coinbase mined at NU6.3 is constrained too
and the rule cannot be bypassed with an older format. The new height-gated check
coinbase_orchard_component_empty subsumes the previous v6-only flagsOrchard==0
check; the error variant is renamed CoinbaseHasOrchardFlags -> CoinbaseHasOrchardActions.

* chore: remove the local NU6.3 Ironwood plan file

The planning doc was a working artifact and should not be part of the PR.

* fix(consensus): verify v5 Orchard bundles at NU6.3 under the NU6.3 circuit

orchard_v5_verifier_for routed every upgrade from NU6.2 onward (Nu6_2, Nu6_3,
Nu7, ZFuture) to the NU6.2 fixed-circuit key. That is wrong from NU6.3: the
Orchard Action circuit changes again at NU6.3 to add the disableCrossAddress
constraint that enforces the Orchard-pool cross-address restriction, and the
fixed key cannot constrain that public input.

Per ZIP-229 the restriction is enforced for every Orchard-pool Action mined from
NU6.3 onward "regardless of transaction version ... so that it cannot be bypassed
by using a version 5 transaction", and ZIP-258 spells out that the verifying key
is selected by block height, so it applies to v5 transactions as well as v6. So
an honest v5 Orchard bundle at NU6.3 proves under the NU6.3 circuit and would be
rejected by the fixed key (different key) — a consensus split — while the fixed
key could not enforce the restriction at all.

Route NU6.3 onward (Nu6_3, Nu7, ZFuture) to the NU6.3 key, leaving Nu6_2 alone on
the fixed key, so a v5 Orchard bundle at NU6.3 shares the key with v6 Orchard and
Ironwood. Keys are still named V5_PRE_NU6_2 / V5_POST_NU6_2 / V6, but the docs now
make clear the key is a function of block era, not transaction version.

Adapts valargroup commit 85a0a38 to this branch's verifier-key names.

* refactor(consensus): name the Orchard verifiers by circuit era, not transaction version

The Orchard Action verifying key is selected by block era (network upgrade), not
by transaction version — a v5 Orchard bundle at NU6.3 uses the NU6.3 key, the
same as v6 Orchard and Ironwood. The version-flavored names (V5_PRE_NU6_2 /
V5_POST_NU6_2 / V6) fought that routing and read as if v5 bundles always used a
"v5" key. Rename the keys and verifiers to circuit-era names:

  VERIFYING_KEY_V5_PRE_NU6_2  -> VERIFYING_KEY_PRE_NU6_2
  VERIFYING_KEY_V5_POST_NU6_2 -> VERIFYING_KEY_NU6_2
  VERIFYING_KEY_V6            -> VERIFYING_KEY_NU6_3_ONWARD
  VERIFIER_V5_PRE_NU6_2       -> VERIFIER_PRE_NU6_2
  VERIFIER_V5_POST_NU6_2      -> VERIFIER_NU6_2
  VERIFIER_V6                 -> VERIFIER_NU6_3_ONWARD

The dispatch functions (orchard_v5_verifier_for / orchard_v6_verifier) keep their
names: they select a verifier for the v5 vs v6 bundle path. Also refreshes the
now-stale 'two eras' docs to three eras, and updates the changelog.

* chore(deps): pin librustzcash crates to NU6.3 testnet-supporting pre-release version. (#10789)

chore(deps): pin librustzcash crates to NU6.3-testnet supporting versions

Add [patch.crates-io] directives pinning equihash, f4jumble, transparent
(zcash_transparent), and the zcash_* crates to librustzcash main
(rev 703fe3e6608fab8be0dedbbcbf684f030a1ea451).

Removes the `zcash_unstable=nu6.3` config flag gates and updates the
miner-reward transaction builders in zebra-rpc to adapt to the removed
lifetime parameter on zcash_primitives' transaction `Builder` (now
`Builder<P, U>` instead of `Builder<'a, P, U>`).

* chore: Set the NU6.3 consensus constants to match librustzcash (#10875)

feat(chain): set the NU6.3 consensus parameters to match zcash_protocol

Match zcash_protocol (librustzcash rev 703fe3e):

- TX_V6_VERSION_GROUP_ID = 0xD884_B698 (was the 0xFFFF_FFFF placeholder)
- NU6.3 consensus branch id = 0x37a5165b (was a test-only 0xffffffff placeholder)
- Testnet NU6.3 activation height = 4_134_000

Mainnet gets no NU6.3 activation height (zcash_protocol MainNetwork returns
None), so only the Testnet table gains an entry.

* chore(deps): Update to librustzcash 4b13be3c (#10876)

chore(deps): Update to librustzcash 4b13be3c2ce6f91a4b386508aed0799358daadb1

Updates to librustzcash 4b13be3c2ce6f91a4b386508aed0799358daadb1 to fix
an error in the flagging for Sprout support in NU6.3.

* fix tests; bump protocol version

* handle HistoryTree size increase

* docs(network): scope NU7 TODO and reflow protocol-version comments

NU6.3 is now fully specified (branch id, activation heights, protocol
version 170_160), so drop it from the "provisional" list. Move the NU7
alternates above the active line and unindent them — the trailing-comment
alignment was pushing lines past 160 chars.

* chore: Update to published librustzcash NU6.3 crates. (#10877)

* test(rpc): bump protocolversion snapshots to 170_160

Follow-up to ed53e03 "fix tests; bump protocol version", which raised
CURRENT_NETWORK_PROTOCOL_VERSION to 170_160 for NU6.3 (Ironwood) but
didn't touch the four `get_info` / `get_network_info` snapshots.

* test(state): move disk_format::chain tests into their own file

Per project convention (see disk_format/tests.rs, disk_db/tests.rs), tests
sit in their own file rather than inline in the production module.

* fix(ci): unbreak docs and cargo-vet after librustzcash bump

- shielded_data.rs: `FlagFormat::Nu6_3` is stale (never existed); the type is
  `FlagsV6`. Drop the redundant explicit target on the `ShieldedDataV6` link
  while at it (rustdoc's redundant_explicit_links).
- supply-chain/config.toml: bump exemption pins for orchard and the seven
  `zcash_*` crates to match the versions in Cargo.toml.

* fix(rpc): drop dead tx_v6 cfg from coinbase_cache test after merge

The merge from main brought in `coinbase_cache_reuses_built_coinbase` calling
`TransactionTemplate::new_coinbase(&net, height, &miner_params, fee, #[cfg(...
tx_v6)] None)`. On nu63-ironwood that function only takes four args, and the
`tx_v6` feature no longer exists in `zebra-rpc/Cargo.toml`, so the cfg was
permanently dead. Under `RUSTFLAGS=-D warnings` (used by clippy, check-cargo-
lock, and unused-deps CI jobs), `unexpected_cfgs` promotes to an error and
cascades to eight red jobs.

* refactor(chain): make ironwood::Nullifier's inner field private

Mirrors the encapsulation of `ironwood::ShieldedData` in the same module.
Adds `impl From<Nullifier> for [u8; 32]` for byte extraction (matching
`orchard::Nullifier`'s public API); callers that constructed via the tuple
form switch to `Nullifier::from(orchard_nullifier)`, and the `IntoDisk`
byte encoder uses `(*self).into()`.

* feat(rpc): mine shielded coinbase to Ironwood pool on NU6.3+ (#10880)

At NU6.3 the coinbase MUST have an empty Orchard component (ZIP-229) and
newly shielded value routes to the Ironwood pool instead. Ironwood outputs
use the same `orchard::Address` recipient shape as Orchard, so a Unified
miner address with an Orchard receiver just gets routed to
`Builder::add_ironwood_output` from NU6.3 onward. Pre-NU6.3 behavior is
unchanged (Orchard output).

Adds a test at the NU6.3 activation height on Testnet confirming the
resulting coinbase is v6, carries Ironwood shielded data, and has no
Orchard component.

* fix(state): gate HistoryTreeParts legacy fallback on UnexpectedEof

Addresses copilot review on #10762: `HistoryTreeParts::from_bytes`
previously fell back to the pre-NU6.3 bincode layout on *any*
deserialization error, so a corrupted current-format record could be
silently reinterpreted as a legacy history tree. Restricts the fallback
to the specific `Io(UnexpectedEof)` a narrower legacy record produces
when the wider current-format deserializer runs out of bytes; any other
error propagates and hits the `expect`.

---------

Co-authored-by: Marek <m@rek.onl>
Co-authored-by: Kris Nuttycombe <kris@nutty.land>
Co-authored-by: Conrado Gouvea <conrado@zfnd.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge Tells Mergify not to merge this PR external-contribution run-stateful-tests Allos to manually trigger a stateful tests run in GCP in PRs S-next-release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants