ZIP: 326
Title: NU6.3 Consequences for Wallets
Owners: Daira-Emma Hopwood <[email protected]>
Status: Draft
Category: Wallet
Created: 2026-06-30
License: MIT
Discussions-To: <https://github.com/zcash/zips/issues/1318>

Terminology

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 “expanded receiver” and “cross-address restriction” are to be interpreted as described in ZIP 2006. 7

The terms “quantum spending key” (\(\mathsf{qsk}\kern-0.15em\) ), the flag \(\mathsf{use\_qsk}\) that affects derivation of viewing keys and addresses, and the term “recoverable note” are to be interpreted as described in ZIP 2005. 8 (Recall from that ZIP that \(\mathsf{use\_qsk}\) does not determine whether a note is quantum-recoverable — every Ironwood-pool note is recoverable and no Orchard-pool note is, regardless of \(\mathsf{use\_qsk}\).)

An “incoming viewing key” (\(\mathsf{ivk}\kern-0.15em\) ) is as described in § 3.1 ‘Payment Addresses and Keys’. 9 We distinguish the external and internal incoming viewing keys of an account, derived as described in ZIP 32. 10

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”. 11

Abstract

The NU6.3 network upgrade 6 introduces the Ironwood pool, an Orchard-protocol successor to the Orchard pool, that ensures quantum recoverability (ZIP 2005 8) and has supply integrity supported from the start by formal verification efforts 12.

The introduction of a new pool requires wallets to take account of the Orchard-to-Ironwood migration strategy specified in 13.

This ZIP specifies the consequences for wallets of several other changes made in NU6.3:

Motivation

For interoperability with existing addresses, Ironwood-pool notes are allowed to use \(\mathsf{use\_qsk} = \textsf{false}\) keys as well as \(\mathsf{use\_qsk} = \textsf{true}\) keys.

In ZIP 2005, the incoming viewing key, the diversifier key, and the outgoing viewing key are derived through \(\mathsf{rivk}\kern-0.05em\textsf{,}\) which depends on \(\mathsf{use\_qsk}\kern-0.05em\textsf{.}\) Consequently the addresses an account exposes, and the \(\mathsf{ivk}\) needed to trial-decrypt notes sent to them, also depend on \(\mathsf{use\_qsk}\kern-0.05em\textsf{.}\) A wallet that did not know which value applied (for example after restoring from seed) would therefore have to trial-decrypt each Ironwood-pool note ciphertext against four incoming viewing keys: the external and the internal, each for \(\mathsf{use\_qsk} \in \{\textsf{false}, \textsf{true}\}\kern-0.05em\textsf{.}\)

A wallet that performed its own key derivation always knows which \(\mathsf{use\_qsk}\) value is used, but that value is not traditionally stored with wallet recovery information, and requiring the user to record it would worsen the recovery user experience.

This ZIP specifies restrictions on key generation, key exposure, and sending Orchard-pool funds, and the scanning rules that they enable. These 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.

This ZIP also specifies how wallets spend from, and rebalance notes within, the Orchard pool under the restrictions that NU6.3 places on that pool. 7

Specification

Scanning for Ironwood-pool and Orchard-pool notes

A receiver, and the corresponding incoming viewing key, is scoped to the Orchard protocol, not to a pool: the same \(\mathsf{ivk}\) is used to trial-decrypt both Orchard-pool and Ironwood-pool note ciphertexts.

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.

Restrictions on key generation and exposure

The following restrictions, together with the restrictions on sending, make the scanning optimizations below sound. They concern only how a wallet generates keys and exposes receivers and viewing keys; they are not consensus rules.

To make these restrictions easier to satisfy, a production wallet MUST NOT generate \(\mathsf{use\_qsk} = \textsf{true}\) keys at all before NU6.3 has activated on Mainnet. It follows that:

Rationale for restrictions on key generation and exposure

Rationale

Orchard-pool notes do not benefit in any way from \(\mathsf{use\_qsk} = \textsf{true}\kern-0.05em\textsf{;}\) they are never recoverable in any case.

If a \(\mathsf{use\_qsk} = \textsf{true}\) account were able to hold Orchard-pool funds, that case would need to be handled by scanning the Orchard pool with \(\mathsf{use\_qsk} = \textsf{true}\kern-0.05em\textsf{,}\) or else the funds would be lost. Neither the extra scanning cost nor the possibility of losing funds are at all desirable.

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 cross-address restriction 7 requires the spend side of an action to be at the same expanded receiver as its output side. Consensus cannot ensure that a \(\mathsf{use\_qsk} = \textsf{true}\) account receives no Orchard-pool funds: any party that knows the account’s spending key can send funds to it regardless of the cross-address restriction, since the spend side of the action can be a dummy. However, control over the spending key is something that wallets can enforce.

Scan ranges

Under the restrictions above and the restrictions on sending, a wallet can reduce its scanning load and complexity:

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 \(\mathsf{use\_qsk} = \textsf{true}\) incoming viewing keys. However, because a conservatively early birthday height has traditionally been safe, such an optimization would not be safe. A \(\mathsf{use\_qsk} = \textsf{true}\) account whose recorded birthday height was set too early would have its \(\mathsf{use\_qsk} = \textsf{true}\) Ironwood-pool notes skipped. So the optimization is not specified.

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 \(\mathsf{use\_qsk}\) \(\mathsf{ivk}\) scope Block range to scan
Orchard \(\textsf{false}\) internal birthday to (Orchard-pool balance known-zero after NU6.3)
Orchard \(\textsf{false}\) external birthday to NU6_3ActivationHeight − 1
Orchard \(\textsf{true}\) 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:

First-funds scanning in the Ironwood pool

Under the rules so far, a wallet that does not yet know its \(\mathsf{use\_qsk}\) value has to scan the Ironwood pool with up to four incoming viewing keys, until it first receives funds there. We describe an optimization for the Ironwood pool below. (It could be applied separately to the Orchard pool, but that is less helpful, because the other rules almost subsume the benefit.)

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. That input 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. That transaction 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 \(\mathsf{use\_qsk}\) values that could have been used by the account: either a singleton set, when the wallet knows its \(\mathsf{use\_qsk}\) value, or \(\{\textsf{false}, \textsf{true}\}\) when it does not (for example when restoring from seed). The resulting scanning algorithm, applied to each account, is:

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):

Scanning when restoring from seed

The only situation in which a wallet does not know an account’s \(\mathsf{use\_qsk}\) value is restoring from seed, since \(\mathsf{use\_qsk}\) is not stored with wallet recovery information. In that situation the wallet MUST take initial_use_qsk_set to be \(\{\textsf{false}, \textsf{true}\}\) and scan with both values until the locking rule narrows it. This must be done even if the wallet implementation always makes a fixed \(\mathsf{use\_qsk}\) choice itself, because restoration must work across wallet implementations: a seed created by one wallet may be restored into another.

Outgoing-note detection

The outgoing viewing key \(\mathsf{ovk}\) (and its internal counterpart) is likewise derived through \(\mathsf{rivk}\) and so also depends on \(\mathsf{use\_qsk}\kern-0.05em\textsf{.}\) A wallet that detects its own outgoing notes via \(\mathsf{ovk}\) uses the \(\mathsf{ovk}\) for the same per-account \(\mathsf{use\_qsk}\) value, so the considerations above apply unchanged. No additional \(\mathsf{use\_qsk}\) dimension is introduced beyond the \(\mathsf{ivk}\) scanning described here.

Receiving funds into the Ironwood pool

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. 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.

Restrictions on sending

The following restrictions concern where a wallet sends funds:

The first two restrictions, together with the rule above against sending funds to an internal address of another account, make the scan ranges above sound. The first also protects privacy. A note that carries value at an external receiver has a real note ciphertext, which anyone holding that receiver’s \(\mathsf{ivk}\) can decrypt. That includes a quantum adversary that has recovered the \(\mathsf{ivk}\) from a published address, whereas internal receivers are never published.

The third restriction follows from the first for the wallet’s own external receivers. For a receiver of another party, it is needed to comply with the consensus rules: after NU6.3 activation, the cross-address restriction 7 makes an Orchard-pool output carrying funds to that receiver invalid.

Migrating a wallet’s own Orchard-pool funds to the Ironwood pool is specified in ZIP 318. 13

Spending from and rebalancing within the Orchard pool

After NU6.3 activation, every Orchard-pool Action is subject to the cross-address restriction: the expanded receiver of its output note must equal the expanded receiver of its spent note. 7 A wallet therefore builds Orchard-pool Actions of two kinds:

A transaction that spends from the Orchard pool uses spend Actions. A transaction that keeps change in the Orchard pool, or rebalances notes within it (for example as note preparation for migration 13), also uses output Actions.

A wallet SHOULD NOT spend a real note and create an output note that carries value in the same Action. Such an output note has a real note ciphertext, which would let a party holding the receiver’s \(\mathsf{ivk}\) link the spend to the receiver, as described for dummy output notes below.

Dummy spent notes

The dummy spent note of an output Action is constructed as in § 4.8.3 ‘Dummy Notes (Orchard)’ 16, except for its key and address. Instead of a full viewing key and address generated from a random spending key, it uses the account’s own full viewing key and the diversifier of the output’s receiver. Its value is zero, so its Merkle path is not checked. Its spend authorization signature is made with the account’s spend authorizing key. So a wallet can only create an output Action at a receiver of its own.

Dummy output notes

For the dummy output note of a spend Action, 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 \(\mathsf{ivk}\kern-0.05em\textsf{,}\) in the same Action that carries the spend’s nullifier. Any party holding that \(\mathsf{ivk}\) could then link the nullifier to the address, and so detect the spend. That includes a quantum adversary, which can recover the \(\mathsf{ivk}\) from the published (diversified) address — although it cannot feasibly recover the nullifier key. Randomizing enc_ciphertext prevents this. The note and its commitment are unchanged.

An output note that carries value keeps a real encryption, so that the wallet can recover the note from the chain, for example after restoring from seed.

In a Partially Created Zcash Transaction (PCZT) 17, the dummy output still carries its explicit recipient, value, and rseed, but no user-supplied address. A signer therefore recovers nothing from the ciphertext, and reads a value of zero. So it classifies the output as a tolerable dummy output, rather than reconstructing an expected ciphertext and rejecting the mismatch.

Value balance and fees

After NU6.3 activation, valueBalanceOrchard MUST be nonnegative 7. So the total value of a transaction’s Orchard-pool output notes cannot exceed the total value of the Orchard-pool notes that it spends. A transaction that rebalances notes within the Orchard pool cannot add value to them from another pool; it can pay its fee from the value of the notes that it spends.

Rationale

The restrictions on key generation, key exposure, and sending Orchard-pool funds are chosen so that the only situation requiring both \(\mathsf{use\_qsk}\) values to be scanned is restoration from seed, and so that no scanning rule relies on the recorded birthday height being accurate as an upper bound. The latter is essential: it has always been safe to set a conservatively early birthday height, and the restore-from-seed case —the one case in which \(\mathsf{use\_qsk}\) is unknown— is also the case in which the birthday height is least reliable. Pruning \(\mathsf{use\_qsk} = \textsf{true}\) Ironwood-pool scanning by birthday height would therefore be unsafe in exactly the case it would apply, so it is not specified.

Requiring \(\mathsf{use\_qsk} = \textsf{true}\) keys not to be generated before NU6.3 Mainnet activation, rather than merely recommending it, keeps the Orchard pool free of \(\mathsf{use\_qsk} = \textsf{true}\) funds without relying on senders' behaviour, and bounds the birthday height of every \(\mathsf{use\_qsk} = \textsf{true}\) account.

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.

Deployment

This ZIP describes wallet behaviour consequent on the deployment of NU6.3. 6 The restrictions on generating and using \(\mathsf{use\_qsk} = \textsf{true}\) keys take effect immediately; the scanning rules apply from NU6.3 activation.

References


  1. 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”  ↩︎

  2. ZIP 200: Network Upgrade Mechanism  ↩︎

  3. Zcash Protocol Specification, Version 2026.8.0 [NU6.3] or later  ↩︎

  4. Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.12: Mainnet and Testnet  ↩︎

  5. ZIP 229: Version 6 Transaction Format  ↩︎

  6. ZIP 258: Deployment of the NU6.3 Network Upgrade  ↩︎

  7. ZIP 2006: Restricting Transfers into the Orchard Pool  ↩︎

  8. ZIP 2005: Ironwood Quantum Recoverability  ↩︎

  9. Zcash Protocol Specification, Version 2026.8.0 [NU6.3] or later. Section 3.1: Payment Addresses and Keys  ↩︎

  10. ZIP 32: Shielded Hierarchical Deterministic Wallets  ↩︎

  11. ZIP 307: Light Client Protocol for Payment Detection  ↩︎

  12. The Ironwood Book  ↩︎

  13. ZIP 318: Orchard to Ironwood Migration  ↩︎

  14. ZIP 2005: Ironwood Quantum Recoverability, section “Usage with FROST”  ↩︎

  15. ZIP 316: Unified Addresses and Unified Viewing Keys  ↩︎

  16. Zcash Protocol Specification, Version 2026.8.0 [NU6.3] or later. Section 4.8.3: Dummy Notes (Orchard)  ↩︎

  17. zcash/zips issue #693: Standardize a protocol for creating shielded transactions offline  ↩︎