Skip to content

perf: wallet balance in dash-qt#6549

Merged
PastaPastaPasta merged 8 commits intodashpay:developfrom
knst:perf-wallet-balance
Feb 14, 2025
Merged

perf: wallet balance in dash-qt#6549
PastaPastaPasta merged 8 commits intodashpay:developfrom
knst:perf-wallet-balance

Conversation

@knst
Copy link
Collaborator

@knst knst commented Feb 1, 2025

Issue being fixed or feature implemented

Wallets with big amount of transactions are loading too long time.

What was done?

Optimized heavy calculations of balance by Wallet. See commits for details.
It makes Dash-Qt also a bit more responsible because a lot of balance calculations are done in main thread to update balances, though "the lagging" is not easy to measure.

How Has This Been Tested?

Import pubkey with a lot transaction, for example yVXDAM73Tg6A44Bm3qduXsMCYxzuqBCT48
call unloadwallet + loadwallet, check time.

Loading of wallet is speed up from 24seconds to 23.5 seconds (average by mutiple runs).

here's detailed breakdown to particular functions (see branch with extra logs https://github.com/knst/dash/commits/perf-wallet-balance-with-logs/):

origin/develop (2f15c2bd81c65bba399b03a8c5186540412d7c7d)
2025-02-01T11:12:29Z [qt-rpcconsole] [w1] Wallet completed loading in            3279ms
2025-02-01T11:12:29Z [qt-rpcconsole] [w1] Rescan completed in              43ms
2025-02-01T11:12:43Z [      main] knst getBalances() 589.92ms
2025-02-01T11:12:43Z [      main] knst getAnonymizableBalance() 173.80ms
2025-02-01T11:12:43Z [      main] knst getDenominatedBalance() 186.28ms
2025-02-01T11:12:43Z [      main] knst getDenominatedBalance() 186.01ms
2025-02-01T11:12:43Z [      main] knst getNormalizedAnonymizedBalance() 27.66ms
2025-02-01T11:12:43Z [      main] knst getAverageAnonymizedRounds() 37.94ms
2025-02-01T11:12:44Z [      main] knst getAnonymizableBalance() 174.10ms
2025-02-01T11:12:44Z [      main] knst getDenominatedBalance() 189.23ms
2025-02-01T11:12:44Z [      main] knst getDenominatedBalance() 187.73ms
2025-02-01T11:12:44Z [      main] knst getNormalizedAnonymizedBalance() 27.77ms
2025-02-01T11:12:44Z [      main] knst getAverageAnonymizedRounds() 38.13ms
2025-02-01T11:12:47Z [      main] knst getBalances() 187.53ms
2025-02-01T11:12:47Z [      main] knst getBalances() 195.72ms
2025-02-01T11:12:48Z [      main] knst getBalances() 192.20ms
2025-02-01T11:12:48Z [      main] knst getBalances() 184.09ms
2025-02-01T11:12:48Z [      main] knst getAnonymizableBalance() 171.66ms
2025-02-01T11:12:48Z [      main] knst getDenominatedBalance() 185.26ms
2025-02-01T11:12:48Z [      main] knst getDenominatedBalance() 186.41ms
2025-02-01T11:12:48Z [      main] knst getNormalizedAnonymizedBalance() 25.77ms
2025-02-01T11:12:48Z [      main] knst getAverageAnonymizedRounds() 36.41ms
2025-02-01T11:12:49Z [      main] knst getBalances() 190.71ms
2025-02-01T11:12:49Z [      main] knst getAnonymizableBalance() 171.36ms
2025-02-01T11:12:49Z [      main] knst getDenominatedBalance() 185.21ms
2025-02-01T11:12:49Z [      main] knst getDenominatedBalance() 185.09ms
2025-02-01T11:12:49Z [      main] knst getNormalizedAnonymizedBalance() 26.28ms
2025-02-01T11:12:49Z [      main] knst getAverageAnonymizedRounds() 36.08ms

In total: 3279  +43  +589.92  +173.80  +186.28  +186.01  +27.66  +37.94  +174.10  +189.23  +187.73  +27.77  +38.13  +187.53  +195.72  +192.20  +184.09  +171.66  +185.26  +186.41  +25.77  +36.41  +190.71  +171.36  +185.21  +185.09  +26.28  +36.08
=7300.35

This PR:

perf-wallet-balance  (b0f71b4d3c714671a7174f7521988030a7a5708f)
# new HEAD
2025-02-01T11:20:11Z [qt-rpcconsole] [w1] Wallet completed loading in            3404ms
2025-02-01T11:20:11Z [qt-rpcconsole] [w1] Rescan completed in               0ms
2025-02-01T11:20:24Z [      main] knst getBalances() 544.79ms
2025-02-01T11:20:25Z [      main] knst getAnonymizableBalance() 173.77ms
2025-02-01T11:20:25Z [      main] knst getBalances() 139.59ms
2025-02-01T11:20:25Z [      main] knst getNormalizedAnonymizedBalance() 25.87ms
2025-02-01T11:20:25Z [      main] knst getAverageAnonymizedRounds() 35.35ms
2025-02-01T11:20:25Z [      main] knst getAnonymizableBalance() 171.82ms
2025-02-01T11:20:25Z [      main] knst getBalances() 141.07ms
2025-02-01T11:20:25Z [      main] knst getNormalizedAnonymizedBalance() 25.77ms
2025-02-01T11:20:25Z [      main] knst getAverageAnonymizedRounds() 35.45ms
2025-02-01T11:20:28Z [      main] knst getBalances() 142.88ms
2025-02-01T11:20:28Z [      main] knst getBalances() 137.15ms
2025-02-01T11:20:29Z [      main] knst getBalances() 136.33ms
2025-02-01T11:20:29Z [      main] knst getBalances() 137.43ms
2025-02-01T11:20:29Z [      main] knst getAnonymizableBalance() 172.26ms
2025-02-01T11:20:29Z [      main] knst getBalances() 140.42ms
2025-02-01T11:20:29Z [      main] knst getNormalizedAnonymizedBalance() 25.78ms
2025-02-01T11:20:29Z [      main] knst getAverageAnonymizedRounds() 34.88ms
2025-02-01T11:20:29Z [      main] knst getBalances() 144.52ms
2025-02-01T11:20:29Z [      main] knst getAnonymizableBalance() 170.65ms
2025-02-01T11:20:30Z [      main] knst getBalances() 141.37ms
2025-02-01T11:20:30Z [      main] knst getNormalizedAnonymizedBalance() 26.05ms
2025-02-01T11:20:30Z [      main] knst getAverageAnonymizedRounds() 36.16ms
2025-02-01T11:20:30Z [      main] knst getBalances() 143.27ms

3404  +0  +544.79  +173.77  +139.59  +25.87  +35.35  +171.82  +141.07  +25.77  +35.45  +142.88  +137.15  +136.33  +137.43  +172.26  +140.42  +25.78  +34.88  +144.52  +170.65  +141.37  +26.05  +36.16  +143.27
=6286.63

Sum-up improvement is slightly higher then real improvement of call loadwallet (1 second as sum of function vs 0.5 second as total RPC time) due to fact that calculations distributed between 2 threads: main and qt-rpcconsole.

Breaking Changes

N/A

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone

@knst knst added this to the 23 milestone Feb 1, 2025
@coderabbitai
Copy link

coderabbitai bot commented Feb 1, 2025

Walkthrough

The pull request consolidates and revises how wallet balances are managed across several modules. Specific virtual methods for retrieving anonymized and denominated balances have been removed from the wallet interface. In their place, new member variables have been added to the WalletBalances struct to centralize balance information. Method signatures in both wallet and transaction classes have been updated, including changing certain parameters from pointers to references, which enhances type safety. Additionally, the overall balance retrieval logic has been integrated into a single method, with updated implementations handling balance reporting more effectively. New methods have been introduced to encapsulate CoinJoin credit information, while existing methods have been streamlined. These changes span interface, UI, and implementation files, aiming to improve clarity and efficiency in how balance states are fetched and represented.

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (5)
src/wallet/wallet.h (1)

1147-1148: Consider returning a struct for consistency

GetBalance returns a comprehensive Balance structure, while GetBalanceAnonymized returns just CAmount. For a more uniform interface, consider returning BalanceAnonymized here too, especially if multiple anonymized balance details are needed.

-    CAmount GetBalanceAnonymized(const CCoinControl& coinControl) const;
+    BalanceAnonymized GetBalanceAnonymized(const CCoinControl& coinControl) const;
src/wallet/wallet.cpp (4)

Line range hint 2301-2318: Clarify early returns on coinbase and negative depth.

The function correctly excludes coinbase rewards that are not yet mature or have negative depth. However, consider clarifying in the comments that coinbase transactions are skipped even if they may be partially spendable in some contexts. This can help avoid confusion for maintainers reading the code.


2335-2351: Check negative depth and uncoinfirmed logic.

Early returns for negative depths and coinbase conditions are well-placed. Consider adding a brief comment explaining that unconfirmed but trusted transactions might still provide an anonymized amount if appropriately validated, to document behavior for future maintainers.


2366-2375: Guard conditions and value accumulations are appropriate.

Good check for spent outputs and denominated amounts before adding anonymized credit. Ensure that partial mixing states are handled correctly: for example, if an output is half-mixed or unexpectedly marked. If partial states occur, consider an additional check or an informative comment.


2533-2544: Review coin loop performance with selected coin control.

This method iterates each spendable transaction, filtering by coin control if needed. In high-transaction-volume scenarios, confirm that the approach remains performant. Caching partial results or indexing might help if further slowdown is observed.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f15c2b and 4c474c56492e7bddbbcfbecbe697f77296102f0f.

📒 Files selected for processing (5)
  • src/interfaces/wallet.h (1 hunks)
  • src/qt/overviewpage.cpp (1 hunks)
  • src/wallet/interfaces.cpp (2 hunks)
  • src/wallet/wallet.cpp (5 hunks)
  • src/wallet/wallet.h (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build Dependencies (linux64, x86_64-pc-linux-gnu)
  • GitHub Check: Build Dependencies (arm-linux, arm-linux-gnueabihf)
🔇 Additional comments (7)
src/wallet/wallet.h (1)

531-532: Confirm concurrency handling or add lock annotations

These methods are flagged with NO_THREAD_SAFETY_ANALYSIS. Please verify that calling code properly locks cs_wallet or otherwise ensures thread safety. If concurrency handling is required, consider adding the appropriate EXCLUSIVE_LOCKS_REQUIRED annotation.

src/wallet/wallet.cpp (6)

2319-2319: Ensure coin selection logic is consistent with user expectations.

When coinControl.HasSelected() is true but a given output isn't selected, the code unconditionally skips the output. Confirm that this behavior aligns with user expectations in all usage scenarios—especially where partial selection might be relevant.


2352-2356: Validate cached logic consistency.

Here, the code reuses cached anonymized credit values. Ensure that caching remains valid if external events (e.g., a coin join cycle) alter relevant outputs. Re-check concurrency safety if modifications can happen in parallel threads.


2361-2365: Simple outpoint construction is correct.

Creating the COutPoint from the transaction hash and output index is straightforward with no apparent issues. Keep an eye on whether large transaction sizes might affect iteration performance, although that’s typically negligible here.


2379-2385: Cache assignment for anonymized amounts.

Storing results in m_amounts[ANON_CREDIT] and m_amounts[DENOM_CREDIT] (or DENOM_UCREDIT) is clean. Just confirm that the concurrency model ensures no invalidation of these cached fields during updates, or that MarkDirty() calls happen elsewhere when needed.


Line range hint 2545-2567: Balance calculation structure is clear.

This logic merges trusted and untrusted pending balances while also including immature coins. Check that newly introduced fields (e.g., anonymized balance) won’t break older code that expects a simpler structure. Possibly add unit tests verifying that partial trust scenarios remain correct.


2568-2574: Ensure correct accumulation of anonymized values.

Appending the anonymized sums and categorizing them into trusted vs. untrusted pending is correct. As the function interacts with coinjoin data, ensure that coinjoin state changes won’t leave stale or incorrectly classified data behind.

@knst knst force-pushed the perf-wallet-balance branch from 4c474c5 to 3b2c5c3 Compare February 1, 2025 12:12
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/wallet/wallet.cpp (2)

Line range hint 2301-2334: Improved type safety by using reference instead of pointer.

The change from CCoinControl* to CCoinControl& eliminates potential null pointer issues and improves type safety, while maintaining the same functionality.

Consider adding a comment explaining why the null check was removed since it's now enforced by the type system:

-    if (coinControl.HasSelected() && !coinControl.IsSelected(outpoint)) {
+    // Using reference instead of pointer guarantees non-null coinControl
+    if (coinControl.HasSelected() && !coinControl.IsSelected(outpoint)) {

2335-2386: Well-designed balance caching mechanism with proper state tracking.

The new GetAnonymizedBalance implementation introduces a robust caching system that:

  • Properly tracks both anonymized and denominated amounts
  • Handles unconfirmed balances separately
  • Uses proper range checking with MoneyRange

Consider adding cache invalidation when transaction states change (e.g. confirmation status) to ensure cache coherency.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c474c56492e7bddbbcfbecbe697f77296102f0f and 3b2c5c337758b05433930169e1a56bbad7834038.

📒 Files selected for processing (5)
  • src/interfaces/wallet.h (1 hunks)
  • src/qt/overviewpage.cpp (1 hunks)
  • src/wallet/interfaces.cpp (2 hunks)
  • src/wallet/wallet.cpp (5 hunks)
  • src/wallet/wallet.h (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/qt/overviewpage.cpp
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Build (linux64_ubsan, x86_64-pc-linux-gnu, linux64)
  • GitHub Check: Build (linux64_tsan, x86_64-pc-linux-gnu, linux64)
  • GitHub Check: Build (linux64_sqlite, x86_64-pc-linux-gnu, linux64)
  • GitHub Check: Build (linux64_nowallet, x86_64-pc-linux-gnu, linux64)
  • GitHub Check: Build (linux64_fuzz, x86_64-pc-linux-gnu, linux64)
  • GitHub Check: Build (linux64_cxx20, x86_64-pc-linux-gnu, linux64)
  • GitHub Check: Build (linux64, x86_64-pc-linux-gnu, linux64)
  • GitHub Check: Build (arm-linux, arm-linux-gnueabihf, arm-linux)
🔇 Additional comments (7)
src/interfaces/wallet.h (1)

385-386: LGTM!

The new fields denominated_untrusted_pending and denominated_trusted are properly added to the WalletBalances struct with correct initialization to track denominated transaction balances.

src/wallet/interfaces.cpp (2)

395-396: LGTM!

The new balance fields are properly initialized from the wallet's balance data.


428-428: LGTM!

Simplified the GetBalance call by removing the unnecessary parameter while maintaining functionality.

src/wallet/wallet.h (3)

523-529: LGTM!

The BalanceAnonymized struct is well-designed to encapsulate anonymized balance information with proper field initialization.


531-532: LGTM!

The new methods GetAnonymizedCredit and GetAnonymizedBalance provide a clean interface for retrieving anonymized balance information.


1147-1148: LGTM!

The modified GetBalance and new GetBalanceAnonymized method signatures align well with the interface changes.

src/wallet/wallet.cpp (1)

Line range hint 2545-2577: Improved balance calculation with proper handling of anonymized funds.

The GetBalance method has been enhanced to properly track:

  • Anonymized balances using the new caching system
  • Denominated amounts for both trusted and untrusted pending transactions

Let's verify the balance calculation changes:

✅ Verification successful

Verified: Balance Calculation Changes Confirmed

The grep results confirm that the updated GetBalance method in src/wallet/wallet.cpp is the sole implementation handling anonymized funds via caching. All callers—such as those in CoinJoin, RPC wallet, and wallet interfaces—are properly using this updated method.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any other balance calculation methods that may need similar updates
rg -A 5 "GetBalance.*\(" --type cpp

Length of output: 9036

@UdjinM6
Copy link

UdjinM6 commented Feb 4, 2025

pls see 66089d0e65023b14489412a1bd75742c7212db97 and c32ca0bf33b048001f7186f984ca76644b463dd2

@knst knst marked this pull request as draft February 4, 2025 12:51
@knst knst force-pushed the perf-wallet-balance branch from 3b2c5c3 to 312319f Compare February 4, 2025 13:00
@knst
Copy link
Collaborator Author

knst commented Feb 4, 2025

pls see 66089d0 and c32ca0b

Good catch, there's a bug has been added by me when I rebased PR to make it review-ready. GetBalanceAnonymized is used now inside getAvailableBalance.

@knst knst marked this pull request as ready for review February 4, 2025 13:09
UdjinM6
UdjinM6 previously approved these changes Feb 4, 2025
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

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

Seems to be working correctly, light ACK 8822b51984eb4ee3a8151248a12febdc5e6a48ea

UdjinM6
UdjinM6 previously approved these changes Feb 4, 2025
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

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

utACK 123f1931827105ef7c0dd08dc2a2fe66725042c9

@github-actions
Copy link

github-actions bot commented Feb 6, 2025

This pull request has conflicts, please rebase.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/wallet/transaction.h (1)

303-304: Consider documenting thread safety requirements.

Both methods are marked with NO_THREAD_SAFETY_ANALYSIS. While there's a TODO comment about this elsewhere in the file, it would be helpful to document the actual thread safety requirements for these specific methods.

Add documentation comments:

+    /** Get anonymized credit amount. Requires cs_wallet lock. */
     CAmount GetAnonymizedCredit(const CCoinControl& coinControl) const NO_THREAD_SAFETY_ANALYSIS;
+    /** Get available CoinJoin credits. Requires cs_wallet lock. */
     CoinJoinCredits GetAvailableCoinJoinCredits() const NO_THREAD_SAFETY_ANALYSIS;
src/interfaces/wallet.h (1)

385-386: Consider adding documentation for the new balance fields.

While the field names are descriptive, adding documentation comments would help clarify their purpose and when they are used.

Add documentation:

+    /** Balance of denominated funds that are pending confirmation */
     CAmount denominated_untrusted_pending = 0;
+    /** Balance of denominated funds that are confirmed */
     CAmount denominated_trusted = 0;
src/wallet/receive.cpp (1)

353-361: Consider extracting CoinJoin balance logic to a separate method.

The CoinJoin balance calculation logic inside GetBalance could be moved to a separate private method to improve readability and maintainability.

+private:
+    void UpdateCoinJoinBalances(const CWalletTx& wtx, Balance& ret) const {
+        const auto balance_anonymized = wtx.GetAnonymizedBalance();
+        ret.m_anonymized += balance_anonymized.m_anonymized;
+        if (balance_anonymized.is_unconfirmed) {
+            ret.m_denominated_untrusted_pending += balance_anonymized.m_denom_credit;
+        } else {
+            ret.m_denominated_trusted += balance_anonymized.m_denom_credit;
+        }
+    }

Then in GetBalance:

     if (cj_enabled) {
-        const auto balance_anonymized = wtx.GetAnonymizedBalance();
-        ret.m_anonymized += balance_anonymized.m_anonymized;
-        if (balance_anonymized.is_unconfirmed) {
-            ret.m_denominated_untrusted_pending += balance_anonymized.m_denom_credit;
-        } else {
-            ret.m_denominated_trusted += balance_anonymized.m_denom_credit;
-        }
+        UpdateCoinJoinBalances(wtx, ret);
     }
src/wallet/wallet.cpp (1)

2047-2053: Consider initializing cache fields explicitly.

The cache fields are checked before being set. Consider initializing them explicitly in the constructor to avoid potential undefined behavior.

-    CWalletTx::CoinJoinCredits ret{0, false};
+    CWalletTx::CoinJoinCredits ret;
+    ret.m_anonymized = 0;
+    ret.m_denominated = 0;
+    ret.is_unconfirmed = false;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 123f1931827105ef7c0dd08dc2a2fe66725042c9 and 8c54133a445ce5caa66336ef1727f5140892736d.

📒 Files selected for processing (7)
  • src/interfaces/wallet.h (1 hunks)
  • src/qt/overviewpage.cpp (1 hunks)
  • src/wallet/interfaces.cpp (2 hunks)
  • src/wallet/receive.cpp (2 hunks)
  • src/wallet/transaction.h (1 hunks)
  • src/wallet/wallet.cpp (3 hunks)
  • src/wallet/wallet.h (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/qt/overviewpage.cpp
  • src/wallet/interfaces.cpp
  • src/wallet/wallet.h
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build Image
🔇 Additional comments (4)
src/wallet/transaction.h (1)

292-297: Good addition of the CoinJoinCredits struct.

The struct provides a clean encapsulation of CoinJoin-related credit information. The use of meaningful field names and initialization of amounts to zero follows good practices.

src/wallet/receive.cpp (1)

316-326: Well-structured implementation of GetBalanceAnonymized.

The method follows good practices:

  • Early return if CoinJoin is disabled
  • Proper locking with LOCK(cs_wallet)
  • Clear variable naming
src/wallet/wallet.cpp (2)

1996-2028: LGTM! Type safety improvements in GetAnonymizedCredit.

The changes improve type safety by using a reference instead of a pointer for coinControl parameter, and make the selection check more explicit with HasSelected().


2030-2033: LGTM! Improved credit calculation consolidation.

The new method consolidates anonymized and denominated credit calculations into a single call with a structured return type, improving API clarity and maintainability.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/wallet/wallet.h (1)

268-293: LGTM! Consider enhancing documentation.

The new structures are well-designed for their purposes. Consider adding:

  • Documentation for COutputEntry fields
  • Example usage in the class comment for CMerkleTx
 struct COutputEntry
 {
+    /** The destination address for this output */
     CTxDestination destination;
+    /** The amount in satoshis */
     CAmount amount;
+    /** The output index in the transaction */
     int vout;
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c54133a445ce5caa66336ef1727f5140892736d and e85b8fdb9eecf7a1dff80df0db0ffed5b3a8a2e1.

📒 Files selected for processing (2)
  • src/wallet/wallet.cpp (3 hunks)
  • src/wallet/wallet.h (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build Dependencies (linux64, x86_64-pc-linux-gnu)
  • GitHub Check: Build Dependencies (arm-linux, arm-linux-gnueabihf)
🔇 Additional comments (6)
src/wallet/wallet.h (4)

247-266: LGTM! Well-structured utility functions for order position handling.

The implementation is clean and handles edge cases appropriately:

  • Safe handling of missing "n" key in ReadOrderPos
  • Skipping writes for negative order positions in WriteOrderPos

524-529: Initialize is_unconfirmed to avoid potential undefined behavior.

The is_unconfirmed field in CoinJoinCredits is not initialized. If it gets used before assignment, it may cause undefined behavior.

 struct CoinJoinCredits
 {
     CAmount m_anonymized{0};
     CAmount m_denominated{0};
-    bool is_unconfirmed;
+    bool is_unconfirmed{false};
 };

537-539: LGTM! Thread-safety requirements are well-documented.

The method is correctly marked with NO_THREAD_SAFETY_ANALYSIS and documents its requirement for the cs_wallet lock.


1093-1094: Verify callers of GetBalance after parameter removal.

The removal of the coinControl parameter from GetBalance might affect existing code. Please ensure all callers have been updated.

Run the following script to find all callers of GetBalance:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Find all callers of GetBalance to verify they've been updated
# after the parameter removal.

# Search for GetBalance calls with 4 parameters (old signature)
echo "Searching for old GetBalance calls with 4 parameters..."
rg -p "GetBalance\([^,]*,[^,]*,[^,]*,[^,]*\)" --type cpp

# Search for all GetBalance calls to verify the update
echo -e "\nSearching for all GetBalance calls..."
rg -p "GetBalance\(" --type cpp

Length of output: 1742


Review Summary: All GetBalance Callers Updated Successfully

The search results confirm that no old calls using the deprecated 4-parameter signature exist. All current calls pass 0–3 arguments, aligning with the updated signature in CWallet. No further modifications are needed.

src/wallet/wallet.cpp (2)

1996-2028: LGTM! Improved type safety with reference parameter.

The function has been updated to use a reference parameter for CCoinControl instead of a pointer, which improves type safety while maintaining the same functionality.


2030-2081: LGTM! Well-structured consolidation of credit calculations.

The new function effectively consolidates anonymized and denominated credit calculations into a single method with proper caching, validation, and error handling. This improves code organization and maintainability.

UdjinM6 and others added 2 commits February 10, 2025 15:33
why:
- we calculate credits only, so "balance"->"credits"
- we calculate unspent credits only, so use word "Available" for func name
- "Anonymized" means "fully mixed denominated", so use word "CoinJoin" for struct and func names instead
@knst knst force-pushed the perf-wallet-balance branch from e85b8fd to df208d4 Compare February 10, 2025 08:35
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/wallet/transaction.h (1)

306-306: Consider adding documentation for the new method.

The new method GetAvailableCoinJoinCredits lacks documentation explaining its purpose and thread safety requirements.

Add documentation similar to other methods in the class:

+    /**
+     * Get available CoinJoin credits including both anonymized and denominated amounts.
+     * Requires cs_wallet lock.
+     * @return CoinJoinCredits struct containing credit information
+     */
     CoinJoinCredits GetAvailableCoinJoinCredits() const NO_THREAD_SAFETY_ANALYSIS;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e85b8fdb9eecf7a1dff80df0db0ffed5b3a8a2e1 and df208d4.

📒 Files selected for processing (2)
  • src/wallet/transaction.h (1 hunks)
  • src/wallet/wallet.cpp (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build Dependencies (linux64, x86_64-pc-linux-gnu)
  • GitHub Check: Build Dependencies (arm-linux, arm-linux-gnueabihf)
🔇 Additional comments (4)
src/wallet/transaction.h (2)

292-297: LGTM! Well-structured CoinJoinCredits struct.

The struct provides a clean encapsulation of CoinJoin-related credit information with clear member initialization.


304-304: Verify impact of parameter type change.

The method signature changed from pointer to reference parameter, which is a good practice for type safety. However, we should verify all call sites are updated.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for all calls to GetAnonymizedCredit to verify they've been updated
rg "GetAnonymizedCredit\s*\(" -A 2

Length of output: 672


Verification Outcome: Call sites correctly use the updated reference parameter

  • The grep output shows that in both wallet/receive.cpp and wallet.cpp the method GetAnonymizedCredit is invoked or defined with the parameter as a const reference.
  • The call in wallet/receive.cpp passes coinControl directly (as a reference), matching the updated signature.
src/wallet/wallet.cpp (2)

1996-2028: LGTM! Improved type safety by using reference instead of pointer.

The change from pointer to reference parameter for coinControl eliminates potential null pointer issues and improves type safety while maintaining the same functionality.


2030-2081: LGTM! Well-structured implementation with performance optimizations.

The new GetAvailableCoinJoinCredits function effectively:

  • Consolidates credit retrieval into a single method
  • Uses caching to improve performance
  • Handles both unconfirmed and confirmed transactions correctly
  • Includes proper range checking and error handling

UdjinM6
UdjinM6 previously approved these changes Feb 10, 2025
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

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

utACK df208d4 🙈

@kxcd
Copy link

kxcd commented Feb 10, 2025

I have many wallets that are slow to load, however, they are due to mixing, eg +1M addresses, that only get used once. So, I will be happy to test those. However, it sounds like this improvement will help Crowdnode more because they have the opposite scenario, millions of TXes involving the same address. Cheers for this PR !

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/wallet/receive.cpp (3)

316-326: Add documentation for the new method.

Please add documentation explaining the purpose of this method, its parameters, and return value. This will help maintainers understand the method's role in anonymized balance calculations.

+/**
+ * Get the total amount of anonymized funds available in the wallet.
+ * @param coinControl Parameters for coin selection and other controls
+ * @return The total amount of anonymized funds
+ */
 CAmount CWallet::GetBalanceAnonymized(const CCoinControl& coinControl) const

Also, consider adding overflow checks when accumulating the anonymized amount:

 CAmount anonymized_amount{0};
 LOCK(cs_wallet);
 for (auto pcoin : GetSpendableTXs()) {
-    anonymized_amount += pcoin->GetAnonymizedCredit(coinControl);
+    const CAmount credit = pcoin->GetAnonymizedCredit(coinControl);
+    if (!MoneyRange(credit) || !MoneyRange(anonymized_amount + credit)) {
+        throw std::runtime_error("CWallet::GetBalanceAnonymized(): value out of range");
+    }
+    anonymized_amount += credit;
 }

328-365: Consider splitting balance calculation logic.

The method is handling multiple types of balances (trusted, untrusted, immature, anonymized, denominated). Consider splitting this into smaller, focused methods for better maintainability.

+CWallet::Balance CWallet::GetStandardBalance(const int min_depth, const bool avoid_reuse, const bool fAddLocked, const std::set<uint256>& trusted_parents, const CWalletTx& wtx) const {
+    Balance ret;
+    isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
+    const CAmount tx_credit_mine = wtx.GetAvailableCredit(true, ISMINE_SPENDABLE | reuse_filter);
+    const CAmount tx_credit_watchonly = wtx.GetAvailableCredit(true, ISMINE_WATCH_ONLY | reuse_filter);
+    const bool is_trusted = IsTrusted(wtx, trusted_parents);
+    const int tx_depth = wtx.GetDepthInMainChain();
+    
+    if (is_trusted && ((tx_depth >= min_depth) || (fAddLocked && wtx.IsLockedByInstantSend()))) {
+        ret.m_mine_trusted = tx_credit_mine;
+        ret.m_watchonly_trusted = tx_credit_watchonly;
+    }
+    if (!is_trusted && tx_depth == 0 && wtx.InMempool()) {
+        ret.m_mine_untrusted_pending = tx_credit_mine;
+        ret.m_watchonly_untrusted_pending = tx_credit_watchonly;
+    }
+    ret.m_mine_immature = wtx.GetImmatureCredit();
+    ret.m_watchonly_immature = wtx.GetImmatureWatchOnlyCredit();
+    return ret;
+}
+
+void CWallet::GetCoinJoinBalance(Balance& ret, const CWalletTx& wtx) const {
+    const auto balance_anonymized = wtx.GetAvailableCoinJoinCredits();
+    ret.m_anonymized += balance_anonymized.m_anonymized;
+    if (balance_anonymized.is_unconfirmed) {
+        ret.m_denominated_untrusted_pending += balance_anonymized.m_denominated;
+    } else {
+        ret.m_denominated_trusted += balance_anonymized.m_denominated;
+    }
+}

Also, add overflow checks when accumulating balances:

 if (cj_enabled) {
     const auto balance_anonymized = wtx.GetAvailableCoinJoinCredits();
+    if (!MoneyRange(balance_anonymized.m_anonymized) || 
+        !MoneyRange(ret.m_anonymized + balance_anonymized.m_anonymized)) {
+        throw std::runtime_error("CWallet::GetBalance(): anonymized value out of range");
+    }
     ret.m_anonymized += balance_anonymized.m_anonymized;
     if (balance_anonymized.is_unconfirmed) {
+        if (!MoneyRange(balance_anonymized.m_denominated) || 
+            !MoneyRange(ret.m_denominated_untrusted_pending + balance_anonymized.m_denominated)) {
+            throw std::runtime_error("CWallet::GetBalance(): denominated value out of range");
+        }
         ret.m_denominated_untrusted_pending += balance_anonymized.m_denominated;
     } else {
+        if (!MoneyRange(balance_anonymized.m_denominated) || 
+            !MoneyRange(ret.m_denominated_trusted + balance_anonymized.m_denominated)) {
+            throw std::runtime_error("CWallet::GetBalance(): denominated value out of range");
+        }
         ret.m_denominated_trusted += balance_anonymized.m_denominated;
     }
 }

332-332: Consider moving CoinJoin check to a class member.

Since CoinJoin status is checked in multiple methods, consider caching it as a class member to avoid repeated calls to CCoinJoinClientOptions::IsEnabled().

 class CWallet {
+    bool m_coinjoin_enabled{false};
+    void UpdateCoinJoinStatus() {
+        m_coinjoin_enabled = CCoinJoinClientOptions::IsEnabled();
+    }
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df208d4 and 0363185.

📒 Files selected for processing (1)
  • src/wallet/receive.cpp (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build Dependencies (linux64, x86_64-pc-linux-gnu)
  • GitHub Check: Build Dependencies (arm-linux, arm-linux-gnueabihf)

@knst knst requested a review from UdjinM6 February 10, 2025 17:40
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

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

utACK 0363185

Comment on lines +320 to +325
CAmount anonymized_amount{0};
LOCK(cs_wallet);
for (auto pcoin : GetSpendableTXs()) {
anonymized_amount += pcoin->GetAnonymizedCredit(coinControl);
}
return anonymized_amount;
Copy link
Member

Choose a reason for hiding this comment

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

could probably use accumulate

Suggested change
CAmount anonymized_amount{0};
LOCK(cs_wallet);
for (auto pcoin : GetSpendableTXs()) {
anonymized_amount += pcoin->GetAnonymizedCredit(coinControl);
}
return anonymized_amount;
LOCK(cs_wallet);
return std::accumulate(GetSpendableTXs().begin(), GetSpendableTXs().end(), CAmount{0},
[&](CAmount sum, const auto& pcoin) {
return sum + pcoin->GetAnonymizedCredit(coinControl);
});```

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

should not use std::accumulate() without ranges, I think.

GetSpendableTXs() does caclucation inside, it doesn't just return reference to internal structure.
So, begin() and end() can even belong to 2 different data structures in memory.

@knst knst requested a review from PastaPastaPasta February 13, 2025 17:18
Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

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

utACK 0363185

@PastaPastaPasta PastaPastaPasta merged commit 793dde0 into dashpay:develop Feb 14, 2025
27 checks passed
@knst knst deleted the perf-wallet-balance branch February 15, 2025 01:58
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.

4 participants