refactor!: answer the #2428 review threads that were still live in code - #2574
Open
zancas wants to merge 4 commits into
Open
refactor!: answer the #2428 review threads that were still live in code#2574zancas wants to merge 4 commits into
zancas wants to merge 4 commits into
Conversation
Oscar's review of #2428 left thirteen unresolved threads; most were resolved by later work, and three still described the code. This commit lands those three, and sharpens one comment a fourth thread questioned. `get_migration_heights` now returns the heights directly, mapping the no-sync-data case to the typed `WalletError::NoSyncData` instead of returning `Result<Option<_>>` for five callers to unwrap identically (#2428 (comment)). The confirmation-wait errors rename to `MigrationTransactionFailed` and `MigrationConfirmationTimeout`: `await_migration_confirmations` serves both the note-splitting rounds and the parts-broadcast path, so the `Split*` names misreported part failures (#2428 (comment)). `SplitDidNotConverge` keeps its name because it is genuinely split-only. The doc comment on `broadcast_due_parts_selected` sheds its duplicated paragraphs (#2428 (comment)), and the ironwood comment in `shielded_spendable_balance` now states why a note can lack a position instead of asserting the conclusion (#2428 (comment)). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zancas
marked this pull request as draft
July 28, 2026 18:09
zancas
marked this pull request as ready for review
July 28, 2026 18:23
…tract This implements the two suggestions from the #2574 review. MigrationError::MigrationTransactionFailed and MigrationError::MigrationConfirmationTimeout repeated the enum's name at every use site; they become TransactionFailed and ConfirmationTimeout. The display strings are unchanged, so no user-facing text moves. The rename is breaking on the public MigrationError enum, exactly as the base PR's rename was. A unit test pins the contract #2574 gave get_migration_heights: a wallet that has never synced returns the typed WalletError::NoSyncData from the wrapper itself, so every migration site inherits the typed refusal from one place and the promise in the wrapper's doc comment is enforced rather than asserted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… synced for the first time
Oscar-Pepper
previously approved these changes
Jul 29, 2026
Oscar-Pepper
left a comment
Contributor
There was a problem hiding this comment.
not merging until #2582 (review) is resolved
refactor!: de-stutter the migration errors and pin the NoSyncData contract
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Your review of #2428 left thirteen threads unresolved when the PR merged. This PR is the collected answer. It changes the code where a comment still described it, and it accounts for every other thread below, so each can be resolved from here.
Changed in this PR
get_migration_heightsreturns the heights directly (thread). Agreed, and done. The helper wrapped an infallible call yet returnedResult<Option<_>>, and all five callers unwrapped theNonecase with the sameok_or(WalletError::NoSyncData). The function now returns the heights directly and maps theNonecase toNoSyncDataitself, which deleted the identicalok_orat all five call sites. The wrapper survives for one reason, which its doc now states: it pins the wallet's ownmin_confirmationsand types the no-sync-data case once.The confirmation-wait errors drop their
Splitnames (thread). Agreed. The function is generic over migration transactions, yet its errors said "split". This PR renames the two shared variants toMigrationTransactionFailedandMigrationConfirmationTimeout;SplitDidNotConvergekeeps its name, being genuinely split-only. The rename is breaking on the publicMigrationErrorenum.The duplicated comment is gone (thread). The doc on
broadcast_due_parts_selectedcarried its two paragraphs twice, verbatim. Fixed.The ironwood balance comment now answers your question (thread). You asked why compact block scanning would fail to find positions for ironwood. It does not fail: scanning finds ironwood positions, and pepper-sync carries ironwood located trees and a nullifier-to-position map. A note lacks a position only until a scan locates its block in the commitment tree, and the wallet records its own outputs at broadcast, before any scan, so those notes hold no position and cannot be witnessed. The comment in
shielded_spendable_balancenow states this reason instead of asserting the conclusion.Resolved by work that landed since the review
The
COINconstant (thread). Agreed: in this codebase "coin" names a transparent output, and the constant collided with that sense. The local constant is gone.params.rsnow importszcash_protocol::value::COIN, so the workspace defines no such name of its own.The
dust_floorfield (thread). The field is renamed, though not todenom_floor. Your name has the virtue of saying what the field does: it floors the denomination ladder. We chosemax_residual_valueinstead, because ZIP 318 names the quantityMAX_RESIDUAL_VALUEand the reference crate exports it under that name; a field that mirrors the specification reads without translation.An error when sync data is missing (thread). Done as you suggested.
WalletError::NoSyncDatanow exists, and every planning and broadcast path returns it whenlast_known_chain_heightisNone. This PR folds the same mapping intoget_migration_heights, so the migration height reads are typed as well.Visibility of
migration_note_values(thread). The function ispub(crate)now.Visibility of
build_note_split_transaction(thread). The function ispub(crate)now.Protection before NU6.3 activation (thread). Later work added the protection you asked about. The start path refuses with
IronwoodEraTooYounguntil the first window boundary after NU6.3 activation, and the scheduler floors every anchor at activation, so a pre-activation release fails once with a typed error instead of striking the build error repeatedly.Checkpoint count versus bucket modulus (thread). The code landed where your third comment arrived. Raising the checkpoint retention would have guaranteed every boundary a witness, but, as you said, it would store extra data forever for the sake of a single migration. Instead,
refresh_part_witnessescaches each part's witness while its boundary checkpoint is still retained, and a part whose boundary is pruned takes theMissedBoundaryskip and is re-assigned.Kept as it is
The
SyncDomainre-export (thread). Removing the re-export would giveSyncDomaina single canonical path and spare the reader one indirection. We kept it nonetheless:wallet/traits.rsandsync/spend.rsboth importcrate::SyncDomainthrough it, and because it ispub(crate)it adds nothing to the crate's public surface.Tracked onward
The dust-floor economics (thread). Later work resolved most of this in your direction. The floor is no longer local policy: it is ZIP 318's
MAX_RESIDUAL_VALUE(0.01 ZEC), imported from the reference crate. Notes at or belowsweep_min, and any pool too small to fund the smallest denomination, now stay in the pre-Ironwood pool as residual — recorded in the plan and covered by the consent hash — rather than folding into fees. One fee-fold remains: when a pool funds at least one denomination, the sizing surplus below the smallest denomination becomes the final split transaction's fee. That surplus is bounded below 0.01 ZEC per migration, but your underlying point stands — the user should not pay value away silently — so #2573 tracks whether the surplus should become a residual V2 change note instead.🤖 Generated with Claude Code