fix(key-wallet): cover CoinJoin/DashPay accounts for AssetLock routing#867
Conversation
`TransactionRouter::get_relevant_account_types(AssetLock)` returned only the BIP44/BIP32 standard accounts plus the identity/asset-lock accounts, omitting the CoinJoin and DashPay fund-bearing accounts. Only `check_core_transaction` marks a UTXO spent, and it scopes spend detection to the account types this router returns. So an asset lock funded from a CoinJoin or DashPay UTXO never had its input debited: the spent UTXO kept counting toward the balance indefinitely while the (BIP44) change output was still credited, inflating the reported balance by exactly the spent amount — on both relay and rescan. Field evidence (Android SDK wallet, dashpay/dash-wallet#1507): outpoint-diff reconciliation against the dashj oracle showed the SDK balance high by exactly the value of asset-lock-spent CoinJoin inputs that were never debited. Fix: the AssetLock arm now consults the full `fund_bearing_account_types()` set (BIP44, BIP32, CoinJoin, DashpayReceivingFunds, DashpayExternalAccount) before the identity/asset-lock accounts. Discovery is membership-based like Dash Core's `IsMine`, so consulting extra accounts never yields false positives. Adds an end-to-end regression test that funds a CoinJoin UTXO, spends it via an asset-lock transaction through `check_core_transaction`, and asserts the CoinJoin UTXO is debited and removed (fails on the old routing: total_sent 0). Also extends the AssetLock routing unit test. Refs: dashpay/platform#4073, dashpay/platform#4074, dashpay/dash-wallet#1507 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAssetLock routing now includes the complete fund-bearing account set, including CoinJoin and DashPay accounts. A regression test verifies that spending a CoinJoin UTXO debits wallet totals, credits BIP44 change, and removes the spent UTXO. ChangesAssetLock routing
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
key-wallet/src/transaction_checking/wallet_checker.rs (1)
702-718: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winAlso assert the aggregate wallet balance.
The regression is balance inflation, but the test currently verifies only per-transaction totals and UTXO removal. Add an assertion against the expected confirmed/spendable wallet balance after the spend to catch future divergence between
spend_resultand persisted balance state.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@key-wallet/src/transaction_checking/wallet_checker.rs` around lines 702 - 718, Extend the asset-lock spending test after the existing UTXO-removal assertions to verify the aggregate wallet balance, including the expected confirmed/spendable value after the spend. Use the wallet’s established balance accessor and expected value derived from the funding and change amounts, while preserving the current per-transaction and UTXO assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@key-wallet/src/transaction_checking/wallet_checker.rs`:
- Around line 702-718: Extend the asset-lock spending test after the existing
UTXO-removal assertions to verify the aggregate wallet balance, including the
expected confirmed/spendable value after the spend. Use the wallet’s established
balance accessor and expected value derived from the funding and change amounts,
while preserving the current per-transaction and UTXO assertions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c3fa87a1-620d-40ae-b307-ff4fa3154f35
📒 Files selected for processing (3)
key-wallet/src/transaction_checking/transaction_router/mod.rskey-wallet/src/transaction_checking/transaction_router/tests/routing.rskey-wallet/src/transaction_checking/wallet_checker.rs
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev #867 +/- ##
==========================================
+ Coverage 73.62% 73.98% +0.36%
==========================================
Files 324 324
Lines 73374 73545 +171
==========================================
+ Hits 54018 54409 +391
+ Misses 19356 19136 -220
|
…oinJoin-debit test Addresses CodeRabbit's nitpick on dashpay#867. The regression the test guards against is balance inflation, but it previously asserted only the per-transaction totals and the CoinJoin UTXO removal. Add confirmed/spendable/ total balance assertions after the spend: a non-debited CoinJoin coin would report funding_value + change_value; a correct debit leaves only the confirmed BIP44 change (the asset-lock credit output is locked into Platform, never a spendable wallet UTXO). Existing assertions preserved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Added the aggregate-balance assertions: after the spend, confirmed/spendable/total all equal |
Summary
TransactionRouter::get_relevant_account_types(TransactionType::AssetLock)omitted theCoinJoin and DashPay fund-bearing account types. Because only
check_core_transactionmarks a UTXO spent, and it scopes spend detection to the account types this router
returns, an asset-lock transaction funded from a CoinJoin, DashpayReceivingFunds, or
DashpayExternalAccount UTXO never had its input debited. The spent UTXO kept counting
toward the balance indefinitely, while the (BIP44) change output was credited — so the
reported balance ended up high by exactly the CoinJoin/DashPay amount spent, on both
initial relay and full rescan.
This is the rust-dashcore half of the fix tracked in dashpay/platform#4073 /
dashpay/platform#4074 (Kotlin SDK) and dashpay/dash-wallet#1507 (Android wallet).
The defect
check_core_transaction(key-wallet/src/transaction_checking/wallet_checker.rs) does:check_transactionis what matches a transaction's inputs against account UTXOs and marksthem spent. If an account type is not in
relevant_types, its UTXOs are never consideredfor spend detection for that transaction. The
AssetLockarm returned only:An asset lock is a normal funded transaction: its inputs can come from any fund-bearing
account. A heavy-mixing wallet naturally funds identity top-ups from mixed (CoinJoin)
coins. When it did, the CoinJoin input outpoint was never matched,
total_sentstayed 0for that input, and the UTXO was left in the CoinJoin account as if unspent.
Field evidence
Android SDK wallet (dashpay/dash-wallet#1507). We reconciled the SDK's tracked UTXO set
against the dashj reference wallet by outpoint diff. The SDK balance was high by exactly
the summed value of CoinJoin outpoints that had been spent by asset-lock (identity
top-up) transactions but were still present in the SDK's CoinJoin account UTXO set. Every
missing debit corresponded to an
AssetLock-classified transaction whose input pointed ata CoinJoin UTXO. The mismatch reproduced on a from-birth rescan, ruling out a transient
relay-ordering artifact — consistent with a routing omission rather than a sync gap.
The fix
Route
AssetLockthrough the full fund-bearing account set before the identity/asset-lockaccounts. On the current
devbranch this set is already factored out asfund_bearing_account_types()(BIP44, BIP32, CoinJoin, DashpayReceivingFunds,DashpayExternalAccount) and is already used by the
Standard | CoinJoinarm, so the fixreuses it:
Discovery is membership-based, exactly like Dash Core's
IsMine— an account only matcheswhen a scriptPubKey or spent-UTXO outpoint actually belongs to it — so consulting the
extra accounts can never produce a false positive. It only closes the case where the input
genuinely does belong to a CoinJoin/DashPay account.
Test
Adds an end-to-end regression test,
wallet_checker::tests::test_asset_lock_spending_coinjoin_utxo_is_debited, that:AssetLockPayloadtransaction that has BIP44 change,check_core_transaction, andtotal_sent == funding_value) and removed, andthe BIP44 change is credited.
Verified it fails on the old routing (
total_sentis0, UTXO not removed) and passeswith the fix. The existing
routing::test_asset_lock_transaction_routingunit test isalso extended to assert CoinJoin/DashPay coverage.
Verification
cargo test -p key-wallet— 548 passed, 0 failedcargo clippy -p key-wallet --all-features --all-targets -- -D warnings— cleancargo fmt -p key-wallet -- --check— cleancc @HashEngineering @QuantumExplorer — this is the AssetLock-routing PR requested on
dashpay/platform#4074 ("If rust-dashcore changes are required, then submit PRs to that
repo").
Summary by CodeRabbit