rpc: add scan_utxoset option to getbalance(s) to verify wallet balance accuracy #33392

pull musaHaruna wants to merge 4 commits into bitcoin:master from musaHaruna:feature/scan-utxoset-balance-check changing 8 files +382 −33
  1. musaHaruna commented at 4:17 pm on September 15, 2025: none

    Fixes #28898 Users who import descriptors with incorrect birthdates may encounter inaccurate wallet balances. In such cases, the wallet may miss historical transactions that belong to it, resulting in underreported balances. Currently, the only way to correct this is to manually run rescanblockchain or reimport descriptors with proper metadata. However, users may not realize that their wallet balance is incomplete. This PR introduces a mechanism for wallets to verify their trusted balance against the UTXO set. By rescanning the current UTXO set, the wallet can detect discrepancies and suggest corrective actions to the user.

    Features Adds a scan_utxoset argument to both getbalance and getbalances RPCs. When enabled: The wallet collects all scriptPubKeys from its descriptors. The node scans the chainstate UTXO set for matching outputs. A scanned balance is calculated independently of the wallet’s transaction history.

    If the scanned balance differs from the wallet’s trusted balance: The result includes a utxo_discrepancy object with both balances and a suggested action. Users are advised to run rescanblockchain or reimport descriptors with correct birthdates.

    Example usage Normal trusted balance $ bitcoin-cli getbalance 0.01000000

    With UTXO set scan

     0$ bitcoin-cli getbalance "* 0 false false true"
     1{
     2  "amount": 0.01000000,
     3  "scanned_amount": 0.01500000,
     4  "warnings": "Scanned UTXO balance (0.01500000 BTC) is larger than the wallet's trusted balance (0.01000000 BTC). If this is unexpected, consider running rescanblockchain or reimporting descriptors with correct birthdates to restore missing transaction history."
     5}
     6
     7$ bitcoin-cli getbalances true
     8{
     9  "mine": {
    10    "trusted": 0.01000000,
    11    "untrusted_pending": 0.00000000,
    12    "immature": 0.00000000
    13  },
    14  "utxo_scanned_balance": 0.01500000,
    15  "utxo_discrepancy": {
    16    "wallet_trusted_balance": 0.01000000,
    17    "utxo_scanned_balance": 0.01500000,
    18    "suggested_action": "If this is unexpected, consider running rescanblockchain or reimporting descriptors with correct birthdates to restore missing transaction history."
    19  },
    20  "lastprocessedblock": {
    21    "height": 900000,
    22    "hash": "000000000000000..."
    23  }
    24}
    

    Implementation Notes Introduces FindCoinsByScript to scan the chainstate UTXO set for a set of scripts. ScanWalletUTXOSet aggregates spendable/watched outputs and tallies balances. Results are merged into existing getbalance / getbalances RPCs behind a new scan_utxoset flag. Coinbase maturity rules are applied consistently.

    Review Notes This does not replace rescanblockchain; it only provides a diagnostic check. For large wallets, UTXO scans may be expensive but should complete faster than a full rescan. Maintains backwards compatibility by defaulting scan_utxoset=false. Adds optional result fields only when scan_utxoset is enabled.

  2. node: add and implement FindCoinsByScript for UTXO-set scan by script
    Add node::FindCoinsByScript and wire it into the node interface as
    Node::getCoinsByScript(), then implement the DB-backed scan that iterates
    the on-disk UTXO DB with a CCoinsViewCursor.
    ba9f134734
  3. DrahtBot added the label RPC/REST/ZMQ on Sep 15, 2025
  4. DrahtBot commented at 4:17 pm on September 15, 2025: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/33392.

    Reviews

    See the guideline for information on the review process. A summary of reviews will appear here.

  5. rpc: add optional scan_utxoset argument to getbalances
    Extend the getbalances RPC with a boolean `scan_utxoset` argument. When
    set to true, the UTXO set is scanned to recalculate the wallet's
    spendable balance independently of the wallet database. Through
    ScanWalletUTXOSet function which  gathers wallet-owned scriptPubKeys
    (mine vs watch-only), queries the chainstate via getCoinsByScript,
    and tallies the resulting coins with coinbase maturity checks.
    The function prefers spendable (mine) results when available,
    otherwise falls back to watch-only.
    
    The result object includes:
     - `utxo_scanned_balance`: recalculated spendable balance
     - `utxo_discrepancy`: only present if the scanned balance differs from
       the wallet's trusted balance, with a suggested action to rescan or
       reimport descriptors.
    
    This allows users to detect balance mismatches caused by missing history
    and decide whether to perform a full rescan.
    7db78c3805
  6. rpc: add optional scan_utxoset argument to getbalance
    Extend the getbalance RPC with a boolean `scan_utxoset` argument. When
    false (default), getbalance continues to return a numeric trusted balance
    for backward compatibility.
    
    When true, getbalance instead returns an object with both:
     - `amount`: the trusted wallet balance, and
     - `scanned_amount`: the spendable balance recalculated by scanning the
       UTXO set.
    
    If the scanned amount exceeds the trusted balance, a `warnings` field is
    added with guidance to run rescanblockchain or reimport descriptors.
    
    This provides a lightweight way to cross-check wallet balances against
    the UTXO set without performing a full rescan.
    b24a23ac66
  7. test: add wallet balance UTXO scan functional tests
    Adds new functional tests for wallet balance behavior when scanning the UTXO set:
    - Detect balance for spendable wallets with incorrect birthtime
    - Detect balance for watch-only wallets with incorrect birthtime
    - Ensure mempool-only outputs are excluded from UTXO scans
    
    Also verifies wallet birthtime updates, rescans, and reindex interactions.
    93baccaa35
  8. musaHaruna force-pushed on Sep 16, 2025
  9. fjahr commented at 2:17 pm on September 16, 2025: contributor
    It seems kind of weird to me to add this as an option to getbalance. The problem is in the importdescriptors call, which is using the wrong birthdate. Have you looked into making this an option of importdescriptors? If the performance isn’t too bad, this could even be on by default. If that is possible that would seem preferred, since you also write “However, users may not realize that their wallet balance is incomplete.”, if they don’t know they likely also won’t call the getbalance with this option. So ideally we could “force” this on users in a way that they don’t feel (much) negative impact from it. If we can’t do that then I am not sure this is of much use since the users could just call rescanblockchain directly if they are aware that there is a problem with their balances.
  10. musaHaruna commented at 4:13 pm on September 16, 2025: none

    Have you looked into making this an option of importdescriptors?

    I have not looked into making it an option for importdecriptors, but I will look into it and check the performance, and get back to you on that.

    Just thinking on a high level and somewhat naively because am a new contributor with little knowledge about the codebase, what if we can atomatically cross check the balance against the utxoset after importing a descriptor so we can rescan automatically if there’s any difference found between the balance? But I don’t know how that will perform though.

    Thanks for the suggestion. I really appreciate it.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2025-09-16 18:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me