ZIP 318 — Orchard to Ironwood Migration

Draft

Terminology

The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", "MAY", and "RECOMMENDED" in this document are to be interpreted as described in BCP 14 1 when, and only when, they appear in all capitals.

The terms "Orchard pool" and "Ironwood pool" are to be interpreted as described in ZIP 229 2. Following the convention in the protocol specification 3, we use slanted text to refer to pool names, in order to more clearly distinguish them from shielded protocols.

The term "network upgrade" is to be interpreted as described in ZIP 200. 4

The terms "Mainnet" and "Testnet" are to be interpreted as defined in the Zcash protocol specification. 5

The terms "trusted TXO", "untrusted TXO", "known-spendable", and "confirmed-spendable" are to be interpreted as defined in ZIP 315. 6

The terms below are to be interpreted as follows:

Migration
The process by which a wallet transfers a user's funds from the Orchard pool to the Ironwood pool.
Pool-crossing transfer
A transaction that moves value from one shielded pool to another. The net amount crossing between the pools is revealed on-chain. 7
Note preparation
A wallet-internal send-to-self transaction (or set of such transactions) whose purpose is to restructure the wallet's existing notes — dividing larger notes, combining smaller ones, or both — into notes that exactly fund the planned migration transactions, so that the entire migration schedule can be pre-signed and broadcast without waiting for change from earlier transactions to confirm.
Migration transaction (or part)
A single pool-crossing transfer that moves part of the user's Orchard-pool balance to the Ironwood pool as part of a migration. In the recommended scheme each migration transaction spends exactly one Orchard-pool note and moves exactly one denomination to a single Ironwood output.
Denomination
A canonical amount of the form n×10k ZEC with n∈{1,2,5} (for example 100, 50, 20, 10, 5, 2, 1, 0.5 ZEC), bounded below by the maximum residual value and above by a cap specified in Amount selection, canonical quantization.
Maximum residual value
The value below which any remainder of the balance is left unmigrated, in order to prevent small, user-identifying amounts from being leaked in pool-crossing transactions.
Reference block
A unit of block count equal to one block at the 75-second block target spacing in effect before NU7. The block-count parameters of this ZIP that represent durations are stated in reference blocks; see Block counts across the NU7 block spacing change.
Anchor-height bucket (boundary)
A shared, network-wide interval of block heights delimited by boundaries: blocks whose height is a multiple of a network-wide modulus M (provisionally 144 reference blocks, scaled at NU7 as specified in Block counts across the NU7 block spacing change). The anchor height of a migration transaction is drawn at random from among past boundaries rather than derived from the wallet's own chain tip, so that the anchor does not reveal when a particular wallet last synchronized.
Cohort
The set of all migration transactions, from all wallets, that share the same boundary anchor.
Multiplicity (k)
The number of migration transactions a single wallet contributes to one cohort.
Migration schedule
The ordered set of migration transactions, together with their scheduled broadcast heights, that a wallet plans in order to complete a migration.
Background scheduling
The use of platform mechanisms (for example, BGTaskScheduler on iOS or WorkManager on Android) to broadcast a migration transaction at an approximate future time without requiring the user to have the application in the foreground.

Abstract

This ZIP specifies best practices for wallets migrating funds from the Orchard pool to the Ironwood pool introduced by the NU6.3 network upgrade 8. It is written with light client wallets in mind, because their reliance on a light wallet server and on mobile background scheduling makes the privacy and reliability concerns most acute, but the same practices apply to desktop and full-node wallets.

Because a pool-crossing transfer reveals the migrated amount on-chain, directly sending from one pool to another can reveal the wallet balance. A naive migration risks linking transfers to a single wallet and to the user's activity, again potentially revealing balance, and also risking that migration transactions could be linked to personally identifying information such as IP addresses. To mitigate this, wallets should decompose the balance into notes with values drawn from a fixed set of denominations, which may then be sent across the turnstile as multiple transactions submitted to the chain over time using a network anonymity layer. In order to avoid linking transactions via anchor selection, proofs are made and updated against anchors drawn from shared, network-wide boundary heights, forming cohorts that mix many wallets and de-correlate transfers from when the user opens the application.

The migration runs as a two-phase process: a note-preparation phase followed by a scheduled transaction-creation phase. Migration transactions are pre-signed, persisted, and then have their proofs updated and are broadcast by best-effort background tasks, with a fallback that prompts the user on the next application open when a scheduled window is missed. Synchronization is kept decoupled in time from broadcast. This ZIP also specifies user-consent, network-privacy, progress-reporting, and error-handling requirements.

Motivation

The Ironwood pool, introduced by the NU6.3 network upgrade 8, enables verification of the Zcash shielded supply after NU6.2 mitigated a soundness flaw in the Orchard circuit 9. To that end, NU6.3 enables a rule that disables ordinary payments within the old Orchard pool while still permitting change 10; therefore, wallets are required to add functionality to migrate Orchard funds to the Ironwood pool.

Migrating funds between shielded pools is fundamentally different from spending within a single pool. Within a pool, both balances and the relationships between transactions remain private; a pool-crossing transfer, however, necessarily reveals the net amount crossing between the pools on-chain 7, so the only privacy levers available to a wallet are the shape of the amounts it migrates and the timing of their broadcast. If a wallet migrates a user's entire balance in a single transaction, that amount, its timing, and (via network-layer metadata) potentially the user's IP address are all exposed at once, and the transfer can be correlated with the wallet's other activity.

ZIP 315 6 already establishes that the user's consent is needed to reveal amounts publicly, and that wallets should minimize pool crossing. A mass migration event is a case where many users perform pool-crossing transfers in a relatively short window. This both creates an opportunity (many simultaneous migrations form an anonymity set) and a risk, because an observer who can distinguish one user's transfers from the crowd learns a great deal.

A precedent exists in ZIP 308 11, which defined a privacy-preserving procedure for the Sprout-to-Sapling migration by sending a bounded amount per period and hiding individual migration transactions among those of all migrating users. This ZIP adapts that idea to the light-client setting, where the wallet does not run a full validator, scheduling is constrained by mobile operating systems, and synchronization happens against a light wallet server whose operator is a potential observer. The security analysis of migration strategies is subtle, and the more obvious strategies leak significant information; this document records the analysis behind the chosen strategy, including several rejected alternatives (see Alternatives).

The goal of this ZIP is therefore to document how a light client wallet SHOULD perform the Orchard-to-Ironwood migration so that, by default, individual transfers are as hard as practical to correlate to a specific wallet or to the user's behaviour, while still completing the migration reliably and giving the user enough information to consent to any residual information leakage. The separate NU6.3 routing change — that new payments to Orchard receivers (inside existing unified addresses) are sent via the Ironwood pool — is specified in ZIP 326 12 and is out of scope here, although the migration destination is the same Ironwood receiver.

Requirements

  • The user can migrate Orchard-pool funds to the Ironwood pool, and is informed that using a privacy preserving migration method is necessary for the user to be able to preserve the privacy of transaction amounts when spending Orchard funds.

  • Before any funds leave the Orchard pool, the wallet obtains the user's consent to the public revelation of pool-crossing amounts, consistent with ZIP 315. 6

  • All Orchard-pool funds, except a residual value, are eventually transferred to the Ironwood pool.

  • The migration process, by default, preserves the following privacy invariants, to the extent practical on the target platform:

    • Transfer unlinkability. An observer cannot cluster the migration transactions of a single wallet — by value, structure, anchor, expiry, or timing — into a per-wallet set.

    • Behavioural de-correlation. Broadcasts of migration transactions are de-correlated from the user's interaction with the application and from wallet synchronization, so that transfers reveal neither when the user opens the application nor when the wallet has synchronized.

    • Balance privacy. Neither any single transfer nor the observable set of transfers reveals any wallet's total Orchard-pool balance.

    • Note-distribution privacy. The migration does not reveal the distribution of individual note values held by the wallet.

    • Holdings-concentration privacy. The migration does not reveal the number of users that hold funds in the Orchard pool or how funds are distributed between those users' holdings.

    • Migration-status privacy. A chain-observing adversary who knows the values of notes a wallet has received — including an adversary who has deliberately targeted a victim by sending notes of attacker-chosen values — cannot determine whether or when the wallet has migrated those funds.

  • The user can authorize the migration in a small number of signing sessions — a single session for typical balances and note distributions. Additional sessions are needed only when multi-layer note preparation is required and the wallet does not pre-sign the layers as a chain; this affects wallets holding very many notes and wallets with very large balances identically.

  • The migration completes reliably even when best-effort background scheduling does not run, without requiring the user to keep the application in the foreground.

  • Before the migration begins, the user is given an informed choice about network-layer privacy options (Tor, VPN, or Nym).

  • The wallet reports migration progress, and does not present the fallback (prompting on the next application open) as an error.

  • The migration adapts safely when the user spends Orchard-pool funds outside the migration, when scheduled transactions expire, or when the application is reinstalled.

Non-requirements

The following are explicitly not requirements of this flow:

  • Statelessness. The migration is permitted to rely on locally persisted state (the migration schedule and pre-signed transactions). It is not required to be reconstructible from on-chain data alone. (The tradeoff against a stateless, chain-recoverable design is discussed in Rationale for the stateful design.)

  • Multi-device and recovery continuity. A migration in progress is not required to be resumable on another device, nor after restoring from seed on a new device. A wallet that detects unspent Orchard-pool funds after such a restore may treat the situation as requiring a fresh migration.

  • Guaranteed background execution. Background scheduling is best-effort. Wallets are not required to guarantee that any transfer is broadcast without the user ever opening the application.

  • Network-layer anonymity. Network-layer anonymity (for example, Tor) is offered as an opt-in but is not mandated; the user MAY proceed without it but this option should not be recommended except in cases where the user is confident in the privacy provided by the attached lightwalletd server, e.g. Tor should not be required if the user connects to their own private lightwalletd.

  • Provably leak-free migration. The procedure does not have to provably leak no information; migration transactions need only be hidden among the population of canonical-shaped Orchard→Ironwood spends (see Rationale for the canonical transaction shape), and a small residual amount is left unmigrated.

Specification

Overview, two-phase architecture

A wallet performing a privacy-preserving migration SHOULD implement it as two phases:

  1. Note preparation (see Phase 1, Note preparation): quantization of the migratable balance into canonical denominations, followed by one or more wallet-internal send-to-self transactions that restructure the Orchard-pool balance into notes that exactly fund the planned migration transactions.

  2. Scheduled migration transactions (see Phase 2, Scheduled migration transactions): pre-signed pool-crossing transfers, each transferring a canonical denomination at a scheduled broadcast height with a boundary anchor, broadcast by best-effort background tasks.

A wallet MAY offer an immediate single-transfer migration in addition to the privacy-preserving flow (see Migration entry point and user consent), but it SHOULD present the privacy trade-off of doing so.

Block counts across the NU7 block spacing change

ZIP 218 13 reduces the block target spacing from 75 seconds to 25 seconds at NU7 activation. Let A be 𝖭𝖴𝟩𝖠𝖼𝗍𝗂𝗏𝖺𝗍𝗂𝗈𝗇𝖧𝖾𝗂𝗀𝗁𝗍, and let R be 𝖭𝖴𝟩𝖯𝗈𝖶𝖳𝖺𝗋𝗀𝖾𝗍𝖲𝗉𝖺𝖼𝗂𝗇𝗀𝖱𝖺𝗍𝗂𝗈=3, both as defined in ZIP 218 14. On a network with no NU7 activation height, take A=∞; then none of the rules below change any quantity.

The parameters PREP_MEAN_DELAY, PREP_MAX_DELAY, MEAN_DELAY, MAX_DELAY, the bucket modulus M, and EXPIRY_MODULUS represent durations. Each is stated in reference blocks and has the same value at all heights. A wallet MUST convert them to heights as follows, which scales them by R after activation as ZIP 218 recommends for block counts that represent durations

  • Delays. A specified delay d≥0 is denominated in reference blocks. The height reached by advancing d reference blocks from height h is

    h + d & \text{if } h + d \leq A \\ h + R \cdot d & \text{if } h \geq A \\ A + R \cdot (h + d - A) & \text{if } h < A < h + d\text{.} \end{cases}$$ A delay that crosses activation therefore keeps its length in time at the target spacing. Where delays accumulate, each height is $\mathsf{Advance}$ of the previous height by the next delay. ParseError: Expected 'EOF', got '&' at position 9: h + d &̲ \text{if } h +…
  • Boundaries. A height h is a boundary if and only if either h<A and h is a multiple of M, or h≥A and h is a multiple of R⋅M. Every boundary at or after A is also a multiple of M. All references to boundaries in this ZIP (the most recent boundary, the next boundary, and anchor ages counted in boundaries) refer to this set. ANCHOR_AGE_CAP is a number of boundaries, and is not scaled.

  • Expiry modulus. The expiry modulus at height h is

    \mathtt{EXPIRY\_MODULUS} & \text{if } h < A \\ R \cdot \mathtt{EXPIRY\_MODULUS} & \text{if } h \geq A\text{.} \end{cases}$$ It is used to compute the canonical expiry height as specified in [Canonical migration transaction structure](#canonicalmigrationtransactionstructure). ParseError: Expected 'EOF', got '&' at position 28: …PIRY\_MODULUS} &̲ \text{if } h <…

Block counts that do not represent durations, such as confirmation depths and mining margins, are not scaled.

Migration entry point and user consent

When a wallet detects a non-zero confirmed-spendable Orchard-pool balance once the NU6.3 network upgrade is scheduled or has activated, it SHOULD surface a dedicated migration entry point. (Surfacing the entry point before activation allows note preparation to begin early; see Phase 1, Note preparation.)

  • The entry point MUST display the Orchard-pool-specific balance at risk.

  • The wallet SHOULD offer the user a choice between:

    • Migrate immediately: a single pool-crossing transfer with no delay and minimal privacy; and
    • Migrate with privacy: the scheduled, quantized approach specified below.
  • Before any funds leave the Orchard pool, the wallet MUST present the full proposed migration schedule and obtain the user's confirmation. This confirmation constitutes the user's consent, required by ZIP 315 6, to the public revelation of the pool-crossing amounts.

  • Once the schedule is confirmed, individual transfers within it SHOULD be broadcast automatically when their scheduled broadcast height arrives, and SHOULD NOT require separate per-transfer confirmation.

To keep the choice tractable for users who may not know what is best, the wallet SHOULD minimize the number of privacy-tuning options exposed.

Phase 1, Note preparation

Note preparation begins with amount selection: quantization of the migratable balance into the canonical denominations that the Phase 2 migration transactions will transfer. Quantization is necessarily the first step, because the note-preparation transactions cannot be constructed without knowing the exact note values they must produce.

Amount selection, canonical quantization

The migratable Orchard-pool balance MUST be decomposed into a set of migration transactions, each transferring a single denomination. It is RECOMMENDED that the denominations be selected by canonical quantization rather than by random or arbitrary sizing. Canonical denominations collide across wallets and resist leaking information that can be used for wallet fingerprinting (see Why value collision provides privacy). A wallet MAY use a different amount-selection strategy provided it achieves the same effect, namely that migrated amounts collide across the migrating population rather than fingerprinting the wallet. Random arbitrary amounts do not achieve this and are a rejected alternative (see Alternatives).

  • Permitted denominations are the values v=n×10k ZEC with n∈{1,2,5} and MAX_RESIDUAL_VALUE ≤v< DENOM_CAP, where:

    • MAX_RESIDUAL_VALUE, the smallest permitted denomination, is 0.01 ZEC; the portion of the balance smaller than this value should not be decomposed and is left unmigrated as the residual.
    • DENOM_CAP is 10000 ZEC plus the canonical fee, making 10000 ZEC the largest pool-crossing denomination; equivalently, DENOM_CAP caps the funding-note values (denomination plus fee) produced by note preparation.
  • The balance is decomposed by decimal digit expansion of the integer- and fractional-ZEC digits, each digit expanding greedily into parts drawn from {5,2,1} times its place value (so a digit of 9 yields 5, 2, 2; 8 yields 5, 2, 1; 7 yields 5, 2; 6 yields 5, 1; 4 yields 2, 2; 3 yields 2, 1). Value in places above the largest denomination is decomposed into repeated maximal-denomination (10000 ZEC) parts. For example:

    123.45  ZEC -> [100, 20, 2, 1, 0.2, 0.2, 0.05]
    540     ZEC -> [500, 20, 20]
    25000   ZEC -> [10000, 10000, 5000]
    
  • The baseline quantization is a pure, deterministic function of the balance: there is no rare, high-entropy "remainder" output. Every emitted denomination is canonical and therefore collision-prone across wallets, whether the decomposition is deterministic or randomized (below). This is the key privacy property (see Privacy Implications).

  • A wallet MAY randomize the decomposition for improved balance hiding, substituting smaller denominations for the greedy choice. At each selection step the available options are the denominations n×10k, for the magnitude 10k of the value remaining, that do not exceed that value. Smaller denominations SHOULD be chosen with low frequency so that expected part counts stay small; provisionally: where all of {1,2,5}×10k are available, 1×10k is chosen with 10% frequency and 2×10k with 25% frequency (5×10k otherwise); where only {1,2}×10k are available, 1×10k is chosen with 25% frequency. Randomization changes only which canonical denominations are emitted, never the values themselves.

  • A wallet SHOULD exclude any preexisting unspent notes that already have quantized values from the computation of balance to be migrated; this situation can arise from a migration that is resumed after a wallet restore and such notes can be used in a Migration Transaction directly.

  • The quantization leaves a deterministic residual of less than MAX_RESIDUAL_VALUE (the balance modulo the smallest denomination) unmigrated; this residual remains in the Orchard pool at the end of the migration. Notes individually smaller than MAX_RESIDUAL_VALUE are nonetheless migratable, because note preparation may combine them; whether consuming a given small note is economic is a ZIP 317 marginal-fee consideration. 15

The fee for each part is in addition to its denomination (the visible Orchard-out value of a part is its denomination plus the canonical fee; equivalently the Ironwood output equals the denomination). Each part is funded by a single note of exactly this value, produced by note preparation.

Note preparation transactions

A migration transaction spends exactly one Orchard-pool note (see Canonical migration transaction structure). The purpose of the note-preparation transactions is to ensure that a note of exactly the right value exists for every part of the schedule, so that every part can be funded independently of the others and the entire schedule can be pre-signed at once. After NU6.3, anchors are no longer committed to by signature digests and therefore migration transactions may be signed as soon as preparation transactions that construct the notes being spent have been constructed. Only the proving step requires that the funding notes have been mined and that a candidate boundary anchor exists (see Anchor-height bucketing and cohorts). Whether preparation divides larger notes into smaller ones, combines smaller notes into larger ones, or both, is purely a function of the wallet's existing note distribution, and the distinction is not visible on-chain (see Rationale for the canonical transaction shape).

  • Note preparation MUST be implemented as one or more wallet-internal send-to-self transactions with no external receiver.

  • Each note-preparation transaction SHOULD use the canonical expiry height specified for migration transactions in Canonical migration transaction structure, computed from the note-preparation transaction's scheduled broadcast height.

  • For mobile wallets, proving time is a concern. For that reason, and also for indistinguishability, Note preparation transactions SHOULD be constructed such that each preparation transaction contains exactly 16 Orchard actions, padding if necessary to reach this threshold. Because a bundle's action count is the sum of its spends and outputs (each is paired with a dummy counterpart under NU6.3 16), a 16-action transaction can consume and produce at most 16 notes in total — for example, 15 spends and one output when consolidating, or one spend and 15 outputs when splitting.

  • The output notes of preparation are determined by the quantization above: one note per scheduled part, valued at exactly that part's denomination plus the canonical fee, together with at residual notes with cumulative value less than MAX_RESIDUAL_VALUE.

  • Where the number of notes involved exceeds what a single 16-action transaction can accommodate, preparation SHOULD be partitioned into multiple transactions with disjoint input notes wherever possible, so that the transactions can confirm in parallel rather than chaining through one another's outputs.

  • Partitioning with disjoint inputs is not always possible: a funding note that must combine value from more notes than one transaction can spend (consolidation), or a large note that must be divided into more funding notes than one transaction can produce (splitting), requires preparation to proceed in layers, with later preparation transactions spending the outputs of earlier ones. While all layers MAY be constructed and signed in a single session, each layer can be proved and broadcast only after the previous layer's outputs have been mined and witnessed, so each layer extends the preparation phase by mining and witness-synchronization latency — a handful of blocks, not an anchor-height bucket, since preparation transactions anchor at the wallet's ordinary near-tip depth (see below). The number of layers required is logarithmic in the number of notes consumed or produced, with base approximately 15 (the per-transaction fan-in or fan-out).

  • On-chain, layered splitting and layered consolidation are indistinguishable from each other and from parallel preparation of the same transaction count: preparation values and direction are fully shielded, so a large-balance wallet dividing a single note and a wallet consolidating many small notes present the same observable footprint (see Rationale for the canonical transaction shape).

  • A note-preparation transaction SHOULD anchor at the wallet's ordinary near-tip anchor depth (a small number of confirmations below the chain tip observed at proving time), not at a bucketed boundary height. The boundary-anchor machinery exists to protect the pool-crossing migration transactions, whose crossing values are public: their anchors must not reveal when a particular wallet synchronized. A preparation transaction is a fully shielded send-to-self — its values, direction, and linkage are hidden — so its anchor reveals only that some wallet recently synchronized, which broadcasting any transaction already reveals. Using the ordinary anchor keeps preparation proofs identical to those of routine shielded spends and lets successive layers proceed without waiting out boundary intervals.

  • The broadcasts of a wallet's preparation transactions SHOULD be temporally decoupled from one another: the delay from each preparation transaction to the next is drawn (using a CSPRNG) from an exponential distribution with mean PREP_MEAN_DELAY (16 reference blocks, about twenty minutes; 48 blocks after NU7), rounded to the nearest whole number of reference blocks, with any draw exceeding PREP_MAX_DELAY (96 reference blocks, about two hours; 288 blocks after NU7) discarded and redrawn. Each delay is converted to a height by 𝖠𝖽𝗏𝖺𝗇𝖼𝖾 (see Block counts across the NU7 block spacing change). A later layer's schedule begins after the previous layer's last scheduled transaction plus a small mining margin. Although preparation values are fully shielded, a burst of identically shaped padded transactions appearing at a single moment is itself a linkable cluster — by shape and timing — and such a burst would also visibly precede the wallet's scheduled transfers. The much tighter spacing relative to the transfers' MEAN_DELAY is possible precisely because no anchor bucketing constrains preparation, and keeps the preparation phase short while preventing the cluster.

  • If the wallet's existing notes already exactly fund every planned part, note preparation MAY be skipped.

  • Note preparation MAY be performed at any time after the user initiates the migration, including before the network upgrade that activates the Ironwood pool has finalized.

  • The wallet MUST wait until the boundary that closes the anchor-height bucket in which a note-preparation transaction was mined has passed before treating its output notes as available to fund the Phase 2 migration transactions. This is implied by the candidate anchor set (see Anchor-height bucketing and cohorts): no boundary at or after the notes' creation exists any earlier. Spending the notes across the turnstile sooner would require anchoring to a non-boundary tree state between the preparation transaction and the next boundary, making it possible for a lightwalletd-compromising adversary to correlate a user's synchronization behaviour (attached to a public IP address) with the anchor selected by their wallet. This constraint applies only to the pool-crossing spends: a later preparation layer may spend the notes as soon as they are mined and witnessed, per the ordinary-anchor rule above.

Phase 2, Scheduled migration transactions

Canonical migration transaction structure

Each migration transaction SHOULD have:

  • exactly one Orchard spend, with the Orchard bundle padded to exactly two actions, so that the presence or absence of an Orchard change output is not observable;
  • exactly one Ironwood output, whose value is a single canonical denomination, in a single-action Ironwood bundle with no padding action (see Rationale for the canonical transaction shape);
  • no transparent inputs or outputs;
  • no spends from any pool other than Orchard;
  • the canonical fee (provisionally the ZIP 317 minimum fee) 15;
  • the canonical anchor described in Anchor-height bucketing and cohorts and the canonical expiry height described below;
  • lock_time = 0;
  • no nonstandard fee amount or any other outputs (this would partition the anonymity set between wallets that add such outputs and those that do not).

The canonical expiry height is computed from the transaction's scheduled broadcast height h. Let E=𝖤𝗑𝗉𝗂𝗋𝗒𝖬𝗈𝖽𝗎𝗅𝗎𝗌(h) (see Block counts across the NU7 block spacing change), where EXPIRY_MODULUS is 34560 reference blocks (30 days), so that E is 34560 blocks before NU7 and 103680 blocks after NU7. The canonical expiry height is (h−(hmodE))+2⋅E: the most recent multiple of E at or below h, plus 2⋅E. This rolling window gives every transaction between one and two months of validity from its scheduled broadcast. A transaction scheduled before NU7 activation whose window extends past activation keeps the pre-activation block count, so its window is shorter in time. On Mainnet, the shortest such window is 1270900 seconds (about 14.7 days) at the target spacing, for a transaction scheduled at height 3543404, which has expiry height 3594240. Such a transaction is pre-signed for both the consensus branch ID before NU7 and that of NU7 (see Pre-signed transaction storage). Unlike the anchor, the expiry height is committed by the transaction digest 2 and is therefore fixed when the schedule is signed; because the schedule precomputes every broadcast height (see Transfer scheduling), the expiry is nonetheless known at signing time. The bucketed rule makes the committed value identical for every migration transaction — from any wallet — whose scheduled broadcast falls within the same 30-day period, so an expiry height reveals only the coarse period in which its broadcast was scheduled, information the broadcast itself substantially makes public. (Deriving the expiry from the construction height instead would reveal the otherwise unobservable period in which the wallet's schedule was created, and would deny the late parts of a long schedule their full validity window, forcing needless rebuilds.) For example, NU6.3 activates at Mainnet height 3428143, so, since NU7 will not have activated for the heights in this example, every migration transaction scheduled for broadcast before height 3456000 has expiry height 3490560, and one scheduled between heights 3456000 and 3490559 has expiry height 3525120. Parts still unbroadcast when their expiry height is reached are rebuilt with a fresh anchor and expiry (see Error handling).

This strict, uniform structure serves two audiences. To an observer, it makes every migration transaction structurally identical both to every other migration transaction and to an ordinary manually created Orchard→Ironwood spend, and it hides whether a change output is present (see Rationale for the canonical transaction shape). To the wallet that created it, the same structure serves as a canonical migration-transaction predicate for chain-based status recovery, with disambiguation from other conforming transactions provided by viewing-key information that an observer lacks: whether the Ironwood output belongs to the wallet's own account, and whether its value is a canonical denomination.

Anchor-height bucketing and cohorts

  • The anchor height of each migration transaction MUST be a shared, network-wide boundary height, rather than a value derived from the wallet's own chain tip, so that the anchor does not reveal precisely when this wallet last synchronized.

  • Buckets are delimited by boundaries, as defined in Block counts across the NU7 block spacing change, with M provisionally 144 reference blocks: before NU7, the multiples of 144; after NU7, the multiples of 3×144=432. At either target block spacing, a boundary occurs approximately every 144×75s=432×25s=3 hours. This interval concentrates migration transactions onto fewer shared anchors, enlarging per-anchor cohorts. EXPIRY_MODULUS is an exact multiple of M (240 buckets), so expiry buckets align with anchor buckets both before and after NU7.

  • Each migration transaction MUST take, as its Orchard anchor, the tree state as of a boundary drawn from the candidate anchor set: those boundaries that are above the NU6.3 activation height, at or after the height at which the transaction's funding note was created, and at or before the most recent boundary the wallet has observed at proving time. An anchor therefore reveals only that its creator had scanned beyond that boundary at some time before broadcast, and sharing it does not link a wallet's transactions to one another (see Rationale for anchor selection).

  • The draw MUST be recency-weighted: the anchor's age a≥1, measured in boundaries before the most recent boundary the wallet has observed at proving time, is drawn (using a CSPRNG) from a geometric distribution with parameter 1/2 — so the most recent boundary is never used, the most probable age is one boundary, and the mean age is two boundaries (about six hours). A draw exceeding ANCHOR_AGE_CAP (provisionally 4 boundaries, about twelve hours) or falling outside the candidate set MUST be discarded and redrawn; equivalently, the distribution is truncated to the candidate set.

  • The set of all migration transactions sharing a boundary anchor forms a cohort. A wallet MAY contribute several migration transactions to the same cohort (multiplicity k≥1), because all denominations are canonical and therefore not attributable to a single wallet by their values (see On multiplicity and cohorts).

Transfer scheduling

The schedule is constructed in block deltas: starting from the height at which the schedule is committed (lower-bounded so that every part can anchor; see below), the wallet assigns each part a scheduled broadcast height by advancing a running height by an independently drawn delay per part, using 𝖠𝖽𝗏𝖺𝗇𝖼𝖾 (see Block counts across the NU7 block spacing change), so that a wallet's broadcasts approximate a Poisson process.

  • The wallet MUST first shuffle the quantized parts into a uniformly random order, so that the temporal sequence of denominations a wallet emits is not a predictable function of its balance (for example, a largest-first ordering would let an observer infer migration progress from any part it could attribute, and would make the sequence predictable to a targeted adversary who knows the balance).

  • The delay to each successive migration transaction MUST be drawn from an exponential distribution with rate λ=1/ MEAN_DELAY, so that the expected delay is MEAN_DELAY (provisionally 66 reference blocks, about 82 minutes; 198 blocks after NU7), rounded to a whole number of reference blocks; any draw exceeding MAX_DELAY (provisionally 576 reference blocks, about twelve hours; 1728 blocks after NU7) MUST be discarded and redrawn.

  • The schedule MUST place every migration transaction at a scheduled height whose candidate anchor set (see Anchor-height bucketing and cohorts) is non-empty, so that no transaction can ever lack a valid boundary to anchor to and no fallback anchor-selection rule is needed. Concretely, the running height from which the delays accumulate MUST start no earlier than the boundary that follows the lowest candidate boundary — the lowest candidate boundary being the first boundary strictly above the NU6.3 activation height and at or after the estimated creation height of the funding notes (the last scheduled note-preparation transaction plus a small mining margin). Lower-bounding the starting height, rather than clamping individual transactions, preserves the drawn inter-arrival gaps and hence the schedule's Poisson character.

  • All randomness used in scheduling — the shuffle, the delay draws, and the anchor-age draws — and in the randomized decomposition of Amount selection, canonical quantization MUST be generated by a cryptographically secure random number generator.

  • The wallet MUST propose the complete schedule to the user before any transfer is sent, obtaining confirmation of the whole schedule (see Migration entry point and user consent).

  • Each migration transaction takes a canonical anchor drawn from the recency-weighted distribution over the candidate anchor set (see Anchor-height bucketing and cohorts). Because the version 6 transaction format commits the anchor as authorizing data rather than in the signature hash 2, the wallet can attach or update a pre-signed transaction's anchor and proof without re-signing.

  • Whenever the application is granted background processing time, the wallet SHOULD synchronize and construct the proofs of upcoming scheduled transactions, drawing each transaction's anchor from the candidate anchor set as observed at proving time, but MUST NOT broadcast a migration transaction within the same background processing window in which a synchronization is performed (see Decoupling synchronization from broadcast).

  • Spreading a wallet's transfers over time with memoryless inter-transfer delays, and drawing each transfer's anchor independently, are the primary means by which the wallet's own transfers are kept from being linked to one another; distinct per-wallet anchor heights are not required, because a shared boundary anchor carries no per-wallet information.

Pre-signed transaction storage

  • Once the schedule is committed, migration transactions SHOULD be pre-signed and persisted, reusing the wallet's existing transaction-resubmission storage mechanism where one exists.

  • Under the version 6 transaction format, the anchor is committed as authorizing data rather than by the signature hash 2, so a pre-signed transaction's anchor and proof can be updated at (or near) broadcast time without re-signing. This is what allows the entire schedule to be signed in a single session while each transaction carries a boundary anchor drawn from the shared candidate set (see Rationale for anchor selection).

  • A transaction's signatures commit to a consensus branch ID, and the transaction is valid only in blocks to which that consensus branch ID applies 4. A migration transaction can be mined at any height from its scheduled broadcast height to its expiry height. If a network upgrade activates within that range, a wallet that pre-signs the transaction MUST sign it for each consensus branch ID that applies in the range. For example, take a transaction scheduled before NU7 activation whose expiry height is at or after the NU7 activation height. It is signed for both the consensus branch ID before NU7 and that of NU7. The versions spend the same notes, so at most one of them can be mined. The wallet broadcasts the version whose consensus branch ID applies to the next block. If a version broadcast before activation has not been mined when the network upgrade activates, the wallet broadcasts the version for the new consensus branch ID.

  • If a stored transaction becomes invalid (for example, because its input notes were spent outside the migration, it expired, or a network upgrade activated that was not scheduled when it was signed), the wallet MUST detect this and prompt the user to re-initiate the affected step (see Error handling).

Background scheduling

  • Background scheduling MUST be treated as best-effort. The operating system chooses the exact execution time within the requested window.

  • Platform schedulers take wall-clock times, while the schedule is in block heights. When converting a scheduled height into a wake-up time, the wallet SHOULD take into account the block target spacing in effect over the heights in between: 75 seconds before NU7 activation and 25 seconds after it 14.

  • A single background processing window MUST be used either to synchronize (updating the anchors and proofs of upcoming transactions) or to broadcast, never both (see Decoupling synchronization from broadcast).

  • If a scheduled window is missed, the wallet MAY fall back to prompting the user on the next application open (see Fallback on application open).

Platform-specific guidance is given in Platform considerations.

Decoupling synchronization from broadcast

A background session is defined as a period of time wherein the wallet is performing computation and network interaction decoupled from any user interaction with the wallet interface. While a background session and user interaction with the wallet interface may coincide, the wallet MUST NOT permit the user's behavior to influence the execution of the background operations.

  • The wallet MUST NOT perform a wallet synchronization in the same background session as a migration broadcast, because doing so would let an observer correlate the synchronization (which may leak wallet-identifying data) with the broadcast.

  • Where a synchronization is needed, it MUST be separated in time from any broadcast. In the recommended flow migration transactions produce no Orchard-pool change, so the required synchronizations are the one that witnesses the notes produced by note preparation before the first scheduled send, and the routine background synchronizations that allow the proofs of upcoming transactions to be constructed (see Transfer scheduling). A migration transaction that produces change in the Orchard pool (for example, one rebuilt during error recovery) requires the wallet to synchronize before that change can be spent; the wallet MUST schedule that synchronization decoupled in time from the broadcast of the next migration transaction.

How a wallet determines when a synchronization is needed is implementation-defined.

Fallback on application open

  • On every application launch, the wallet MUST reconcile the scheduled broadcast times against the transfers actually completed, and MUST surface any overdue transfers. This on-launch reconciliation is the primary catch-up mechanism and MUST NOT rely on notification delivery.

  • When the user is prompted to send overdue transfers, the wallet SHOULD disclose that sending them at application-open time correlates the broadcasts with the wallet's sync activity, weakening the privacy of those transfers.

  • The wallet MUST NOT send more than one overdue transfer at wallet open. While this may delay the migration of user funds, the alternative (sending multiple transactions with pool-crossing value within the same session) leaks balance.

  • When a window is missed and the remaining transfers are rescheduled, the wallet SHOULD shift the remaining schedule by the offset, or redraw the delays of the remaining transfers.

  • A slip of up to approximately two hours in which the background task eventually runs SHOULD be treated as normal operation and SHOULD NOT be surfaced as an error.

Destination

  • The destination of a migration MUST be the user's internal change address in the Ironwood pool. Migration to the Sapling pool MUST NOT be offered or recommended as part of this flow.

Network-layer privacy

Before the schedule is committed, the wallet MUST present a network-privacy step.

  • The wallet MUST offer Tor 17 and (if available) Nym 18 network options. When enabled, all migration transaction broadcasts MUST be routed through the selected network.

  • If Tor and Nym are unavailable on the user's network or region, the wallet SHOULD suggest using a trusted VPN before proceeding.

  • The user MAY proceed without Tor, Nym, or a VPN; the choice is the user's.

  • The wallet MUST display a disclaimer explaining the IP-correlation risk: because pool-crossing transfers are visible on-chain, without network-level privacy a server or network operator can correlate the broadcasting IP address with the on-chain event; if IPs are reused, this may leak the total Orchard balance of the wallet to the server operator.

  • Broadcasting migration transactions to a different server than the one used for synchronization MAY further reduce correlation, and a wallet MAY do so.

Platform considerations

This section is non-normative except where it restates requirements from Background scheduling and Decoupling synchronization from broadcast. The flow applies to both iOS and Android; differences are noted where they affect implementation.

iOS

  • Background scheduling SHOULD use BGTaskScheduler with a background-processing task type suitable for longer work.
  • A single task execution MUST either synchronize (updating anchors and proofs) or broadcast, never both.
  • If the task does not run, the next application open MUST surface the fallback prompt.

Android

  • Background scheduling SHOULD use WorkManager with a flex interval aligned to the scheduled broadcast heights.
  • A single Worker execution MUST either synchronize (updating anchors and proofs) or broadcast, never both.
  • OEM battery-optimization behaviour (for example on MIUI or One UI) may suppress tasks even when permissions are granted; this is acknowledged as a known limitation. During onboarding the wallet SHOULD request a battery-optimization exemption to improve reliability, and MUST continue to function (relying on the on-launch fallback) if the exemption is denied.
  • On every application launch, the wallet MUST reconcile whether a pending transfer is overdue and surface the fallback prompt if needed.
  • WorkManager task persistence across reboots SHOULD be verified by the implementation; if a boot receiver is used to re-enqueue tasks, the relevant platform permission must be declared.

Status notifications

  • On a successful transfer, the wallet MAY show a notification.

  • On a failed transfer, the wallet MUST surface the failure with the ability to retry. The retry affordance SHOULD lead to a self-contained screen that does not start a synchronization.

  • Notification delivery SHOULD NOT be relied upon as the primary status mechanism; on-launch reconciliation is primary (see Fallback on application open).

  • On platforms where notifications can be cleared by a reboot or suppressed by the OS or OEM managers (notably Android), the wallet MUST disclose that notification delivery is not guaranteed and MUST direct the user to return to the application to check progress.

Progress visibility

The wallet SHOULD report migration progress.

User-facing copy requirements

Because background scheduling is best-effort, product copy SHOULD NOT describe the migration as automatic or guaranteed. The fallback of prompting on the next application open MUST be presented as a normal part of the flow, not as an error state.

Error handling

The wallet MUST handle at least the following conditions.

  • Transient broadcast failure (network unavailable). The stored pre-signed transaction MUST NOT be discarded. The task SHOULD retry within its window and rely on background backoff for the next window. If broadcast continues to fail, the on-launch fallback MUST offer the user the choice to send now or retry in the background.

  • Invalid transaction (input note already spent). If the user spends Orchard-pool funds outside the migration, a queued migration transaction may become invalid. The wallet MUST detect this (the detection condition is that the confirmed-spendable Orchard-pool balance is greater than zero while the next scheduled migration transaction is missing or invalid), MUST notify the user of the failure, and MUST construct a revised schedule for the remaining unspent balance using a fresh anchor. The screen that does so MUST be self-contained and MUST NOT trigger a synchronization. A rebuilt transaction MAY produce change in the Orchard pool: the canonical shape hides whether change is present, so this carries no privacy penalty, only the operational cost of a later synchronization to recover the change.

  • Expired transaction. A pre-signed transaction that reaches its expiry height 19 before being broadcast MUST be detected on the next application open and handled in the same way as an invalid transaction: a new transaction with a fresh anchor and expiry is constructed for the affected part, with its denomination unchanged.

  • Canonical fee change. Exact-value funding notes bake the canonical fee into note values at preparation time. If the canonical fee changes between preparation and broadcast, the affected pre-signed parts no longer match the canonical structure and SHOULD be rebuilt (producing or consuming the difference as Orchard-pool change) or the remaining balance re-prepared. This carries no privacy penalty, but costs an additional confirmation round.

  • Negligible remaining balance. If, after the last scheduled transfer, only an amount below a significance threshold remains (for example, due to fee rounding), the wallet MAY treat the migration as complete and inform the user that a negligible amount remains. If the remaining amount is above MAX_RESIDUAL_VALUE, the wallet MAY add a final transfer. Wallets SHOULD apply the ZIP 317 marginal-fee considerations 15 when deciding whether a residual note is economic to migrate.

  • Note-preparation transaction unconfirmed or failed. If the application is closed before a note-preparation transaction confirms, on re-open the wallet MUST report that it is waiting for confirmation and MUST proceed to scheduling once the preparation transactions confirm. If a preparation transaction expired, the wallet MUST offer to retry it. Where preparation was partitioned across multiple transactions, partial confirmation MUST be handled: parts funded by confirmed preparation outputs MAY proceed on schedule while the remainder waits.

  • Application reinstall or restore on a new device. On detecting unspent Orchard-pool funds with no local migration state, the wallet MAY treat the situation as requiring a fresh migration (already-confirmed transfers remain on-chain; only the remaining balance is rescheduled). See Non-requirements.

Rationale

Rationale for the scheduled background approach

The chosen approach broadcasts migration transactions from best-effort background tasks at pre-determined, randomly drawn times that are de-correlated from the user's interaction with the application. An alternative considered was a guided "on-open" approach, in which each transfer is created at the moment the user opens the application.

The on-open approach was dismissed primarily because user-triggered sends leak too much information: the scan ranges a wallet requests from a light wallet server already link its notes to one user, and if each pool-crossing send also coincides with an application open, an observer can monitor the wallet across the entire migration. It also provides no temporal de-correlation, because every transfer is anchored to the moment of user interaction.

The scheduled approach achieves temporal de-correlation in the common case. Its worst case (when no background task ever runs and the user sends everything on a single application open) is no worse than the on-open approach, while its best case is significantly better. Pre-signed transactions can be stored using existing transaction-resubmission infrastructure.

Rationale for Poisson transfer scheduling

This subsection is non-normative.

Drawing inter-transfer delays from an exponential distribution makes the schedule memoryless: observing one of a wallet's broadcasts gives an observer no information about when its next broadcast will occur, so there is no per-wallet timing signature to recover. Because the superposition of independent Poisson processes is itself a Poisson process, the aggregate traffic of all wallets following this schedule blends into a single pooled process from which no individual wallet's contribution is separable by timing analysis. Shuffling the parts before scheduling extends the same property to values: the sequence of denominations a wallet emits is not a predictable function of its balance.

The mean delay must not be too short relative to the anchor-bucket interval. A schedule whose delays are much shorter than the bucket interval (for example, minutes) would compress a wallet's entire migration into a tight, predictable window, and the burst itself would be a clustering signal. With MEAN_DELAY = 66 reference blocks and the bucket modulus M = 144 reference blocks (both scaled by the same factor after NU7), a wallet broadcasts about two parts per bucket on average while the exponential tail continues to spread its transfers across several cohorts. This roughly halves the expected inter-transfer latency compared with a 144-block mean. Keeping MAX_DELAY at 576 blocks, more than eight mean delays, preserves nearly all of the exponential distribution's variance while bounding the worst-case gap so that migration completes in bounded time and canonical expiry windows remain practical.

Delays are denominated in reference blocks, and converted to heights, rather than in wall-clock time because bucket boundaries, anchors, and expiry heights are all block-denominated, and block height is what an on-chain observer measures.

Routine background synchronization does not undermine the schedule's de-correlation properties: a synchronization reveals to the light wallet server only that some wallet has synchronized past a given height, while a migration transaction's anchor is the shared boundary anchor common to its entire cohort. Provided synchronization and broadcast never share a background processing window, the server learns nothing that correlates a specific synchronizing wallet with a specific migration transaction.

The same reasoning applies, at a smaller scale, to the note-preparation broadcasts. Their values and direction are fully shielded, but a burst of identically shaped padded transactions appearing at a single instant is a shape-and-timing cluster attributable to one wallet, and such a burst also telegraphs that a migration schedule will follow it. Spacing the preparation broadcasts with a smaller-mean exponential (PREP_MEAN_DELAY) prevents the cluster from forming without materially delaying the migration: preparation is unconstrained by anchor buckets — its transactions anchor at the wallet's ordinary near-tip depth — so its spacing serves only to break up the burst, not to place transactions into shared anchor cohorts.

Rationale for the stateful design

An attractive alternative is a stateless, chain-recoverable design: persist no scheduler state at all, and instead recover migration progress from the chain using the wallet's viewing keys (identifying migration transactions by the canonical predicate) plus a deterministic re-computation of the quantization of the remaining balance. Because the quantization is a pure function of the balance, it recomputes identically on every activation and after restore-from-seed, which makes that design robust to restart and to multi-device restore.

This unified proposal nonetheless adopts a stateful design (a persisted schedule plus pre-signed transactions) and treats statelessness and multi-device/recovery continuity as non-requirements (see Non-requirements). The reasons are:

  • Persisted, pre-signed transactions make best-effort background broadcast practical, and make temporal de-correlation of broadcast from synchronization and from application opens straightforward to implement.
  • Statelessness was judged to make the desired temporal de-correlation harder, not easier, while the robustness it buys (restart and restore continuity) is not required for this flow.

The chain-recoverability property is not wasted: the canonical migration-transaction predicate of Canonical migration transaction structure still lets a wallet, using its viewing keys, reconstruct which denominations have confirmed, which is how the reinstall/restore case is handled as a fresh migration. The full stateless design is recorded as a rejected alternative (see Alternatives).

Rationale for canonical amount selection

Privacy for value-transparent transfers comes from value collision (many wallets emitting the same amount) and ambiguity of grouping (an observer cannot tell which amounts belong together), not from unpredictability of the amount. A high-entropy random amount collides with no other wallet and is a near-unique fingerprint; a random partition into high-entropy pieces additionally exposes the balance to subset-sum reconstruction. Canonical denominations are therefore preferred precisely because they are not distinctive: many wallets emit identical denominations, so no single transfer is identifiable by its value, and a wallet MAY safely place several transfers in one cohort. Randomizing the choice among canonical denominations (see Amount selection, canonical quantization) is categorically different from randomizing amounts: every emitted part still collides with the population, while the multiset emitted for a given balance is no longer predictable, which improves balance hiding and further weakens targeted subset-sum. The detailed privacy argument is in Why value collision provides privacy and the sections that follow it; the discarded random-amount strategies are listed in Alternatives.

Rationale for the canonical transaction shape

The canonical migration transaction spends exactly one Orchard-pool note, pads the Orchard bundle to exactly two actions, and produces exactly one Ironwood output. Under NU6.3, Orchard bundles pair every spend and every output with a dummy counterpart 16, so a bundle's action count is the sum of its spends and outputs; values and the dummy/real distinction are hidden. The canonical two-action Orchard bundle therefore consists of one spend action and one output action, and does not reveal whether the output action carries a change output or a zero-valued dummy. This has two consequences:

  • Indistinguishability from manual spends. An automated migration transaction is structurally identical to a manually created Orchard→Ironwood spend, which takes the same shape. The anonymity set for a migration transaction is therefore the whole population of conforming cross-pool spends, not merely the transactions generated by migrating wallets. Because NU6.3 disables ordinary payments within the old pool 10, that population constitutes essentially all post-upgrade Orchard-pool activity; this strengthens the ZIP 308 11 stance that migration transactions need be hidden only among themselves.

  • Change carries no privacy penalty. A change-bearing migration transaction (for example, one rebuilt during error recovery, or one sent by a wallet that skipped note preparation) is indistinguishable from a change-free one. Producing change has only an operational cost: the change cannot be spent until a later synchronization witnesses it, and a part that would spend it cannot be pre-signed until then.

The Ironwood bundle is a single action, not padded to the two-action minimum. Unlike the Orchard side, where padding hides whether a change output is present, the Ironwood side of a canonical transfer has exactly one output and nothing left to hide: its shape is already fixed by the canonical structure. A padding dummy would therefore buy no indistinguishability, while costing an extra action's proof — a real burden when a hardware device generates the proofs, since each action's proving data must cross the device's bandwidth-constrained link. What matters is that one action count is pinned as canonical: if padding were left optional, conforming wallets would split into distinguishable padded and unpadded populations, partitioning the very anonymity set the canonical shape exists to unify. (The indistinguishability argument above extends to manually created Orchard→Ironwood spends to the extent that they are built to this same single-action shape, which wallet builders are encouraged to use for this transfer form.)

A corollary is that note preparation and pre-signing are motivated operationally rather than by privacy: the privacy properties come from the canonical shape, the canonical values, and the scheduling, while exact-value funding notes are what allow the entire schedule to be pre-signed at once and broadcast in any order.

The same hiding applies to note preparation: because dummy components are indistinguishable from real ones, an N-action preparation transaction reveals nothing about how its actions divide between spends and outputs — whether it divides notes, combines them, or both. Only its action count and timing are observable.

Rationale for decoupling synchronization from broadcast

Linking synchronization and broadcast in the same background session would let an observer correlate the two events and thereby link the broadcast to the wallet. Keeping them separated in time preserves the de-correlation that the scheduling is designed to provide. The principal place a synchronization is unavoidable is after the note-preparation transactions confirm, since the wallet must witness the prepared notes before the first scheduled send; the exceptional case of a migration transaction that outputs change back to the Orchard pool is handled the same way, by scheduling the synchronization at a different time from the next broadcast.

Rationale for anchor-height bucketing

Drawing anchors from shared, network-wide boundaries means a transaction's anchor reveals only that the creator had scanned beyond that boundary at some time before broadcast, not the wallet's precise sync height or sync schedule, and allows many users' transactions to share anchors. This mirrors the goal of ZIP 308 11 (hiding individual migration transactions among those of all migrating users), adapted to anchor selection in the light-client setting, and complements the anchor-selection guidance of ZIP 315. 6

Rationale for scaling block counts at NU7

The delays, the bucket modulus, and the expiry modulus are chosen for their durations: the time between broadcasts, the time for a cohort to form, and the validity period of a pre-signed transaction. A fixed number of blocks would last one third as long after NU7 reduces the block target spacing from 75 to 25 seconds 13, compressing a schedule into a tighter burst, shrinking cohorts, and shortening validity to 10–20 days. This ZIP therefore states these parameters in reference blocks and scales them by 𝖭𝖴𝟩𝖯𝗈𝖶𝖳𝖺𝗋𝗀𝖾𝗍𝖲𝗉𝖺𝖼𝗂𝗇𝗀𝖱𝖺𝗍𝗂𝗈, as ZIP 218 recommends 20.

A delay that crosses activation is converted piecewise so that it keeps its length in time exactly; it is otherwise drawn as before, so a schedule that spans activation keeps its Poisson character in time.

Boundaries after activation are absolute multiples of R⋅M, not multiples of R⋅M counted from A, so that all wallets agree on the grid using only the height, and so that every boundary is a multiple of M. A wallet that retains tree states at every multiple of M therefore retains every boundary, before and after activation, without regard to A. The one bucket that contains A can be shorter or longer in time than the others, up to about six hours. On Mainnet, that bucket runs from height 3543264 to height 3543696, and lasts 17800 seconds (4 hours 56 minutes 40 seconds) at the target spacing, rather than 3 hours.

The expiry modulus is selected by the scheduled broadcast height alone, so that the canonical expiry height remains a function of that height and every transaction scheduled in the same period shares it. Every canonical expiry height is a multiple of EXPIRY_MODULUS.

Rationale for anchor selection

This subsection is non-normative.

A "sign ahead, broadcast later" workflow can leak information through anchor selection: if the anchor were fixed at signing time — as it is under the version 5 transaction-digest rules 21, where the anchor is committed by the signature hash — then at broadcast time the gap between the anchor's age and the broadcast height would reveal when the transaction was prepared.

Two design choices eliminate this leak. First, the version 6 transaction format 2 commits the anchor as authorizing data — a public input to the proof — rather than in the signature hash, so the prover can choose the anchor after signing, and the wallet can update a pre-signed transaction's anchor and proof at broadcast time without re-signing (see Pre-signed transaction storage). Second, the anchor is drawn at random from the shared candidate set of boundaries rather than pinned to the most recent boundary.

Pinning to the most recent boundary would create a bottleneck at each boundary: the anonymity set for a transaction anchored at boundary B is the set of wallets that had scanned beyond B by proving time — a set that an adversary who compromises a light wallet server can approximate by observing which clients have fetched blocks past B — and shortly after each boundary that set is small. Drawing the anchor at random from all candidate boundaries means an observed anchor implies membership only in the set of wallets that scanned beyond that boundary at some time before broadcast, a set that has had time to grow. This is also why no privacy-preserving block-download mechanism is required for these invariants: the light wallet server learns which wallets have scanned past a given height, but that knowledge narrows the sender of a migration transaction no further than the anchor itself already does.

The randomized draw carries a tension: an earlier anchor requires the transaction's funding note to have existed at that boundary, so the population able to emit early anchors is bounded by the wallets whose note preparation had confirmed by then — potentially a smaller global anonymity set. This is mitigated by the fact that note preparation can be performed before the network upgrade activates, so the funding notes of many wallets already exist at the earliest post-activation boundaries. The candidate set is nonetheless always restricted to boundaries above the NU6.3 activation height, so that wallets that prepared before the upgrade do not form a distinguishable subpopulation emitting pre-activation anchors.

The draw is recency-weighted rather than uniform over the candidate set, for four reasons. First, uniformity across wallets: under a uniform draw over each wallet's own window, the anchor-age distribution a wallet emits is a function of when it prepared — early preparers emit anchors spread over a wide window, late preparers only recent ones — so anchor age becomes a statistical fingerprint of preparation date. Under a fixed, capped recency-weighted distribution, every wallet whose window is at least ANCHOR_AGE_CAP deep draws from the identical distribution, and wallets with shallower windows are forced toward recent anchors, where the common distribution already concentrates. Second, the cap bounds the inevitable preparation-time leak: an anchor always places the creation of the funding note before it, but no anchor can reveal more than "prepared at least ANCHOR_AGE_CAP boundaries ago" — little more than the act of broadcasting already reveals. Third, concentration: the same reasoning that prefers canonical denominations to high-entropy amounts prefers few, heavily-used anchors to many thinly-used ones; recency weighting produces large per-anchor cohorts. Fourth, alignment with manual spends: a conforming wallet making a one-off Orchard→Ironwood spend will naturally use a recent boundary, and keeping the migration flow's anchor ages similar prevents anchor age from partitioning automated and manual traffic. The cap also bounds the tree states a wallet must retain for proving to the last ANCHOR_AGE_CAP boundaries.

The cost of recency weighting is a sharper posterior on proving time (a recent anchor implies proving shortly after it); this is blunted by the decoupling of proving from broadcast and by the exponential scheduling delay between them, which together turn "shortly after the anchor" into a multi-boundary window. The most recent boundary is excluded entirely because transactions anchored to it could only have been produced by wallets that synchronized within the current boundary interval — the bottleneck in miniature. In the first days after activation, every wallet's window is shallow and all distributions collapse onto the few boundaries that exist; privacy in that regime rests on cohort size (all migrating wallets share those anchors), not on anchor diversity.

Privacy Implications

The migration necessarily reveals information; the requirements in this ZIP are intended to minimize it and to ensure the user consents to what remains. The principal sources of leakage are:

  • Migrated amounts. Each migration transaction reveals on-chain the value it moves from the Orchard pool to the Ironwood pool. 7 Quantizing the balance into canonical denominations means no single transaction reveals the user's whole Orchard-pool balance, and each amount collides with those of many other wallets.

  • Linkage between a wallet's own transfers. If multiple migration transactions could be grouped by a shared distinctive value, anchor, expiry, or broadcast time, an observer might cluster them as belonging to one wallet. Canonical amounts, shared network-wide boundary anchors, bucketed expiries, and de-correlated broadcast times are intended to defeat this clustering.

  • Synchronization timing. The anchor height of a transaction reveals that its creator had scanned beyond that height at some time before broadcast. Drawing anchors at random from the shared boundary set prevents an observer from inferring when a specific wallet synchronized, and from narrowing the sender to the initially small set of wallets that had scanned past the most recent boundary. Triggering a wallet synchronization in the same background session as a broadcast would let an observer correlate the two; synchronization is therefore kept decoupled in time from broadcast.

  • Correlation with user behaviour. If transfers are broadcast at the moment the user opens the application, an observer (in particular a light wallet server that also sees the user's scan-range requests) can monitor the wallet across the whole migration. De-correlating broadcasts from application opens is the primary privacy goal of the chosen approach.

  • Network-layer metadata. Without network-level privacy, the server or network operator that receives a broadcast can correlate the broadcasting IP address with the on-chain pool-crossing event. This ZIP requires offering Tor as an opt-in and disclosing the residual IP-correlation risk.

  • Change back to the Orchard pool. A migration transaction that produces change in the Orchard pool requires the wallet to synchronize before that change can be spent, creating a point where synchronization and migration interact and must be carefully separated in time. Because the canonical shape pads the Orchard bundle to two actions, the presence or absence of change is not itself visible on-chain; the concern is operational, not a distinguisher.

  • Note-preparation transactions. A note-preparation transaction is fully shielded: its values, and whether it divides notes, combines them, or both, are hidden. Its only observable features are its action count and its timing, which reveal at most a loose bound on the number of notes involved.

The remaining subsections of this section are non-normative; they analyze the privacy properties of the procedure specified above.

Threat model

This subsection is non-normative.

The adversary observes all on-chain migration transactions: for each, the net Orchard-out and Ironwood-in amounts and the block (hence time). The adversary does not see addresses. The adversary's goal is clustering: partitioning the global stream of migration transactions into per-wallet sets. Successful clustering reconstructs a wallet's migration (and hence its balance and behavior) without any address.

We additionally consider a targeted adversary who augments chain observation with off-chain knowledge of a victim's incoming note values — in the strongest case having deliberately sent the victim notes of attacker-chosen values ("tagging"). This adversary's goal is to determine whether, when, or in what amounts the victim has migrated (see On targeted note tagging).

Why value collision provides privacy

This subsection is non-normative.

Privacy for value-transparent transfers comes from value collision (many wallets emitting the same amount) and ambiguity of grouping (an observer cannot tell which amounts belong together), not from unpredictability of the amount. Randomizing amounts is counterproductive: a high-entropy amount collides with no other wallet and is a near-unique fingerprint.

Every part in this design is a canonical denomination drawn from a small shared set (…, 100, 50, 20, 10, 5, 2, 1, 0.5, …). Many wallets emit identical denominations, so no single part is distinctive by its value.

On multiplicity and cohorts

This subsection is non-normative.

A wallet MAY place several parts into the same cohort. This does not let the adversary attribute those parts to one wallet, because:

  • Canonical values are not self-identifying. Several 100-ZEC parts in a cohort cannot be attributed to one contributor versus several; the adversary cannot tell whether three co-located 100s came from one wallet or three. The grouping is ambiguous by construction, and there is no rare value to anchor a reconstruction (the design has no high-entropy remainder).
  • Parts are not linked by inputs. Each part is a separate transaction spending an Orchard-pool note, which publishes only an unlinkable nullifier; shielded inputs do not visibly link a wallet's transactions. The mandated absence of transparent components, the shared boundary anchors, the canonical fee, and the uniform two-action, single-output structure mean nothing on-chain ties one wallet's co-located parts together.

This is precisely why multiplicity is safe only under canonical amounts.

What a cohort does reveal is only the aggregate throughput of the entire migrating population (the total value of all parts sharing that anchor), which is population-level information, not any individual's magnitude.

On subset-sum

This subsection is non-normative.

A worry with canonical decompositions is targeted subset-sum (find a subset summing to a known target). This specification accepts the limited residual exposure on the following grounds:

  • Canonical values compose ambiguously: many unrelated parts sum to clean totals, so the constraint "this subset sums to a round number" is weak and yields exponentially many spurious solutions for an untargeted adversary (one without a known total).
  • If an adversary already knows a wallet's total balance from off-chain data, learning that the wallet migrated that balance reveals no new information; the only further gain would be transaction-level linkage, which remains hard due to canonical collision, dispersion, and the lack of intra-cohort linkage.
  • Therefore the dominant practical risk is rarity (below), which the DENOM_CAP bounds.

On targeted note tagging

This subsection is non-normative.

An adversary may target a victim by sending notes of attacker-chosen, distinctive values, hoping to recognize those values (or their sum) when the victim's funds cross the pool boundary. Three mechanisms defeat this:

  • Quantization severs received values from migrated values. Migrated amounts are drawn from the canonical denomination set regardless of the values of the notes that fund them; a distinctive received value never appears as a pool-crossing amount, and any sub-MAX_RESIDUAL_VALUE tail it contributes is withheld as the residual rather than revealed.

  • Note preparation absorbs tagged notes invisibly. The preparation transaction is fully shielded, so consuming a tagged note and re-minting canonical funding notes reveals nothing about the tagged note's value or whether it participated at all.

  • Randomized decomposition hides the multiset. Even an adversary who knows the victim's entire balance (for example, having funded it) cannot predict the emitted denomination multiset when the wallet randomizes its decomposition, and every denomination the adversary might look for collides with those of concurrently migrating wallets.

What the tagging adversary retains is population-level information: that some wallets migrated during a given period. Determining that a specific victim's funds have — or have not — migrated requires clustering the victim's transactions out of that population, which the mechanisms above are designed to prevent.

Accepted residual leaks

This subsection is non-normative.

  • Rarity bounded by the cap. No single part exceeds the maximum denomination of 10000 ZEC, so no transfer is extremely large/rare. Larger denominations (e.g. 10000 ZEC) are rarer than smaller ones, but every value is shared by many wallets in a large migrating population.
  • Aggregate cohort throughput. A cohort reveals the population's total migrated value at that boundary (not individual magnitudes).
  • Total part count and migration duration for very large balances loosely indicate bucketed magnitude (a whale emits many parts over a longer period), which is far weaker than a unique value.
  • Whale self-selection (deliberate). Large holders who want fast, clean UX may choose to migrate in a single transfer instead of using this procedure. This is accepted: forcing whales into a long, manually-driven many-transaction process would harm adoption, and lower adoption shrinks the anonymity set for everyone.

Whale handling

This subsection is non-normative.

For large balances:

  • The number of parts grows with the balance, and with it the expected duration of the migration (roughly MEAN_DELAY reference blocks per part). A very large balance therefore takes proportionally longer to migrate.
  • Dividing a very large note into its funding notes may require layered preparation (see Note preparation transactions), adding each layer's scheduled broadcast spread plus its mining latency to the preparation phase, and additional signing sessions if the layers are not pre-signed as a chain. This is exactly the situation of a wallet consolidating very many small notes, and the two are indistinguishable on-chain.
  • k per cohort is bounded by K_MAX (enforced by redrawing anchor ages) to limit the per-cohort count signal, keeping per-wallet multiplicity within a range that the (canonical, ambiguous) cohort can absorb.
  • Whales remain weakly protected and may self-select to a single transfer, as above.

A note on cohort size vs. per-wallet multiplicity

This subsection is non-normative.

Two different quantities should not be conflated:

  • Cohort size (parts from all wallets at a boundary): larger is always better for privacy (bigger crowd). Coordinated boundaries exist to maximize it.
  • Per-wallet multiplicity k: the number of a single wallet's parts in one cohort. Under canonical-only amounts, k>1 is safe (parts are unattributable), but K_MAX (enforced by redrawing anchor ages) keeps k modest relative to expected cohort size so that per-wallet multiplicity stays well within the crowd.

Deployment

This is a Wallet-category ZIP and does not itself introduce consensus rules. It applies to wallets once the Ironwood pool is activated by its network upgrade 8 and the migration becomes relevant. The note-preparation phase MAY be performed before that activation (see Phase 1, Note preparation).

Reference implementation

TODO: link to the SDK and wallet pull requests implementing this flow. Phase 2 is intended to be implemented in a Rust backend exposed through a public SDK interface, so that wallets can integrate against a stable interface (initially with mocked data) and later switch to the production implementation.

The zcash_pool_migration crate 22 implements a backend-agnostic engine for this migration in Rust.

Alternatives

The following alternative designs were considered and rejected.

  • Migrating the raw balance in a single transfer publicly reveals the exact balance, and the transfer is trivially linkable to the wallet's other activity.

  • Random arbitrary amounts per transfer produce high-entropy values that collide with no other wallet; each transfer is a near-unique fingerprint.

  • Random partition into high-entropy pieces: the pieces sum to the balance, exposing it to subset-sum reconstruction, and each piece is self-fingerprinting.

  • Whole-ZEC bucketed parts with a high-entropy sub-1-ZEC remainder: the rare remainder is a discriminator that makes co-locating parts in a cohort dangerous, and is itself near-unique. Superseded by canonical-only quantization, which has no remainder output.

  • Randomized per-wallet multiplicity k adds behavioural entropy — itself a fingerprint — without dispersing co-located parts; it is dominated by a deterministic, cohort-size-governed k.

  • Guided on-open sending: creating each transfer at the moment the user opens the application makes every broadcast coincide with application opens and light-wallet-server scan-range requests, letting an observer track the wallet across the whole migration; it provides no temporal de-correlation (see Rationale for the scheduled background approach).

  • Short-interval memoryless timing (inter-transfer delays of minutes rather than hours): delays much shorter than the anchor-bucket interval compress a wallet's entire migration into one or two buckets, producing a tight, predictable burst that is itself a clustering signal, and such schedules are incompatible with shared boundary anchors (see Rationale for Poisson transfer scheduling).

  • Stateless, chain-recoverable migration (no persisted schedule): robust to restart and restore-from-seed, but statelessness is a non-requirement here, and persisted state makes temporal de-correlation and pre-signed background scheduling simpler (see Rationale for the stateful design).

  • A privacy/speed slider exposed to the user: the decision and the UI are too complex for typical users; the wallet picks sensible defaults instead.

References

Footnotes

  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 229: Version 6 Transaction Format ↩ ↩2 ↩3 ↩4 ↩5

  3. Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later ↩

  4. ZIP 200: Network Upgrade Mechanism ↩ ↩2

  5. Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.12: Mainnet and Testnet ↩

  6. ZIP 315: Best Practices for Wallet Implementations ↩ ↩2 ↩3 ↩4 ↩5

  7. ZIP 209: Prohibit Negative Shielded Chain Value Pool Balances ↩ ↩2 ↩3

  8. ZIP 258: Deployment of the NU6.3 Network Upgrade ↩ ↩2 ↩3

  9. ZIP 257: Deployment of the Orchard Temporary Vulnerability Mitigation and NU6.2 Network Upgrade ↩

  10. ZIP 2006: Restricting Transfers into the Orchard Pool ↩ ↩2

  11. ZIP 308: Sprout to Sapling Migration ↩ ↩2 ↩3

  12. ZIP 326: NU6.3 Consequences for Wallets, section "Restrictions on sending" ↩

  13. ZIP 218: 25-second Block Target Spacing ↩ ↩2

  14. ZIP 218: 25-second Block Target Spacing — Block target spacing ↩ ↩2

  15. ZIP 317: Proportional Transfer Fee Mechanism ↩ ↩2 ↩3

  16. ZIP 326: NU6.3 Consequences for Wallets, section "Spending from and rebalancing within the Orchard pool" ↩ ↩2

  17. Tor Project ↩

  18. Nym ↩

  19. ZIP 203: Transaction Expiry ↩

  20. ↩
  21. ZIP 244: Transaction Identifier Non-Malleability ↩

  22. zcash_pool_migration crate ↩