Review fixes for #909: generation guard, surfaced record rewrites, restored public API#1
Merged
bfoss765 merged 2 commits intoJul 22, 2026
Conversation
… modification, tighten deser cap - compensate_for_observed_spends: only replace the match-derived net_amount when the compensation actually dropped an output detail, keeping the no-observed-spend path byte-identical to pre-#649 behavior; pinned by a new unit test. - record_observed_spends: report whether the persisted observed-spent map actually changed, and surface that as state_modified in check_core_transaction — a consumer persisting only on reported modifications must not lose a recorded spend across a restart; pinned by a new regression test (new spend reports, unchanged redelivery and mempool spends do not). - Replace a comment reference to a nonexistent test with the inline rationale for why input_details and account_match.sent populate together. - Tighten MAX_OBSERVED_SPENT_OUTPOINTS 10M -> 1M (load-time allocation cap from a few hundred MB to a few tens of MB), still far above any legitimate size. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…urface guard-rewritten records, restore public account API Addresses three external review findings on top of PR #909: 1. [P1] The commit-time contiguity guard could still certify unscanned coverage for a newly added account: a rewind landing INSIDE a scanned batch's range passed the height check, and an account add that moved no heights (checkpoint already at the birth floor) was undetectable by any height comparison. ManagedWalletInfo now carries an in-memory account_generation counter bumped on every account add (even height-invisible ones); filter scan snapshots it per wallet and commit refuses to advance a wallet whose generation changed since scan. Pinned by three new dash-spv tests including the mid-batch rewind repro (9000 -> scan [5000..9999] -> rewind 7499 -> commit keeps 7499) and the unmoved-checkpoint case. 2. [P2] The funding-first guard rewrote funding records without ever surfacing them: remove_spent_from_accounts now returns post-compensation clones of every rewritten record, the checker adds them to updated_records on both the relevant and irrelevant paths, and the manager propagates updated_records independent of is_relevant, so consumers persisting per-record updates see the rewrite. 3. [P2] ManagedAccountRefMut::record_transaction/confirm_transaction had silently gone pub -> pub(crate) with changed signatures. The public methods are restored with their original signatures (recording with no observed-spend context, the pre-#649 behavior); the checker uses new pub(crate) *_with_observed_spends variants. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bfoss765
merged commit Jul 22, 2026
3723925
into
bfoss765:fix/wallet-utxo-spend-not-marked-649
1 of 2 checks passed
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.
Review fixes for dashpay#909
Two commits addressing findings from review of dashpay#909, targeting this PR's branch so they land as part of it. All findings were independently verified against the code before fixing.
Round 1 (
aac1bd99)net_amountrecompute incompensate_for_observed_spends: only replace the match-derivednet_amountwhen the compensation actually dropped an output detail, keeping the no-observed-spend path byte-identical to pre-bug: out-of-order block processing causes SPV wallet to miss UTXO spends dashpay/rust-dashcore#649 behavior. Pinned by a new unit test.record_observed_spendsnow returns whether the persisted map changed andcheck_core_transactionsurfaces that viastate_modified— a consumer persisting only on reported modifications must not lose a recorded spend across a restart (that would reopen bug: out-of-order block processing causes SPV wallet to miss UTXO spends dashpay/rust-dashcore#649 for the coin). Pinned by a new regression test.MAX_OBSERVED_SPENT_OUTPOINTS10M → 1M (load-time allocation cap from ~hundreds of MB to ~tens of MB).Round 2 (
aa250e49) — external review findings[P1] Generation guard for mid-flight account adds. The height-only contiguity guard could still certify unscanned coverage: a rewind landing inside a scanned batch's range passes the height check (scan
[5000..9999]at 9000, rewind to 7499 → committed to 9999), and an account add that moves no heights (checkpoint already at the birth floor) is invisible to any height comparison.ManagedWalletInfonow carries an in-memoryaccount_generationbumped on every account add — even height-invisible ones;scan_batchsnapshots it per wallet (FiltersBatch.scanned_walletsis now a wallet → generation map, captured under the same read lock as the behind-set) and commit refuses to advance any wallet whose generation changed since scan. The wallet stays behind and the tick rescan picks it up. Pinned by three new dash-spv tests including both repro shapes.[P2] Surface guard-rewritten funding records.
remove_spent_from_accountsrewrote funding records (compensation) without emitting them; on the unmatched path the result returnedis_relevant == false, so downstream persistence could retain the stale+valuerecord. It now returns post-compensation clones, the checker emits them asupdated_recordson both the relevant and irrelevant paths, andWalletManagerpropagatesupdated_recordsindependent ofis_relevant. The compensation itself is kept: it is what keeps aggregate history consistent with in-order processing — the defect was the silent mutation.[P2] Restore the public account API.
ManagedAccountRefMut::record_transaction/confirm_transactionhad gonepub→pub(crate)with changed signatures on a publicly re-exported type. The public methods are restored with their original signatures (recording with no observed-spend context — the pre-bug: out-of-order block processing causes SPV wallet to miss UTXO spends dashpay/rust-dashcore#649 behavior for callers driving an account directly); the wallet checker uses newpub(crate)*_with_observed_spendsvariants.Verification
dashd_sync(live dashd regtest): 29 passed / 0 failedcargo fmtclean;cargo clippy --all-features --all-targetsclean on all three crates;key-wallet-ffi/dash-spv-fficompile🤖 Generated with Claude Code