Skip to content
ZIPs.tools

ZIP 318

Orchard to Ironwood Migration

  • Draft
Proposal metadata
ID
318
Status
Draft
Category
Wallet
Owners
  • Schell Carl Scivally
  • Pacu Gindre
  • Kris Nuttycombe
Created
2026-06-16
License
MIT
Contents
  1. Platform considerations
  2. Status notifications
  3. Progress visibility
  4. User-facing copy requirements
  5. Error handling
  6. Rationale
  7. Rationale for the scheduled background approach
  8. Rationale for Poisson transfer scheduling
  9. Rationale for the stateful design
  10. Rationale for canonical amount selection
  11. Rationale for the canonical transaction shape
  12. Rationale for decoupling synchronization from broadcast
  13. Rationale for anchor-height bucketing
  14. Rationale for anchor selection
  15. Privacy Implications
  16. Threat model
  17. Why value collision provides privacy
  18. On multiplicity and cohorts
  19. On subset-sum
  20. On targeted note tagging
  21. Accepted residual leaks
  22. Whale handling
  23. A note on cohort size vs. per-wallet multiplicity
  24. Deployment
  25. Reference implementation
  26. Alternatives
  27. References
  28. Footnotes
  • 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.

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

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 blocks and the bucket modulus MM = 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.

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 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 NN-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 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 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 BB is the set of wallets that had scanned beyond BB by proving time — a set that an adversary who compromises a light wallet server can approximate by observing which clients have fetched blocks past BB — 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 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.
  • kk 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 kk: the number of a single wallet's parts in one cohort. Under canonical-only amounts, k>1k > 1 is safe (parts are unattributable), but K_MAX (enforced by redrawing anchor ages) keeps kk 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.

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 kk adds behavioural entropy — itself a fingerprint — without dispersing co-located parts; it is dominated by a deterministic, cohort-size-governed kk.

  • 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 ↩

  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. NU6.2 and NU6.3 updates to the Orchard Circuit (draft) ↩

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

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

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

  13. Tor Project ↩

  14. Nym ↩

  15. ZIP 203: Transaction Expiry ↩

  16. ZIP 244: Transaction Identifier Non-Malleability ↩

Citation graph

Hover or tap a node to see its title and citations.
Loading citation graph…