Skip to content

fix(wallet): restore redeemed DD positions after importdescriptors#388

Merged
JaredTate merged 3 commits intofeature/digidollar-v1from
fix/wallet-restore-redemption-state
Mar 5, 2026
Merged

fix(wallet): restore redeemed DD positions after importdescriptors#388
JaredTate merged 3 commits intofeature/digidollar-v1from
fix/wallet-restore-redemption-state

Conversation

@DigiSwarm
Copy link

Reopens #386 with the proper Dandelion++ fix included.

Summary

Fixes Bug #8: After wallet recovery via listdescriptors trueimportdescriptors, the Qt wallet shows active "Redeem DigiDollar" buttons for positions that have already been redeemed.

Root Cause (two bugs)

1. Full-redemption REDEEM txs invisible to rescan parser
ProcessDDTxForRescan() relied solely on OP_RETURN parsing. Full-redemption REDEEM transactions (ddChange == 0) have no OP_RETURN — so ddTxType stayed 0, the REDEEM branch was never entered, and positions were never marked is_active = false during rescan.

2. ValidatePositionStates() only ran at startup
Was designed as a safety net but only fired from postInitProcess(), never after importdescriptors or rescanblockchain.

Fix

Fix 1: ProcessDDTxForRescan now uses GetDigiDollarTxType(tx) (version field) as primary type detection.

Fix 2: ScanForWalletTransactions() now calls ScanForDDUTXOs() after any successful rescan.

Dandelion++ CI Fix

Adds -dandelion=0 to 4 p2p tests that test bloom filters, disconnect/ban, invalid txs, and compact blocks — none of which test Dandelion. Dandelion++ adds INV/GETDATA/NOTFOUND exchanges during peer handshake that cause thread scheduling races on slow macOS CI runners. The dedicated p2p_dandelion.py test covers Dandelion behavior. Also removes the macOS --exclude list so all p2p tests run on both platforms.

Testing

  • New functional test: digidollar_wallet_restore_redeem.py
  • 1972/1972 C++ unit tests pass (including 1141 DigiDollar tests)
  • Existing functional tests pass: digidollar_redemption_e2e.py, digidollar_persistence.py, digidollar_mint.py

Bug #8: After wallet recovery via listdescriptors/importdescriptors, the
wallet GUI showed active 'Redeem DigiDollar' buttons for positions that
were already redeemed. Reported multiple times by users.

ROOT CAUSE (two bugs):

1. ProcessDDTxForRescan relied solely on OP_RETURN parsing to detect DD
   transaction types. Full-redemption REDEEM txs (ddChange == 0) have NO
   OP_RETURN with DD metadata, making them invisible to the rescan parser.
   Positions were created during MINT processing but never marked inactive
   when the REDEEM tx was encountered.

2. ValidatePositionStates() — which cross-checks every active position
   against the actual UTXO set — only ran at wallet startup (postInitProcess),
   never after importdescriptors or rescanblockchain rescans.

FIX:

1. ProcessDDTxForRescan now uses GetDigiDollarTxType() (version field) as
   the primary tx type detection. The version field ALWAYS encodes the type
   correctly via SetDigiDollarType(). OP_RETURN parsing is retained for
   supplementary data extraction (DD change amounts).

2. ScanForWalletTransactions() now calls ScanForDDUTXOs() after any
   successful rescan completes. This runs ValidatePositionStates() which
   cross-checks all active positions against the UTXO set, catching any
   positions whose collateral was spent (redeemed).

Both fixes are defense-in-depth: Fix 1 prevents the bug, Fix 2 catches
any edge cases that Fix 1 might miss (e.g., blocks skipped by the fast
BIP158 block filter during rescan).

Includes functional test: digidollar_wallet_restore_redeem.py
These 4 p2p tests test bloom filter rejection, disconnect/ban behavior,
invalid transactions, and compact blocks — none of them test Dandelion.
Dandelion++ adds INV/GETDATA/NOTFOUND exchanges during peer handshake
that create thread scheduling races between ThreadMessageHandler and
ThreadSocketHandler on slow macOS CI runners, causing disconnect
detection timeouts.

Adding -dandelion=0 is proper test isolation: these tests should test
what they're designed to test without interference from an unrelated
feature. The dedicated p2p_dandelion.py test covers Dandelion behavior.

Also removes the macOS --exclude list from ci.yml so all p2p tests
now run on both platforms — no more skipped tests.
… test

After disconnect_nodes(3, 4), the Dandelion stempool thread may leave the
RPC HTTP connection in Request-sent state. When stop_nodes() fires in
shutdown(), it gets CannotSendRequest on nodes 3/4 (Dandelion-enabled),
causing the macOS ARM64 CI runner to fail with leftover processes.

Add a 5s sleep post-disconnect to let Dandelion thread state drain cleanly
before the test framework's stop_nodes() is called.

Reproduces reliably on macOS 14 ARM64 GitHub Actions runners.
@JaredTate JaredTate merged commit b49b511 into feature/digidollar-v1 Mar 5, 2026
2 checks passed
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.

2 participants