feat: implement exponential retention for wallet backups#7005
feat: implement exponential retention for wallet backups#7005PastaPastaPasta wants to merge 12 commits intodashpay:developfrom
Conversation
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
WalkthroughThis PR makes wallet backup retention configurable and refactors status access: adds CLI options Sequence Diagram(s)sequenceDiagram
participant CLI as Command Line (ArgsManager)
participant Init as WalletInit
participant Wallet as CWallet
participant Backup as BackupLogic
participant FS as File System
participant UI as Qt/CoinJoin UI
CLI->>Init: Parse -createwalletbackups / -maxwalletbackups
Init->>Wallet: Set nWalletBackups, nMaxWalletBackups
rect rgba(200,220,240,0.5)
Wallet->>Backup: Trigger auto/manual backup
Backup->>Backup: GetBackupsToDelete(backups, nWalletBackups, nMaxWalletBackups)
Backup->>FS: Write new backup
Backup->>FS: Delete selected old backups
end
rect rgba(220,200,240,0.5)
Wallet->>Init: On unlock call InitAutoBackup(ArgsManager)
Init->>Wallet: Clamp/enforce nWalletBackups <= nMaxWalletBackups
end
Wallet->>UI: WalletImpl.getWalletBackupStatus()
UI->>UI: Update coinjoin/overview logic based on status
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/wallet/init.cpp (1)
60-63: New wallet backup options look consistent; consider clarifying edge semantics.The
-createwalletbackups=<n>and-maxwalletbackups=<n>args are wired correctly and the help text mentions defaults and exponential spacing. As a follow‑up, you may want to explicitly document how negative or zero values are treated (e.g., clamping and whether 0 disables backups or the max cap) to avoid confusion for operators.src/wallet/wallet.h (1)
82-86: Backup policy declarations are fine; tighten documentation of retention rule.The new defaults (
DEFAULT_MAX_BACKUPS,DEFAULT_N_WALLET_BACKUPS,MAX_N_WALLET_BACKUPS) and theGetBackupsToDeleteAPI plus staticCWallet::nWalletBackups/nMaxWalletBackupsprovide a clean central configuration. One improvement would be to make the comment onGetBackupsToDeleteexplicit that backups are sorted newest‑first and that “powers of 2 indices” refer to 1‑based positions from the newest backup, so behavior is unambiguous and matches the tests/CLI help.Also applies to: 87-94, 505-507
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/Makefile.test.include(1 hunks)src/wallet/init.cpp(1 hunks)src/wallet/wallet.cpp(4 hunks)src/wallet/wallet.h(2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
Applied to files:
src/Makefile.test.includesrc/wallet/wallet.hsrc/wallet/init.cppsrc/wallet/wallet.cpp
📚 Learning: 2025-08-11T17:16:36.654Z
Learnt from: PastaPastaPasta
Repo: dashpay/dash PR: 6804
File: src/qt/proposalwizard.cpp:40-42
Timestamp: 2025-08-11T17:16:36.654Z
Learning: In the Dash repository, when a PR adds new files that are not from Bitcoin backports, these files must be added to the list in test/util/data/non-backported.txt. This applies to newly created files like qt/proposalwizard.{h,cpp} and forms/proposalwizard.ui. Limited exemptions may exist for subtrees and similar cases.
Applied to files:
src/Makefile.test.include
📚 Learning: 2025-02-14T15:19:17.218Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6529
File: src/wallet/rpcwallet.cpp:3002-3003
Timestamp: 2025-02-14T15:19:17.218Z
Learning: The `GetWallet()` function calls in `src/wallet/rpcwallet.cpp` are properly validated with null checks that throw appropriate RPC errors, making additional validation unnecessary.
Applied to files:
src/wallet/wallet.cpp
🧬 Code graph analysis (3)
src/wallet/wallet.h (1)
src/wallet/wallet.cpp (2)
GetBackupsToDelete(3258-3305)GetBackupsToDelete(3258-3258)
src/wallet/init.cpp (1)
src/dummywallet.cpp (1)
argsman(31-31)
src/wallet/wallet.cpp (1)
src/wallet/init.cpp (2)
InitAutoBackup(237-240)InitAutoBackup(237-237)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: linux64_tsan-build / Build source
- GitHub Check: Lint / Run linters
- GitHub Check: x86_64-w64-mingw32 / Build depends
- GitHub Check: x86_64-apple-darwin / Build depends
- GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends
- GitHub Check: x86_64-pc-linux-gnu / Build depends
- GitHub Check: arm-linux-gnueabihf / Build depends
🔇 Additional comments (1)
src/Makefile.test.include (1)
208-223: Backup tests correctly wired into test suite; ensure non‑backported tracking updated.Adding
wallet/test/backup_tests.cppunderENABLE_WALLETintegrates the new tests properly intotest_dash. Also, ifwallet/test/backup_tests.cppis Dash‑specific and not a direct Bitcoin backport, it should be added totest/util/data/non-backported.txtto keep the tracking list consistent. Based on learnings.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
src/wallet/test/backup_tests.cpp (1)
106-108: Same file_time_type construction issue.This has the same issue as lines 20-22. Use
fs::file_time_type::clock::now() - std::chrono::seconds(seconds_ago)instead.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/wallet/test/backup_tests.cpp(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{cpp,h,hpp,cc}
📄 CodeRabbit inference engine (CLAUDE.md)
Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1
Files:
src/wallet/test/backup_tests.cpp
src/{test,wallet/test}/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework
Files:
src/wallet/test/backup_tests.cpp
src/wallet/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Wallet implementation must use Berkeley DB and SQLite
Files:
src/wallet/test/backup_tests.cpp
🧠 Learnings (3)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{test,wallet/test}/**/*.{cpp,h} : Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework
Applied to files:
src/wallet/test/backup_tests.cpp
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
Applied to files:
src/wallet/test/backup_tests.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: linux64_tsan-build / Build source
- GitHub Check: Lint / Run linters
- GitHub Check: x86_64-w64-mingw32 / Build depends
- GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends
- GitHub Check: x86_64-apple-darwin / Build depends
- GitHub Check: x86_64-pc-linux-gnu / Build depends
- GitHub Check: arm-linux-gnueabihf / Build depends
🔇 Additional comments (3)
src/wallet/test/backup_tests.cpp (3)
31-44: LGTM!Cases 1 and 2 correctly test that no deletions occur when the backup count is at or below
nWalletBackups.
72-101: LGTM!Case 4 correctly tests index-based exponential distribution with varied ages. The expected deletion count (7) matches the logic of keeping the latest 10 plus the 16th backup.
12-13: No action needed—GetBackupsToDelete is properly accessible for testing.The function is declared in the public header
src/wallet/wallet.h:93withoutstaticor anonymous namespace restrictions, giving it external linkage. The forward declaration in the test file is valid and matches the exact signature. The linker will successfully resolve the external reference.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/wallet/test/backup_tests.cpp (1)
31-102: Good coverage of index‑based exponential retention; consider one assertion on actual paths if behavior changes later.The
exponential_backup_logiccase nicely documents and tests:
- “< nWalletBackups” and “== nWalletBackups” no‑deletion behavior.
- Index‑based exponential retention (latest N plus 16th, 32nd, etc.), via deletion counts.
Because you only assert on
to_delete.size(), these tests will still pass if the implementation changes which specific historical backups it keeps, as long as the total count matches. That’s fine for now, but if the retention policy ever relies on keeping particular historical points (e.g., exact 16th/32nd snapshots), you may want to add at least one check on the actual kept/deleted indices (e.g., by comparing the returnedfs::pathnames) to catch regressions.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/wallet/test/backup_tests.cpp(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{cpp,h,hpp,cc}
📄 CodeRabbit inference engine (CLAUDE.md)
Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1
Files:
src/wallet/test/backup_tests.cpp
src/{test,wallet/test}/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework
Files:
src/wallet/test/backup_tests.cpp
src/wallet/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Wallet implementation must use Berkeley DB and SQLite
Files:
src/wallet/test/backup_tests.cpp
🧠 Learnings (4)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{test,wallet/test}/**/*.{cpp,h} : Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework
Applied to files:
src/wallet/test/backup_tests.cpp
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
Applied to files:
src/wallet/test/backup_tests.cpp
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
Applied to files:
src/wallet/test/backup_tests.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: linux64_tsan-build / Build source
- GitHub Check: x86_64-w64-mingw32 / Build depends
- GitHub Check: Lint / Run linters
- GitHub Check: x86_64-apple-darwin / Build depends
- GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends
- GitHub Check: arm-linux-gnueabihf / Build depends
- GitHub Check: x86_64-pc-linux-gnu / Build depends
🔇 Additional comments (4)
src/wallet/test/backup_tests.cpp (4)
104-135:hard_max_limittest correctly exercises exponential retention with a generous cap.This case clearly verifies that, for many backups (100) and a high
maxBackups(50), the algorithm keeps only the exponentially spaced subset (13) and deletes the rest (87), rather than “topping up” to the max. That’s a useful invariant to lock in and pairs well with the next test wheremaxBackupsis actually binding.
137-160: Nice targeted test formaxBackupshard cap interaction.
hard_max_limit_caps_retentioncleanly verifies that when exponential retention would keep 13 backups butmaxBackups=12, the hard cap wins (38 deletions). This directly tests the core behavior added in the PR and should catch future regressions in the cap logic.
10-16: I'm unable to verify the forward declaration concern due to a repository access issue. The shell scripts cannot execute because the repository clone failed.Based on the information available:
Forward declaration verification blocked — Cannot access the repository to confirm whether
GetBackupsToDeleteis already declared inwallet.hor whether the forward declaration inbackup_tests.cppis truly redundant.To complete this verification, manual inspection of the following would be needed:
- Whether
wallet.hcontains aGetBackupsToDeletedeclaration- Whether
backup_tests.cppincludes the appropriate header files- The function signature in both locations (if both exist)
20-27: Let me try accessing the repository through GitHub CLI:
<function_calls>
#!/bin/bashTry using GitHub CLI to read the file directly
gh repo clone dashpay/dash -- --depth 1 --filter=blob:none 2>&1 | head -20
</function_calls>Let me search for this file on GitHub:
<function_calls>
dashpay/dash src/wallet/test/backup_tests.cpp make_time lambda
</function_calls>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/wallet/wallet.cpp (1)
3446-3446: Update outdated comment to reflect new retention behavior.The comment still references the old "last 10 backups" behavior, but the code now implements exponential retention with configurable limits.
- // Keep only the last 10 backups, including the new one of course + // Keep the most recent nWalletBackups plus exponentially spaced older backups, capped at nMaxWalletBackups
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/wallet/test/backup_tests.cpp(1 hunks)src/wallet/wallet.cpp(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/wallet/test/backup_tests.cpp
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{cpp,h,hpp,cc}
📄 CodeRabbit inference engine (CLAUDE.md)
Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1
Files:
src/wallet/wallet.cpp
src/wallet/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Wallet implementation must use Berkeley DB and SQLite
Files:
src/wallet/wallet.cpp
🧠 Learnings (4)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
Applied to files:
src/wallet/wallet.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/wallet/**/*.{cpp,h} : Wallet implementation must use Berkeley DB and SQLite
Applied to files:
src/wallet/wallet.cpp
📚 Learning: 2025-02-14T15:19:17.218Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6529
File: src/wallet/rpcwallet.cpp:3002-3003
Timestamp: 2025-02-14T15:19:17.218Z
Learning: The `GetWallet()` function calls in `src/wallet/rpcwallet.cpp` are properly validated with null checks that throw appropriate RPC errors, making additional validation unnecessary.
Applied to files:
src/wallet/wallet.cpp
🧬 Code graph analysis (1)
src/wallet/wallet.cpp (4)
src/wallet/init.cpp (2)
InitAutoBackup(237-240)InitAutoBackup(237-237)src/wallet/test/backup_tests.cpp (1)
GetBackupsToDelete(14-14)src/fs.h (1)
path(42-42)src/wallet/interfaces.cpp (1)
PathToString(720-723)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: linux64_tsan-build / Build source
- GitHub Check: Lint / Run linters
- GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends
- GitHub Check: x86_64-w64-mingw32 / Build depends
- GitHub Check: x86_64-apple-darwin / Build depends
- GitHub Check: x86_64-pc-linux-gnu / Build depends
- GitHub Check: arm-linux-gnueabihf / Build depends
🔇 Additional comments (5)
src/wallet/wallet.cpp (5)
55-61: LGTM! Compile-time invariant and static initialization are correct.The
static_assertenforces a safe relationship between the defaults at compile time, preventing misconfiguration. The static members are properly initialized from constants.
3261-3313: Exponential retention algorithm looks correct.The implementation correctly:
- Returns early when
maxBackups <= 0(defensive guard)- Keeps the newest
nWalletBackupsunconditionally- Additionally keeps power-of-2 ranked positions (
(rank & (rank - 1)) == 0)- Truncates to
maxBackupsif neededThe power-of-2 check is a well-known bit manipulation pattern and handles positions 1, 2, 4, 8, 16, etc. correctly.
3315-3330: LGTM! Clamping logic correctly enforces the invariant.The clamping at lines 3327-3329 ensures
nWalletBackups <= nMaxWalletBackups, addressing the configuration consistency concern from the previous review.
3461-3471: LGTM! Clean integration with existing error handling.The deletion loop correctly uses
GetBackupsToDeleteand preserves the existing logging and error handling patterns.
3677-3685: LGTM! Consistent withInitAutoBackuplogic.The re-initialization logic mirrors
InitAutoBackup, including the same clamping and invariant enforcement. This ensures consistent behavior when the wallet is unlocked after a backup-disabled state (nWalletBackups == -2).
|
Say we have 10 backups (default settings). We create a new 11th backup but because we only keep 10 most recent ones we drop the oldest one and so we are back to 10 backups again. We never get to the point were we could keep the next exponential (16th) one 🤷♂️ Or am I missing smth? |
|
🙈 I guess implicit in my instructions was to not just keep the exact exponential, but the oldest in that subrange. see: 2794905 (still untested besides unit tests) |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/wallet/wallet.cpp (1)
3331-3340: Based on my investigation, I can now provide the rewritten review comment:Clarify semantics for
-maxwalletbackups=0—currently disables all backups but intended meaning is ambiguous.The config clamping logic correctly enforces
nWalletBackups ∈ [0, MAX_N_WALLET_BACKUPS]andnMaxWalletBackups >= 0, withnWalletBackups <= nMaxWalletBackups. However, the behavior when-maxwalletbackups=0is unclear: the code forcesnWalletBackupsto 0, which disables automatic backups entirely (sinceAutoBackupWalletexits whennWalletBackups <= 0).This conflicts with existing Dash Core semantics where
-createwalletbackups=0explicitly disables backups as a documented feature. If-maxwalletbackups=0is meant to represent "no extra cap beyond-createwalletbackups," the enforcement logic should skip the clamp (e.g.,if (nMaxWalletBackups > 0 && nWalletBackups > nMaxWalletBackups)).Add help text or code comments clarifying the intended behavior for zero values, or adjust the logic to match the intended semantics.
🧹 Nitpick comments (1)
src/wallet/wallet.h (1)
82-96: Clarify retention comment to match index-based implementation andmaxBackupssemantics.The constants and signature look fine, but the doc block is a bit under-specified:
- The exponential ranges are applied over indices in a “newest first” ordering (0 = newest,
nWalletBackups-1= last of the “recent” set). Stating this explicitly would avoid confusion with phrases like “16th oldest”.- Implementation starts
range_endat 16 and then finds the first power of two>= nWalletBackups; fornWalletBackups > 16the first range is actually[nWalletBackups, 32), not[nWalletBackups, 16). The comment should describe “powers of two starting from the first2^k >= nWalletBackups” rather than hard‑coding 16.GetBackupsToDeletecurrently treatsmaxBackups <= 0as “delete nothing” (early return). Combined withInitAutoBackup/AutoBackupWalletthis effectively disables automatic deletion when the runtimenMaxWalletBackupsis non‑positive. It’d be good to document here what-maxwalletbackups=0is supposed to mean (no extra cap vs. disable), to keep behavior and help text aligned.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/wallet/test/backup_tests.cpp(1 hunks)src/wallet/wallet.cpp(4 hunks)src/wallet/wallet.h(2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{cpp,h,hpp,cc}
📄 CodeRabbit inference engine (CLAUDE.md)
Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1
Files:
src/wallet/wallet.hsrc/wallet/test/backup_tests.cppsrc/wallet/wallet.cpp
src/wallet/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Wallet implementation must use Berkeley DB and SQLite
Files:
src/wallet/wallet.hsrc/wallet/test/backup_tests.cppsrc/wallet/wallet.cpp
src/{test,wallet/test}/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework
Files:
src/wallet/test/backup_tests.cpp
🧠 Learnings (6)
📓 Common learnings
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
Applied to files:
src/wallet/wallet.hsrc/wallet/test/backup_tests.cppsrc/wallet/wallet.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/wallet/**/*.{cpp,h} : Wallet implementation must use Berkeley DB and SQLite
Applied to files:
src/wallet/wallet.hsrc/wallet/wallet.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{test,wallet/test}/**/*.{cpp,h} : Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework
Applied to files:
src/wallet/test/backup_tests.cpp
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
Applied to files:
src/wallet/test/backup_tests.cpp
📚 Learning: 2025-02-14T15:19:17.218Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6529
File: src/wallet/rpcwallet.cpp:3002-3003
Timestamp: 2025-02-14T15:19:17.218Z
Learning: The `GetWallet()` function calls in `src/wallet/rpcwallet.cpp` are properly validated with null checks that throw appropriate RPC errors, making additional validation unnecessary.
Applied to files:
src/wallet/wallet.cpp
🧬 Code graph analysis (3)
src/wallet/wallet.h (2)
src/fs.h (1)
path(42-42)src/wallet/wallet.cpp (2)
GetBackupsToDelete(3261-3324)GetBackupsToDelete(3261-3261)
src/wallet/test/backup_tests.cpp (1)
src/wallet/wallet.cpp (2)
GetBackupsToDelete(3261-3324)GetBackupsToDelete(3261-3261)
src/wallet/wallet.cpp (2)
src/wallet/init.cpp (2)
InitAutoBackup(237-240)InitAutoBackup(237-237)src/fs.h (1)
path(42-42)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build container / Build container
🔇 Additional comments (5)
src/wallet/wallet.h (1)
506-508: Static backup config is global across wallets; confirm that’s intended.
CWallet::nWalletBackupsandCWallet::nMaxWalletBackupsarestaticand shared by all wallet instances, so-createwalletbackups/-maxwalletbackupsare effectively global, not per‑wallet. This matches prior global behavior, but it’s worth explicitly confirming that per‑wallet configurability is not required before this interface hardens further.src/wallet/wallet.cpp (3)
55-60: Static defaults + compile‑time invariant look good.The
static_assertcleanly enforcesDEFAULT_N_WALLET_BACKUPS <= DEFAULT_MAX_BACKUPSfor non‑zero defaults, and the static initializers forCWallet::nWalletBackups/nMaxWalletBackupscorrectly mirror the header constants. No issues here.
3472-3481: Helper integration inAutoBackupWalletis correct, but overall retention still depends on fixingGetBackupsToDelete.Using
GetBackupsToDelete(folder_set, nWalletBackups, nMaxWalletBackups)here centralizes the retention policy and respects the configured limits. The deletion loop and logging around it look fine and match the tested behavior for a single application over the directory contents.Given the current
GetBackupsToDeletedynamics, though, this code will converge to keeping only the oldest backup plus the lastnWalletBackupsbackups over time. Once the retention algorithm is adjusted (or confirmed) to match the intended “exponential” semantics, this call site will automatically follow suit.
3688-3696: I'll verify this review comment by examining the relevant code sections. Let me start by exploring the wallet backup logic mentioned in the review.
<function_calls>
#!/bin/bashFind and examine InitAutoBackup function
rg -n "InitAutoBackup" --type cpp -A 20 -B 5 src/wallet/
</function_calls>
#!/bin/bashFind and examine AutoBackupWallet function
rg -n "AutoBackupWallet" --type cpp -A 15 -B 5 src/wallet/
</function_calls>
#!/bin/bashGet context around lines 3688-3696 in wallet.cpp
sed -n '3670,3710p' src/wallet/wallet.cpp
</function_calls>
#!/bin/bashSearch for -maxwalletbackups documentation or usage
rg -n "maxwalletbackups|MAX_N_WALLET_BACKUPS|DEFAULT_MAX_BACKUPS" --type cpp -B 3 -A 3 src/wallet/
</function_calls>
#!/bin/bashLook for related constants definitions
rg -n "DEFAULT_N_WALLET_BACKUPS|DEFAULT_MAX_BACKUPS|MAX_N_WALLET_BACKUPS" --type h src/wallet/ -B 2 -A 2
</function_calls>
#!/bin/bashSearch for how -maxwalletbackups is used in the codebase
rg -n "-maxwalletbackups" --type cpp -B 5 -A 5 src/
</function_calls>src/wallet/test/backup_tests.cpp (1)
15-120: Based on my investigation, I've encountered a critical issue that prevents verification of this review comment.The referenced file and functions do not exist in the dashpay/dash repository.
Web searches confirm:
- No
backup_tests.cppfile insrc/wallet/test/- No
GetBackupsToDeletefunction implementation in dashpay/dash- No
exponential_backup_logictest case- No exponential backup retention algorithm currently in the upstream codebase
Additionally, the repository clone failed, preventing direct code inspection.
Context needed for verification:
- Is this review for a PR that hasn't been merged yet? (If so, provide the PR number or branch name)
- Is this referencing a different repository or fork?
- Is there a specific commit or branch where this code exists?
Without access to the actual codebase or PR context, I cannot verify whether:
- The reviewer's concern about missing dynamic testing is justified
- The algorithm implementation is correct
- The convergence claim (repeated calls leading to 11 retained files) is accurate
- Other related tests already cover the suggested dynamic scenario
|
Basically, indexes won't work because they are fresh new every time, can't use them. Pls check commits here https://github.com/UdjinM6/dash/commits/refs/heads/pr_7005/ for time-based retention logic and a couple more fixes. |
|
need to rebase probably; github shows 116 commits in the PR |
bdabc70 to
94ffc35
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/wallet/init.cpp (1)
62-63: CLI options are correctly wired; consider documenting 0-value semanticsThe new
-createwalletbackupsand-maxwalletbackupsoptions are hooked up to the right defaults and constants and feed intoCWallet::InitAutoBackup()as expected.Given how the retention logic treats these values, it may be worth making the following behaviors explicit in the help text:
-createwalletbackups=0disables automatic backups entirely (status 0).-maxwalletbackups=0effectively means “no hard cap” sinceGetBackupsToDelete()returns without deleting whenmaxBackups <= 0.Making that clear would help users understand how to intentionally disable backups or the cap without having to read the code.
src/wallet/test/backup_tests.cpp (1)
15-198: Retention tests accurately mirror the algorithm; only minor duplication remainsThese Boost tests exercise the retention logic very thoroughly: they cover the boundary around
nWalletBackups, exponential time ranges across dense and sparse histories, and the effect ofmaxBackupsas a hard cap. The kept/deleted sets you assert are consistent with the wallet.cpp implementation (newest‑first ordering, “oldest in range” selection).The only nit is the repeated
make_time/make_pathlambdas and multimap setup code across the four test cases. If you touch this file again, consider extracting small helpers (e.g., astruct BackupFixture { ... }with shared builders) to reduce duplication, but it’s fine as‑is for functionality.src/wallet/wallet.cpp (1)
3588-3598: Backup deletion integration looks correct, but error handling may be overly strict.The delegation to
GetBackupsToDeleteis clean. However, returningfalsewhen old backup deletion fails (lines 3594-3596) might be too aggressive—the new backup was already created successfully. Consider returningtruewith a warning instead, as failing to clean up old backups shouldn't fail the overall backup operation.} catch(fs::filesystem_error &error) { warnings.push_back(strprintf(_("Failed to delete backup, error: %s"), fsbridge::get_filesystem_error_message(error))); WalletLogPrintf("%s\n", Join(warnings, Untranslated("\n")).original); - return false; + // Continue despite cleanup failure - the new backup was created successfully } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/Makefile.test.include(1 hunks)src/coinjoin/client.cpp(1 hunks)src/interfaces/wallet.h(1 hunks)src/qt/overviewpage.cpp(5 hunks)src/util/system.cpp(0 hunks)src/util/system.h(0 hunks)src/wallet/init.cpp(1 hunks)src/wallet/interfaces.cpp(1 hunks)src/wallet/test/backup_tests.cpp(1 hunks)src/wallet/wallet.cpp(4 hunks)src/wallet/wallet.h(3 hunks)
💤 Files with no reviewable changes (2)
- src/util/system.h
- src/util/system.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
- src/Makefile.test.include
🧰 Additional context used
📓 Path-based instructions (7)
src/**/*.{cpp,h,hpp,cc}
📄 CodeRabbit inference engine (CLAUDE.md)
Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1
Files:
src/qt/overviewpage.cppsrc/wallet/test/backup_tests.cppsrc/interfaces/wallet.hsrc/wallet/interfaces.cppsrc/coinjoin/client.cppsrc/wallet/wallet.hsrc/wallet/wallet.cppsrc/wallet/init.cpp
src/qt/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
GUI implementation in src/qt/ must use Qt 5
Files:
src/qt/overviewpage.cpp
src/{test,wallet/test}/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework
Files:
src/wallet/test/backup_tests.cpp
src/wallet/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Wallet implementation must use Berkeley DB and SQLite
Files:
src/wallet/test/backup_tests.cppsrc/wallet/interfaces.cppsrc/wallet/wallet.hsrc/wallet/wallet.cppsrc/wallet/init.cpp
src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data
Files:
src/coinjoin/client.cpp
src/coinjoin/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs
Files:
src/coinjoin/client.cpp
src/{masternode,llmq,evo,coinjoin,governance}/**/*.{cpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Use unordered_lru_cache for efficient caching with LRU eviction in Dash-specific data structures
Files:
src/coinjoin/client.cpp
🧠 Learnings (8)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
Applied to files:
src/qt/overviewpage.cppsrc/wallet/test/backup_tests.cppsrc/interfaces/wallet.hsrc/wallet/interfaces.cppsrc/coinjoin/client.cppsrc/wallet/wallet.hsrc/wallet/wallet.cppsrc/wallet/init.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{test,wallet/test}/**/*.{cpp,h} : Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework
Applied to files:
src/wallet/test/backup_tests.cpp
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
Applied to files:
src/wallet/test/backup_tests.cpp
📚 Learning: 2025-02-14T15:19:17.218Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6529
File: src/wallet/rpcwallet.cpp:3002-3003
Timestamp: 2025-02-14T15:19:17.218Z
Learning: The `GetWallet()` function calls in `src/wallet/rpcwallet.cpp` are properly validated with null checks that throw appropriate RPC errors, making additional validation unnecessary.
Applied to files:
src/wallet/interfaces.cppsrc/coinjoin/client.cppsrc/wallet/wallet.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/wallet/**/*.{cpp,h} : Wallet implementation must use Berkeley DB and SQLite
Applied to files:
src/wallet/interfaces.cppsrc/coinjoin/client.cppsrc/wallet/wallet.hsrc/wallet/wallet.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/coinjoin/**/*.{cpp,h} : CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs
Applied to files:
src/coinjoin/client.cpp
📚 Learning: 2025-11-04T18:24:27.241Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.
Applied to files:
src/wallet/wallet.cpp
🧬 Code graph analysis (5)
src/wallet/test/backup_tests.cpp (1)
src/wallet/wallet.cpp (2)
GetBackupsToDelete(3359-3440)GetBackupsToDelete(3359-3359)
src/coinjoin/client.cpp (1)
src/wallet/interfaces.cpp (1)
nWalletBackups(184-184)
src/wallet/wallet.h (3)
src/wallet/wallet.cpp (4)
GetBackupsToDelete(3359-3440)GetBackupsToDelete(3359-3359)InitAutoBackup(3442-3457)InitAutoBackup(3442-3442)src/wallet/interfaces.cpp (1)
nWalletBackups(184-184)src/wallet/init.cpp (2)
InitAutoBackup(240-243)InitAutoBackup(240-240)
src/wallet/wallet.cpp (2)
src/wallet/interfaces.cpp (1)
nWalletBackups(184-184)src/wallet/init.cpp (2)
InitAutoBackup(240-243)InitAutoBackup(240-240)
src/wallet/init.cpp (1)
src/dummywallet.cpp (1)
argsman(31-31)
🪛 Cppcheck (2.18.0)
src/qt/overviewpage.cpp
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
src/wallet/test/backup_tests.cpp
[information] 9-9: Include file
(missingIncludeSystem)
[information] 144-144: Include file
(missingIncludeSystem)
[information] 145-145: Include file
(missingIncludeSystem)
[information] 146-146: Include file
(missingIncludeSystem)
[information] 147-147: Include file
(missingIncludeSystem)
[information] 150-150: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 23-23: Include file
(missingIncludeSystem)
[information] 24-24: Include file
(missingIncludeSystem)
[information] 25-25: Include file
(missingIncludeSystem)
[information] 26-26: Include file
(missingIncludeSystem)
[information] 27-27: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 23-23: Include file
(missingIncludeSystem)
[information] 24-24: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 9-9: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 25-25: Include file
(missingIncludeSystem)
[information] 26-26: Include file
(missingIncludeSystem)
[information] 27-27: Include file
(missingIncludeSystem)
[information] 28-28: Include file
(missingIncludeSystem)
[information] 29-29: Include file
(missingIncludeSystem)
[information] 30-30: Include file
(missingIncludeSystem)
[information] 31-31: Include file
(missingIncludeSystem)
[information] 32-32: Include file
(missingIncludeSystem)
[information] 33-33: Include file
(missingIncludeSystem)
[information] 34-34: Include file
(missingIncludeSystem)
[information] 35-35: Include file
(missingIncludeSystem)
[information] 36-36: Include file
(missingIncludeSystem)
[information] 51-51: Include file
(missingIncludeSystem)
[information] 80-80: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 27-27: Include file
(missingIncludeSystem)
[information] 28-28: Include file
(missingIncludeSystem)
[information] 29-29: Include file
(missingIncludeSystem)
[information] 30-30: Include file
(missingIncludeSystem)
[information] 31-31: Include file
(missingIncludeSystem)
[information] 32-32: Include file
(missingIncludeSystem)
[information] 33-33: Include file
(missingIncludeSystem)
[information] 34-34: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 23-23: Include file
(missingIncludeSystem)
[information] 24-24: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 10-10: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 8-8: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 11-11: Include file
(missingIncludeSystem)
[information] 12-12: Include file
(missingIncludeSystem)
[information] 13-13: Include file
(missingIncludeSystem)
[error] 24-24: #error No known always_inline attribute for this platform.
(preprocessorErrorDirective)
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
src/wallet/interfaces.cpp
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
src/coinjoin/client.cpp
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
src/wallet/wallet.cpp
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
src/wallet/init.cpp
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: arm-linux-build / Build source
- GitHub Check: mac-build / Build source
- GitHub Check: win64-build / Build source
- GitHub Check: linux64_sqlite-build / Build source
- GitHub Check: linux64_ubsan-build / Build source
- GitHub Check: linux64-build / Build source
- GitHub Check: linux64_fuzz-build / Build source
- GitHub Check: linux64_nowallet-build / Build source
- GitHub Check: linux64_tsan-build / Build source
- GitHub Check: Lint / Run linters
🔇 Additional comments (7)
src/wallet/interfaces.cpp (1)
183-185: Forwarding backup status via WalletImpl is appropriateReturning
CWallet::nWalletBackupshere cleanly exposes the global backup configuration/status (including 0 / -1 / -2 sentinels) through the interface, matching how CoinJoin and the GUI consume it.src/coinjoin/client.cpp (1)
720-748: Switching toCWallet::nWalletBackupspreserves existing CoinJoin backup gatingUsing
CWallet::nWalletBackupsin theCheckAutomaticBackup()switch keeps the previous behavior (0 = disabled, -1 = error, -2 = locked) while routing it through the new static backup state. The logic around legacy vs descriptor wallets remains intact.src/interfaces/wallet.h (1)
111-114: Backup status accessor API is well-defined and matches existing semanticsThe new
getWalletBackupStatus()method and its documentation clearly expose the existing sentinel scheme (1..20 / 0 / -1 / -2) through the wallet interface, which is exactly what the CoinJoin client and GUI need. The contract is explicit enough for future callers to interpret the value correctly.src/wallet/wallet.h (1)
83-96: Verify constant name alignment in wallet.cpp implementationThe constants and
GetBackupsToDelete(...)declaration in the header are well-defined and properly documented. However, manual verification is needed to confirm that the implementation inwallet.cppuses the correct constant nameMAX_N_WALLET_BACKUPSconsistently. The review identified a potential typo (MAX_N_WALBACKUPSmissing "LET"), which would cause a compilation failure if present.src/wallet/wallet.cpp (3)
58-64: LGTM!The compile-time invariant via
static_assertis well-placed to catch configuration mismatches at build time, with runtime enforcement handled separately inInitAutoBackup.
3359-3440: Time-based exponential retention logic looks correct.The algorithm properly addresses previous feedback by using actual backup ages (file timestamps) rather than positional indices. Key observations:
- Early return when
maxBackups <= 0correctly implements "no hard cap" semantics- Using
sorted_backups[0].first(newest backup time) as the reference point for age calculations is reasonable for consistency- The exponential day ranges
[1,2), [2,4), [4,8)...provide good historical coverageOne minor note: consider adding a brief comment explaining that
maxBackups <= 0means "no hard cap on total backups" rather than "disable all backups" for future maintainers.
3801-3805: LGTM!Using
InitAutoBackup(m_args)to re-initialize backup configuration after keypool replenishment correctly addresses the previous review feedback and ensures consistent configuration handling.
| // Disable any PS UI for masternode or when autobackup is disabled or failed for whatever reason | ||
| if (clientModel->node().isMasternode() || nWalletBackups <= 0) { | ||
| int backupStatus = walletModel->wallet().getWalletBackupStatus(); | ||
| if (clientModel->node().isMasternode() || backupStatus <= 0) { | ||
| DisableCoinJoinCompletely(); | ||
| if (nWalletBackups <= 0) { | ||
| if (backupStatus <= 0) { | ||
| ui->labelCoinJoinEnabled->setToolTip(tr("Automatic backups are disabled, no mixing available!")); | ||
| } | ||
| return; |
There was a problem hiding this comment.
Differentiate “disabled” vs “error/locked” backup states in CoinJoin UI
Right now coinJoinStatus() treats every non‑positive backupStatus the same in the early guard:
backupStatus <= 0disables CoinJoin completely and sets a generic tooltip “Automatic backups are disabled, no mixing available!”.- Later, you have more specific handling for
backupStatus == -1(fatal backup error) andbackupStatus == -2(wallet locked, keypool not replenished), but that code is only effective within the same invocation where the failure occurs. On subsequent timer ticks, the earlybackupStatus <= 0guard runs first and overwrites those more informative messages with the generic one.
This makes it hard for users to distinguish “I turned backups off” from “backups are failing” or “unlock your wallet to replenish the keypool”, and for the -2 case you end up disabling the CoinJoin UI entirely on the next tick even though the later block was only warning.
Consider restricting the early guard to the truly “disabled” state and letting the negative sentinel states be handled exclusively by the later block:
- int backupStatus = walletModel->wallet().getWalletBackupStatus();
- if (clientModel->node().isMasternode() || backupStatus <= 0) {
- DisableCoinJoinCompletely();
- if (backupStatus <= 0) {
- ui->labelCoinJoinEnabled->setToolTip(tr("Automatic backups are disabled, no mixing available!"));
- }
- return;
- }
+ const int backupStatus = walletModel->wallet().getWalletBackupStatus();
+ if (clientModel->node().isMasternode()) {
+ DisableCoinJoinCompletely();
+ return;
+ }
+ if (backupStatus == 0) {
+ // Backups explicitly disabled via settings/CLI.
+ DisableCoinJoinCompletely();
+ ui->labelCoinJoinEnabled->setToolTip(tr("Automatic backups are disabled, no mixing available!"));
+ return;
+ }With this change:
0(disabled) still fully disables CoinJoin with the generic tooltip.-1and-2are handled only in the legacy‑wallet block later in the function, so the more detailed error/warning text is preserved and not overwritten on the next tick.DisableCoinJoinCompletely()continues to usegetWalletBackupStatus() <= 0to decide when to show the red “(Disabled)” label, which remains appropriate for both “disabled” and error states.
Also applies to: 597-599, 642-655, 751-761
🧰 Tools
🪛 Cppcheck (2.18.0)
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
| // Enforce nWalletBackups <= nMaxWalletBackups | ||
| if (nWalletBackups > nMaxWalletBackups) { | ||
| nWalletBackups = nMaxWalletBackups; | ||
| } |
There was a problem hiding this comment.
Missing guard for nMaxWalletBackups > 0 creates semantic inconsistency.
The current logic at line 3454 unconditionally clamps nWalletBackups to nMaxWalletBackups. If a user sets -maxwalletbackups=0 (intending "no additional cap beyond createwalletbackups"), this would set nWalletBackups=0, which disables backups entirely at line 3489.
However, GetBackupsToDelete treats maxBackups <= 0 as "no hard cap" (returns empty list, deletes nothing). This creates an inconsistency.
Apply this diff to preserve backups when maxwalletbackups=0:
// Enforce nWalletBackups <= nMaxWalletBackups
- if (nWalletBackups > nMaxWalletBackups) {
+ if (nMaxWalletBackups > 0 && nWalletBackups > nMaxWalletBackups) {
nWalletBackups = nMaxWalletBackups;
}Alternatively, if maxwalletbackups=0 should indeed disable backups, update the help text and GetBackupsToDelete semantics accordingly.
🧰 Tools
🪛 Cppcheck (2.18.0)
[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.
(toomanyconfigs)
040ac27 ci: bump CTCACHE_COMMIT to e393144d5c49b060a1dbc7ae15b9c6973efb967d (UdjinM6) 7c310c7 refactor: derive ctcache Python script path from wrapper location (UdjinM6) 28577db refactor: improve ctcache configuration maintainability (UdjinM6) 7ef44b6 chore: adjust removal count message (UdjinM6) 64ff657 fix: improve file removal pipeline robustness (UdjinM6) 2944562 feat: verify clang and ctcache are installed correctly (UdjinM6) a1b98b1 chore: make paths more maintainable (UdjinM6) ead9136 feat: integrate ctcache for clang-tidy result caching in CI (UdjinM6) Pull request description: ## Issue being fixed or feature implemented Add [ctcache](https://github.com/matus-chochlik/ctcache) to cache clang-tidy analysis results, reducing time spent in "Run linters" step from 33-34 minutes to 3-4 minutes in the best case scenarios. ## What was done? - Download ctcache in `contrib/containers/ci/ci.Dockerfile` and patch it to work correctly - Add new steps in `.github/workflows/build-src.yml` to save/restore ctcache cache (for `linux64_multiprocess`, save on pushes to default branch only) - Adjust `ci/dash/lint-tidy.sh` to configure ctcache, force `run-clang-tidy` to use it - Add manual cache cleanup in `ci/dash/lint-tidy.sh` since ctcache doesn't have a way to set internal limits like ccache does ## How Has This Been Tested? Run, check "Run linters" step in `linux64_multiprocess-build` job. develop, no ctcache: [31m 43s](https://github.com/UdjinM6/dash/actions/runs/20273923736/job/58217581581) develop*, empty cache: 0% hits, [33m 19s](https://github.com/UdjinM6/dash/actions/runs/20287332888/job/58265404189) develop*, second run, using cache: 100% hits, [3m 27s](https://github.com/UdjinM6/dash/actions/runs/20287332888/job/58281396883) New branch*, merged #6947 (chainlock, evo): 96.5% hits, [5m 13s](https://github.com/UdjinM6/dash/actions/runs/20300087485/job/58303447008) New branch*, merged #7056 (chainlock, instantsend, llmq, etc): 59.3%, [19m 50s](https://github.com/UdjinM6/dash/actions/runs/20300940029/job/58306338715) New branch*, merged #7005 (wallet, utils): 40.9% hits, [25m 49s](https://github.com/UdjinM6/dash/actions/runs/20293363566/job/58282142424) _* with this patch merged into develop_ ## 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 _(for repository code-owners and collaborators only)_ ACKs for top commit: kwvg: utACK 040ac27 PastaPastaPasta: utACK 040ac27 Tree-SHA512: 3fe629b533773c6200caf0037c541180c91d7d9b9468eba410c0c51c17ae5df28d3d02380a4d4c14654eb73e20ea07d2c0748462874b11e33e1a7a1ae0dd22f4
There was a problem hiding this comment.
I think relying on timestamps of file is not a solid solution by several reasons:
- some filesystem may not keep timestamps for file or intentionally configured to don't do it.
- if files are synced to cloud, the cloud application my update timestamps of files by unexpected ways, depending on internal sync logic
- if user's time will be altered due to any hardware or software issue, user may lost its backup
- if user copy-pasted backups from other machine, timestamp will be updated to current date & time, if user haven't kept timestamps intentionally. They are kept in the most popular OS only if user moved files withing 1 logical storage; if user moved from C: to D: in Windows or from /home/ to /mnt/sdaX/ most likely timestamps will reset.
Beside these potentially dangerous cases (what may happen or may not happen), explanation to user how exactly backups are kept is not a trivial thing to avoid any possible misunderstandings. Also, if user will blunder once machine time, he may loose older backups unexpectedly and it would be difficult to justify to user, especially if he had wrong time only very short time (seconds after OS rebooted before daemons set correct time).
The logic that has been implemented sounds and applyable in my opinion for other things, such as re-trying to reconnect to remove server; re-trying to send transaction to network; re-trying to make a backup if storage is not available; or something else where out-come of failure is low.
I would suggest to create backup with timestamp or with date&time in the name; and filter them by index & by time.
such as:
wallet.dat
->
backup-wallet-000-20260101002233.dat
backup-wallet-001-20260101002133.dat
backup-wallet-002-20260102002133.dat
backup-wallet-003-20250102002133.dat
please note that both "number of backup" and "timestamp" are required in filename.
If user have wrong time on machine, it should be removed oldest files by index even if date/time are completely wrong and having an index in the file name prevent from it.
Secondly, all files no need to be renamed now; it may be just old one removed; but number in backups may continously be increased. it prevents from a situation when filesystem is corrupted, user trying to recover data and see 1000 of historical files with exactly same name and it's not clear which one is which one.
p.s. instead date&time filenames may have timestamp: it's less user friendly IMO but it helps to avoid possible bugs and issues if user changed timezone. So, both timestamp&dates have pros and cons.
| argsman.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting many (possibly all) or none, instead of selecting on a per-output basis. Privacy is improved as addresses are mostly swept with fewer transactions and outputs are aggregated in clean change addresses. It may result in higher fees due to less optimal coin selection caused by this added limitation and possibly a larger-than-necessary number of inputs being used. Always enabled for wallets with \"avoid_reuse\" enabled, otherwise default: %u.", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); | ||
| argsman.AddArg("-consolidatefeerate=<amt>", strprintf("The maximum feerate (in %s/kvB) at which transaction building may use more inputs than strictly necessary so that the wallet's UTXO pool can be reduced (default: %s).", CURRENCY_UNIT, FormatMoney(DEFAULT_CONSOLIDATE_FEERATE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); | ||
| argsman.AddArg("-createwalletbackups=<n>", strprintf("Number of automatic wallet backups (default: %u)", nWalletBackups), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); | ||
| argsman.AddArg("-createwalletbackups=<n>", strprintf("Number of automatic wallet backups to keep most recent (default: %u, max: %u). Older backups are kept at exponentially spaced intervals.", DEFAULT_N_WALLET_BACKUPS, MAX_N_WALLET_BACKUPS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); |
There was a problem hiding this comment.
I think doc/managing-wallets.md should include detailed explanation how exactly exponential intervals works; this short explanation is not enough without looking to code, but init.cpp is not a place for detailed help
| std::set<size_t> indices_to_keep; | ||
|
|
||
| // Always keep the latest nWalletBackups (by count, not time) | ||
| for (size_t i = 0; i < std::min(sorted_backups.size(), (size_t)nWalletBackups); ++i) { |
There was a problem hiding this comment.
nit: avoid C-style cast https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#coding-style-c
Use a named cast or functional cast, not a C-Style cast. When casting between integer types, use functional casts such as int(x) or int{x} instead of (int) x. When casting between more complex types, use static_cast. Use reinterpret_cast and const_cast as appropriate.
|
✅ Review complete (commit 30a0fb2) |
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
The exponential retention algorithm is well-designed and the test coverage for the happy-path retention buckets is solid. Two issues: -maxwalletbackups=0 silently disables all automatic backups (surprising for a 'max retention' parameter), and GetBackupsToDelete() doesn't actually enforce its documented hard cap when called with inconsistent parameters — it relies on callers to pre-clamp.
Reviewed commit: 94ffc35
🟡 2 suggestion(s)
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/wallet/wallet.cpp`:
- [SUGGESTION] lines 3447-3455: `-maxwalletbackups=0` silently disables automatic backups entirely
The startup clamp computes `nWalletBackups = min(createwalletbackups, maxwalletbackups)`, so `-maxwalletbackups=0` forces `nWalletBackups` to 0. `AutoBackupWallet()` then exits early on `nWalletBackups <= 0`, creating no new backup and leaving existing backups untouched.
For a parameter documented as "Maximum total number of automatic wallet backups to keep", silently disabling the entire backup system is surprising. A typo like `-maxwalletbackups=0` now removes the protection the feature provides with no warning.
Either: (a) validate in `ParameterInteraction()` and reject `maxwalletbackups < 1` when auto-backups are enabled, or (b) explicitly document that `0` disables auto-backups and log a warning at startup.
- [SUGGESTION] lines 3359-3365: `GetBackupsToDelete()` doesn't enforce its documented hard cap independently
The header comment says the helper "Enforces a hard limit of maxBackups", but the function returns early when `backups.size() <= nWalletBackups` and unconditionally keeps the first `nWalletBackups` entries before considering `maxBackups`. So `GetBackupsToDelete(backups, 10, 5)` can keep 10 files, and `GetBackupsToDelete(backups, 10, 0)` keeps everything.
The production caller masks this by clamping `nWalletBackups <= nMaxWalletBackups` first, but the extracted helper's contract is now stronger than its behavior. Either clamp `nWalletBackups` internally or add an assertion, and add a test for inconsistent inputs.
| nWalletBackups = args.GetIntArg("-createwalletbackups", DEFAULT_N_WALLET_BACKUPS); | ||
| nWalletBackups = std::max(0, std::min(MAX_N_WALLET_BACKUPS, nWalletBackups)); | ||
|
|
||
| nMaxWalletBackups = args.GetIntArg("-maxwalletbackups", DEFAULT_MAX_BACKUPS); | ||
| nMaxWalletBackups = std::max(0, nMaxWalletBackups); | ||
|
|
||
| // Enforce nWalletBackups <= nMaxWalletBackups | ||
| if (nWalletBackups > nMaxWalletBackups) { | ||
| nWalletBackups = nMaxWalletBackups; |
There was a problem hiding this comment.
🟡 Suggestion: -maxwalletbackups=0 silently disables automatic backups entirely
The startup clamp computes nWalletBackups = min(createwalletbackups, maxwalletbackups), so -maxwalletbackups=0 forces nWalletBackups to 0. AutoBackupWallet() then exits early on nWalletBackups <= 0, creating no new backup and leaving existing backups untouched.
For a parameter documented as "Maximum total number of automatic wallet backups to keep", silently disabling the entire backup system is surprising. A typo like -maxwalletbackups=0 now removes the protection the feature provides with no warning.
Either: (a) validate in ParameterInteraction() and reject maxwalletbackups < 1 when auto-backups are enabled, or (b) explicitly document that 0 disables auto-backups and log a warning at startup.
source: ['codex']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/wallet/wallet.cpp`:
- [SUGGESTION] lines 3447-3455: `-maxwalletbackups=0` silently disables automatic backups entirely
The startup clamp computes `nWalletBackups = min(createwalletbackups, maxwalletbackups)`, so `-maxwalletbackups=0` forces `nWalletBackups` to 0. `AutoBackupWallet()` then exits early on `nWalletBackups <= 0`, creating no new backup and leaving existing backups untouched.
For a parameter documented as "Maximum total number of automatic wallet backups to keep", silently disabling the entire backup system is surprising. A typo like `-maxwalletbackups=0` now removes the protection the feature provides with no warning.
Either: (a) validate in `ParameterInteraction()` and reject `maxwalletbackups < 1` when auto-backups are enabled, or (b) explicitly document that `0` disables auto-backups and log a warning at startup.
| std::vector<fs::path> GetBackupsToDelete(const std::multimap<fs::file_time_type, fs::path>& backups, int nWalletBackups, int maxBackups) | ||
| { | ||
| std::vector<fs::path> paths_to_delete; | ||
|
|
||
| // Early guards | ||
| if (maxBackups <= 0) { | ||
| return paths_to_delete; |
There was a problem hiding this comment.
🟡 Suggestion: GetBackupsToDelete() doesn't enforce its documented hard cap independently
The header comment says the helper "Enforces a hard limit of maxBackups", but the function returns early when backups.size() <= nWalletBackups and unconditionally keeps the first nWalletBackups entries before considering maxBackups. So GetBackupsToDelete(backups, 10, 5) can keep 10 files, and GetBackupsToDelete(backups, 10, 0) keeps everything.
The production caller masks this by clamping nWalletBackups <= nMaxWalletBackups first, but the extracted helper's contract is now stronger than its behavior. Either clamp nWalletBackups internally or add an assertion, and add a test for inconsistent inputs.
source: ['codex']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/wallet/wallet.cpp`:
- [SUGGESTION] lines 3359-3365: `GetBackupsToDelete()` doesn't enforce its documented hard cap independently
The header comment says the helper "Enforces a hard limit of maxBackups", but the function returns early when `backups.size() <= nWalletBackups` and unconditionally keeps the first `nWalletBackups` entries before considering `maxBackups`. So `GetBackupsToDelete(backups, 10, 5)` can keep 10 files, and `GetBackupsToDelete(backups, 10, 0)` keeps everything.
The production caller masks this by clamping `nWalletBackups <= nMaxWalletBackups` first, but the extracted helper's contract is now stronger than its behavior. Either clamp `nWalletBackups` internally or add an assertion, and add a test for inconsistent inputs.
Enhances the wallet backup system to retain more historical backups while managing disk space efficiently. Instead of keeping only the N most recent backups, the system now keeps the N most recent plus exponentially-spaced older backups (16th, 32nd, 64th oldest, etc.). - Add `-maxwalletbackups` parameter for hard limit on total backups - Replace magic numbers with named constants (DEFAULT_MAX_BACKUPS=30, DEFAULT_N_WALLET_BACKUPS=10, MAX_N_WALLET_BACKUPS=20) - Extract backup deletion logic into GetBackupsToDelete() function - Add comprehensive unit tests in wallet/test/backup_tests.cpp - Update help text to clarify exponential spacing behavior
The previous algorithm kept backups at fixed power-of-2 indices (16th, 32nd, 64th), but these positions were unreachable since backups are deleted incrementally - the 11th backup was always deleted before accumulating to 16. Changed to keep the oldest backup in each exponential range: - [nWalletBackups, 16), [16, 32), [32, 64), etc. This allows exponential retention to work from the first backup beyond nWalletBackups. With 11 backups, all 11 are now kept. With 12, index 10 is deleted while index 11 (oldest in range) is preserved. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The previous index-based algorithm was fundamentally flawed - it would delete backups down to 11 total and never accumulate beyond that, making exponential retention impossible. The new time-based algorithm: - Keeps latest N backups by count (regardless of age) - For older backups, keeps one per exponential day range: [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), etc. - Processes ranges until maxBackups limit is reached - Backups naturally accumulate over time with exponential spacing - Works correctly with irregular backup schedules and long gaps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Rewrote all backup retention tests to use time-based (days) instead of index-based logic. New tests cover: - Basic retention with < nWalletBackups - Time-based exponential ranges [1,2), [2,4), [4,8), etc. - Hard maxBackups limit enforcement - Irregular backup schedules (multiple per day, gaps) - Long inactivity periods 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Make InitAutoBackup() accept an ArgsManager parameter and call it from Unlock() instead of duplicating the configuration reading and validation logic. This ensures consistency and reduces code duplication.
Remove the unused global nWalletBackups from util/system and use CWallet::nWalletBackups everywhere. Qt and CoinJoin code now access the backup status through the interfaces::Wallet::getWalletBackupStatus() method instead of directly accessing wallet internals. This fixes a bug where the global was never updated from its initial value of 10, causing Qt/CoinJoin to see incorrect backup status.
94ffc35 to
30a0fb2
Compare
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
The PR implements a well-structured time-based exponential retention policy for wallet backups. The core algorithm is correct and tests are comprehensive. One substantive logic issue exists: the clamping in InitAutoBackup lacks a guard for nMaxWalletBackups > 0, creating a semantic inconsistency where -maxwalletbackups=0 silently disables all backups. Codex's blocking finding about position-vs-age retention is a false positive — the HEAD commit explicitly implements time-based retention as intended.
Reviewed commit: 30a0fb2
🟡 3 suggestion(s) | 💬 2 nitpick(s)
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/wallet/wallet.cpp`:
- [SUGGESTION] lines 3593-3596: Missing `nMaxWalletBackups > 0` guard creates semantic inconsistency for -maxwalletbackups=0
The clamping at line 3594 unconditionally enforces `nWalletBackups <= nMaxWalletBackups`. When a user sets `-maxwalletbackups=0`, this sets `nWalletBackups=0`, which disables backups entirely at line 3629. However, `GetBackupsToDelete` treats `maxBackups <= 0` as "no hard cap" (returns empty list, deletes nothing). These two codepaths disagree on what `maxwalletbackups=0` means.
Additionally, no diagnostic is logged when clamping occurs — a user setting `-maxwalletbackups=5` without changing `-createwalletbackups` will silently have their effective backup count reduced from 10 to 5.
- [SUGGESTION] lines 3504-3510: GetBackupsToDelete silently mishandles negative nWalletBackups
`GetBackupsToDelete` is a public function (declared in `wallet.h`, tested directly). If called with a negative `nWalletBackups`, the cast `(size_t)nWalletBackups` at line 3508 wraps to a huge value, causing the early return (all backups kept). At line 3518, the same cast would fill `indices_to_keep` with an enormous range. While `AutoBackupWallet` guards against this at line 3629, the function's precondition should be explicit since it's a public API exercised directly by tests.
In `src/wallet/test/backup_tests.cpp`:
- [SUGGESTION] lines 140-198: Missing edge case test for maxBackups == nWalletBackups
No test covers `maxBackups == nWalletBackups` (e.g., `GetBackupsToDelete(backups, 10, 10)`), which is the boundary where exponential slots go to zero — pure recent-N retention only. This validates that the new algorithm degrades gracefully to the old behavior. Similarly, `nWalletBackups=0` (all backups treated as "old") is untested.
| // Enforce nWalletBackups <= nMaxWalletBackups | ||
| if (nWalletBackups > nMaxWalletBackups) { | ||
| nWalletBackups = nMaxWalletBackups; | ||
| } |
There was a problem hiding this comment.
🟡 Suggestion: Missing nMaxWalletBackups > 0 guard creates semantic inconsistency for -maxwalletbackups=0
The clamping at line 3594 unconditionally enforces nWalletBackups <= nMaxWalletBackups. When a user sets -maxwalletbackups=0, this sets nWalletBackups=0, which disables backups entirely at line 3629. However, GetBackupsToDelete treats maxBackups <= 0 as "no hard cap" (returns empty list, deletes nothing). These two codepaths disagree on what maxwalletbackups=0 means.
Additionally, no diagnostic is logged when clamping occurs — a user setting -maxwalletbackups=5 without changing -createwalletbackups will silently have their effective backup count reduced from 10 to 5.
💡 Suggested change
| // Enforce nWalletBackups <= nMaxWalletBackups | |
| if (nWalletBackups > nMaxWalletBackups) { | |
| nWalletBackups = nMaxWalletBackups; | |
| } | |
| // Enforce nWalletBackups <= nMaxWalletBackups | |
| if (nMaxWalletBackups > 0 && nWalletBackups > nMaxWalletBackups) { | |
| LogPrintf("Warning: -createwalletbackups=%d exceeds -maxwalletbackups=%d, reducing to %d\n", | |
| nWalletBackups, nMaxWalletBackups, nMaxWalletBackups); | |
| nWalletBackups = nMaxWalletBackups; | |
| } |
source: ['claude', 'coderabbit']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/wallet/wallet.cpp`:
- [SUGGESTION] lines 3593-3596: Missing `nMaxWalletBackups > 0` guard creates semantic inconsistency for -maxwalletbackups=0
The clamping at line 3594 unconditionally enforces `nWalletBackups <= nMaxWalletBackups`. When a user sets `-maxwalletbackups=0`, this sets `nWalletBackups=0`, which disables backups entirely at line 3629. However, `GetBackupsToDelete` treats `maxBackups <= 0` as "no hard cap" (returns empty list, deletes nothing). These two codepaths disagree on what `maxwalletbackups=0` means.
Additionally, no diagnostic is logged when clamping occurs — a user setting `-maxwalletbackups=5` without changing `-createwalletbackups` will silently have their effective backup count reduced from 10 to 5.
| if (maxBackups <= 0) { | ||
| return paths_to_delete; | ||
| } | ||
|
|
||
| if (backups.size() <= (size_t)nWalletBackups) { | ||
| return paths_to_delete; | ||
| } |
There was a problem hiding this comment.
🟡 Suggestion: GetBackupsToDelete silently mishandles negative nWalletBackups
GetBackupsToDelete is a public function (declared in wallet.h, tested directly). If called with a negative nWalletBackups, the cast (size_t)nWalletBackups at line 3508 wraps to a huge value, causing the early return (all backups kept). At line 3518, the same cast would fill indices_to_keep with an enormous range. While AutoBackupWallet guards against this at line 3629, the function's precondition should be explicit since it's a public API exercised directly by tests.
💡 Suggested change
| if (maxBackups <= 0) { | |
| return paths_to_delete; | |
| } | |
| if (backups.size() <= (size_t)nWalletBackups) { | |
| return paths_to_delete; | |
| } | |
| // Early guards | |
| if (maxBackups <= 0 || nWalletBackups < 0) { | |
| return paths_to_delete; | |
| } |
source: ['claude']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/wallet/wallet.cpp`:
- [SUGGESTION] lines 3504-3510: GetBackupsToDelete silently mishandles negative nWalletBackups
`GetBackupsToDelete` is a public function (declared in `wallet.h`, tested directly). If called with a negative `nWalletBackups`, the cast `(size_t)nWalletBackups` at line 3508 wraps to a huge value, causing the early return (all backups kept). At line 3518, the same cast would fill `indices_to_keep` with an enormous range. While `AutoBackupWallet` guards against this at line 3629, the function's precondition should be explicit since it's a public API exercised directly by tests.
| int range_start_days = 1; | ||
| int range_end_days = 2; |
There was a problem hiding this comment.
💬 Nitpick: Signed integer overflow in range_end_days is technically undefined behavior
range_end_days is an int that doubles each iteration (line 3568). After 31 iterations, it overflows signed int, which is undefined behavior in C++. While this requires backups older than ~2.9 million years to trigger in practice, it's trivially fixable. The days duration type on line 3526 already uses int64_t — the range variables should match.
💡 Suggested change
| int range_start_days = 1; | |
| int range_end_days = 2; | |
| int64_t range_start_days = 1; | |
| int64_t range_end_days = 2; |
source: ['claude']
| BOOST_AUTO_TEST_CASE(hard_max_limit) | ||
| { | ||
| auto now = std::chrono::file_clock::now(); | ||
| auto make_time = [&now](int64_t days_ago) { | ||
| auto days_duration = std::chrono::duration<int64_t, std::ratio<86400>>(days_ago); | ||
| return fs::file_time_type{now - days_duration}; | ||
| }; | ||
| auto make_path = [](int i) { | ||
| return fs::u8path("backup_" + ToString(i) + ".dat"); | ||
| }; | ||
|
|
||
| std::multimap<fs::file_time_type, fs::path> backups; | ||
|
|
||
| // Create 100 daily backups and set maxBackups=15 | ||
| for (int i = 0; i < 100; ++i) { | ||
| backups.insert({make_time(i), make_path(i)}); | ||
| } | ||
|
|
||
| auto to_delete = GetBackupsToDelete(backups, 10, 15); | ||
|
|
||
| // Without maxBackups limit, we'd keep 14 backups (see Case 5 above) | ||
| // With maxBackups=15, we still keep 14 (under the limit) | ||
| BOOST_CHECK_EQUAL(to_delete.size(), 86); | ||
|
|
||
| // Verify same backups kept as in Case 5 | ||
| std::set<std::string> expected_kept_15 = { | ||
| "backup_0.dat", "backup_1.dat", "backup_2.dat", "backup_3.dat", "backup_4.dat", | ||
| "backup_5.dat", "backup_6.dat", "backup_7.dat", "backup_8.dat", "backup_9.dat", | ||
| "backup_15.dat", "backup_31.dat", "backup_63.dat", "backup_99.dat" | ||
| }; | ||
| std::set<std::string> actual_kept_15; | ||
| for (const auto& [time, path] : backups) { | ||
| if (std::find(to_delete.begin(), to_delete.end(), path) == to_delete.end()) { | ||
| actual_kept_15.insert(fs::PathToString(path.filename())); | ||
| } | ||
| } | ||
| BOOST_CHECK(expected_kept_15 == actual_kept_15); | ||
|
|
||
| // Now test with maxBackups=12 (less than natural retention) | ||
| to_delete = GetBackupsToDelete(backups, 10, 12); | ||
|
|
||
| // Should cap at 12 backups: keep latest 10 + 2 oldest time ranges | ||
| // Total: 12 kept, 88 deleted | ||
| BOOST_CHECK_EQUAL(to_delete.size(), 88); | ||
|
|
||
| // Verify exact backups kept when capped | ||
| std::set<std::string> expected_kept_12 = { | ||
| "backup_0.dat", "backup_1.dat", "backup_2.dat", "backup_3.dat", "backup_4.dat", | ||
| "backup_5.dat", "backup_6.dat", "backup_7.dat", "backup_8.dat", "backup_9.dat", | ||
| "backup_15.dat", "backup_31.dat" | ||
| }; | ||
| std::set<std::string> actual_kept_12; | ||
| for (const auto& [time, path] : backups) { | ||
| if (std::find(to_delete.begin(), to_delete.end(), path) == to_delete.end()) { | ||
| actual_kept_12.insert(fs::PathToString(path.filename())); | ||
| } | ||
| } | ||
| BOOST_CHECK(expected_kept_12 == actual_kept_12); | ||
| } |
There was a problem hiding this comment.
🟡 Suggestion: Missing edge case test for maxBackups == nWalletBackups
No test covers maxBackups == nWalletBackups (e.g., GetBackupsToDelete(backups, 10, 10)), which is the boundary where exponential slots go to zero — pure recent-N retention only. This validates that the new algorithm degrades gracefully to the old behavior. Similarly, nWalletBackups=0 (all backups treated as "old") is untested.
source: ['claude']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/wallet/test/backup_tests.cpp`:
- [SUGGESTION] lines 140-198: Missing edge case test for maxBackups == nWalletBackups
No test covers `maxBackups == nWalletBackups` (e.g., `GetBackupsToDelete(backups, 10, 10)`), which is the boundary where exponential slots go to zero — pure recent-N retention only. This validates that the new algorithm degrades gracefully to the old behavior. Similarly, `nWalletBackups=0` (all backups treated as "old") is untested.
| argsman.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting many (possibly all) or none, instead of selecting on a per-output basis. Privacy is improved as addresses are mostly swept with fewer transactions and outputs are aggregated in clean change addresses. It may result in higher fees due to less optimal coin selection caused by this added limitation and possibly a larger-than-necessary number of inputs being used. Always enabled for wallets with \"avoid_reuse\" enabled, otherwise default: %u.", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); | ||
| argsman.AddArg("-consolidatefeerate=<amt>", strprintf("The maximum feerate (in %s/kvB) at which transaction building may use more inputs than strictly necessary so that the wallet's UTXO pool can be reduced (default: %s).", CURRENCY_UNIT, FormatMoney(DEFAULT_CONSOLIDATE_FEERATE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); | ||
| argsman.AddArg("-createwalletbackups=<n>", strprintf("Number of automatic wallet backups (default: %u)", nWalletBackups), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); | ||
| argsman.AddArg("-createwalletbackups=<n>", strprintf("Number of automatic wallet backups to keep most recent (default: %u, max: %u). Older backups are kept at exponentially spaced intervals.", DEFAULT_N_WALLET_BACKUPS, MAX_N_WALLET_BACKUPS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); |
There was a problem hiding this comment.
💬 Nitpick: Awkward grammar in -createwalletbackups help text
The help text reads "Number of automatic wallet backups to keep most recent" which is grammatically unclear — "keep most recent" lacks an article and reads ambiguously.
💡 Suggested change
| argsman.AddArg("-createwalletbackups=<n>", strprintf("Number of automatic wallet backups to keep most recent (default: %u, max: %u). Older backups are kept at exponentially spaced intervals.", DEFAULT_N_WALLET_BACKUPS, MAX_N_WALLET_BACKUPS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); | |
| argsman.AddArg("-createwalletbackups=<n>", strprintf("Number of most recent automatic wallet backups to keep (default: %u, max: %u). Older backups are kept at exponentially spaced intervals.", DEFAULT_N_WALLET_BACKUPS, MAX_N_WALLET_BACKUPS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); |
source: ['claude']
Issue being fixed or feature implemented
Enhances the wallet backup system to retain more historical backups while managing disk space efficiently. Instead of keeping only the N most recent backups, the system now keeps the N most recent plus exponentially-spaced older backups (16th, 32nd, 64th oldest, etc.).
-maxwalletbackupsparameter for hard limit on total backupsWhat was done?
How Has This Been Tested?
NOT TESTED; opening so I don't forget about this patch set.
Breaking Changes
Checklist:
Go over all the following points, and put an
xin all the boxes that apply.