Skip to content

Wallet: Compute GUI balances in one wallet scan to fix per-block stutter - #1896

Open
reubenyap wants to merge 1 commit into
masterfrom
claude/single-pass-gui-balances
Open

Wallet: Compute GUI balances in one wallet scan to fix per-block stutter#1896
reubenyap wants to merge 1 commit into
masterfrom
claude/single-pass-gui-balances

Conversation

@reubenyap

Copy link
Copy Markdown
Member

PR intention

Remove the GUI stutter that pulses in step with block arrivals on large wallets. The 250 ms balance poll (WalletModel::checkBalanceChanged) recomputes balances on the GUI thread whenever the block height changes, and CWallet::TryGetBalances performed four full scans of mapWalletGetBalance(), GetUnconfirmedBalance(), GetImmatureBalance(), and GetBalance(true) for the mintable amount — calling IsTrusted() up to three times per transaction.

The mintable scan was the worst offender: the exclude-locked variant of CWalletTx::GetAvailableCredit can never be served from the per-transaction credit cache, and it also overwrote and then invalidated that cache on every call, so the next poll's available scan recomputed every transaction's credit from scratch as well.

Code changes brief

  • TryGetBalances now computes all four balances in a single scan of mapWallet, calling IsTrusted() once per transaction. The per-transaction conditions and credit calls are copied verbatim from the replaced getters, so the computed amounts are identical.
  • When no coins are locked (setLockedCoins empty), the mintable sum reuses the available credit instead of a second, uncacheable GetAvailableCredit(true, true) computation — excluding locked coins only skips outputs in setLockedCoins, so the values are equal by definition.
  • GetAvailableCredit no longer touches the credit cache when fExcludeLocked is set; that variant never reads the cache, and destroying it forced constant recomputation. The individual getters (GetBalance etc.) are unchanged for all other callers.

Testing

  • git diff --check
  • Each branch of the consolidated loop was verified line-by-line against the original GetBalance, GetUnconfirmedBalance, GetImmatureBalance and GetBalance(true) implementations; locks (cs_main/cs_wallet via try-lock) are held across the scan exactly as before.
  • Not built locally (no toolchain in this environment); CI is expected to provide build and test coverage.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TV7dpUCcTQrSSxnXaR7Xuq


Generated by Claude Code

The 250 ms balance poll recomputes balances on the GUI thread
whenever the block height changes. TryGetBalances performed four
full scans of mapWallet (available, unconfirmed, immature and
mintable), calling IsTrusted up to three times per transaction.
The mintable scan, GetBalance(true), was the worst offender: the
exclude-locked variant of GetAvailableCredit can never be served
from the per-transaction credit cache, and it also overwrote and
invalidated that cache on every call, forcing the next poll's
available scan to recompute every credit from scratch. On large
wallets this shows up as GUI stutter pulsing with block arrivals.

Fold the four sums into a single scan that calls IsTrusted once
per transaction, reuse the available credit for the mintable sum
when no coins are locked (excluding locked coins only skips
outputs in setLockedCoins, so the values are identical), and stop
the exclude-locked credit computation from touching the cache it
never reads. Per-transaction conditions and credit calls are
otherwise unchanged, so the computed amounts stay identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01TV7dpUCcTQrSSxnXaR7Xuq
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 883031b3-3643-4ac6-87bb-24fcad397be7

📥 Commits

Reviewing files that changed from the base of the PR and between 98f7d13 and 3d449bd.

📒 Files selected for processing (1)
  • src/wallet/wallet.cpp

Summary by CodeRabbit

  • Bug Fixes
    • Improved wallet balance calculations to preserve cached available-credit results.
    • Ensured balance, unconfirmed, immature, and mintable amounts remain consistent while being calculated more efficiently.

Walkthrough

The wallet preserves cached unfiltered credit when calculating exclude-locked credit. TryGetBalances now computes all balance categories during one mapWallet traversal.

Changes

Wallet balance calculations

Layer / File(s) Summary
Preserve unfiltered credit cache
src/wallet/wallet.cpp
Exclude-locked credit calculations no longer overwrite the unfiltered available-credit cache.
Consolidate balance aggregation
src/wallet/wallet.cpp
TryGetBalances replaces four getter scans with one wallet transaction traversal for trusted, unconfirmed, immature, and mintable balances.

Estimated code review effort: 3 (Moderate) | ~15–30 minutes

Possibly related PRs

  • firoorg/firo#1883: Updates WalletModel::TryGetBalances for lock contention.
  • firoorg/firo#1885: Modifies wallet credit and balance calculations in src/wallet/wallet.cpp.

Suggested labels: Spark, size:M

Suggested reviewers: psolstice, levoncrypto

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: consolidating wallet balance scans to fix GUI stutter during block arrivals.
Description check ✅ Passed The description includes both required sections and clearly explains the problem, implementation, behavior, and testing status.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/single-pass-gui-balances

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@reubenyap
reubenyap marked this pull request as ready for review August 6, 2026 17:13
@coderabbitai
coderabbitai Bot requested review from levoncrypto and psolstice August 6, 2026 17:14
@coderabbitai coderabbitai Bot added size:M This PR changes 30-99 lines, ignoring generated files Spark labels Aug 6, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3d449bd84a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/wallet/wallet.cpp
Comment on lines +2823 to +2825
if (pcoin->IsTrusted()) {
const CAmount nAvailable = pcoin->GetAvailableCredit();
balance += nAvailable;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add regression tests for consolidated wallet balances

This behavior change replaces four wallet-balance scans and changes available-credit cache handling, but the commit adds no test coverage. Add a targeted wallet unit test that compares TryGetBalances with the individual getters for trusted, unconfirmed, immature, locked, and unlocked outputs, and verifies that an exclude-locked scan preserves the unfiltered cached value; otherwise regressions in this high-risk wallet accounting path will only surface at runtime.

AGENTS.md reference: AGENTS.md:L240-L245

Useful? React with 👍 / 👎.

reubenyap pushed a commit that referenced this pull request Aug 6, 2026
Cover CWallet::TryGetBalances, which computes the four GUI balances in
a single scan of mapWallet, by checking it against the individual
getters (GetBalance, GetUnconfirmedBalance, GetImmatureBalance,
GetBalance(true)) for trusted, immature-coinbase, unconfirmed (in and
out of the mempool), zero-conf self-send, locked and unlocked outputs,
with exact expected amounts at every stage.

Also pin the CWalletTx::GetAvailableCredit cache contract: the
exclude-locked variant must neither overwrite nor invalidate the
cached unfiltered credit, and must never be served from the cache.

Requested in review of #1896.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01FKeafjZzU2tPsbLyjZJQ8T
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M This PR changes 30-99 lines, ignoring generated files Spark

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants