Skip to content

Release 0.8.1: spend legacy Orchard notes after NU6.3 - #39

Merged
emersonian merged 2 commits into
mainfrom
port-081
Sep 27, 2026
Merged

emersonian merged 2 commits into
mainfrom
port-081

Conversation

@emersonian

@emersonian emersonian commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

On 0.8.0, any send that spends a legacy Orchard-pool note after NU6.3 fails with Orchard proof generation failed: Prover(ProofFailed(InvalidInstances)) and broadcasts nothing. The note's age does not matter: besides notes received before activation (mainnet block 3,428,143), software that keeps change in the Orchard pool still creates them in v6 transactions afterwards. Only the PCZT send path is affected; sends through the fused builder (Sapling spends, transparent sources, z_shieldcoinbase, z_mergetoaddress) already chose the right key.

Cause

From NU6.3 the Orchard pool is on protocol V3, whose bundles must disable cross-address transfers (consensus-mandated), and only the post-NU6.3 circuit constrains that flag. BundleVersion::circuit_version maps both V3 pools to PostNu6_3, and the fused builder derives its key from the consensus branch accordingly. The PCZT send path instead proved every Orchard bundle with the FixedPostNu6_2 key, which Proof::create refuses for a bundle carrying the flag.

Fix

  • orchard_circuit_for_branch derives the Orchard bundle's circuit from the proposal's consensus branch; it travels with the PCZT through the inline and pipelined paths.
  • The prove step uses the cached key for that circuit, and an Orchard-only transaction is verified with the matching verifying key. A transaction mixing Orchard spends and Ironwood outputs already let the extractor derive keys per bundle.

Test

The regtest tier activates NU6.3 at height 8, before any coinbase matures, so no funded wallet ever held an Orchard-pool note across activation. regtest_orchard_v2_spend activates it at 200 instead, funds a wallet with a legacy Orchard note before that, pins the spend's input pool to Orchard, and pays a different wallet after activation, requiring the transaction to be mined. It passes with this change and fails without it, with exactly the error above.

Release

Release 0.8.1: CHANGELOG, version, lockfile, and the license bundle's version line. A drop-in upgrade from 0.8.0.

A send that spent an Orchard-pool note after NU6.3 failed with `Orchard proof
generation failed: Prover(ProofFailed(InvalidInstances))` and broadcast
nothing. Such notes are not only those received before activation (mainnet
block 3,428,143): software that keeps change in the Orchard pool still creates
them in v6 transactions after it, so the note's age does not matter.

From NU6.3 the Orchard pool is on protocol V3, whose bundles must disable
cross-address transfers, and only the post-NU6.3 circuit constrains that flag.
The PCZT send path proved the Orchard bundle with the NU6.2 (`FixedPostNu6_2`)
key regardless, which the prover refuses for a bundle carrying the flag. The
fused builder never had the problem, because it derives the circuit from the
transaction's consensus branch.

The send path now does the same. `orchard_circuit_for_branch` names the circuit
from the proposal's branch, and it travels with the PCZT through both the
inline and the pipelined paths, so the prove step uses the cached key for that
circuit and an Orchard-only transaction is verified with the matching
verifying key. A transaction mixing Orchard spends with Ironwood outputs
already let the extractor derive its keys per bundle.

The regtest tier could not see it: NU6.3 activates at height 8, before any
coinbase matures, so every funded wallet only ever held Ironwood notes.
`regtest_orchard_v2_spend` activates it at 200 instead, funds a wallet with a
legacy Orchard note before that, and pays a different wallet after it. Without
this change it fails with exactly the error above.
@emersonian
emersonian force-pushed the port-081 branch 2 times, most recently from f004541 to b053cc1 Compare September 25, 2026 20:51
@emersonian
emersonian merged commit ff6b412 into main Sep 27, 2026
14 checks passed
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