Context
WalletTransactionChecker::check_transaction narrows which accounts are checked by the transaction's classified type (TransactionRouter::get_relevant_account_types). A transaction that spends an account's coin but classifies to a type excluding that account (example: an AssetLock-classified transaction spending a CoinJoin-account coin) is never matched against the owning account, so update_utxos never runs there and the spent coin stays in the UTXO set, inflating the balance.
This is a pre-existing gap on dev, independent of block-delivery order. It only self-corrects if the funding is ever redelivered after the spend (the #649 observed-spent guard then blocks re-insertion), which cannot be relied on.
PR #851 briefly carried an un-gated remove_spent_from_accounts pass that scanned all funding accounts per block transaction. It was dropped from the final PR to keep it scoped to #649. A proper fix should decide where this belongs:
- an un-gated spend reconciliation pass (the dropped approach, reinstated deliberately with its own tests and cost analysis), or
- widening
get_relevant_account_types so any account type owning a spent input is always checked, or
- an outpoint-indexed lookup (wallet-level
OutPoint -> account index) making the reconciliation O(inputs) instead of O(inputs x accounts).
References
Context
WalletTransactionChecker::check_transactionnarrows which accounts are checked by the transaction's classified type (TransactionRouter::get_relevant_account_types). A transaction that spends an account's coin but classifies to a type excluding that account (example: an AssetLock-classified transaction spending a CoinJoin-account coin) is never matched against the owning account, soupdate_utxosnever runs there and the spent coin stays in the UTXO set, inflating the balance.This is a pre-existing gap on
dev, independent of block-delivery order. It only self-corrects if the funding is ever redelivered after the spend (the #649 observed-spent guard then blocks re-insertion), which cannot be relied on.PR #851 briefly carried an un-gated
remove_spent_from_accountspass that scanned all funding accounts per block transaction. It was dropped from the final PR to keep it scoped to #649. A proper fix should decide where this belongs:get_relevant_account_typesso any account type owning a spent input is always checked, orOutPoint -> accountindex) making the reconciliation O(inputs) instead of O(inputs x accounts).References