Skip to content

fix(toolkit): propagate DustWallet spend state update [PM-20016]#877

Open
m2ux wants to merge 10 commits intomainfrom
fix/PM-20016-dustwallet-spend-state-update
Open

fix(toolkit): propagate DustWallet spend state update [PM-20016]#877
m2ux wants to merge 10 commits intomainfrom
fix/PM-20016-dustwallet-spend-state-update

Conversation

@m2ux
Copy link
Contributor

@m2ux m2ux commented Mar 6, 2026

Summary

Fix DustWallet::speculative_spend to return the updated DustLocalState alongside spends, and extend mark_spent to commit the state atomically with nullifier recording. Prevents stale-state bugs where consecutive spends select already-spent outputs. This is Least Authority audit finding Issue AO (Medium impact / Low severity, pp. 56-57).

🎫 Ticket 📐 Engineering


Motivation

DustWallet::speculative_spend clones dust_local_state, applies DustLocalState::spend iteratively (which sets pending_until on spent UTXOs to exclude them from future utxos() calls), then discards the updated state. The companion mark_spent method records nullifiers in spent_utxos but does not apply the state changes from DustLocalState::spend.

This means consecutive transaction constructions operate on stale state — spent UTXOs remain selectable, balances are overreported, and the ledger may reject transactions due to nullifier reuse. The issue persists until event replay rebuilds state from on-chain data.


Changes

  • wallet/dust.rsspeculative_spend — Return type changed from Result<Vec<DustSpend>> to Result<(Vec<DustSpend>, Sp<DustLocalState>)>, returning the accumulated state alongside spends
  • wallet/dust.rsmark_spent — Extended to accept updated_state: Sp<DustLocalState> and assign it to self.dust_local_state before recording nullifiers
  • transaction.rsgather_dust_spends — Collects per-wallet updated states in HashMap<WalletSeed, Sp<DustLocalState>> alongside spends
  • transaction.rsconfirm_dust_spends — Consumes per-wallet updated states via .remove() and passes each to mark_spent

📌 Submission Checklist

  • Changes are backward-compatible (or flagged if breaking)
  • Pull request description explains why the change is needed
  • Self-reviewed the diff
  • I have included a change file, or skipped for this reason: changeset added
  • If the changes introduce a new feature, I have bumped the node minor version
  • Update documentation (if relevant)
  • No new todos introduced

🔱 Fork Strategy

  • Node Runtime Update
  • Node Client Update
  • Other
  • N/A

🗹 TODO before merging

  • Ready for review

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

kics-logo

KICS version: v2.1.19

Category Results
CRITICAL CRITICAL 0
HIGH HIGH 2
MEDIUM MEDIUM 52
LOW LOW 3
INFO INFO 64
TRACE TRACE 0
TOTAL TOTAL 121
Metric Values
Files scanned placeholder 27
Files parsed placeholder 27
Files failed to scan placeholder 0
Total executed queries placeholder 73
Queries failed to execute placeholder 0
Execution time placeholder 11

@m2ux m2ux self-assigned this Mar 6, 2026
@m2ux m2ux force-pushed the fix/PM-20016-dustwallet-spend-state-update branch from be637f8 to fb44f53 Compare March 6, 2026 13:50
@m2ux m2ux marked this pull request as ready for review March 6, 2026 16:52
@m2ux m2ux requested a review from a team as a code owner March 6, 2026 16:52
@m2ux m2ux enabled auto-merge March 10, 2026 15:12
@m2ux m2ux requested a review from ozgb March 10, 2026 17:47
m2ux added 10 commits March 12, 2026 09:38
Initial changeset for fix/PM-20016. Implementation to follow after
planning phase completion.

Made-with: Cursor
Initial changeset for fix/PM-20016. Implementation to follow after
planning phase completion.

Made-with: Cursor
Modify speculative_spend to return the updated DustLocalState alongside
spends, and extend mark_spent to accept and assign the updated state to
self.dust_local_state atomically with nullifier recording.

Update gather_dust_spends and confirm_dust_spends in transaction.rs to
thread per-wallet updated states from speculation through to commit.

This ensures DustLocalState::spend's pending_until flags are propagated,
preventing utxos() from returning already-spent outputs in consecutive
spend operations.

Addresses Least Authority audit finding Issue AO.

Made-with: Cursor
Made-with: Cursor
@m2ux m2ux force-pushed the fix/PM-20016-dustwallet-spend-state-update branch from 95b390d to 489bc6e Compare March 12, 2026 09:39
@m2ux m2ux requested a review from justinfrevert March 12, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants