ZIP: 2006
Title: Restricting Transfers into the Orchard Pool
Owners: Daira-Emma Hopwood <[email protected]>
Credits: Sean Bowe
Schell Carl Scivally
Jack Grigg
Kris Nuttycombe
Status: Final
Category: Consensus
Created: 2025-06-21
License: MIT
Discussions-To: <https://github.com/zcash/zips/issues/1305>
The key words “MUST”, “MUST NOT”, and “SHOULD” in this document are to be interpreted as described in BCP 14 1 when, and only when, they appear in all capitals.
The term “network upgrade” in this document is to be interpreted as described in ZIP 200. 2
The character § is used when referring to sections of the Zcash Protocol Specification. 3
The terms “Orchard protocol”, “Orchard pool”, “Ironwood pool”, “Orchard-pool Action”, and “Ironwood-pool Action” are to be interpreted as described in ZIP 229. 4
The expanded receiver of an Orchard-protocol shielded payment address or note is the pair \((\mathsf{g_d}, \mathsf{pk_d})\) of its diversified base and its diversified transmission key, as defined in § 4.2.3 ‘Orchard Key Components’. 5
NU6.3 6 specifies two restrictions intended to limit economic activity within the Orchard pool:
So the Orchard pool can still be spent from, including across the turnstile into the Ironwood pool, but cannot be used to transfer value to an address whose spending key the sender does not hold.
This ZIP specifies the enableCrossAddress flag that expresses the second
restriction; the corresponding condition in the Action statement; and the
consensus rules that enforce both restrictions for the Orchard pool.
The Orchard Action circuit deployed before NU6.2 had a soundness vulnerability that could have allowed value to be created in the Orchard pool. 7 NU6.3 introduces the Ironwood pool, which uses the Orchard protocol with a corrected circuit. Value moves from the Orchard pool into it subject to the “turnstile” checks on chain value pool balances specified in ZIP 209 8.
For those checks to give the desired balance integrity assurances, newly created shielded value has to be directed into the Ironwood pool, and value in the Orchard pool should only be able to leave it. If users could still pay each other within the Orchard pool, it would remain a working payment pool, and any value created through the vulnerability could circulate in it without ever being checked at the turnstile. Disallowing new value from entering the Orchard pool, and payments between users within it, prevents that, while still letting each holder move their own funds out. It does not prevent users from rebalancing note values within the Orchard pool in order to reduce the information revealed when migrating those funds to the Ironwood pool, as specified in ZIP 318 9.
Without the cross-address restriction, unrelated real spends and outputs can share Actions, so the minimum Action count is the maximum of the numbers of real spends and real outputs. Under the restriction, a real spend and a real output can share an Action only if they use the same expanded receiver. When no such pairing is possible, the minimum Action count is instead the sum of those numbers. This increases transaction size and reduces the hiding of transaction arity provided by spend/output pairing. Same-receiver pairing and additional dummy Actions remain possible, so the public Action count does not uniquely determine the numbers of real spends and real outputs. Since the restriction applies to all Orchard-pool Actions, the resulting increase in Action counts is not by itself a wallet-specific fingerprint.
By preventing payments between users within the Orchard pool, this ZIP encourages users to transfer their funds out of that pool. Such transfers reveal the net amount leaving the pool on-chain, regardless of its destination. Transferring a wallet’s entire balance at once can therefore reveal that balance, and the amounts and timing of transfers can help link them. The restrictions preserve the ability to rebalance notes within the Orchard pool without crossing the turnstile. ZIP 318 9 specifies amount-selection and timing measures for migration to the Ironwood pool, and ZIP 326 10 describes how to construct the spend and output Actions needed for rebalancing under the cross-address restriction.
flagsOrchard as a reserved zero bit can still
produce valid Orchard-pool spends.enableCrossAddress flag Each Action has a flag \(\mathsf{enableCrossAddress} \;{\small ⦂}\; \mathbb{B}\kern-0.05em\textsf{,}\) which is set in order to allow the expanded receiver of the Action’s output note to differ from the expanded receiver of its input note. The value of \(\mathsf{enableCrossAddress}\) is the same for all Actions of a bundle, and is encoded once per bundle in bit 2 of its flags byte:
flagsIronwood field of a version 6 transaction, bit 2 encodes
enableCrossAddress for the Ironwood-pool Actions. 4flagsOrchard field, bit 2 MUST be 0, independent of the transaction
version. In a version 5 transaction it is still reserved and MUST be 0. In a
version 6 transaction it encodes enableCrossAddress, and MUST be 0.For blocks before NU6.3 activation, cross-address transfers are always permitted: \(\mathsf{enableCrossAddress}\) is taken to be \(1\) for every Action. From NU6.3 activation, \(\mathsf{enableCrossAddress}\) is bit 2 of the flags byte for each pool, so it is \(0\) for every Orchard-pool Action.
For a given Orchard-protocol Action description, let \(\mathsf{disableCrossAddress} = 1 - \mathsf{enableCrossAddress}\kern-0.05em\textsf{.}\)
From NU6.3 activation, the Action statement gains a primary input \(\mathsf{disableCrossAddress} \;{\small ⦂}\; \mathbb{B}\kern-0.05em\textsf{,}\) after \(\mathsf{enableOutputs}\kern-0.05em\textsf{,}\) and the following condition:
Cross-address restriction: \(\mathsf{disableCrossAddress} = 0\kern-0.05em\textsf{,}\) or \(\mathsf{g_d^{old}} = \mathsf{g_d^{new}}\) and \(\mathsf{pk_d^{old}} = \mathsf{pk_d^{new}}\kern-0.05em\textsf{.}\)
The auxiliary input of the Action statement already includes the spent note’s expanded receiver \((\mathsf{g_d^{old}}, \mathsf{pk_d^{old}})\) and the output note’s expanded receiver \((\mathsf{g_d^{new}}, \mathsf{pk_d^{new}})\kern-0.05em\textsf{.}\)
A single circuit, and a single verifying key identified as
OrchardCircuitVersion::PostNu6_3, are used for the Actions of both the
Orchard pool and the Ironwood pool from NU6.3 activation. The pools differ
in the consensus restrictions on the value of \(\mathsf{enableCrossAddress}\kern-0.05em\textsf{,}\)
not in the circuit.
The following consensus rules apply from NU6.3 activation to every transaction, whatever its version:
OrchardCircuitVersion::PostNu6_3, and the proof MUST be valid
for the primary input that includes \(\mathsf{disableCrossAddress}\kern-0.05em\textsf{.}\)valueBalanceOrchard field
when it is present, MUST be nonnegative. That is, a transaction can take value
from the Orchard pool, but MUST NOT add value to it.Together with the rule that bit 2 of flagsOrchard is 0, these ensure that
after NU6.3 activation no value enters the Orchard pool, and every
Orchard-pool Action sends its output to the expanded receiver of the note that
it spends.
When an Action with \(\mathsf{enableCrossAddress} = 0\) needs a dummy output note, that note MUST be sent to the expanded receiver of the Action’s spent note (which may itself be a dummy note), rather than to a random address. Likewise, a dummy spent note paired with an output note that is not a dummy MUST be at the expanded receiver of the output note.
ZIP 326 specifies how wallets spend from, and rebalance notes within, the Orchard pool after NU6.3 activation. 10 In particular, an Action that creates an output note carrying value pairs it with a dummy spent note at the same expanded receiver, and the dummy output note of an Action that spends a real note has a random note ciphertext.
This section is written as a set of changes relative to version 2026.7.0 of the protocol specification 11. They are applied in version 2026.8.0. 3 Each change applies from NU6.3 activation, and is marked as such in the specification.
Add:
Let \(\mathsf{pool} \;{\small ⦂}\; \{\mathsf{OrchardPool}, \mathsf{IronwoodPool}\}\) be the pool of this Action description.
Define an expanded receiver as in ZIP 2006, i.e. \((\mathsf{g_d}, \mathsf{pk_d})\) for a shielded payment address.
Add \(\mathsf{enableCrossAddress}\) to the components of an Action description, after \(\mathsf{enableOutputs}\kern-0.05em\textsf{,}\) with the description:
\(\mathsf{enableCrossAddress} \;{\small ⦂}\; \mathbb{B}\) is a flag that is set in order to enable the expanded receiver of the output note to be different from the expanded receiver of the input note in this Action.
In the description of the proof, and in the consensus rule requiring it to be valid, append \(\mathsf{disableCrossAddress}\) to the primary input, where \(\mathsf{disableCrossAddress} = 1 - \mathsf{enableCrossAddress}\kern-0.05em\textsf{.}\)
In the note on components that are encoded once for each pool, add \(\mathsf{enableCrossAddress}\) to the components listed.
Add the consensus rule:
[NU6.3 onward] \(\mathsf{vk}\) MUST be the verifying key identified by
OrchardCircuitVersion::PostNu6_3.
After the steps that construct the spend-related fields for a dummy Orchard-pool input note, add:
[NU6.3 onward] If \(\mathsf{enableCrossAddress} = 0\) for the Action, and its output note is not a dummy note, then the first two steps are replaced: \((\mathsf{ak}, \mathsf{nk}, \mathsf{rivk})\) and \((\mathsf{d}, \mathsf{pk_d})\) are instead the full viewing key and diversified payment address of the output note’s recipient, and the spend authorization signature for the Action is made with the corresponding spend authorizing key.
After the paragraph saying that a dummy Orchard-pool output note is sent to a random shielded payment address, add:
[NU6.3 onward] If \(\mathsf{enableCrossAddress} = 0\) for the Action, the dummy output note is instead sent to the expanded receiver of the Action’s spent note. If the spent note is not a dummy note, the transmitted note ciphertext of the dummy output note is filled with random bytes rather than being an encryption of its note plaintext. 10
Append to the primary input:
[NU6.3 onward] \(\mathsf{disableCrossAddress} \;{\small ⦂}\; \mathbb{B}\)
In its encoding, append \(\mathsf{disableCrossAddress} \pmod{q_{\mathbb{P}}}\) to the existing nine-element sequence, giving a sequence of type \(\mathbb{F}_{q_{\mathbb{P}}}^{10}\) from NU6.3 onward. Qualify the existing nine-element encoding with “[Pre-NU6.3]”.
After the “Enable output flag” condition, add:
Cross-address restriction [NU6.3 onward]: \(\mathsf{disableCrossAddress} = 0\kern-0.05em\textsf{,}\) or \(\mathsf{g_d^{old}} = \mathsf{g_d^{new}}\) and \(\mathsf{pk_d^{old}} = \mathsf{pk_d^{new}}\kern-0.05em\textsf{.}\)
In the combined version 5/6 transaction-format table, replace the list in the
Description column for flagsOrchard:
enableSpendsOrchardflag (bit \(0\kern-0.15em\) )enableOutputsOrchardflag (bit \(1\kern-0.15em\) )- Reserved, zeros (bits \(2..7\kern-0.15em\) ).
with:
enableSpendsflag (bit \(0\kern-0.15em\) )enableOutputsflag (bit \(1\kern-0.15em\) )- Bit \(2\kern-0.05em\textsf{:}\) reserved (v5) or
enableCrossAddress(v6), always \(0\)- Reserved, zeros (bits \(3..7\kern-0.15em\) ).
In the Description column for flagsIronwood, the version-6-only field added
by ZIP 229 4, replace:
enableSpendsIronwoodflag (bit \(0\kern-0.15em\) )enableOutputsIronwoodflag (bit \(1\kern-0.15em\) )- Reserved, zeros (bits \(2..7\kern-0.15em\) ).
with:
enableSpendsflag (bit \(0\kern-0.15em\) )enableOutputsflag (bit \(1\kern-0.15em\) )enableCrossAddressflag (bit \(2\kern-0.15em\) )- Reserved, zeros (bits \(3..7\kern-0.15em\) ).
Add:
[NU6.3 onward] A coinbase transaction MUST NOT have any Orchard-pool Action descriptions.
[NU6.3 onward] \(\mathsf{v^{balanceOrchard}}\) MUST be nonnegative.
After the rule that the reserved bits \(2..7\) of the flagsOrchard field MUST be
zero, add:
[NU6.3 onward] In a version 6 transaction, bit 2 of
flagsOrchardencodes \(\mathsf{enableCrossAddress}\) for the Orchard-pool Actions; it is covered by the preceding rule, and so is \(0\kern-0.05em\textsf{.}\)
In the Consensus Rules section, replace
Note that the reserved-bit rules above leave bit 2 of
flagsOrchardreserved as 0 (bits 2..7), while bit 2 offlagsIronwoodis theenableCrossAddressbit (reserved bits 3..7); the Orchard pool’s cross-address restriction is enforced for its Actions from NU6.3 onward as specified in [ZIP 2006].
with
Note that bit 2 of
flagsOrchardis required to be 0 by the reserved-bit rule above (bits 2..7), independent of the transaction version: in a version 5 transaction it is still reserved, and in a version 6 transaction it encodesenableCrossAddress. Bit 2 offlagsIronwoodis theenableCrossAddressbit (reserved bits 3..7). The Orchard pool’s cross-address restriction is enforced for its Actions from NU6.3 onward as specified in [ZIP 2006].
The flag is encoded in the enabled sense: bit 2 is 1 when cross-address transfers are enabled, the normal case for the Ironwood pool. With the opposite sense, a post-NU6.3 Orchard-pool spend would have to set bit 2 to 1. Signers that treat bit 2 as a reserved zero bit, such as hardware wallet firmware that has not been upgraded, would then be unable to produce valid Orchard-pool spends. In the enabled sense, the restricted state is bit 2 = 0, which is exactly what such signers already produce.
Within the circuit, the primary input is the negated value \(\mathsf{disableCrossAddress} = 1 - \mathsf{enableCrossAddress}\kern-0.05em\textsf{.}\) Then a condition of the form “\(\mathsf{disableCrossAddress} = 0\) or \(a = b\)” is enforced directly by the constraint \(\mathsf{disableCrossAddress} \cdot (a - b) = 0\kern-0.05em\textsf{.}\)
The Orchard pool and the Ironwood pool use the same circuit and verifying
key from NU6.3 activation. They are distinguished by their bundles, note
commitment trees, nullifier sets, and chain value pools, and by the value of
\(\mathsf{enableCrossAddress}\kern-0.05em\textsf{.}\) This avoids a separate circuit for the restricted
pool. It also leaves open the option of enforcing similar restrictions on the
Ironwood pool in future, should there be a reason to. This intentionally
mirrors the design of the enableSpends and enableOutputs flags.
In the circuit, the points of the expanded receivers are witnessed in affine coordinates, and the condition is enforced coordinate-wise: for each \(\mathsf{P} \in \{\mathsf{g_d}, \mathsf{pk_d}\}\) and each coordinate \(\mathsf{c} \in \{x, y\}\kern-0.05em\textsf{,}\) \(\mathsf{disableCrossAddress} \cdot (\mathsf{c}(\mathsf{P^{old}}) - \mathsf{c}(\mathsf{P^{new}})) = 0\kern-0.05em\textsf{.}\) Both coordinates are needed.
The cross-address restriction alone does not stop new value from entering the Orchard pool, since an Action can send value to its own spent note’s address. The rule that \(\mathsf{v^{balanceOrchard}}\) is nonnegative, and the rule that coinbase transactions have no Orchard-pool Actions, close that path.
This ZIP was deployed with NU6.3 6, which activated on Mainnet at block height 3428143.
orchard crate, version 0.16.0, implements the enableCrossAddress flag,
its encoding, and the cross-address restriction in the Action circuit.
12Information on BCP 14 — “RFC 2119: Key words for use in RFCs to Indicate Requirement Levels” and “RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words” ↩︎
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.2.3: Orchard Key Components ↩︎
ZIP 257: Deployment of the Orchard Temporary Vulnerability Mitigation and NU6.2 Network Upgrade ↩︎
ZIP 326: NU6.3 Consequences for Wallets, section “Spending from and rebalancing within the Orchard pool” ↩︎