ZIP: 318
Title: Orchard to Ironwood Migration
Owners: Schell Carl Scivally <efsubenovex@gmail.com>
Pacu Gindre <pacu@zecdev.org>
Kris Nuttycombe <kris@nutty.land>
Status: Draft
Category: Wallet
Created: 2026-06-16
License: MIT
Discussions-To: <https://github.com/zcash/zips/issues/1315>
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:
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.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.
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 10 — is a general send-path concern and is out of scope, although the migration destination is the same Ironwood receiver.
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.
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.
A wallet performing a privacy-preserving migration SHOULD implement it as two phases:
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.
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.
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.
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.
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 \times 10^k\) ZEC with
\(n \in \{1, 2, 5\}\) and MAX_RESIDUAL_VALUE \(\le 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 \times 10^k\kern-0.05em\textsf{,}\) for the magnitude \(10^k\) 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\} \times 10^k\) are available, \(1 \times 10^k\) is chosen with 10% frequency and \(2 \times 10^k\) with 25% frequency (\(5 \times 10^k\) otherwise); where only \(\{1, 2\} \times 10^k\) are available, \(1 \times 10^k\) 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. 12
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.
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 fabricated counterpart under NU6.3), 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 blocks, about twenty minutes at 75-second
block spacing), rounded to the nearest whole number of blocks, with any
draw exceeding PREP_MAX_DELAY (96 blocks, about two
hours) discarded and redrawn; 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.
Each migration transaction SHOULD have:
lock_time = 0;The canonical expiry height is computed from the transaction’s scheduled
broadcast height: take the most recent height at or below it that
EXPIRY_MODULUS (the number of blocks corresponding to 30 days, 34560 blocks at 75-second block
spacing) divides without remainder, and add 2 × EXPIRY_MODULUS
(69120 blocks): a rolling window that guarantees every transaction between one
and two months of validity from its scheduled broadcast. 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 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.
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: blocks whose height is
\(\equiv 0 \pmod{M}\kern-0.05em\textsf{,}\) with \(M\) provisionally 144. At the current target
block spacing of 75 seconds, a boundary occurs approximately every
\(144 \times 75\,\text{s} = 3\) hours. This interval concentrates migration
transactions onto fewer shared anchors, enlarging per-anchor cohorts.
EXPIRY_MODULUS (34560 blocks) is an exact multiple of \(M\) (240 buckets),
so expiry buckets align with anchor buckets.
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 \geq 1\kern-0.05em\textsf{,}\) 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 \geq 1\kern-0.15em\) ), because all denominations are canonical and therefore not attributable to a single wallet by their values (see On multiplicity and cohorts).
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, 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 \(\lambda = 1 /\) MEAN_DELAY, so that the
expected delay is MEAN_DELAY (provisionally 66 blocks, about 82
minutes at 75-second block spacing), rounded to a whole number of blocks; any
draw exceeding MAX_DELAY (provisionally 576 blocks, about twelve
hours) 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 one full bucket interval past the lowest candidate boundary — 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.
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).
If a stored transaction becomes invalid (for example, because its input notes were spent outside the migration, or it expired), the wallet MUST detect this and prompt the user to re-initiate the affected step (see Error handling).
Background scheduling MUST be treated as best-effort. The operating system chooses the exact execution time within the requested window.
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.
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.
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.
Before the schedule is committed, the wallet MUST present a network-privacy step.
The wallet MUST offer Tor 13 and (if available) Nym 14 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.
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.
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.
The wallet SHOULD report migration progress.
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.
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 15 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 12 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.
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.
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 blocks and the bucket modulus \(M\) = 144, 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 blocks rather than 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.
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:
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).
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.
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 fabricated counterpart, so a bundle’s action count is the sum of its spends and outputs; values and the fabricated/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 fabricated components are indistinguishable from real ones, an \(N\kern-0.15em\) -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.
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.
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
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 16, 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.
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.
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).
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.
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:
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).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.
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:
DENOM_CAP bounds.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.
This subsection is non-normative.
This subsection is non-normative.
For large balances:
MEAN_DELAY blocks per part). A very
large balance therefore takes proportionally longer to migrate.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.This subsection is non-normative.
Two different quantities should not be conflated:
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.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).
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 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\kern-0.05em\textsf{.}\)
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.
Information on BCP 14, “RFC 2119: Key words for use in RFCs to Indicate Requirement Levels” and “RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words” ↩︎
Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later ↩︎
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.12: Mainnet and Testnet ↩︎
ZIP 209: Prohibit Negative Shielded Chain Value Pool Balances ↩︎