Skip to content

[ZIP 318] Orchard to Ironwood migration#1317

Open
pacu wants to merge 13 commits into
mainfrom
pacu/ironwood-migration-unified
Open

[ZIP 318] Orchard to Ironwood migration#1317
pacu wants to merge 13 commits into
mainfrom
pacu/ironwood-migration-unified

Conversation

@pacu

@pacu pacu commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR rewrites zips/draft-schell-ironwood-migration.md in place into a
single unified draft that integrates two earlier Orchard-to-Ironwood migration
drafts: the stateful, background-scheduled wallet flow from issue
#1315 ("Path A") and this branch's
stateless, canonical-quantization proposal. Editing the same file keeps the
review diff clean as an update/counter-proposal to the existing draft.

How the two designs were reconciled

  • Normative mechanism = Path A. A two-phase flow: (1) mandatory
    note-splitting (wallet-internal send-to-self), then (2) scheduled, pre-signed,
    background-broadcast migration transactions de-correlated from app opens.
    Best-effort background scheduling (iOS BGTaskScheduler / Android WorkManager)
    with an on-launch fallback; synchronization decoupled in time from broadcast;
    Tor opt-in network-privacy step; detailed progress, notification, platform,
    and error-handling requirements.
  • Canonical quantization folded in as the RECOMMENDED amount-selection
    heuristic
    inside Phase 2's split/scheduling algorithm. The split now produces
    canonical power-of-ten denominations rather than high-entropy random sizes,
    with the collision-privacy argument as rationale; "random arbitrary/partitioned
    amounts" are listed among rejected alternatives.
  • Unified timing concept. Path A's ~6h anchor-height buckets and this branch's
    height ≡ 0 mod M boundaries/cohorts (~5.3h) are merged into a single
    network-wide bucket/boundary scheme forming cohorts that mix wallets. The
    shared previous-boundary anchor and the auth-data forward-looking
    recommendation are retained.
  • State conflict resolved explicitly. The mechanism is stateful (persisted
    schedule + pre-signed transactions); statelessness and multi-device recovery are
    non-requirements (per [ZIP 318] Orchard to Ironwood Migration #1315). The stateless, chain-recoverable approach is
    preserved as a discussed rejected alternative with its tradeoff rationale,
    not silently dropped.

Where schell's analysis was preserved (non-normative)

The following are kept and clearly marked non-normative: Consensus context, Why
amounts leak, Threat model, Why value collision provides privacy, On multiplicity
and cohorts, On subset-sum, Accepted residual leaks, Whale handling, cohort size
vs. per-wallet multiplicity, Rationale for anchor selection, and the
Rejected-alternatives table (extended with Path A's rejected on-open / "Path B"
approach and the stateless alternative).

Path A's user-path and error-case material is included as non-normative
appendices in <details> blocks.

Preamble / conventions

Dual owners (Schell Carl Scivally, Pacu Gindre), Kris Nuttycombe credited,
Status: Draft, Category: Wallet, Discussions-To → issue #1315. BCP 14
boilerplate lists only the keywords actually used. Validated with multimarkdown:
definition lists render (10 <dt>), no unresolved footnote refs, every internal
(#anchor) link resolves to a generated id, and all headings are unique.

Refs #1315. This integrates that issue's "Path A" specification; it intentionally does not use a closing keyword, since the issue should remain open as the discussion thread and this PR targets a feature branch rather than the default branch.

AI disclosure

Claude Opus 4.8 was used to integrate and draft this unified document under
maintainer direction; the human contributor is the responsible author. The commit
carries the corresponding Co-Authored-By: trailer.

🤖 Generated with Claude Code

schell and others added 2 commits June 16, 2026 10:02
@pacu
pacu requested a review from schell June 29, 2026 18:26
@pacu
pacu force-pushed the pacu/ironwood-migration-unified branch from 8ece50f to 5df468a Compare June 29, 2026 18:28
@pacu
pacu force-pushed the pacu/ironwood-migration-unified branch from 5df468a to 8c18192 Compare June 29, 2026 22:27
@linear

linear Bot commented Jun 29, 2026

Copy link
Copy Markdown

COR-1376

Comment thread zips/draft-schell-ironwood-migration.md Outdated
Comment thread zips/draft-schell-ironwood-migration.md Outdated
Comment thread zips/draft-schell-ironwood-migration.md Outdated
Comment thread zips/draft-schell-ironwood-migration.md Outdated
Comment thread zips/draft-schell-ironwood-migration.md Outdated
Comment thread zips/draft-schell-ironwood-migration.md Outdated
Comment thread zips/draft-schell-ironwood-migration.md Outdated
Comment thread zips/draft-schell-ironwood-migration.md Outdated
Comment thread zips/draft-schell-ironwood-migration.md Outdated
…ration draft

This unifies two earlier Orchard-to-Ironwood migration drafts into a single
Wallet best-practices draft:

- Adopts the issue #1315 "Path A" mechanism as the normative wallet flow: a
  two-phase process (mandatory note-splitting send-to-self, then scheduled,
  pre-signed, background-broadcast migration transactions de-correlated from
  app opens), with best-effort background scheduling (iOS BGTaskScheduler /
  Android WorkManager), an on-launch fallback, sync decoupled from broadcast,
  a Tor opt-in network-privacy step, and detailed progress/error/platform
  requirements.
- Folds schell's canonical power-of-ten quantization in as the RECOMMENDED
  amount-selection heuristic inside the Phase 2 split/scheduling algorithm,
  with the collision-privacy argument as rationale and random amounts listed
  among rejected alternatives.
- Unifies the timing concept: Path A's ~6h anchor-height buckets and schell's
  height-mod-M boundaries/cohorts are merged into one network-wide
  bucket/boundary scheme forming cohorts that mix wallets.
- Keeps schell's non-normative analysis (consensus context, why amounts leak,
  threat model, why collision provides privacy, multiplicity/cohorts,
  subset-sum, accepted residual leaks, whale handling, anchor selection) and
  his rejected-alternatives table, extended with Path A's rejected on-open
  approach.
- Resolves the state conflict explicitly: the mechanism is stateful (persisted
  schedule + pre-signed transactions); statelessness/multi-device recovery are
  non-requirements. The stateless, chain-recoverable approach is preserved as a
  discussed rejected alternative with its tradeoff rationale.
- Updates preamble (dual owners, Kris credit, issue #1315 discussions link) and
  adds user-path and error-case appendices as non-normative details blocks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pacu
pacu force-pushed the pacu/ironwood-migration-unified branch from 8c18192 to d6ffeaf Compare June 30, 2026 01:21
@pacu
pacu requested a review from daira June 30, 2026 13:41
@nuttycom
nuttycom changed the base branch from feat/ironwood-migration to main June 30, 2026 14:21
Comment thread zips/draft-schell-ironwood-migration.md Outdated
Comment on lines +284 to +321
The migration flow MUST satisfy the following high-level goals. The Specification
section defines how they are met; this section does not itself impose conformance
requirements beyond those.

* The user MUST be able to migrate *Orchard-pool* funds to the *Ironwood pool*,
and MUST be informed that doing so is necessary to retain access to those funds.

* The wallet MUST obtain the user's consent, before any funds leave the
*Orchard pool*, to the public revelation of pool-crossing amounts, consistent
with ZIP 315. [^zip-0315]

* All *Orchard-pool* funds at or above the dust floor SHOULD eventually be
transferred to the *Ironwood pool*.

* By default the migration SHOULD de-correlate individual transfers from each
other and from the user's interaction with the application, to the extent
practical on the target platform. In particular it SHOULD mitigate leakage of:
the linkage of particular migration transactions to one another (clustering);
information about the distribution of individual note values; and information
about a wallet's total balance.

* The migration SHOULD let a user complete migration with a small number of
signing sessions for typical balances, because adoption drives the size of the
anonymity set on which everyone's privacy depends.

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

* The user MUST be given an informed choice about network-layer privacy (Tor or
VPN) before the migration begins.

* The wallet MUST report migration progress and MUST NOT present the fallback
(prompting on the next application open) as an error.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: requirements don't use conformance language; these are bounds that we evaluate against to determine whether the specification below satisfies the requirements.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — updated in 4d46580. The Requirements section no longer uses BCP 14 conformance keywords; the goals are now stated as plain declarative bounds ("The user can migrate…", "All Orchard-pool funds… are eventually transferred…", etc.), and the intro clarifies that these are the bounds the Specification is evaluated against, with conformance language reserved for the Specification section.

pacu and others added 2 commits June 30, 2026 20:48
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address review: the Requirements section stated high-level goals using
BCP 14 conformance keywords (MUST/SHOULD/etc.). Requirements are bounds
the Specification is evaluated against, not conformance requirements
themselves, so restate them in plain declarative language and keep the
conformance keywords to the Specification.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread zips/draft-schell-ironwood-migration.md Outdated
* The exact splitting heuristic (number of notes and their sizes) is an open
point for the Zcash core team. Sizing notes so that the canonical denominations
of [Amount selection, canonical quantization](#amountselectioncanonicalquantization)
can each be funded without waiting on change is a natural target.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we include note consolidation here too? Is that a viable alternative?

If I have two Orchard notes of 4, 6 + N ZEC, where N is the amount I would need to pay as fee for a consolidation transaction, wouldn't it make more sense to just consolidate instead of splitting my notes into ten 1 ZEC notes?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ideal is that you split your balance into transactions that are fee inclusive in a way that the balances can't be easily be deduces as belonging to the same wallet.

@daira daira changed the title [ZIP 318] Integrate Path A scheduled-background wallet flow into the Ironwood migration draft [ZIP 318] Orchard to Ironwood migration Jul 5, 2026
zancas added a commit to zingolabs/zingolib that referenced this pull request Jul 14, 2026
…sent

ZIP 318 (zcash/zips#1317, Turnstile section) confirms the spec target:
payments to Orchard receivers must travel as Ironwood once NU6.3
activates. The pinned zebrad/librustzcash stack does not yet enforce
that routing, and the 2026-07-14 live runs adjudicated the open
hypotheses the other way: coinbase and ordinary sends still land as
legacy Orchard notes with V5-era fees. The ADR now states both layers
and defines the live suite's i: 0 assertions as canaries that flip to
the spec-target expectation when the stack catches up, rather than
regressions.

The glossary attributes the receiver-routing rule to the ZIP 318
Turnstile and gains a Turnstile entry, so the Ironwood entry no longer
presents unenforced spec behavior as an observed chain fact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread zips/draft-schell-ironwood-migration.md Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be renamed to zip-0318.md since the ZIP number has now been assigned.

@zookoatshieldedlabs

Copy link
Copy Markdown

Shielded Labs has written a threat model doc, visible to anyone who has this link: https://docs.google.com/document/d/1z4Aj7tO34RKk0SXZYkNXtswxdBXKbR_IJ_Xw5EJljkc . It includes a slightly different algorithm for selecting amounts and timings and anchors (Appendix A), but don't get hung up on that! The point of the document is that Appendix A is much less important for practical protection of real users than the other parts. :-) And, as described in that document, we currently think that it is not necessary for all or many wallet implementations and users to use compatible (indistinguishable on the blockchain) migration transactions, so that's another reason that Appendix A is less important than the rest of it.

Please review! If there are any major errors in this doc, please let us know ASAP. The main recommendation is to use Tor/Nym in specific ways, and we don't yet know how feasible this will be, and Zodl in particular is at the head of the class on that particular area, so please teach us.

nuttycom and others added 8 commits July 16, 2026 09:57
Generalize Phase 1 from note splitting to note preparation, covering the
full spectrum of existing note distributions (splitting, joining, or
both). Preparation is restated as the pre-signing prerequisite: an
exact-value funding note must exist on-chain for every scheduled part.

Specify the canonical migration transaction shape as exactly one Orchard
spend padded to two actions and one Ironwood output, so that the presence
of change is unobservable and automated migration transactions are
indistinguishable from manual Orchard -> Ironwood spends. Document the
consequences: an enlarged anonymity set, change carrying operational cost
but no privacy penalty, and owner-side chain recovery disambiguated by
viewing-key information.

Update the residual rule (deterministic sub-DUST_FLOOR remainder),
error handling (preparation partitioning, canonical fee changes), and
open issues accordingly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Remove the Ironwood pool exposition from the Abstract and lead the
Motivation with the primary purpose of the pool: enabling verification of
the Zcash shielded supply after NU6.2 mitigated an Orchard circuit
soundness flaw.

Remove the non-normative "Consensus context" section, folding its
load-bearing content into the sections that use it: the turnstile and
migration necessity into the Motivation, the routing-change scope
exclusion into the Motivation's goal paragraph, the post-upgrade old-pool
population argument into the canonical-transaction-shape rationale, and
the predicate/consensus-shape alignment into the amount-selection
section. Well-known pool mechanics are dropped in favor of references.

Remove the "Why amounts leak" section; the Motivation now simply notes
that pool-crossing transfers reveal net amounts, keeping the corollary
that amount shape and broadcast timing are the only privacy levers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move amount selection into Phase 1 as its first step, since the note
preparation transactions cannot be constructed without the target note
values; the canonical migration transaction structure remains in Phase 2
under its own heading.

Replace pure power-of-ten denominations with the 1-2-5 series
(v = n * 10^k, n in {1, 2, 5}), with 10000 ZEC as the largest
pool-crossing denomination and DENOM_CAP = 10000 ZEC plus the canonical
fee bounding funding-note values. Add an optional randomized
decomposition for balance hiding, with provisional selection frequencies
biased toward larger denominations.

Fix the canonical note-preparation transaction size at 16 Orchard
actions (provisional) and require confirmation to the end of the anchor
epoch before prepared notes are spendable.

Rename DUST_FLOOR to MAX_RESIDUAL_VALUE, define both quantization
parameters at first use, and add a terminology entry giving the privacy
rationale for the residual: preventing small user-identifying amounts
from leaking in pool-crossing transactions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t model

Break the single de-correlation requirement into named privacy
invariants: transfer unlinkability, behavioural de-correlation, balance
privacy, note-distribution privacy, holdings-concentration privacy, and
migration-status privacy (resistance to note tagging by an adversary
seeking to learn whether a victim has migrated).

Extend the threat model with the targeted adversary who knows or chooses
a victim's incoming note values, and add a non-normative analysis of why
note tagging fails: quantization severs received values from migrated
values, note preparation absorbs tagged notes invisibly, and randomized
decomposition makes the emitted multiset unpredictable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ection

Rewrite Transfer scheduling: parts are shuffled into random order, then
scheduled in block deltas with inter-transfer delays drawn from an
exponential distribution (mean MEAN_DELAY = 144 blocks, draws above
MAX_DELAY = 576 blocks redrawn), forming a Poisson process; all
scheduling and decomposition randomness requires a CSPRNG. Background
processing windows either synchronize (updating anchors and proofs of
upcoming transactions) or broadcast, never both. A new rationale
subsection covers memorylessness, Poisson superposition, the need for
delays comparable to the bucket interval, and why routine background
synchronization is safe under shared boundary anchors.

Specify bucketed expiry heights: the most recent multiple of
EXPIRY_MODULUS (34560 blocks) plus 69120, a one-to-two-month rolling
window shared by every wallet constructing in the same 30-day period.

Set the anchor bucket modulus M to 144 blocks (3 hours), equal to
MEAN_DELAY and an exact divisor of EXPIRY_MODULUS; the prior 256 was a
holdover from a predecessor draft.

Update Pre-signed transaction storage and the anchor-selection rationale
to reflect that the version 6 format commits anchors as authorizing
data, so pre-signed transactions' anchors and proofs are updated at
broadcast time without re-signing.

Replace the Rejected-alternatives table with a top-level Alternatives
section (per the ZIP 231 precedent), adding short-interval memoryless
timing as a rejected alternative, and strip references to the
predecessor drafts throughout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace previous-boundary anchor pinning with a randomized draw from the
candidate anchor set: boundaries above the NU6.3 activation height, at
or after the funding note's creation, and at or before the most recent
boundary observed at proving time. Pinning to the newest boundary would
bottleneck the anonymity set at each boundary to the wallets that had
already scanned past it — a set a compromised light wallet server can
approximate — and shortly after each boundary that set is small. A
randomized anchor implies only that its creator scanned beyond that
boundary at some time before broadcast; consequently no
privacy-preserving block-download mechanism is required for the
document's invariants.

Weight the draw by recency: the anchor age in boundaries is geometric
with parameter 1/2 over ages >= 1 (the most recent boundary is never
used), capped at ANCHOR_AGE_CAP = 16 boundaries and truncated to the
candidate set. The rationale records the trade-off analysis: a fixed,
capped distribution makes anchor age identical across wallets rather
than a fingerprint of preparation date, bounds the inherent
preparation-time leak, concentrates transactions into large per-anchor
cohorts, aligns with the recent anchors of manual cross-pool spends,
and bounds proving-state retention; the cost, a sharper posterior on
proving time, is blunted by proving/broadcast decoupling and the
exponential scheduling delays.

Redefine cohorts as transactions sharing a boundary anchor, note that M
no longer affects migration speed, and update the synchronization-timing
analysis, fallback rescheduling, and open issues accordingly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reconcile the specification with the Poisson schedule and randomized
anchors: multiplicity now emerges from the anchor-age draws (K_MAX
enforced by redrawing), the signing-session machinery is replaced by
single-session authorization with additional sessions only for
multi-layer preparation not pre-signed as a chain, and stale
pinned-anchor phrasing is updated throughout (terminology, overview,
whale handling, accepted leaks, open issues).

Represent layered preparation: when a funding note must combine value
from more notes than one transaction can spend, or a large note must be
divided into more funding notes than one transaction can produce,
preparation proceeds in layers that may be signed in a single session
but cost one bucket interval each to prove and broadcast. Layered
splitting (whales) and layered consolidation (many small notes) are
indistinguishable on-chain.

Correct the pre-signing justification: Orchard nullifiers are
position-independent, so the schedule can be signed as soon as the
preparation transactions are constructed; only proving requires mined
notes and a candidate boundary anchor.

Adopt NU6.3 action-counting semantics (actions = spends + outputs, each
paired with a fabricated counterpart), updating the canonical-shape
mechanism text and the 16-action preparation budget (fan-in/out ~15).

Surface the migration entry point once NU6.3 is scheduled, enabling
pre-activation preparation; align the network-privacy section with the
Requirements by offering Nym alongside Tor; replace the undefined
"anchor epoch" term with anchor-height bucket.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread zips/zip-0318.md

### Note preparation transactions

A migration transaction spends exactly one *Orchard-pool* note (see

@dannywillems dannywillems Jul 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Human review/implementation note (will be edited) - don't review:

Let's define $N$ the total balance to migrate. $N$ is decomposed into $l$ quantas, $n_l$, plus $l$ fees. $N = \sum_{i = 1}^l n_i + l * fees$.
The value $N$ is not necessarily the total balance of the wallet user. The total balance of the user can be higher for very large whales, and therefore the denomination/note splitting process will happen multiple times.

The user wallet has $M$ notes, note $M_i$, each of a balance $b_i$.
We want at the end to have a set $M'$ of notes containing, at least, the notes for the quanta $n_l$. The total balance of the notes in M' will be strictly smaller than the balance of the notes in $M$, as transactions incur fees. If we note $B(M)$ the sum of the values of each note in the set $M$, $Q$ the set of notes for the quanta, we have:

$B(M) = B(Q) + B(M' \setminus Q) + l * \text{fees}$

The problem of note preparation is actually a note consolidation problem, which is actually a system of multilinear equations, where variables are existing notes, quanta and changes, with "selectors" between rows to pick or not the previously created changes.
The problem is to create $l$ transactions (we have the constraints below that one note per quanta for the output), at least, from the notes $M_i$.
For each quantum $n_l$, the implementation must select $k$ notes from the set $M$ such that

$$\sum_{i = 1}^{k} b_{i, l} = n_l + \text{fee} + \text{change}_{l}$$

with $k$ depending on $l$.

The change will go back to a change address authorized by the Orchard pool after NU6.3. This change will become part of a set $M'$.
The consolidation problem will happen in layers, which we can see as a finite sequence of intermediary set $M_k$ which converges to the wanted set $M'$. Why a finite sequence and not directly a set $M'$? Because the change might be needed to create the next quanta.

If we develop the set of $l$ equations, we have the following:

$$\begin{align} \sum_{i = 1}^{k_{1}} b_{i, 1} & & = n_{1} + \text{fee} + c_{1} \\\ \sum_{i = 1}^{k_{2}} b_{i, 2} & + p_{1,1} c_{1} & = n_{2} + \text{fee} + c_{2} \\\ \sum_{i = 1}^{k_{3}} b_{i, 3} & + p_{1,2} c_{1} + p_{2, 1} c_{2} & = n_{3} + \text{fee} + c_{3} \\\ ... \\\ \sum_{i = 1}^{k_{l}} b_{i, l} & + \sum_{j = 1}^{l - 1} p_{j, t} c_{j} & = n_{l} + \text{fee} + c_{l} \\\ \end{align}$$

with $p_{i, t} = 0$ or $p_{i, t} = 1$ depending on using or not the change, and they must be only used once.
We can relax the system by having a set of system of equations (or layers) where all $p_{i, t}$ are equal $0$ within the same system, and $c_{i}$ are reported to the next system. In other words, in each layer, the equations are independent, and we want to minimize the set of layers as more layers mean we need to wait for more transaction confirmations/blocks.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: we're getting a DAG of notes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants