Skip to content

Appendix D: Solutions for Chapter 38

Solutions and editorial notes for Chapter 38: Wallets, addresses, and key rotation. Compute exercises have worked solutions; open-ended exercises have an editorial note describing what a strong answer addresses.

The fuller versions of these routines are in the wallet_rotation package under solutions/ch38-wallets-addresses-key-rotation. From a clone of the companion repository, pytest tests/ch38 runs its suite. Appendix C has the setup.

Worked solution. Wallet Mosca-window breach under three Z scenarios for the Strand wallet surface (X = 10, Y = 4).

Breach window per scenario, computed as X+YZ=14ZX + Y - Z = 14 - Z:

ScenarioZBreach windowSafe window (ZYZ - Y)
Aggressive410 years0 years
NCSC working assumption95 years5 years
Mid-2040140 years (boundary)10 years

Cadence options ranked by operational cost (lowest first): calendar, every-N-years, every-N-transactions, external-trigger. Per-scenario feasibility:

CadenceAggressive (Z=4Z = 4)NCSC (Z=9Z = 9)Mid-2040 (Z=14Z = 14)
calendarinfeasible (breach > 0)infeasible (breach > 0)feasible (no breach)
every-N-yearsinfeasible (safe window 0)feasible at N=5N = 5feasible at any N10N \le 10
every-N-transactionsinfeasible (same constraint)feasiblefeasible
external-triggerdefinable, not risk-clearingfeasiblefeasible

The cheapest feasible cadence under each scenario: none for the aggressive scenario, every-N-years (with N=ZY=5N = Z - Y = 5) for the NCSC scenario, and calendar (with N=Y=4N = Y = 4) for the mid-2040 scenario.

The aggressive case has no feasible cadence, and that is the answer rather than a gap in it. The safe window collapses to zero years: the wallet’s full migration time alone fills the budget to CRQC arrival, so no fixed interval fits, and a trigger firing at time tt can finish migrating by ZZ only if t+YZt + Y \le Z, which here means t0t \le 0. There is no positive waiting interval left to wait through. recommend still returns external-trigger in this branch, and that return records a definable fallback rather than a feasibility proof: it is the branch the function reaches when every interval-based option has already been ruled out. The chapter names what remains, and none of it is a cadence: immediate migration, an explicit reduction of wallet exposure (freeze or move the at-risk balances), or explicit risk acceptance recorded as a decision. The NCSC case admits a five-year rotation interval, which brings effective seed lifetime to 5 and clears the breach. The mid-2040 case has no breach to resolve. The operator rotates on the same calendar cadence the address book already turns over.

Worked solution. Redesigned BIP-32-like derivation tree under ML-DSA-65 with a bounded watch-only architecture (periodic-publication property).

The redesign drops non-hardened branches because ML-DSA-65 has no public-key-only derivation. The chapter framing is that PQ wallets reuse BIP-32 path syntax and the HMAC-SHA-512 chain-code schedule as a BIP-32-like seed schedule rather than literal BIP-32 CKD. Every step on the BIP-44-style path becomes hardened, with the child key produced by ML-DSA.KeyGen_internal on the derived 32-byte seed (the chapter’s framing, since the external KeyGen API takes no seed). The path under the redesign reads m/44'/0'/0'/i'/j' where i' is the publication-epoch index and j' is the address index within that epoch.

At the start of each rotation cycle the wallet operator publishes a signed view descriptor for the current epoch’s branch (m/44'/0'/0'/i'). The descriptor is a finite, pre-derived list (or a Merkle commitment to a list) of child addresses under that branch. The watcher monitors that bounded set. The operator stages a fresh epoch (i' \to (i+1)') at the next publication event, hands the new view descriptor to the watcher, and rotates funds from the old epoch to the new. The chapter’s “xpub” framing is misleading under this redesign. In BIP-32 an xpub authorizes unbounded non-hardened child-public-key derivation. Here the derivation capability lives on the wallet operator side, and the watcher’s horizon is bounded by what the operator has explicitly pre-derived and published.

A defensible publication cadence: every NN years where NN matches the chapter’s recommended every-N-years cadence under the operator’s ZZ scenario. Under Z=9Z = 9 this is N=5N = 5 per Exercise 1.

The bound on the watcher’s address-derivation horizon between publications is the number of child addresses the operator commits to under each epoch’s branch. Each hardened child requires the operator’s secret to derive, so the watcher cannot extend the address set on its own. Suppose the operator commits 256 addresses per epoch (one-byte address-index space): the watcher monitors at most 256 pre-derived addresses between epoch publications. A more aggressive operator with 65536 addresses per epoch (two-byte address-index space) gives the watcher a 65536-address horizon at the cost of 65536 hardened derivations per epoch.

A wider per-epoch horizon lowers per-spend operational cost at the cost of a higher bookkeeping burden on the watcher. The chain imposes no constraint on the address count per epoch, so the wallet vendor decides.

Editorial note. A defensible answer applies the four-shape custody taxonomy and picks a candidate per shape from the chapter’s matrix.

The custodial exchange holding user balances under a single corporate hot wallet runs as single-device-hot if the hot wallet sits behind a single hardware security module cluster, or multi-device-hot if the cluster replicates state across multiple data centers. ML-DSA-65 is the candidate per the chapter’s matrix in either case: stateless, fits all shapes, lowest byte cost among the post-quantum candidates. The constraint ML-DSA-65 imposes that legacy ECDSA did not is the 3309-byte signature size per FIPS 204 Table 2 against ECDSA’s 64-byte canonical form. Per-transaction on-chain throughput drops by the factors Ch 37 derives at that signature size: 12.7-fold on Bitcoin and 3.4-fold on Ethereum.

The self-custody hot wallet running on a phone and a desktop with a shared seed runs as multi-device-hot. The chapter’s matrix excludes XMSS-MT and LMS from this shape because the stateful index cannot be synchronized reliably across devices. ML-DSA-65 or the Ed25519+ML-DSA-65 composite both fit. The constraint ML-DSA-65 imposes that legacy ECDSA did not is the loss of the watch-only wallet property: a desktop wallet that previously monitored phone-derived child addresses via the parent xpub cannot do so under ML-DSA. The user-facing failure mode is a watch-only wallet on the desktop that stops resolving new addresses unless the wallet operator redesigns the publication chain into a bounded watch-only architecture (see Exercise 2).

The 2-of-3 hardware-wallet multisig held by three independent trustees runs as multisig-cold. The chapter’s matrix admits XMSS-MT and LMS here: each trustee’s device holds an independent key with its own index, so each is the single-signer state machine of the cold-hw shape and no index is shared. What the matrix does not price is the operational bill, which is the cold-hw state discipline multiplied by three: three atomic non-rollback counters, three backup procedures that must never restore a stale index, and a retired cosigner key whenever any one device loses its state. SLH-DSA-128s is the candidate per the conservative-assumption preference (cold-storage shape, hash-only security argument, and stateless signing that removes the per-cosigner counter discipline entirely). The constraint SLH-DSA-128s imposes that legacy ECDSA did not is the 7856-byte signature per cosigner. With two of the three cosigners signing and all three public keys named in the witness script, the witness carries 3 times 32 bytes for SLH-DSA pubkeys plus 2 times 7856 bytes for signatures, 15,808 bytes before framing. The legacy 2-of-3 ECDSA-secp256k1 setup carries 2 times 64 = 128 bytes of signature plus 3 times 33 = 99 bytes of pubkey, 227 bytes.

All three custody arrangements lose the watch-only-wallet property under any post-quantum candidate the chapter’s matrix recommends. That is a redesign, not a configuration change. SLH-DSA-128s on the multisig is a tradeoff against the byte cost: the operator weighs the conservative-assumption preference over the byte budget for cold storage that holds value for years between spends.

Worked solution. Strand rotation cadence under Z=9Z = 9 given 200000 active wallet addresses, 12 minutes per rotation, and parallelism 100.

Wall-clock time for a full address-book rotation:

time=200,000 addresses×12 minutes per address100 parallel slots=24,000 minutes16.67 days.\begin{aligned} \text{time} &= \frac{200{,}000 \text{ addresses} \times 12 \text{ minutes per address}}{100 \text{ parallel slots}} \\ &= 24{,}000 \text{ minutes} \approx 16.67 \text{ days}. \end{aligned}

Steady-state cadence: the operator can complete one full address-book rotation every 16.67 days running the parallel infrastructure continuously. Over a year of continuous operation, the operator could complete approximately 365/16.6721.9365 / 16.67 \approx 21.9 full rotations.

The chapter’s recommendation under Z=9Z = 9 is every-N-years with N=ZY=5N = Z - Y = 5. Comparison: the operator has to complete one full rotation every 5 years. The parallel infrastructure suffices to do so in 16.67 days, which is 16.67/(5×365)0.9116.67 / (5 \times 365) \approx 0.91 percent of the available window. The infrastructure is therefore over-provisioned for the recommended cadence.

The over-provisioning lets the operator pick a more aggressive cadence than the recommendation if the conservative-assumption preference favors it. A two-year cadence (effective seed lifetime 2 against ZY=5Z - Y = 5) takes 16.67/(2×365)2.316.67 / (2 \times 365) \approx 2.3 percent of the available window. A one-year cadence takes 16.67/3654.616.67 / 365 \approx 4.6 percent of the window. Both are well under the parallel-infrastructure capacity.

The recommendation under Z=9Z = 9 remains N=5N = 5 years because the breach window closes at Xeff+YZX_\text{eff} + Y \le Z when Xeff=ZY=5X_\text{eff} = Z - Y = 5. A more aggressive cadence does not improve the breach decision; it only adds operational cost without changing the Mosca-window outcome. The infrastructure is over-provisioned for the recommended cadence. That buffer is reserved for a tighter ZZ scenario, not for tightening the cadence under the stated Z=9Z = 9.

Editorial note. A defensible mapping pairs each wallet-migration phase with a Ch 30 program phase and names the work-stream owner per the same Ch 30 framework.

The wallet migration sits in discovery during the wallet vendor’s candidate-evaluation period and the hardware-wallet vendor’s firmware-capability survey. The Ch 30 work-stream owner is the wallet vendor for the software side and the hardware-wallet vendor for the firmware side. The work-stream output is a per-shape candidate decision (which primitive fits which custody shape, per the chapter’s matrix) and a per-device-generation firmware-update inventory (which devices admit a PQ-primitive firmware path and which do not).

It moves to first-wave as wallet vendors ship an ML-DSA-65 or SLH-DSA-128s implementation per shape. The Ch 30 work-stream owner is the wallet vendor for the software wallets (mobile and desktop apps) and the hardware-wallet vendor for the secure-element firmware. The hardware-wallet vendor’s firmware-update path is the binding constraint for the cold-storage shapes: hardware wallets in service today may not admit a firmware path to ML-DSA-65 or SLH-DSA-128s without a hardware refresh.

It shifts to broad-rollout as users migrate balances from legacy ECDSA addresses to post-quantum addresses. The Ch 30 work-stream owner is the custody platform for institutional balances (the platform pushes a per-customer rotation cadence through its operations team) and the end user for self-custody balances (with in-app prompts from the wallet vendor’s UI). The chapter’s recommended cadence under Z=9Z = 9 (every-N-years with N=5N = 5) determines the rotation pace in the broad-rollout phase.

It closes in end-of-migration with the legacy ECDSA support sunset. The Ch 30 work-stream owner is the chain governance forum: the BIP process for Bitcoin and the All Core Devs forum for Ethereum. The end-of-migration trigger is the legacy-ECDSA-output sunset block on Bitcoin, or the analogous deprecation block on Ethereum if a deprecation rule lands. Neither exists today, and neither follows from BIP-360: that proposal removes the always-exposed Taproot key path and imposes no sunset on existing ECDSA outputs, so the sunset modeled in Ch 37 Exercise 4 is Strand-specific governance policy standing in for a future, separately approved rule (Ch 41, Exercise 2). The Ethereum half of this sentence already carries the qualification, and the Bitcoin half needs it too.

The hardware-wallet refresh cycle gates the program tempo for cold-storage shapes. The chain’s hard-fork cadence is not the binding constraint there. Bitcoin’s two-layer path (BIP-360 P2MR Draft plus a future PQ-signature soft fork, Ch 37) can activate quickly relative to the hardware-wallet refresh; Ethereum’s account-abstraction path requires no chain-level activation but still depends on the hardware-wallet refresh for cold-storage user balances. The four-phase program for the wallet surface therefore runs longer than the four-phase program for the transaction surface from Ch 37, which sits entirely in the chain-level deployment work.