ZIP: 326
Title: NU6.3 Consequences for Wallets
Owners: Daira-Emma Hopwood <daira@jacaranda.org>
Status: Draft
Category: Wallet
Created: 2026-06-30
License: MIT
Discussions-To: <https://github.com/zcash/zips/issues/1318>
The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" 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 "Mainnet" and "Testnet" are to be interpreted as described in § 3.12 ‘Mainnet and Testnet’. 4
The terms "Orchard protocol", "Orchard pool", "Ironwood pool", "Orchard-pool Action", and "version 6 transaction" are to be interpreted as described in 5. NU6.3 and its activation height are as defined in 6.
The terms "quantum spending key" (
An "incoming viewing key" (
The "birthday height" of an account is a lower bound, known to the wallet, on the height of the first block in which the account could have received funds. A correctly recorded birthday height is at or below the height at which the account's keys were first usable; it has traditionally been safe for a wallet to use a conservatively early birthday height.
A "light wallet" is a wallet that does not validate the consensus rules itself. It requires the assistance of a light wallet server in order to learn of relevant transactions by trial-decrypting (scanning) "compact ciphertexts". 10
The NU6.3 network upgrade 6 introduces the Ironwood pool, an Orchard-protocol successor to the Orchard pool, that ensures quantum recoverability (ZIP 2005 7) and has supply integrity supported from the start by formal verification efforts 11.
The introduction of a new pool requires wallets to take account of the Orchard-to-Ironwood migration strategy specified in 12.
This ZIP specifies the consequences for wallets of several other changes made in NU6.3:
- ZIP 2005 introduces a
$\mathsf{use_qsk}$ key distinction, which affects how viewing keys and addresses are derived 13. In order to more efficiently support scanning for incoming Ironwood-pool funds, we place constraints on how$\mathsf{use_qsk}$ is chosen and describe optimizations to the scanning algorithm. - We also explain how scanning for Orchard-pool funds should be handled across the upgrade.
- We describe how wallets should randomize note ciphertexts when spending under the Orchard pool's post-NU6.3 same-address restriction, in order to maintain privacy against quantum and discrete-log-breaking adversaries.
For interoperability with existing addresses, Ironwood-pool notes are allowed to use
In ZIP 2005, the incoming viewing key (and the diversifier key and outgoing viewing key) are
derived through
A wallet that performed its own key derivation always knows which
This ZIP specifies key-generation restrictions, and the scanning rules they enable, that keep scanning cost close to its pre-NU6.3 level in the common cases while remaining correct in the restore-from-seed case.
The recommendations here assume that pools and activations refer to Mainnet, and that a secure wallet either does not support Testnet, or strictly segregates key material between Testnet and Mainnet.
A receiver, and the corresponding incoming viewing key, is scoped to the Orchard protocol, not
to a pool: the same
We assume that secure wallets do not expose internal receivers or viewing keys to any party that is not subject to the same restrictions as the wallet itself. A wallet MUST NOT use the fact that it knows the internal addresses of all of its accounts to send funds from one account to an internal address of another.
The following restrictions make the scanning optimizations below sound. They concern only how a wallet generates and uses keys; they are not consensus rules.
- For each ZIP 32 account, a wallet MUST generate keys with the same value of
$\mathsf{use_qsk}$ — either all$\mathsf{use_qsk} = \textsf{true}$ or all$\mathsf{use_qsk} = \textsf{false}$ , never a mixture. - A wallet MUST NOT send funds to any external receiver (including its own) in the Orchard pool after NU6.3 activation.
- Any wallet that generates
$\mathsf{use_qsk} = \textsf{true}$ keys:- MUST NOT send Orchard-pool funds to them at any point; and
- MUST NOT expose the corresponding Orchard-protocol receivers or viewing keys before NU6.3
activation, since otherwise Orchard-pool funds could be sent to a
$\mathsf{use_qsk} = \textsf{true}$ account, which would incur extra scanning cost or risk loss of funds.
To make these restrictions easier to satisfy, a production wallet MUST NOT generate
- the mistake of exposing the corresponding receivers or viewing keys before NU6.3 Mainnet activation is excluded; and
- any account that creates
$\mathsf{use_qsk} = \textsf{true}$ keys will have a birthday height after NU6.3 Mainnet activation.
Rationale
Orchard-pool notes do not benefit in any way from
If a
Exposing the address or viewing key is not enough to send Orchard-pool funds to a
$\mathsf{use_qsk} = \textsf{true}$ account after NU6.3 activation: at that point a sender would need the spending key, because the Orchard pool's same-address restriction 14 requires the spend side of an action to be at the same expanded receiver as its output side, and control over the spending key is something that wallets can enforce. This property cannot be enforced by consensus — any party that knows a$\mathsf{use_qsk} = \textsf{true}$ spending key can send funds to it regardless of the same-address restriction, since the spend side of the action can be a dummy — but the wallet restrictions above are sufficient.
Under the restrictions above, a wallet can reduce its scanning load and complexity:
-
Orchard-pool note ciphertexts SHOULD NOT be scanned with
$\mathsf{use_qsk} = \textsf{true}$ incoming viewing keys. -
For an account with birthday height after NU6.3 activation, the account will not have received Orchard-pool funds, so Orchard-pool note ciphertexts SHOULD NOT be scanned at all, with either the internal or the external
$\mathsf{ivk}$ . -
For an account with birthday height before NU6.3 activation, Orchard-pool note ciphertexts SHOULD NOT be scanned with the external
$\mathsf{use_qsk} = \textsf{false}$ $\mathsf{ivk}$ after NU6.3 activation. (A transaction containing such an output would have to have been created by a non-conformant wallet that sent funds to an external Orchard-pool receiver after NU6.3.) -
If an account's Orchard-pool balance is known to be zero at a block at or after NU6.3 activation, then Orchard-pool note ciphertexts SHOULD NOT be scanned in any block that descends from that block in the block tree. (After NU6.3, no new value may enter the Orchard pool 6, so a zero balance there cannot subsequently become non-zero.)
Note that, for an account with birthday height before NU6.3, it might seem as though scanning
could be further optimized by not scanning the Ironwood pool with
These rules are summarized in the following table. NU6_3ActivationHeight is the NU6.3 activation
height 6; later_of denotes the maximum of two heights.
| Pool |
|
Block range to scan | |
|---|---|---|---|
| Orchard | internal | birthday to (Orchard-pool balance known-zero after NU6.3) | |
| Orchard | external | birthday to NU6_3ActivationHeight − 1 | |
| Orchard | either | nothing | |
| Ironwood | either* | either* | later_of(birthday, NU6_3ActivationHeight) to tip |
"either*" means subject to the first-funds optimization below.
The following additional rule does not fit well into the table:
- Once a wallet that is scanning sees that it has received funds in either the Orchard pool or
the Ironwood pool with a given
$\mathsf{use_qsk}$ value, it SHOULD stop scanning with the other$\mathsf{use_qsk}$ value. (Because$\mathsf{use_qsk}$ is uniform per account, observing funds under one value is reliable evidence of the account's$\mathsf{use_qsk}$ choice; this remains true even if the transaction that provided the evidence is later reorged away, since it was consensus-valid on some chain.)
The rules so far do not, on their own, save a wallet that does not yet know its
While the wallet knows that no Ironwood-pool funds have been received by the account before the
block being scanned, it only needs to use the external incoming viewing keys, with one exception: if
the transaction's valueBalanceIronwood is negative (value is entering the Ironwood pool), then
the transaction has an input to the pool via the value balance, which might go to an
Ironwood-pool note at an internal receiver of this wallet. (No other party can address an
internal receiver, so before the account has received Ironwood-pool funds, an internal note for
it can only arise from the account's own transaction, which must be funded either by spending
existing Ironwood-pool funds — of which there are none yet — or by such an inflow.)
Let initial_use_qsk_set be the set of
let mut use_qsk_set : Set<bool> := initial_use_qsk_set
// Incoming shielded funds cannot be spent in the same block in which they are
// received. The optimization can therefore be applied up to *and including* this
// block (or the NU6.3 activation block, if later).
let mut no_ironwood_until_after : Height = later_of(birthday, NU6_3ActivationHeight)
for height in arbitrary order {
// Account for reorgs: retain the conclusion that no Ironwood funds have been
// received only up to the fork height (the highest block still on the
// canonical chain), floored at the NU6.3 activation block.
if (a reorg has occurred whose last unchanged block is at fork_height)
and fork_height < no_ironwood_until_after {
no_ironwood_until_after := later_of(fork_height, NU6_3ActivationHeight)
}
let received_ironwood_in_block : bool := false
for each Ironwood output in each transaction at height {
let scope_set :=
if valueBalanceIronwood < 0 or height > no_ironwood_until_after {
{internal, external}
} else {
{external}
}
for (scope in scope_set, use_qsk in use_qsk_set) {
if the output decrypts using ivk(scope, use_qsk) {
record the funds
use_qsk_set := {use_qsk}
received_ironwood_in_block := true
}
}
}
if !received_ironwood_in_block and height == no_ironwood_until_after {
// No Ironwood funds were received in this or any prior block, so the
// optimization can be applied to the next block.
no_ironwood_until_after := height + 1
}
}
A wallet MAY scan in any order. Only scanning forward from the birthday height can take full advantage of the optimization, but it is possible to scan forward and backward in parallel.
As further optimizations (not shown in the pseudocode):
-
A wallet can also track whether the account has received any funds at all. This avoids having to conservatively scan other users' migration transactions with
scope_setset to${\textsf{internal}, \textsf{external}}$ . -
If the wallet is scanning for Orchard-pool funds in parallel — which, as specified above, uses only
$\mathsf{use_qsk} = \textsf{false}$ — it can narrowuse_qsk_setto${\textsf{false}}$ whenever it sees any.
The only situation in which a wallet does not know an account's initial_use_qsk_set to be
The outgoing viewing key
Because receivers are scoped to the Orchard protocol rather than to a pool (see above), an Orchard-protocol receiver that was exposed before NU6.3 can receive Ironwood-pool funds after NU6.3, and a wallet that supports NU6.3 will scan for them as specified above. Once an Orchard-protocol receiver has been exposed, no party can be prevented from sending funds to it in either pool, subject to the consensus rules.
The send-path change by which a wallet chooses to route new payments to an Orchard-protocol receiver into the Ironwood pool rather than the Orchard pool is a general send-path concern, and is out of scope here; see the migration ZIP. 12
Note: The primary home for this requirement is expected to be the action-circuit update ZIP 14, which introduces the same-address restriction that causes it. It is stated here until that ZIP is written, after which this section will cross-reference it.
After NU6.3, every Orchard-pool Action is subject to the same-address restriction: the output expanded receiver [TODO define] of each Action must equal its spent-note expanded receiver. 14 To spend in the Orchard pool under this restriction, a wallet pairs each real spend with a fabricated, zero-valued output addressed to the spent note's own receiver.
For such a fabricated output, the wallet MUST fill the note ciphertext enc_ciphertext with
random bytes, rather than with a real encryption of the note plaintext to the spent note's
receiver.
If enc_ciphertext were instead a real encryption, it would trial-decrypt under the spent note's
receiver's enc_ciphertext
prevents this. The note and its commitment are unchanged.
In a Partially Created Zcash Transaction (PCZT) 15, the fabricated output still carries its
explicit recipient, value, and rseed, but no user-supplied address; a signer therefore recovers
nothing from the ciphertext, reads a value of zero, and classifies the output as a tolerable dummy
output, rather than reconstructing an expected ciphertext and rejecting the mismatch.
The key-generation restrictions are chosen so that the only situation requiring both
Requiring
The first-funds optimization rests on the observation that, before an account has received any Ironwood-pool funds, an internal Ironwood-pool note for it can only be created by the account's own transaction, and only when value enters the pool via the value balance. The "up to and including this block" treatment is sound because incoming shielded funds cannot be spent in the block in which they are received.
The reorg handling retains the "no funds received" conclusion only up to the fork height, not the chain tip: a shallow reorg at the scanning frontier can replace blocks below the watermark while leaving the chain the same length or longer, so keying the rollback on the tip height would leave a stale watermark and could cause an internal Ironwood-pool note on the new branch to be missed.
This ZIP describes wallet behaviour consequent on the deployment of NU6.3. 6 The
restrictions on generating and using
Footnotes
-
Information 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] or later ↩
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.12: Mainnet and Testnet ↩
-
ZIP 258: Deployment of the NU6.3 Network Upgrade ↩ ↩2 ↩3 ↩4 ↩5
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3] or later. Section 3.1: Payment Addresses and Keys ↩
-
ZIP 2005: Ironwood Quantum Recoverability, section "Usage with FROST" ↩
-
ZIP 2006: Restricting Transfers into the Orchard Pool ↩ ↩2 ↩3
-
zcash/zips issue #693: Standardize a protocol for creating shielded transactions offline ↩