Repository navigation
backport: partial bitcoin#28868 (wallet: reload wallet if migration exited early, keep mixed watchonly txs) - #7793
Conversation
Backport of bitcoin/bitcoin@78ba0e6748d2 from bitcoin#28868. Migration will unload loaded wallets prior to beginning. It will then perform some checks which may exit early. Such unloaded wallets should be reloaded prior to exiting. Adapted to Dash: the "already a descriptor wallet" check keeps using GetLegacyScriptPubKeyMan(), the Dash backup filename logic is kept, and the successful-migration path still reloads the wallet directly because bitcoin#28609 (which introduced the reload_wallet helper there) has not been backported, so the upstream hunk moving the helper out of that branch does not apply. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Backport of bitcoin/bitcoin@71cb28ea8cb5 from bitcoin#28868. We want to make sure that all of the transactions are being copied to the watchonly and solvable wallets as expected. The automatic rescanning behavior can cause us to pass a test by finding the transaction on loading rather than having it be copied as expected. The helper also reloads the wallet before each migration, so it fails if an earlier failed migratewallet call left the wallet unloaded. The Dash-only test_wallet_name_with_slashes migration goes through the helper as well. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Backport of bitcoin/bitcoin@c62a8d03a862 from bitcoin#28868. It is possible for a transaction that has an output that belongs to the migrated wallet, and another output that belongs to the watchonly wallet. Such transaction should appear in both wallets during migration. Adapted to Dash: the watchonly copy still goes through AddToWallet() because bitcoin#28125 (LoadToWallet()/CopyFrom() and the shared watchonly WalletBatch) has not been backported. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Backport of bitcoin/bitcoin@4da76ca24725 from bitcoin#28868. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 25 minutes. View limit detailsLimit details: You’ve used all 4 included reviews currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (2)
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 |
|
✅ Final review complete — no blockers (commit 17b0589) |
thepastaclaw
left a comment
There was a problem hiding this comment.
Final validation — Phase 1 + Phase 2
At the exact reviewed head, the wallet fixes preserve the intended upstream behavior without an identified production correctness defect. One non-blocking upstream test transformation is missing and is not documented among the declared omissions. Verification was static: the supplied CI snapshot shows successful formatting, merge, and amd64 container checks, queued arm64 container checks, and a description-validation failure confirmed by its job log to be caused by upstream commit-reference syntax.
🟡 1 suggestion(s)
Review provenance
Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: backport-reviewer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
normalbygpt-6.1-sol(effort low) — The diff changes wallet migration error recovery and transaction assignment between migrated wallets, but its contained logic does not meet the large-or-intricate threshold for critical. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity below 15% reserve: weekly 15% left, 5h 100% left),glm-5.3-flash(not used above high effort; tier asks max) - Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— backport-reviewer (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— dash-core-commit-history (completed, effort high); agentphase2-reviewer
🤖 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 `test/functional/wallet_migration.py`:
- [SUGGESTION] test/functional/wallet_migration.py:54-64: Missing test prerequisite: bitcoin#28038 addressbook migration scenario
Upstream commit 71cb28ea8cb5 also routes the migration in `test_addressbook` through `self.migrate_wallet(wallet)`, adding the no-Rescanning assertion to a scenario that creates both auxiliary wallets and checks migrated addressbook data. That scenario was introduced by 7ecc29a0b7a23d8f5d3c1e6a0dad29b3ad839eb9 in bitcoin#28038 and is absent from both the Dash base and this head, so this upstream test hunk has not been carried over. The existing multisig scenario exercises both auxiliary wallets, but it does not reproduce the addressbook assertions. This does not establish a prerequisite for either production fix, and the PR explicitly advertises a partial backport; however, its omission list does not identify this additional dropped test hunk. Add the adapted scenario and use this helper, or explicitly document the omitted transformation and its deferred test prerequisite.
| def migrate_wallet(self, wallet_rpc, *args, **kwargs): | ||
| # Helper to ensure that only migration happens | ||
| # Since we may rescan on loading of a wallet, make sure that the best block | ||
| # is written before beginning migration | ||
| # Reload to force write that record | ||
| wallet_name = wallet_rpc.getwalletinfo()["walletname"] | ||
| wallet_rpc.unloadwallet() | ||
| self.nodes[0].loadwallet(wallet_name) | ||
| # Migrate, checking that rescan does not occur | ||
| with self.nodes[0].assert_debug_log(expected_msgs=[], unexpected_msgs=["Rescanning"]): | ||
| return wallet_rpc.migratewallet(*args, **kwargs) |
There was a problem hiding this comment.
⏭️ Deferred (maintainer decision; not blocking) at
17b05897; see the replies below.
🟡 Suggestion: Missing test prerequisite: bitcoin#28038 addressbook migration scenario
Upstream commit 71cb28e also routes the migration in test_addressbook through self.migrate_wallet(wallet), adding the no-Rescanning assertion to a scenario that creates both auxiliary wallets and checks migrated addressbook data. That scenario was introduced by 7ecc29a in bitcoin#28038 and is absent from both the Dash base and this head, so this upstream test hunk has not been carried over. The existing multisig scenario exercises both auxiliary wallets, but it does not reproduce the addressbook assertions. This does not establish a prerequisite for either production fix, and the PR explicitly advertises a partial backport; however, its omission list does not identify this additional dropped test hunk. Add the adapted scenario and use this helper, or explicitly document the omitted transformation and its deferred test prerequisite.
source: gpt-6.1-sol (phase2-reviewer: general, backport-reviewer, dash-core-commit-history)
There was a problem hiding this comment.
Agreed that it was undocumented. The hunk cannot be carried over because test_addressbook comes from bitcoin#28038, which is not backported, so there is nothing for it to change here. The omission is now listed under "Dash adaptations and omitted hunks" in the PR description.
🤖 Posted autonomously by Claude on behalf of pasta.
There was a problem hiding this comment.
I confirmed that test_addressbook is absent from both the Dash base and this head, and the updated PR description now explicitly identifies the omitted helper transformation and its bitcoin#28038 prerequisite; that addresses my documentation concern, and I’m satisfied to leave the scenario outside this backport.
Noted as intentionally deferred; I will not press it further here.
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review after discussion — commit 17b0589
Standing review was COMMENTED; this re-review is APPROVE: no blocking findings remain.
Approved: the discussion withdrew or deferred every finding the full review of 17b05897 (run 3281) raised, and the approval rests on that review.
Same commit as the standing review; the inline threads above carry the per-finding outcome. This follow-up exists only to correct the verdict.
Review provenance
Source: conversation lane gpt-6.1-sol (agent: conversation); no reviewer or verifier lanes ran for this follow-up
- Verdict moved because the inline discussion withdrew or deferred findings of the standing review of this commit; the code was not re-reviewed
…f migration exited early, keep mixed watchonly txs) 17b0589 test: test migration of tx with both spendable and watchonly (pasta) 03821b7 fix(wallet): keep txs that belong to both watchonly and migrated wallets (pasta) c305aab test: make sure that migration test does not rescan on reloading (pasta) 1f9bf08 fix(wallet): reload the wallet if migration exited early (pasta) Pull request description: ## Issue being fixed or feature implemented `migratewallet` (and the GUI "Migrate wallet" action, which calls the same `MigrateLegacyToDescriptor()`) has two bugs that upstream fixed in bitcoin#28868. That PR is listed as outstanding in the "next batch" section of #7277. 1. **A failed migration leaves the wallet unloaded.** If the wallet is loaded, `MigrateLegacyToDescriptor()` unloads it first and then runs its checks. Several of those checks return early without loading it again: the wallet is already a descriptor wallet, the backup could not be written, or the passphrase is missing or wrong. After any of these errors the wallet is gone from `listwallets`, and RPC calls to it fail with `Requested wallet does not exist or is not loaded` until the user runs `loadwallet` manually. Mistyping the passphrase once on an encrypted legacy wallet is enough to trigger this. 2. **A transaction with outputs in both resulting wallets only stays in the migrated wallet.** During migration, `ApplyMigrationData()` only offers a transaction to the new `<name>_watchonly` wallet when the migrated wallet does *not* claim it. If one output pays a spendable address and another pays an imported watch-only address, the transaction stays in the migrated wallet and is never copied to `<name>_watchonly`. That wallet is created at the chain tip, so it does not rescan, and its history and balance silently omit that transaction. This is the watch-only bug mentioned in #7275. ### Why this is a real problem Code path at c14104b: - The loaded wallet is unloaded before any validation: [wallet.cpp#L5006-L5012](https://github.com/dashpay/dash/blob/c14104b172a2/src/wallet/wallet.cpp#L5006-L5012) - These early returns do not reload it: "already a descriptor wallet" [#L5033-L5035](https://github.com/dashpay/dash/blob/c14104b172a2/src/wallet/wallet.cpp#L5033-L5035), backup failure [#L5055-L5057](https://github.com/dashpay/dash/blob/c14104b172a2/src/wallet/wallet.cpp#L5055-L5057), missing or wrong passphrase [#L5062-L5075](https://github.com/dashpay/dash/blob/c14104b172a2/src/wallet/wallet.cpp#L5062-L5075) - Watch-only copying only runs when `!IsMine(tx) && !IsFromMe(tx)`: [wallet.cpp#L4759-L4781](https://github.com/dashpay/dash/blob/c14104b172a2/src/wallet/wallet.cpp#L4759-L4781) I reproduced all three cases against an unfixed `dashd` built from c14104b, using a functional-test-style script on regtest: <details><summary>Reproduction on c14104b (unfixed)</summary> Steps: 1. `createwallet desc` (descriptor), then `migratewallet desc`, then `listwallets` 2. `createwallet enc descriptors=false`, then `encryptwallet pass`, then `migratewallet enc badpass`, then `listwallets` and `getwalletinfo` on `enc` 3. `createwallet imports descriptors=false`, then `importaddress <addr from default wallet>`. From the default wallet, `send` one output to an `imports` address and one to the imported address, then mine a block. Then `migratewallet` and `gettransaction <txid>` on `imports_watchonly` ``` TestFramework (INFO): Case 1: migratewallet on a loaded descriptor wallet TestFramework (INFO): listwallets before: ['default_wallet', 'desc'] TestFramework (INFO): listwallets after: ['default_wallet'] TestFramework (INFO): Case 2: migratewallet on a loaded encrypted legacy wallet with a wrong passphrase TestFramework (INFO): listwallets before: ['default_wallet', 'enc'] TestFramework (INFO): listwallets after: ['default_wallet'] TestFramework (INFO): getwalletinfo on 'enc' -> RPC error -18 (wallet not loaded) TestFramework (INFO): Case 3: tx with a spendable output and a watch-only output TestFramework (INFO): imports_watchonly.gettransaction(cfbc39814ffc474a35bae511c660de7a759653f3a362639f08e38fe2bd6382ef) -> Invalid or non-wallet transaction id (-5) TestFramework (INFO): RESULT descriptor wallet still loaded: False TestFramework (INFO): RESULT encrypted wallet still loaded: False TestFramework (INFO): RESULT mixed tx present in watchonly wallet: False ``` With this branch, the same script reports `listwallets after: ['default_wallet', 'desc']` and `['default_wallet', 'desc', 'enc']`, and all three results are `True`. </details> The upstream tests backported here also fail on the unfixed code (see "How Has This Been Tested?"). ### Why it matters Neither bug loses funds or keys. The first one is a usability trap in a v24 feature: a typo in the passphrase makes the wallet disappear from the node, the GUI and RPC clients, with no hint that a `loadwallet` is needed. The second one leaves the post-migration watch-only wallet with incomplete history and balance, and nothing reports it. Users migrating real wallets have already hit it (see #7275). ## What was done? Backport of bitcoin#28868, minus one commit: - bitcoin/bitcoin commit `78ba0e6748d2` "wallet: Reload the wallet if migration exited early": remember whether the wallet was loaded (`was_loaded`), define the `reload_wallet` helper before the early exits, and reload the wallet on the descriptor-wallet, backup-failure and passphrase-failure exits. As upstream does, the passphrase unlock moves out of the `LOCK(cs_wallet)` scope so the reload does not happen while the wallet lock is held. - bitcoin/bitcoin commit `71cb28ea8cb5` "test: Make sure that migration test does not rescan on reloading": adds the `migrate_wallet()` helper, which reloads the wallet before migrating and checks that migration does not log `Rescanning`. Because the helper calls `getwalletinfo` on the wallet first, it fails if an earlier failed `migratewallet` left the wallet unloaded. The Dash-only `test_wallet_name_with_slashes` case also goes through the helper. - bitcoin/bitcoin commit `c62a8d03a862` "wallet: Keep txs that belong to both watchonly and migrated wallets": every transaction is offered to the watch-only wallet. It is removed from the migrated wallet only if the migrated wallet does not also own it. - bitcoin/bitcoin commit `4da76ca24725` "test: Test migration of tx with both spendable and watchonly" Dash adaptations and omitted hunks: - **Omitted bitcoin/bitcoin commit `9332c7edda79`** "wallet: Write bestblock to watchonly and solvable wallets". Upstream needs it because, after bitcoin#28609, the watch-only and solvable wallets are created in an empty context and reloaded at the end of migration, and without a best block record they rescan on that reload. Dash has not backported bitcoin#28609. Here those wallets are created with `CreateWallet(context, ...)`, which already writes the chain tip as their best block on first run, and they are not reloaded during migration. The commit fixes nothing on Dash today and belongs with a bitcoin#28609 backport. - 78ba0e6: Dash's "already a descriptor wallet" check (`!GetLegacyScriptPubKeyMan()`) and Dash's backup filename logic are kept as they are. The upstream hunk that moves `reload_wallet` out of the success branch does not apply, because that helper came from bitcoin#28609. The success path keeps its existing direct reload. - c62a8d0: the watch-only copy still uses `AddToWallet()`, because bitcoin#28125 (`LoadToWallet()`/`CopyFrom()` and the shared watch-only `WalletBatch`) is not backported. Only the ownership logic changes, exactly as upstream. - 71cb28e: the upstream hunk that routes `test_addressbook` through `self.migrate_wallet()` is not carried over. That scenario was added by bitcoin#28038, which Dash has not backported, so there is no `test_addressbook` to change. Every other scenario that upstream routes through the helper does so here as well. - 4da76ca: applied to Dash's existing `test_other_watchonly`. The upstream context lines that check copied tx metadata come from bitcoin#28125 and are not part of this change. ### Why this is the correct minimal fix The wallet goes missing because the unload happens before the checks, while only the success path reloads it. Reloading on each early exit is the upstream fix, and every exit taken after the unload but before any database change is covered. Moving the checks ahead of the unload would mean running the backup and the passphrase check against the loaded instance, which is a larger restructuring than this fix needs. As upstream does, no reload is attempted when `MakeWalletDatabase()`/`CWallet::Create()` fail (the same open would fail again) or when `MigrateToSQLite()` fails (by then the original BDB file may already have been removed, and the failed-migration restore path does not run). The `.legacy.bak` file written before a wrong-passphrase exit is also kept, as upstream does. The watch-only change is the upstream one-condition fix, and the rest of bitcoin#28609/bitcoin#28125 is not needed for either bug. The `migratewallet` RPC first shipped in v24.0.0-rc.1 (#7275/#7277 are on v24.0.x), so this is a candidate for v24.0.x. ## How Has This Been Tested? macOS arm64, `--enable-debug --enable-werror`, built `dashd`/`dash-cli`/`dash-wallet` at each step. - Reproduction script above: on c14104b it fails with all three bugs present. With this branch it passes. - `test/functional/wallet_migration.py` with only the 71cb28e test change, against the unfixed c14104b binary: **fails** in `test_encrypted`. After the expected wrong-passphrase errors, `migrate_wallet()` → `getwalletinfo` raises `Requested wallet does not exist or is not loaded (-18)`. All earlier migrations pass the no-`Rescanning` check. - Same test after 78ba0e6: passes. - `wallet_migration.py` with the 4da76ca test change, against a binary that has 78ba0e6 but not c62a8d0: **fails** at `watchonly.gettransaction(watchonly_spendable_txid)` with `Invalid or non-wallet transaction id (-5)`. The new listtransactions counts before migration (6) and in the migrated wallet (2) already pass at that point. - Full branch: `test/functional/test_runner.py wallet_migration.py` passes. - `test/lint/lint-python.py` and `test/lint/lint-whitespace.py`: clean. `clang-format-diff` only suggests re-wrapping the passphrase error strings. They keep upstream's layout to stay 1:1, and `wallet.cpp` is not in `non-backported.txt`. ## Breaking Changes None. After a failed `migratewallet`, a wallet that was loaded beforehand is now loaded again, as it was before the call. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) ACKs for top commit: knst: utACK 17b0589 Tree-SHA512: c245f96c7a9a5922ae1ffad9e5c0a3a6eab3494949239cc1d94fab5cdab090239cc13fa4ea3d0a4cd017ef36657aac834d6dd0f84d2bbfd482430ed639e8b1c0 (cherry picked from commit 233aefd) Conflict in test/functional/wallet_migration.py: test_conflict_txs() comes from bitcoin#28542 (#7762), which v24.0.x does not have, so it is left out along with its migratewallet() -> migrate_wallet() change.
fbe6526 Merge #7807: feat(consensus): hold EvoNode shares and multiple payouts behind a new evo_shares deployment (pasta) 57fd2d9 Merge #7776: fix: keep a pending ProUpServTx across a registrar update that keeps the operator key (pasta) ca69801 Merge #7803: fix: return non-zero amount of accounts after sethdseed (pasta) 57a8f07 Merge #7796: backport: bitcoin#26720 (remaining), partial bitcoin#29523: weight-check the knapsack exact-match selection (pasta) 4eb82f6 Merge #7786: fix(net): keep a dedicated onion listener when -bind is given and correct the bind release note (pasta) 70626a0 Merge #7793: backport: partial bitcoin#28868 (wallet: reload wallet if migration exited early, keep mixed watchonly txs) (pasta) 250a3cd Merge #7790: fix: guard cached governance object flags with the object lock (pasta) 700c195 Merge #7787: fix: only sign EHF signals inside the deployment's start/timeout window (pasta) a3b0f3c Merge #7785: fix: restore the txindex requirement when governance validation is enabled (pasta) 2d1735d Merge #7794: fix(wallet): floor the denomination gap threshold so rebalancing cannot oscillate (pasta) d9f6592 Merge #7783: fix: stop fish completion from re-running the typed dash-cli command (pasta) 89d44c8 Merge #7795: backport: bitcoin#34272, partial bitcoin#31650 (pass PSBT by const reference in PSBTInputSignedAndVerified) (pasta) a1d7411 Merge #7779: fix!: report legacy evonode Platform addresses when listdiff changes its address (pasta) 73f6ea2 Merge #7789: fix(qt): report malformed lang/font settings instead of aborting at startup (pasta) 40de493 Merge #7781: fix(rpc): use a consistent tip height in quorum dkgstatus (pasta) 7d1a90d Merge #7791: fix(net): request object votes from nPeersPerHashMax peers (pasta) 51b145c Merge #7778: test: use single-member in functional tests: feature_mnehf, feature_notifications, p2p_instantsend (pasta) Pull request description: ## Issue being fixed or feature implemented Backports for v24.0.0-rc.3. `v24.0.x` was cut at the v24.0.0-rc.2 tag (1e239d4), and develop has since moved to 24.1.0 (#7775), so rc.3 is built from this branch instead of being tagged on develop like rc.1 and rc.2. ## What was done? Cherry-picked (`git cherry-pick -m1 -x`) every develop merge since rc.2 that carries `backport-candidate-24.0.x`, plus #7778, which #7807's test needs. They are in develop merge order: - #7778 test: use single-member in functional tests: feature_mnehf, feature_notifications, p2p_instantsend (test-only. Needed because #7807's `feature_evo_shares_activation.py` depends on its `mine_quorum_single_member()` fix and fails without it.) - #7791 fix(net): request object votes from nPeersPerHashMax peers - #7781 fix(rpc): use a consistent tip height in quorum dkgstatus - #7789 fix(qt): report malformed lang/font settings instead of aborting at startup - #7779 fix!: report legacy evonode Platform addresses when listdiff changes its address - #7795 backport: bitcoin#34272, partial bitcoin#31650 (pass PSBT by const reference in PSBTInputSignedAndVerified) - #7783 fix: stop fish completion from re-running the typed dash-cli command - #7794 fix(wallet): floor the denomination gap threshold so rebalancing cannot oscillate - #7785 fix: restore the txindex requirement when governance validation is enabled - #7787 fix: only sign EHF signals inside the deployment's start/timeout window - #7790 fix: guard cached governance object flags with the object lock - #7793 backport: partial bitcoin#28868 (wallet: reload wallet if migration exited early, keep mixed watchonly txs) - #7786 fix(net): keep a dedicated onion listener when -bind is given and correct the bind release note. Includes partial bitcoin#36170 (5f40d56). Upstream's dedicated-onion-bind changes to `feature_torcontrol.py` and `p2p_private_broadcast.py` are not included, because those tests depend on upstream commits (5693833, e74d54e) that Dash does not have. - #7796 backport: bitcoin#26720 (remaining), partial bitcoin#29523: weight-check the knapsack exact-match selection - #7803 fix: return non-zero amount of accounts after sethdseed - #7776 fix: keep a pending ProUpServTx across a registrar update that keeps the operator key - #7807 feat(consensus): hold EvoNode shares and multiple payouts behind a new evo_shares deployment One pick needed conflict resolution, in a test only, described in its commit message: #7793, `test/functional/wallet_migration.py`. `test_conflict_txs()` comes from bitcoin#28542 (#7762), which is not on this branch, so it is left out. The C++ part of #7793 is identical to develop. Merged since rc.2 but intentionally not included: #7775 (24.1 version bump), #7260 and #7804 (new Qt feature and its CI fix), #7584 (refactor), routine upstream backports #7257, #7759, #7762, #7768, #7788, #7801, and test/CI-only changes #7777 and #7797. No version change is needed. `configure.ac` is already 24.0.0 with `CLIENT_VERSION_IS_RELEASE` false, so the build takes its version string from the `v24.0.0-rc.3` tag, as it did for rc.2. ## How Has This Been Tested? Built on macOS arm64 against depends with `--enable-debug --enable-werror` (dashd, dash-qt, tests). - `make check`: all unit test suites pass, including `test_dash-qt`. - Functional tests, all passing: `feature_evo_shares_activation.py`, `feature_masternode_shares.py`, `feature_mnehf.py`, `feature_governance.py --descriptors`, `feature_governance_objects.py`, `feature_governance_txindex_devnet.py`, `feature_proxy.py`, `feature_notifications.py`, `feature_llmq_connections.py`, `feature_llmq_chainlocks_automatic.py`, `feature_llmq_singlenode.py`, `p2p_instantsend.py`, `rpc_blockchain.py` (v1 and v2 transport), `rpc_netinfo.py`, `rpc_quorum.py`, `rpc_verifychainlock.py`, `wallet_hd.py` (legacy and descriptors), `wallet_migration.py`. - `feature_bind_extra.py` (from #7786) only runs on Linux and was skipped locally, so CI covers it. Every touched file was diffed against develop. The remaining differences come only from develop PRs that are not part of this backport. ## Breaking Changes Carried over from the picked PRs: - #7807: consensus change behind the new `evo_shares` deployment (bit 14). Mainnet and testnet are unaffected until it activates. Devnets with `v24` already active and an EvoNode carrying several owner payouts need to check or reset. New RPC `protx shared_register_prepare_evo`. - #7785: outside regtest, a node with `-txindex=0` and without `-disablegovernance` refuses to start again, as before v23.1.0. - #7786: carries a partial bitcoin#36170. A node started with `-bind` but no specific `-bind=<addr:port>=onion` now refuses to start while `-listenonion` is enabled. That is the default when listening, even without Tor configured. A wildcard onion bind is also refused. Such nodes need `-bind=127.0.0.1:<port>=onion` or `-listenonion=0`. Nodes without `-bind`, including those using only `-whitebind`, keep the default `127.0.0.1:9996` (`19996` testnet) onion target. See `doc/release-notes-7300.md`. - #7779: `protx listdiff` now includes `platform_p2p` and `platform_https` for a legacy-address evonode whose core P2P address changes. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone 🤖 Generated with [Claude Code](https://claude.com/claude-code) Top commit has no ACKs. Tree-SHA512: 8cb01bdb0234f31b5dd2c105b69673de148922991a7523627395c0e0ccfa22b94be5ffbecc842398d443bc90fd9baf2eee6a9cf6fcdbbd1626eb00b93726a55a
…ave both spendable and watchonly outputs BACKPORT NOTE: Only 9332c7e (write bestblock to watchonly and solvable wallets) is taken here. The other commits of this PR were already backported in dashpay#7793. Needed now because bitcoin#28609 reloads the watch-only and solvables wallets after migration, and without a bestblock record they rescan from genesis on that reload. 4da76ca test: Test migration of tx with both spendable and watchonly (Ava Chow) c62a8d0 wallet: Keep txs that belong to both watchonly and migrated wallets (Ava Chow) 71cb28e test: Make sure that migration test does not rescan on reloading (Ava Chow) 78ba0e6 wallet: Reload the wallet if migration exited early (Ava Chow) 9332c7e wallet: Write bestblock to watchonly and solvable wallets (Ava Chow) Pull request description: A transaction does not necessarily have to belong to either the migrated wallet (with the private keys) and the watchonly wallet (with watchonly things), it could have multiple outputs with each isminetype. So we should be putting such transactions in one or the other wallet, but rather putting it in both. I've added a test for this behavior, however the test also revealed a few other issues. Notably, it revealed that `migratewallet` would have the watchonly wallet rescan from genesis when it is reloaded at the end of migration. This could be a cause for migration appearing to be very slow. This is resolved by first writing best block records to the watchonly and solvable wallets, as well as updating the test to make sure that rescans don't happen. The change to avoid rescans also found an issue where some of our early exits would result in unloading the wallet even though nothing happened. So there is also a commit to reload the wallet for such early exits. ACKs for top commit: ryanofsky: Code review ACK 4da76ca. This looks great. The code is actually cleaner than before, two bugs are fixed, and the test checking for rescanning is pretty clever and broadens test coverage. furszy: Code review ACK 4da76ca Tree-SHA512: 5fc210cff16ca6720d7b2d0616d7e3f295c974147854abc704cf99a3bfaad17572ada084859e7a1b1ca94da647ad130303219678f429b7995f85e040236db35c
…ave both spendable and watchonly outputs BACKPORT NOTE: Only 9332c7e (write bestblock to watchonly and solvable wallets) is taken here. The other commits of this PR were already backported in dashpay#7793. Needed now because bitcoin#28609 reloads the watch-only and solvables wallets after migration, and without a bestblock record they rescan from genesis on that reload. 4da76ca test: Test migration of tx with both spendable and watchonly (Ava Chow) c62a8d0 wallet: Keep txs that belong to both watchonly and migrated wallets (Ava Chow) 71cb28e test: Make sure that migration test does not rescan on reloading (Ava Chow) 78ba0e6 wallet: Reload the wallet if migration exited early (Ava Chow) 9332c7e wallet: Write bestblock to watchonly and solvable wallets (Ava Chow) Pull request description: A transaction does not necessarily have to belong to either the migrated wallet (with the private keys) and the watchonly wallet (with watchonly things), it could have multiple outputs with each isminetype. So we should be putting such transactions in one or the other wallet, but rather putting it in both. I've added a test for this behavior, however the test also revealed a few other issues. Notably, it revealed that `migratewallet` would have the watchonly wallet rescan from genesis when it is reloaded at the end of migration. This could be a cause for migration appearing to be very slow. This is resolved by first writing best block records to the watchonly and solvable wallets, as well as updating the test to make sure that rescans don't happen. The change to avoid rescans also found an issue where some of our early exits would result in unloading the wallet even though nothing happened. So there is also a commit to reload the wallet for such early exits. ACKs for top commit: ryanofsky: Code review ACK 4da76ca. This looks great. The code is actually cleaner than before, two bugs are fixed, and the test checking for rescanning is pretty clever and broadens test coverage. furszy: Code review ACK 4da76ca Tree-SHA512: 5fc210cff16ca6720d7b2d0616d7e3f295c974147854abc704cf99a3bfaad17572ada084859e7a1b1ca94da647ad130303219678f429b7995f85e040236db35c Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
…ave both spendable and watchonly outputs BACKPORT NOTE: Only 9332c7e (write bestblock to watchonly and solvable wallets) is taken here. The other commits of this PR were already backported in dashpay#7793. Needed now because bitcoin#28609 reloads the watch-only and solvables wallets after migration, and without a bestblock record they rescan from genesis on that reload. 4da76ca test: Test migration of tx with both spendable and watchonly (Ava Chow) c62a8d0 wallet: Keep txs that belong to both watchonly and migrated wallets (Ava Chow) 71cb28e test: Make sure that migration test does not rescan on reloading (Ava Chow) 78ba0e6 wallet: Reload the wallet if migration exited early (Ava Chow) 9332c7e wallet: Write bestblock to watchonly and solvable wallets (Ava Chow) Pull request description: A transaction does not necessarily have to belong to either the migrated wallet (with the private keys) and the watchonly wallet (with watchonly things), it could have multiple outputs with each isminetype. So we should be putting such transactions in one or the other wallet, but rather putting it in both. I've added a test for this behavior, however the test also revealed a few other issues. Notably, it revealed that `migratewallet` would have the watchonly wallet rescan from genesis when it is reloaded at the end of migration. This could be a cause for migration appearing to be very slow. This is resolved by first writing best block records to the watchonly and solvable wallets, as well as updating the test to make sure that rescans don't happen. The change to avoid rescans also found an issue where some of our early exits would result in unloading the wallet even though nothing happened. So there is also a commit to reload the wallet for such early exits. ACKs for top commit: ryanofsky: Code review ACK 4da76ca. This looks great. The code is actually cleaner than before, two bugs are fixed, and the test checking for rescanning is pretty clever and broadens test coverage. furszy: Code review ACK 4da76ca Tree-SHA512: 5fc210cff16ca6720d7b2d0616d7e3f295c974147854abc704cf99a3bfaad17572ada084859e7a1b1ca94da647ad130303219678f429b7995f85e040236db35c Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
…itcoin#34370, partials bitcoin#31423, bitcoin#34156 (migratewallet fixes from 28.x) 1574eb0 Merge bitcoin#34370: [29.x] Fix bitcoin#34222 backport bugs (Konstantin Akimov) 9857f73 Merge bitcoin#34226: wallet: test: Relative wallet failed migration cleanup (Konstantin Akimov) 7b7d0c6 partial Merge bitcoin#34156: wallet: fix unnamed legacy wallet migration failure (Konstantin Akimov) cfd0412 Merge bitcoin#34215: wallettool: fix unnamed createfromdump failure walletsdir deletion (Konstantin Akimov) c8f51bb partial Merge bitcoin#31423: wallet: migration, avoid creating spendable wallet from a watch-only legacy wallet (Konstantin Akimov) 3fd819a Merge bitcoin#28868: wallet: Fix migration of wallets with txs that have both spendable and watchonly outputs (Konstantin Akimov) 8a922f1 Merge bitcoin#28609: wallet: Reload watchonly and solvables wallets after migration (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented A failed `migratewallet` could delete more than it created: for the unnamed (default) wallet the cleanup removed the whole `wallets/` directory, a relative wallet name removed the directory the name pointed at, and a failed `restorewallet` or `createfromdump` used `fs::remove_all` on a directory it did not own. Watch-only and solvables wallets created during migration were also left registered in the global context and were never reloaded, so a reload failure after migration went unnoticed. Upstream fixed this in bitcoin#34156 / bitcoin#34215 / bitcoin#34226 and backported it to 28.x and 29.x as bitcoin#34223 / bitcoin#34222; bitcoin#34370 fixes bugs of that backport. Neither 26.x nor 27.x received these fixes so 28.x and 29.x is a closest adaptation to the current Dash Core state. ## What was done? Master merge commits are cherry-picked with `-m1`. Conflicts are resolved against the 28.x copies (bitcoin#34223 and the fix commits in bitcoin#34620), because master's versions target the BERKELEY_RO-only migration path that Dash does not have. - bitcoin#28609: create watch-only/solvables wallets in a local context and reload all wallets after migration; copy full tx metadata. Prerequisite for the cleanup code below. Dash's "tx belongs to both wallets" handling from #7793 is kept. - bitcoin#31423 (partial): only `WalletDatabase::Files()` and the sqlite path cleanup. The watch-only migration behaviour change and the reload dedup are not taken (BERKELEY_RO only). `BerkeleyDatabase::Files()` and the `DummyDatabase` override come from 28.x. - bitcoin#34215: `createfromdump` cleanup removes only the files it created. - bitcoin#34156 (partial): `RestoreWallet` and migration cleanup remove only created files; `MigrationPrefixName` names the default wallet's backup and auxiliary wallets `default_wallet_*`; post-migration load failure is logged and rolls back. Skipped: `test_pruned_wallet_backup` (needs bitcoin#30678). The backup filename keeps Dash's basename reduction for path-style wallet names and only adopts `MigrationPrefixName` for the unnamed wallet. - bitcoin#34226: parameterised failed-migration cleanup test (unnamed, relative, absolute and plain names). Expected backup paths follow Dash's basename rule. - bitcoin#34370: BDB-aware `Files()`, backup moved into the wallet directory when a non-directory wallet becomes a directory, correct wallet name in the load-failure log, no more copying of the backup back and forth, `db.log` cleanup test. Not backported, with TODOs in `MigrateLegacyToDescriptor` saying what to replace: - bitcoin#32273: backups move to the top-level walletdir and are named from the wallet path. Then drop the basename reduction, `test_wallet_name_with_slashes` and the 28.x-only "move backup into wallet dir" block, re-derive the bitcoin#34226 path cases from master, and take bitcoin#33104 / bitcoin#33122. - bitcoin#31451: `load_after_restore` so a wallet that was not loaded before migration is not loaded after a failed one. Re-derive from the 29.x commits d91f56e and a074d36. - bitcoin#28976 (blank wallets) and bitcoin#30678 (backup writes the best block locator): not needed for these fixes, left for a follow-up. - When bitcoin#31248 lands, re-take the test commits from the 29.x series instead of the 28.x ones. ## How Has This Been Tested? New and changed tests: - `wallet_migration.py`: `test_failed_migration_cleanup`, `test_default_wallet_watch_only`, `test_migration_failure` for `""`, `../`, an absolute path and a plain name (checks that unrelated files survive and the original wallet is restored as BDB), `unsynced_wallet_on_pruned_node_fails`; `test_other_watchonly` now checks tx metadata in the watch-only wallet. - `wallet_backup.py`: `test_restore_existent_dir`, `test_restore_into_unnamed_wallet`; `restore_wallet_existent_name` and `restore_preserves_existing_directory` expect the new "Database file exists" error. - `tool_wallet.py`: `createfromdump` with an unnamed wallet keeps the wallets directory and `db.log` of a shared BDB environment. ## Breaking Changes - `restorewallet` now accepts an existing empty directory and refuses only an existing `wallet.dat`. - Migration backups are named `<name>_<time>.legacy.bak` (`default_wallet_<time>.legacy.bak` for the unnamed wallet) instead of `<name>-<time>.legacy.bak` / `wallet-<time>.legacy.bak`, and the unnamed wallet's auxiliary wallets are `default_wallet_watchonly` / `default_wallet_solvables` instead of `_watchonly` / `_solvables`. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone Top commit has no ACKs. Tree-SHA512: c561c70e3273b2f1847eced60a9661c70262b2c9a0017e8cbb49eba092ecbb5e95aea383024b56ee6c7e54a4cbc452eb647d6c26f866662dc3c2a11b68a9e183
…4226, bitcoin#34370, partials bitcoin#31423, bitcoin#34156 (migratewallet fixes from 28.x) 1574eb0 Merge bitcoin#34370: [29.x] Fix bitcoin#34222 backport bugs (Konstantin Akimov) 9857f73 Merge bitcoin#34226: wallet: test: Relative wallet failed migration cleanup (Konstantin Akimov) 7b7d0c6 partial Merge bitcoin#34156: wallet: fix unnamed legacy wallet migration failure (Konstantin Akimov) cfd0412 Merge bitcoin#34215: wallettool: fix unnamed createfromdump failure walletsdir deletion (Konstantin Akimov) c8f51bb partial Merge bitcoin#31423: wallet: migration, avoid creating spendable wallet from a watch-only legacy wallet (Konstantin Akimov) 3fd819a Merge bitcoin#28868: wallet: Fix migration of wallets with txs that have both spendable and watchonly outputs (Konstantin Akimov) 8a922f1 Merge bitcoin#28609: wallet: Reload watchonly and solvables wallets after migration (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented A failed `migratewallet` could delete more than it created: for the unnamed (default) wallet the cleanup removed the whole `wallets/` directory, a relative wallet name removed the directory the name pointed at, and a failed `restorewallet` or `createfromdump` used `fs::remove_all` on a directory it did not own. Watch-only and solvables wallets created during migration were also left registered in the global context and were never reloaded, so a reload failure after migration went unnoticed. Upstream fixed this in bitcoin#34156 / bitcoin#34215 / bitcoin#34226 and backported it to 28.x and 29.x as bitcoin#34223 / bitcoin#34222; bitcoin#34370 fixes bugs of that backport. Neither 26.x nor 27.x received these fixes so 28.x and 29.x is a closest adaptation to the current Dash Core state. ## What was done? Master merge commits are cherry-picked with `-m1`. Conflicts are resolved against the 28.x copies (bitcoin#34223 and the fix commits in bitcoin#34620), because master's versions target the BERKELEY_RO-only migration path that Dash does not have. - bitcoin#28609: create watch-only/solvables wallets in a local context and reload all wallets after migration; copy full tx metadata. Prerequisite for the cleanup code below. Dash's "tx belongs to both wallets" handling from dashpay#7793 is kept. - bitcoin#31423 (partial): only `WalletDatabase::Files()` and the sqlite path cleanup. The watch-only migration behaviour change and the reload dedup are not taken (BERKELEY_RO only). `BerkeleyDatabase::Files()` and the `DummyDatabase` override come from 28.x. - bitcoin#34215: `createfromdump` cleanup removes only the files it created. - bitcoin#34156 (partial): `RestoreWallet` and migration cleanup remove only created files; `MigrationPrefixName` names the default wallet's backup and auxiliary wallets `default_wallet_*`; post-migration load failure is logged and rolls back. Skipped: `test_pruned_wallet_backup` (needs bitcoin#30678). The backup filename keeps Dash's basename reduction for path-style wallet names and only adopts `MigrationPrefixName` for the unnamed wallet. - bitcoin#34226: parameterised failed-migration cleanup test (unnamed, relative, absolute and plain names). Expected backup paths follow Dash's basename rule. - bitcoin#34370: BDB-aware `Files()`, backup moved into the wallet directory when a non-directory wallet becomes a directory, correct wallet name in the load-failure log, no more copying of the backup back and forth, `db.log` cleanup test. Not backported, with TODOs in `MigrateLegacyToDescriptor` saying what to replace: - bitcoin#32273: backups move to the top-level walletdir and are named from the wallet path. Then drop the basename reduction, `test_wallet_name_with_slashes` and the 28.x-only "move backup into wallet dir" block, re-derive the bitcoin#34226 path cases from master, and take bitcoin#33104 / bitcoin#33122. - bitcoin#31451: `load_after_restore` so a wallet that was not loaded before migration is not loaded after a failed one. Re-derive from the 29.x commits d91f56e and a074d36. - bitcoin#28976 (blank wallets) and bitcoin#30678 (backup writes the best block locator): not needed for these fixes, left for a follow-up. - When bitcoin#31248 lands, re-take the test commits from the 29.x series instead of the 28.x ones. ## How Has This Been Tested? New and changed tests: - `wallet_migration.py`: `test_failed_migration_cleanup`, `test_default_wallet_watch_only`, `test_migration_failure` for `""`, `../`, an absolute path and a plain name (checks that unrelated files survive and the original wallet is restored as BDB), `unsynced_wallet_on_pruned_node_fails`; `test_other_watchonly` now checks tx metadata in the watch-only wallet. - `wallet_backup.py`: `test_restore_existent_dir`, `test_restore_into_unnamed_wallet`; `restore_wallet_existent_name` and `restore_preserves_existing_directory` expect the new "Database file exists" error. - `tool_wallet.py`: `createfromdump` with an unnamed wallet keeps the wallets directory and `db.log` of a shared BDB environment. ## Breaking Changes - `restorewallet` now accepts an existing empty directory and refuses only an existing `wallet.dat`. - Migration backups are named `<name>_<time>.legacy.bak` (`default_wallet_<time>.legacy.bak` for the unnamed wallet) instead of `<name>-<time>.legacy.bak` / `wallet-<time>.legacy.bak`, and the unnamed wallet's auxiliary wallets are `default_wallet_watchonly` / `default_wallet_solvables` instead of `_watchonly` / `_solvables`. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone Top commit has no ACKs. Tree-SHA512: c561c70e3273b2f1847eced60a9661c70262b2c9a0017e8cbb49eba092ecbb5e95aea383024b56ee6c7e54a4cbc452eb647d6c26f866662dc3c2a11b68a9e183 v24.0.x backport notes (conflicts come from develop-only prerequisites; none of them was backported, and no dashpay#8014 hunk or test was dropped): - src/wallet/salvage.cpp, src/wallet/test/util.h: dashpay#8014 only adds a Files() override to DummyDatabase/MockableDatabase, which live there on develop (bitcoin#26715 and follow-ups). v24.0.x has DummyDatabase in src/wallet/db.h and the test mock FailDatabase in src/wallet/test/wallet_tests.cpp, so the same override is added there. - src/wallet/wallet.cpp: only dashpay#8014's MigrationPrefixName() is taken; CWallet::CanGrindR() in the same hunk is develop-only context. - test/functional/tool_wallet.py: dashpay#8014's added lines are taken as they are; the existing badload assertion keeps v24.0.x's form. - test/functional/wallet_backup.py: the new "Database file exists" error is taken; wallet_file keeps v24.0.x's string form, so its existence check stays os.path.exists(). - test/functional/wallet_migration.py: dashpay#8014's test_failed_migration_cleanup and unsynced_wallet_on_pruned_node_fails are taken; test_conflict_txs and test_addressbook in the same conflict region are develop-only context and are not part of dashpay#8014. The pruned node is stopped with EXPECTED_STDERR_NO_GOV_PRUNE, because v24.0.x still prints the pruned-node governance warning that dashpay#7860 removed on develop (as in the rc.6 pick of dashpay#8005). - All three tests: v24.0.x's test framework has neither TestNode.wallets_path nor datadir_path (bitcoin#27884), and its RPC proxy cannot serialize pathlib.Path. wallets_path is written as chain_path / "wallets", and datadir_path joins as os.path.join(datadir, ...), so paths passed to RPCs stay strings. (cherry picked from commit 347c380)
…4226, bitcoin#34370, partials bitcoin#31423, bitcoin#34156 (migratewallet fixes from 28.x) 1574eb0 Merge bitcoin#34370: [29.x] Fix bitcoin#34222 backport bugs (Konstantin Akimov) 9857f73 Merge bitcoin#34226: wallet: test: Relative wallet failed migration cleanup (Konstantin Akimov) 7b7d0c6 partial Merge bitcoin#34156: wallet: fix unnamed legacy wallet migration failure (Konstantin Akimov) cfd0412 Merge bitcoin#34215: wallettool: fix unnamed createfromdump failure walletsdir deletion (Konstantin Akimov) c8f51bb partial Merge bitcoin#31423: wallet: migration, avoid creating spendable wallet from a watch-only legacy wallet (Konstantin Akimov) 3fd819a Merge bitcoin#28868: wallet: Fix migration of wallets with txs that have both spendable and watchonly outputs (Konstantin Akimov) 8a922f1 Merge bitcoin#28609: wallet: Reload watchonly and solvables wallets after migration (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented A failed `migratewallet` could delete more than it created: for the unnamed (default) wallet the cleanup removed the whole `wallets/` directory, a relative wallet name removed the directory the name pointed at, and a failed `restorewallet` or `createfromdump` used `fs::remove_all` on a directory it did not own. Watch-only and solvables wallets created during migration were also left registered in the global context and were never reloaded, so a reload failure after migration went unnoticed. Upstream fixed this in bitcoin#34156 / bitcoin#34215 / bitcoin#34226 and backported it to 28.x and 29.x as bitcoin#34223 / bitcoin#34222; bitcoin#34370 fixes bugs of that backport. Neither 26.x nor 27.x received these fixes so 28.x and 29.x is a closest adaptation to the current Dash Core state. ## What was done? Master merge commits are cherry-picked with `-m1`. Conflicts are resolved against the 28.x copies (bitcoin#34223 and the fix commits in bitcoin#34620), because master's versions target the BERKELEY_RO-only migration path that Dash does not have. - bitcoin#28609: create watch-only/solvables wallets in a local context and reload all wallets after migration; copy full tx metadata. Prerequisite for the cleanup code below. Dash's "tx belongs to both wallets" handling from dashpay#7793 is kept. - bitcoin#31423 (partial): only `WalletDatabase::Files()` and the sqlite path cleanup. The watch-only migration behaviour change and the reload dedup are not taken (BERKELEY_RO only). `BerkeleyDatabase::Files()` and the `DummyDatabase` override come from 28.x. - bitcoin#34215: `createfromdump` cleanup removes only the files it created. - bitcoin#34156 (partial): `RestoreWallet` and migration cleanup remove only created files; `MigrationPrefixName` names the default wallet's backup and auxiliary wallets `default_wallet_*`; post-migration load failure is logged and rolls back. Skipped: `test_pruned_wallet_backup` (needs bitcoin#30678). The backup filename keeps Dash's basename reduction for path-style wallet names and only adopts `MigrationPrefixName` for the unnamed wallet. - bitcoin#34226: parameterised failed-migration cleanup test (unnamed, relative, absolute and plain names). Expected backup paths follow Dash's basename rule. - bitcoin#34370: BDB-aware `Files()`, backup moved into the wallet directory when a non-directory wallet becomes a directory, correct wallet name in the load-failure log, no more copying of the backup back and forth, `db.log` cleanup test. Not backported, with TODOs in `MigrateLegacyToDescriptor` saying what to replace: - bitcoin#32273: backups move to the top-level walletdir and are named from the wallet path. Then drop the basename reduction, `test_wallet_name_with_slashes` and the 28.x-only "move backup into wallet dir" block, re-derive the bitcoin#34226 path cases from master, and take bitcoin#33104 / bitcoin#33122. - bitcoin#31451: `load_after_restore` so a wallet that was not loaded before migration is not loaded after a failed one. Re-derive from the 29.x commits d91f56e and a074d36. - bitcoin#28976 (blank wallets) and bitcoin#30678 (backup writes the best block locator): not needed for these fixes, left for a follow-up. - When bitcoin#31248 lands, re-take the test commits from the 29.x series instead of the 28.x ones. ## How Has This Been Tested? New and changed tests: - `wallet_migration.py`: `test_failed_migration_cleanup`, `test_default_wallet_watch_only`, `test_migration_failure` for `""`, `../`, an absolute path and a plain name (checks that unrelated files survive and the original wallet is restored as BDB), `unsynced_wallet_on_pruned_node_fails`; `test_other_watchonly` now checks tx metadata in the watch-only wallet. - `wallet_backup.py`: `test_restore_existent_dir`, `test_restore_into_unnamed_wallet`; `restore_wallet_existent_name` and `restore_preserves_existing_directory` expect the new "Database file exists" error. - `tool_wallet.py`: `createfromdump` with an unnamed wallet keeps the wallets directory and `db.log` of a shared BDB environment. ## Breaking Changes - `restorewallet` now accepts an existing empty directory and refuses only an existing `wallet.dat`. - Migration backups are named `<name>_<time>.legacy.bak` (`default_wallet_<time>.legacy.bak` for the unnamed wallet) instead of `<name>-<time>.legacy.bak` / `wallet-<time>.legacy.bak`, and the unnamed wallet's auxiliary wallets are `default_wallet_watchonly` / `default_wallet_solvables` instead of `_watchonly` / `_solvables`. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone Top commit has no ACKs. Tree-SHA512: c561c70e3273b2f1847eced60a9661c70262b2c9a0017e8cbb49eba092ecbb5e95aea383024b56ee6c7e54a4cbc452eb647d6c26f866662dc3c2a11b68a9e183 v24.0.x backport notes (conflicts come from develop-only context; no dashpay#8014 hunk or test was dropped): - Prerequisite: bitcoin#28542 (from dashpay#7762) is picked in the commit before this one. dashpay#8014's migration copies transactions into chainless wallets with LoadToWallet(), which reaches MarkConflicted() for a conflicted parent and child; without bitcoin#28542's guard that aborts in GetLastBlockHeight(). No other prerequisite was backported. - src/wallet/salvage.cpp, src/wallet/test/util.h: dashpay#8014 only adds a Files() override to DummyDatabase/MockableDatabase, which live there on develop (bitcoin#26715 and follow-ups). v24.0.x has DummyDatabase in src/wallet/db.h and the test mock FailDatabase in src/wallet/test/wallet_tests.cpp, so the same override is added there. - src/wallet/wallet.cpp: only dashpay#8014's MigrationPrefixName() is taken; CWallet::CanGrindR() in the same hunk is develop-only context. - test/functional/tool_wallet.py: dashpay#8014's added lines are taken as they are; the existing badload assertion keeps v24.0.x's form. - test/functional/wallet_backup.py: the new "Database file exists" error is taken; wallet_file keeps v24.0.x's string form, so its existence check stays os.path.exists(). - test/functional/wallet_migration.py: dashpay#8014's test_failed_migration_cleanup and unsynced_wallet_on_pruned_node_fails are taken, after test_conflict_txs from the bitcoin#28542 pick, as on develop; test_addressbook in the same conflict region is develop-only context and is not part of dashpay#8014. The pruned node is stopped with EXPECTED_STDERR_NO_GOV_PRUNE, because v24.0.x still prints the pruned-node governance warning that dashpay#7860 removed on develop (as in the rc.6 pick of dashpay#8005). - All three tests: v24.0.x's test framework has neither TestNode.wallets_path nor datadir_path (bitcoin#27884), and its RPC proxy cannot serialize pathlib.Path. wallets_path is written as chain_path / "wallets", and datadir_path joins as os.path.join(datadir, ...), so paths passed to RPCs stay strings. (cherry picked from commit 347c380)
…4226, bitcoin#34370, partials bitcoin#31423, bitcoin#34156 (migratewallet fixes from 28.x) 1574eb0 Merge bitcoin#34370: [29.x] Fix bitcoin#34222 backport bugs (Konstantin Akimov) 9857f73 Merge bitcoin#34226: wallet: test: Relative wallet failed migration cleanup (Konstantin Akimov) 7b7d0c6 partial Merge bitcoin#34156: wallet: fix unnamed legacy wallet migration failure (Konstantin Akimov) cfd0412 Merge bitcoin#34215: wallettool: fix unnamed createfromdump failure walletsdir deletion (Konstantin Akimov) c8f51bb partial Merge bitcoin#31423: wallet: migration, avoid creating spendable wallet from a watch-only legacy wallet (Konstantin Akimov) 3fd819a Merge bitcoin#28868: wallet: Fix migration of wallets with txs that have both spendable and watchonly outputs (Konstantin Akimov) 8a922f1 Merge bitcoin#28609: wallet: Reload watchonly and solvables wallets after migration (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented A failed `migratewallet` could delete more than it created: for the unnamed (default) wallet the cleanup removed the whole `wallets/` directory, a relative wallet name removed the directory the name pointed at, and a failed `restorewallet` or `createfromdump` used `fs::remove_all` on a directory it did not own. Watch-only and solvables wallets created during migration were also left registered in the global context and were never reloaded, so a reload failure after migration went unnoticed. Upstream fixed this in bitcoin#34156 / bitcoin#34215 / bitcoin#34226 and backported it to 28.x and 29.x as bitcoin#34223 / bitcoin#34222; bitcoin#34370 fixes bugs of that backport. Neither 26.x nor 27.x received these fixes so 28.x and 29.x is a closest adaptation to the current Dash Core state. ## What was done? Master merge commits are cherry-picked with `-m1`. Conflicts are resolved against the 28.x copies (bitcoin#34223 and the fix commits in bitcoin#34620), because master's versions target the BERKELEY_RO-only migration path that Dash does not have. - bitcoin#28609: create watch-only/solvables wallets in a local context and reload all wallets after migration; copy full tx metadata. Prerequisite for the cleanup code below. Dash's "tx belongs to both wallets" handling from dashpay#7793 is kept. - bitcoin#31423 (partial): only `WalletDatabase::Files()` and the sqlite path cleanup. The watch-only migration behaviour change and the reload dedup are not taken (BERKELEY_RO only). `BerkeleyDatabase::Files()` and the `DummyDatabase` override come from 28.x. - bitcoin#34215: `createfromdump` cleanup removes only the files it created. - bitcoin#34156 (partial): `RestoreWallet` and migration cleanup remove only created files; `MigrationPrefixName` names the default wallet's backup and auxiliary wallets `default_wallet_*`; post-migration load failure is logged and rolls back. Skipped: `test_pruned_wallet_backup` (needs bitcoin#30678). The backup filename keeps Dash's basename reduction for path-style wallet names and only adopts `MigrationPrefixName` for the unnamed wallet. - bitcoin#34226: parameterised failed-migration cleanup test (unnamed, relative, absolute and plain names). Expected backup paths follow Dash's basename rule. - bitcoin#34370: BDB-aware `Files()`, backup moved into the wallet directory when a non-directory wallet becomes a directory, correct wallet name in the load-failure log, no more copying of the backup back and forth, `db.log` cleanup test. Not backported, with TODOs in `MigrateLegacyToDescriptor` saying what to replace: - bitcoin#32273: backups move to the top-level walletdir and are named from the wallet path. Then drop the basename reduction, `test_wallet_name_with_slashes` and the 28.x-only "move backup into wallet dir" block, re-derive the bitcoin#34226 path cases from master, and take bitcoin#33104 / bitcoin#33122. - bitcoin#31451: `load_after_restore` so a wallet that was not loaded before migration is not loaded after a failed one. Re-derive from the 29.x commits d91f56e and a074d36. - bitcoin#28976 (blank wallets) and bitcoin#30678 (backup writes the best block locator): not needed for these fixes, left for a follow-up. - When bitcoin#31248 lands, re-take the test commits from the 29.x series instead of the 28.x ones. ## How Has This Been Tested? New and changed tests: - `wallet_migration.py`: `test_failed_migration_cleanup`, `test_default_wallet_watch_only`, `test_migration_failure` for `""`, `../`, an absolute path and a plain name (checks that unrelated files survive and the original wallet is restored as BDB), `unsynced_wallet_on_pruned_node_fails`; `test_other_watchonly` now checks tx metadata in the watch-only wallet. - `wallet_backup.py`: `test_restore_existent_dir`, `test_restore_into_unnamed_wallet`; `restore_wallet_existent_name` and `restore_preserves_existing_directory` expect the new "Database file exists" error. - `tool_wallet.py`: `createfromdump` with an unnamed wallet keeps the wallets directory and `db.log` of a shared BDB environment. ## Breaking Changes - `restorewallet` now accepts an existing empty directory and refuses only an existing `wallet.dat`. - Migration backups are named `<name>_<time>.legacy.bak` (`default_wallet_<time>.legacy.bak` for the unnamed wallet) instead of `<name>-<time>.legacy.bak` / `wallet-<time>.legacy.bak`, and the unnamed wallet's auxiliary wallets are `default_wallet_watchonly` / `default_wallet_solvables` instead of `_watchonly` / `_solvables`. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone Top commit has no ACKs. Tree-SHA512: c561c70e3273b2f1847eced60a9661c70262b2c9a0017e8cbb49eba092ecbb5e95aea383024b56ee6c7e54a4cbc452eb647d6c26f866662dc3c2a11b68a9e183 v24.0.x backport notes (conflicts come from develop-only context; no dashpay#8014 hunk or test was dropped): - Prerequisite: bitcoin#28542 (from dashpay#7762) is picked in the commit before this one. dashpay#8014's migration copies transactions into chainless wallets with LoadToWallet(), which reaches MarkConflicted() for a conflicted parent and child; without bitcoin#28542's guard that aborts in GetLastBlockHeight(). No other prerequisite was backported. - src/wallet/salvage.cpp, src/wallet/test/util.h: dashpay#8014 only adds a Files() override to DummyDatabase/MockableDatabase, which live there on develop (bitcoin#26715 and follow-ups). v24.0.x has DummyDatabase in src/wallet/db.h and the test mock FailDatabase in src/wallet/test/wallet_tests.cpp, so the same override is added there. - src/wallet/wallet.cpp: only dashpay#8014's MigrationPrefixName() is taken; CWallet::CanGrindR() in the same hunk is develop-only context. - test/functional/tool_wallet.py: dashpay#8014's added lines are taken as they are; the existing badload assertion keeps v24.0.x's form. - test/functional/wallet_backup.py: the new "Database file exists" error is taken; wallet_file keeps v24.0.x's string form, so its existence check stays os.path.exists(). - test/functional/wallet_migration.py: dashpay#8014's test_failed_migration_cleanup and unsynced_wallet_on_pruned_node_fails are taken, after test_conflict_txs from the bitcoin#28542 pick, as on develop; test_addressbook in the same conflict region is develop-only context and is not part of dashpay#8014. The pruned node is stopped with EXPECTED_STDERR_NO_GOV_PRUNE, because v24.0.x still prints the pruned-node governance warning that dashpay#7860 removed on develop (as in the rc.6 pick of dashpay#8005). - All three tests: v24.0.x's test framework has neither TestNode.wallets_path nor datadir_path (bitcoin#27884), and its RPC proxy cannot serialize pathlib.Path. wallets_path is written as chain_path / "wallets", and datadir_path joins as os.path.join(datadir, ...), so paths passed to RPCs stay strings. (cherry picked from commit 347c380)
…4226, bitcoin#34370, partials bitcoin#31423, bitcoin#34156 (migratewallet fixes from 28.x) 1574eb0 Merge bitcoin#34370: [29.x] Fix bitcoin#34222 backport bugs (Konstantin Akimov) 9857f73 Merge bitcoin#34226: wallet: test: Relative wallet failed migration cleanup (Konstantin Akimov) 7b7d0c6 partial Merge bitcoin#34156: wallet: fix unnamed legacy wallet migration failure (Konstantin Akimov) cfd0412 Merge bitcoin#34215: wallettool: fix unnamed createfromdump failure walletsdir deletion (Konstantin Akimov) c8f51bb partial Merge bitcoin#31423: wallet: migration, avoid creating spendable wallet from a watch-only legacy wallet (Konstantin Akimov) 3fd819a Merge bitcoin#28868: wallet: Fix migration of wallets with txs that have both spendable and watchonly outputs (Konstantin Akimov) 8a922f1 Merge bitcoin#28609: wallet: Reload watchonly and solvables wallets after migration (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented A failed `migratewallet` could delete more than it created: for the unnamed (default) wallet the cleanup removed the whole `wallets/` directory, a relative wallet name removed the directory the name pointed at, and a failed `restorewallet` or `createfromdump` used `fs::remove_all` on a directory it did not own. Watch-only and solvables wallets created during migration were also left registered in the global context and were never reloaded, so a reload failure after migration went unnoticed. Upstream fixed this in bitcoin#34156 / bitcoin#34215 / bitcoin#34226 and backported it to 28.x and 29.x as bitcoin#34223 / bitcoin#34222; bitcoin#34370 fixes bugs of that backport. Neither 26.x nor 27.x received these fixes so 28.x and 29.x is a closest adaptation to the current Dash Core state. ## What was done? Master merge commits are cherry-picked with `-m1`. Conflicts are resolved against the 28.x copies (bitcoin#34223 and the fix commits in bitcoin#34620), because master's versions target the BERKELEY_RO-only migration path that Dash does not have. - bitcoin#28609: create watch-only/solvables wallets in a local context and reload all wallets after migration; copy full tx metadata. Prerequisite for the cleanup code below. Dash's "tx belongs to both wallets" handling from dashpay#7793 is kept. - bitcoin#31423 (partial): only `WalletDatabase::Files()` and the sqlite path cleanup. The watch-only migration behaviour change and the reload dedup are not taken (BERKELEY_RO only). `BerkeleyDatabase::Files()` and the `DummyDatabase` override come from 28.x. - bitcoin#34215: `createfromdump` cleanup removes only the files it created. - bitcoin#34156 (partial): `RestoreWallet` and migration cleanup remove only created files; `MigrationPrefixName` names the default wallet's backup and auxiliary wallets `default_wallet_*`; post-migration load failure is logged and rolls back. Skipped: `test_pruned_wallet_backup` (needs bitcoin#30678). The backup filename keeps Dash's basename reduction for path-style wallet names and only adopts `MigrationPrefixName` for the unnamed wallet. - bitcoin#34226: parameterised failed-migration cleanup test (unnamed, relative, absolute and plain names). Expected backup paths follow Dash's basename rule. - bitcoin#34370: BDB-aware `Files()`, backup moved into the wallet directory when a non-directory wallet becomes a directory, correct wallet name in the load-failure log, no more copying of the backup back and forth, `db.log` cleanup test. Not backported, with TODOs in `MigrateLegacyToDescriptor` saying what to replace: - bitcoin#32273: backups move to the top-level walletdir and are named from the wallet path. Then drop the basename reduction, `test_wallet_name_with_slashes` and the 28.x-only "move backup into wallet dir" block, re-derive the bitcoin#34226 path cases from master, and take bitcoin#33104 / bitcoin#33122. - bitcoin#31451: `load_after_restore` so a wallet that was not loaded before migration is not loaded after a failed one. Re-derive from the 29.x commits d91f56e and a074d36. - bitcoin#28976 (blank wallets) and bitcoin#30678 (backup writes the best block locator): not needed for these fixes, left for a follow-up. - When bitcoin#31248 lands, re-take the test commits from the 29.x series instead of the 28.x ones. ## How Has This Been Tested? New and changed tests: - `wallet_migration.py`: `test_failed_migration_cleanup`, `test_default_wallet_watch_only`, `test_migration_failure` for `""`, `../`, an absolute path and a plain name (checks that unrelated files survive and the original wallet is restored as BDB), `unsynced_wallet_on_pruned_node_fails`; `test_other_watchonly` now checks tx metadata in the watch-only wallet. - `wallet_backup.py`: `test_restore_existent_dir`, `test_restore_into_unnamed_wallet`; `restore_wallet_existent_name` and `restore_preserves_existing_directory` expect the new "Database file exists" error. - `tool_wallet.py`: `createfromdump` with an unnamed wallet keeps the wallets directory and `db.log` of a shared BDB environment. ## Breaking Changes - `restorewallet` now accepts an existing empty directory and refuses only an existing `wallet.dat`. - Migration backups are named `<name>_<time>.legacy.bak` (`default_wallet_<time>.legacy.bak` for the unnamed wallet) instead of `<name>-<time>.legacy.bak` / `wallet-<time>.legacy.bak`, and the unnamed wallet's auxiliary wallets are `default_wallet_watchonly` / `default_wallet_solvables` instead of `_watchonly` / `_solvables`. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone Top commit has no ACKs. Tree-SHA512: c561c70e3273b2f1847eced60a9661c70262b2c9a0017e8cbb49eba092ecbb5e95aea383024b56ee6c7e54a4cbc452eb647d6c26f866662dc3c2a11b68a9e183 v24.0.x backport notes (conflicts come from develop-only context; no dashpay#8014 hunk or test was dropped): - Prerequisites, picked in the two commits before this one: - bitcoin#28038 (from dashpay#7863): dashpay#8014 reloads the watch-only and solvables wallets from disk, so address book entries that ApplyMigrationData() doesn't write (a "send" entry with an empty label) would be lost right after migration. - bitcoin#28542 (from dashpay#7762): dashpay#8014's migration copies transactions into chainless wallets with LoadToWallet(), which reaches MarkConflicted() for a conflicted parent and child; without bitcoin#28542's guard that aborts in GetLastBlockHeight(). No other prerequisite was backported. - src/wallet/salvage.cpp, src/wallet/test/util.h: dashpay#8014 only adds a Files() override to DummyDatabase/MockableDatabase, which live there on develop (bitcoin#26715 and follow-ups). v24.0.x has DummyDatabase in src/wallet/db.h and the test mock FailDatabase in src/wallet/test/wallet_tests.cpp, so the same override is added there. - src/wallet/wallet.cpp: only dashpay#8014's MigrationPrefixName() is taken; CWallet::CanGrindR() in the same hunk is develop-only context. - test/functional/tool_wallet.py: dashpay#8014's added lines are taken as they are; the existing badload assertion keeps v24.0.x's form. - test/functional/wallet_backup.py: the new "Database file exists" error is taken; wallet_file keeps v24.0.x's string form, so its existence check stays os.path.exists(). - test/functional/wallet_migration.py: dashpay#8014's test_failed_migration_cleanup and unsynced_wallet_on_pruned_node_fails are taken, after test_conflict_txs and test_addressbook from the bitcoin#28542 and bitcoin#28038 picks, as on develop. The pruned node is stopped with EXPECTED_STDERR_NO_GOV_PRUNE, because v24.0.x still prints the pruned-node governance warning that dashpay#7860 removed on develop (as in the rc.6 pick of dashpay#8005). - All three tests: v24.0.x's test framework has neither TestNode.wallets_path nor datadir_path (bitcoin#27884), and its RPC proxy cannot serialize pathlib.Path. wallets_path is written as chain_path / "wallets", and datadir_path joins as os.path.join(datadir, ...), so paths passed to RPCs stay strings. (cherry picked from commit 347c380)
d6c066f chore: add v23.1.10 to the flatpak release history (pasta) 202632c chore: release v24.0.0 (pasta) 2bf133c docs: cite #8042 for the index fixes in the v24.0.0 release notes (pasta) 8aa11f6 docs: add the v24.0.0-rc.7 picks and index fixes to the v24.0.0 release notes (pasta) ad4e5d6 docs: drop the index caveat from the -reindex-chainstate man page entry (pasta) 19a90bb test: blocks orphaned while the address index is disabled are removed from it (pasta) d2f2a1f fix(index): don't adopt an empty or rebuilt async index as migrated legacy data (pasta) e3bafdb fix(index): reject an uncommitted tip the block index lost (pasta) 9198962 fix(index): check the blocks governance resumes from at startup (pasta) baff347 fix(index): resume from the tip a skipped commit leaves behind (pasta) 2dbf5ca fix(index): write a rewound block's erasures in one batch with its locator (pasta) 252acbe fix(index): move the committed locator below the entries a rewind erases (Konstantin Akimov) e9f9c8a Merge bitcoin#36150: indexes: set prune lock to genesis before first block (Konstantin Akimov) 2edeb98 partial Merge bitcoin#34897: indexes: Don't commit ahead of the flushed chainstate (Konstantin Akimov) 3eba84c Merge bitcoin#32875: index: handle case where pindex_prev equals chain tip in NextSyncBlock() (Konstantin Akimov) 399125c Merge bitcoin#32882: index: remove unnecessary locator cleaning in BaseIndex::Init() (Konstantin Akimov) bb724c6 Merge bitcoin#33212: index: Don't commit state in BaseIndex::Rewind (Konstantin Akimov) d5ff431 Merge bitcoin#32878: index: fix wrong assert of current_tip == m_best_block_index (Konstantin Akimov) 31c7d8f partial Merge bitcoin#32580: wallet, test: best block locator matches scan state follow-ups (Konstantin Akimov) 94f5c58 partial Merge bitcoin#30221: wallet: Ensure best block matches wallet scan state (Konstantin Akimov) 8e0b1e8 partial Merge bitcoin#32069: test: fix intermittent failure in wallet_reorgsrestore.py (Konstantin Akimov) 4d17947 Merge bitcoin#25193: indexes: Read the locator's top block during init, allow interaction with reindex-chainstate (Konstantin Akimov) 0127df8 Merge #8045: test: scale the mempool-loaded wait by timeout_factor so TSAN reindex restarts stop timing out (pasta) 62d0cef Merge #8036: fix(qt): let a shared masternode participant follow a revised invitation that keeps their share (merge-script) 775d31c Merge #8018: fix(consensus): require IPv4 masternode primaries until IPv4 is optional, pin mainnet ports (merge-script) e1b826e Merge #8026: fix(qt): keep shared masternode contribution fields locked while coins are reserved (merge-script) 9fa3df3 Merge #8020: fix(rpc): make protx shared_sign sign only dissolutions that return every principal (merge-script) 246a465 Merge #8024: fix(qt): keep the shared masternode dissolve dialog consistent with what it will send (merge-script) 590ad0a Merge #8021: fix(qt): don't load a newer shared masternode session copy that changes your own share (merge-script) 974aaa3 Merge #8034: fix(qt): check a shared masternode's service and refund addresses before coins are reserved (merge-script) 0026e3e Merge #8027: fix(qt): fund a shared masternode's service update from this wallet by default (merge-script) 6a5d2dc Merge #8035: fix(qt): don't strand coins reserved for a shared masternode session (merge-script) df6083a Merge #8032: fix(qt): don't offer a new operator key when reopening a shared masternode session (merge-script) 2b5bf30 Merge #8029: fix: let dashd -version, -help and first start work without an existing data directory (merge-script) e33d9b1 Merge #8037: docs: list the config file path each Linux init system uses (merge-script) 6030ea0 docs: add the #8014 migratewallet fixes to the v24.0.0 release notes (pasta) 1b1ea95 Merge #8014: backport: bitcoin#28609, bitcoin#34215, bitcoin#34226, bitcoin#34370, partials bitcoin#31423, bitcoin#34156 (migratewallet fixes from 28.x) (merge-script) 608ff63 Merge bitcoin#28542: wallet: Check for uninitialized last processed and conflicting heights in MarkConflicted (DCG-Claude) 3af6f63 Merge bitcoin#28038: wallet: address book migration bug fixes (DCG-Claude) 2fe41fb docs: add ecosystem notes on Asset Unlock txids, coinbase payees and RPC errors (pasta) 0e3f653 docs: correct operator guidance in the v24.0.0 release notes for rc.7 (pasta) 11c8140 Merge #8019: fix(qt): stop reporting refused shared masternode broadcasts as sent (merge-script) cdb250c doc: note operator-relevant v23.1.x patch-release changes for v24.0.0 (pasta) fcd332b doc: state the provider-transaction InstantSend change on its own (pasta) e6f3ad4 fix(bls): stop weighted contribution batch verification on an invalid partial aggregate (pasta) e04acba fix(llmq): weight batched DKG contribution verification (UdjinM6) ed96227 fix(bls): weight batch-verified signatures so complementary errors cannot cancel (UdjinM6) b5c7985 fix(governance): don't vote on triggers while not synced (pasta) a79f432 fix(governance): don't vote NO-FUNDING on triggers before their maturity window (pasta) 8615244 fix(llmq): accept equal DKG secret key shares when aggregating a member's share (pasta) 1c356f4 test: cover DKG members dealing identical contributions (UdjinM6) f23f748 fix(llmq): drop DKG members whose verification vectors share coefficients (UdjinM6) 914c052 docs: add #8003 to the v24.0.0 release notes and man pages (pasta) e81c511 Merge #8003: fix(net): don't refuse to start over the automatic onion service unless -listenonion=1 (merge-script) 41b4578 ci: run cppcheck as test/lint/cppcheck-dash.py, as develop does since #7966 (pasta) c97f3b3 docs: regenerate man pages for v24.0.0-rc.6 (pasta) 784312c docs: add the v24.0.0-rc.6 changes to the v24.0.0 release notes (pasta) f15a53a Merge #7991: fix(qt): shared masternode wizard fixes for taken shares, unconfirmed funding and reply handling (merge-script) 1c46224 Merge #7995: fix(wallet): don't let a waiting CoinJoin promotion/demotion queue block standard mixing (merge-script) 546db2f Merge #7996: fix(wallet): fill a CoinJoin promotion with sibling coins when needed (merge-script) 349859f Merge #7957: fix(qt): validate shared-masternode shares with the consensus rules (merge-script) 9091f0b Merge #8005: backport: bitcoin#27114, bitcoin#33395 (merge-script) e915ab5 Merge #7990: fix(wallet): pay for 3 byte input and output counts so 253+ input sends work (merge-script) 4f4d188 Merge #7984: chore: Translations 2026-10 (pasta) 129a1fc Merge #7980: chore: update seeds, checkpoints, minimum chain work, assumevalid and tx stats for v24 (pasta) 5800e5d docs: state that SPORK_3/SPORK_9 were already hardened on mainnet (pasta) ddc82a8 docs: separate extended addresses from shared masternodes in v24 notes (pasta) 8660e54 docs: complete the v24.0.0 release notes against the full v24 changeset (pasta) 11ea306 Merge #7947: fix: stop quorum key warming from stalling DKG on 2-core masternodes (pasta) 25d7c85 Merge #7826: fix: skip repeated authenticated Platform bans (pasta) 18073dd Merge #7850: perf: let CHECKDATASIG use the signature cache (pasta) 3759397 Merge #7833: fix: gate signing intake before quorum retrieval (pasta) db98ee2 fix: keep flat-file positions 64-bit clean at the remaining raw seek/tell sites (Konstantin Akimov) 54c9738 Merge bitcoin#36667: net: enforce timeout interval for paused send peers (Konstantin Akimov) f1adb2c Merge bitcoin#36665: sock: align exception monitoring between poll() and select() backends (Konstantin Akimov) 8becf6a Merge bitcoin#36669: refactor: store positions as a 64 bits integer in flatfiles (Konstantin Akimov) 4c0cbd6 fix(llmq): judge every sig-share sender by its own verified shares (pasta) 649259f fix(governance): cap the orphan parent objects re-requested per tick (pasta) 418f5ee fix(llmq): verify DKG contributions individually when a batch vvec check fails (pasta) abf2eb0 fix(llmq): reject invalid decrypted DKG shares before aggregation (pasta) 39bda8f fix(consensus): reject negative transaction nVersion after DIP3 (pasta) 1ba2609 Merge #7951: fix(net): stop peers lingering after disconnect under edge-triggered epoll (pasta) 696d45f Merge #7861: feat: keep at least 4-days worth blocks after pruning (merge-script) 9cd1c72 Merge #7869: fix: let startup block replay handle quorum commitments and asset unlocks (pasta) 73e7254 Merge #7682: build: finish relic_conf.h regeneration before compiling BLS consumers (pasta) 7312925 ci: bake the macOS SDK into the CI runner image (pasta) d885ecd Merge #7916: backport: bitcoin#27800, bitcoin#28125, bitcoin#28232, bitcoin#27695 (merge-script) f55ad1d Merge bitcoin#28139: test: create wallet specific for test_locked_wallet case (DCG-Claude) 80f6cbe chore: add the v23.1.9 release entry to flatpak metainfo (pasta) 5ba801f chore: use translation source strings generated from v24.0.x (pasta) a3b9e30 Merge #7974: chore: update man pages, translation strings, SECURITY.md and flatpak for v24.0.0 (pasta) b5d26bd feat(consensus): schedule V24 and evo_shares on mainnet for November 9, 2026 (pasta) fe78211 docs: state the earliest v24 activation height and the v23.1.9 fixes (pasta) 3bbe3e2 docs: address review of v24.0.0 release notes (pasta) 28c39fc docs: add v24.0.0 release notes and remove individual release notes files (pasta) e8cbfaf docs: archive v23.1.8 and v23.1.9 release notes (pasta) 6c02b2b ci: let DEPENDS_ARCH override the machine arch for multiprocess and tsan (pasta) ce5dbf3 Merge #7888: fix(consensus): canonical ExtNetInfo encoding and strict netInfo decoding (pasta) 54cba5c Merge #7537: fix: data races and check-then-act races in the CoinJoin server (pasta) 08d8bca Merge #7909: feat(mining): build the masternode list incrementally during block assembly (pasta) 5633761 Merge #7895: test: cover retroactive provider tx refusal, fix wording (pasta) cbb9500 Merge #7846: fix: treat pending owner keys and Platform node IDs as one conflict namespace (pasta) 8efdf5e Merge #7857: fix: mine a pending ProUpServTx before a key change of its masternode (pasta) e56e6ae Merge #7842: fix(wallet): reject mnemonic passphrases that seed derivation truncates (pasta) 79480ff test: follow-up for #7821: check if wallet is locked after failed upgrade instead creating brand-new scenario (Konstantin Akimov) a56ed47 fix(net): announce sporks to new peers instead of requesting them during mnsync (pasta) cdaea5f Merge #7894: fix: due to logical conflicts feature_pruning.py had been broken while (pasta) 98d92f6 Merge #7817: backport: partial bitcoin#34156 restore cleanup ownership (pasta) 35e6d18 Merge #7858: fix: do not InstantSend-lock provider transactions (pasta) 904d73d Merge #7881: fix: refuse to sign shared registrar updates that move the funder's coins (pasta) 95982f5 Merge #7782: fix(wallet): persist pending-observation coin locks once the record becomes readable (pasta) cdf65d0 Merge #7836: backport: partial bitcoin#25812, bitcoin#29855 (pasta) cc5716f Merge #7824: fix: retain initialized DKG sessions during inventory reads (pasta) 588e458 Merge #7827: backport: partial bitcoin#35984 (pasta) 795c6f1 Merge #7839: fix: release InstantSend tracking after mempool eviction (pasta) dc1a6b2 Merge #7875: docs(rpc): clarify getchainlockbyheight availability (pasta) e4798ff Merge #7876: fix(consensus): capture operator key scheme changes with unchanged bytes in state diffs (pasta) fe68ecb Merge #7871: backport!: Merge bitcoin#27217: wallet: Replace use of purpose strings with an enum (pasta) 8348d22 Merge #7867: fix: refuse masternode list RPCs for blocks outside the active chain (pasta) 861a579 Merge #7866: fix: stop `masternode payments` at the genesis block (pasta) efcef71 Merge #7521: fix: update masternode rate limit on failed governance trigger path (pasta) 0623182 Merge #7831: fix: bound governance object signatures and reset the governance cache (pasta) b230657 Merge #7841: fix: authenticate cached InstantSend proof bytes (pasta) a1f37ea Merge #7820: fix: hold chainstate lock during CoinJoin input validation (pasta) 50a5617 Merge #7829: fix: preserve wallet permissions when changing passphrases (pasta) 2ff0103 Merge #7818: fix: return REST errors for unavailable deployment data (pasta) cb39369 Merge #7834: fix: validate Electrum import input boundaries (pasta) e7e3daa Merge #7821: fix: relock encrypted wallets after HD upgrade failures (pasta) 2fea45f Merge #7792: fix: treat missing snapshots during evodb repair as repair errors (pasta) 3c5c039 Merge #7838: fix(wallet): authenticate unlock deadline extensions (pasta) 0bf80f8 Merge #7810: fix(rpc): handle unavailable masternode payment inputs (pasta) bcefee4 Merge #7822: fix: reject malformed detached quorum share batches (pasta) 86a7737 Merge #7828: fix: omit recovery words from invalid mnemonic errors (pasta) 886d5c4 Merge #7816: fix: check credit pool amount accumulation (pasta) 00fc3b9 Merge #7851: fix(rpc): correct masternode status help for top-level service field (pasta) 0e75c22 Merge #7814: fix(qt): restore HTML escaping of send confirmation recipients (pasta) fbe6526 Merge #7807: feat(consensus): hold EvoNode shares and multiple payouts behind a new evo_shares deployment (pasta) 57fd2d9 Merge #7776: fix: keep a pending ProUpServTx across a registrar update that keeps the operator key (pasta) ca69801 Merge #7803: fix: return non-zero amount of accounts after sethdseed (pasta) 57a8f07 Merge #7796: backport: bitcoin#26720 (remaining), partial bitcoin#29523: weight-check the knapsack exact-match selection (pasta) 4eb82f6 Merge #7786: fix(net): keep a dedicated onion listener when -bind is given and correct the bind release note (pasta) 70626a0 Merge #7793: backport: partial bitcoin#28868 (wallet: reload wallet if migration exited early, keep mixed watchonly txs) (pasta) 250a3cd Merge #7790: fix: guard cached governance object flags with the object lock (pasta) 700c195 Merge #7787: fix: only sign EHF signals inside the deployment's start/timeout window (pasta) a3b0f3c Merge #7785: fix: restore the txindex requirement when governance validation is enabled (pasta) 2d1735d Merge #7794: fix(wallet): floor the denomination gap threshold so rebalancing cannot oscillate (pasta) d9f6592 Merge #7783: fix: stop fish completion from re-running the typed dash-cli command (pasta) 89d44c8 Merge #7795: backport: bitcoin#34272, partial bitcoin#31650 (pass PSBT by const reference in PSBTInputSignedAndVerified) (pasta) a1d7411 Merge #7779: fix!: report legacy evonode Platform addresses when listdiff changes its address (pasta) 73f6ea2 Merge #7789: fix(qt): report malformed lang/font settings instead of aborting at startup (pasta) 40de493 Merge #7781: fix(rpc): use a consistent tip height in quorum dkgstatus (pasta) 7d1a90d Merge #7791: fix(net): request object votes from nPeersPerHashMax peers (pasta) 51b145c Merge #7778: test: use single-member in functional tests: feature_mnehf, feature_notifications, p2p_instantsend (pasta) Pull request description: ## Issue being fixed or feature implemented Merges the v24.0.0 release back into develop, as the release process asks once `master` is fast-forwarded. `master`, `v24.0.x` and the signed `v24.0.0` tag all point to the same commit (938fba5). ## What was done? One merge commit of `master` into develop. `v24.0.x` was cut from develop, so all of its code is already here. Every commit on it that develop lacks by patch is one of these: - a `cherry-pick -x` of a develop commit; - covered by a develop PR: the index fixes by #8042, the mainnet V24 schedule by #7973, the cppcheck CI move by #7966; - release bookkeeping. Git reports conflicts in many files, because `v24.0.x` carries adapted copies of develop's changes. All of them resolve to develop's side, which keeps develop's code, translations and version (`configure.ac` stays at 24.1.0, `CLIENT_VERSION_IS_RELEASE` false). Taken from v24.0.0: - `doc/release-notes.md`: the v24.0.0 release notes. The v23.1.10 notes move to `doc/release-notes/dash/`. - The man pages regenerated for v24.0.0, replacing develop's older v24.0.0 pages. A few entries (`-prune`, `-par`, `-parbls`, `-maxconnections`) still trail develop's help text, as they did before, until the next regeneration. - `feature_addressindex.py`: the v24.0.x test that blocks orphaned while the address index is disabled are removed from it once it is re-enabled. Release notes: - Individual `doc/release-notes-*.md` files whose content is in the v24.0.0 notes are removed. This includes `release-notes-8018.md`, `release-notes-8029.md` and `release-notes-27114.md`, whose changes shipped in v24.0.0 without the files. - `release-notes-28113.md` and `release-notes-28414.md` also covered `descriptorprocesspsbt`, which only develop has. They are trimmed to that RPC instead of removed. - Notes for changes v24.0.0 doesn't include stay: #7812, #7847, #7860, #7862, bitcoin#25943, bitcoin#26485 and bitcoin#27501. ## How Has This Been Tested? macOS arm64 against depends, `--enable-debug --enable-werror --disable-hardening --without-gui`. `dashd`, `dash-cli` and `test_dash` build cleanly. - `src/` is byte-identical to develop's. The only non-doc change is the new `feature_addressindex.py` case. - Functional tests, all passing: `feature_addressindex.py` (with the new case) and `feature_spentindex.py`. - Unit suites, all passing: `txindex_tests`, `blockfilter_index_tests`, `coinstatsindex_tests`, `index_migration_tests`, `baseindex_tests`. - `lint-python.py`, `lint-whitespace.py`, `lint-includes.py` and `lint-circular-dependencies.py` pass. - An independent review checked every non-merge commit in `develop..master` against develop (by patch, by cherry-pick source, and line by line for the rest), re-merged each `v24.0.x` merge commit to look for content added in a merge, and checked each release-note file decision. I did not run the full functional suite locally; CI covers it. ## Breaking Changes None. Develop's code is unchanged; the only non-doc change is the new functional test case. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(if you have permission to set one)_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Top commit has no ACKs. Tree-SHA512: 3c2d1b7d2885f983f1457a18920f42907009c8ca77522a6116739d387b011d8d5583da6a4b6a1227bcaa21fa07a7ef635aae58fb8b19e4f31a95582ee1ce2318
Issue being fixed or feature implemented
migratewallet(and the GUI "Migrate wallet" action, which calls the sameMigrateLegacyToDescriptor()) has two bugs that upstream fixed in bitcoin#28868. That PR is listed as outstanding in the "next batch" section of #7277.MigrateLegacyToDescriptor()unloads it first and then runs its checks. Several of those checks return early without loading it again: the wallet is already a descriptor wallet, the backup could not be written, or the passphrase is missing or wrong. After any of these errors the wallet is gone fromlistwallets, and RPC calls to it fail withRequested wallet does not exist or is not loadeduntil the user runsloadwalletmanually. Mistyping the passphrase once on an encrypted legacy wallet is enough to trigger this.ApplyMigrationData()only offers a transaction to the new<name>_watchonlywallet when the migrated wallet does not claim it. If one output pays a spendable address and another pays an imported watch-only address, the transaction stays in the migrated wallet and is never copied to<name>_watchonly. That wallet is created at the chain tip, so it does not rescan, and its history and balance silently omit that transaction. This is the watch-only bug mentioned in feat: new RPC migratewallet to migrate legacy wallets to backport wallets #7275.Why this is a real problem
Code path at c14104b:
!IsMine(tx) && !IsFromMe(tx): wallet.cpp#L4759-L4781I reproduced all three cases against an unfixed
dashdbuilt from c14104b, using a functional-test-style script on regtest:Reproduction on c14104b (unfixed)
Steps:
createwallet desc(descriptor), thenmigratewallet desc, thenlistwalletscreatewallet enc descriptors=false, thenencryptwallet pass, thenmigratewallet enc badpass, thenlistwalletsandgetwalletinfoonenccreatewallet imports descriptors=false, thenimportaddress <addr from default wallet>. From the default wallet,sendone output to animportsaddress and one to the imported address, then mine a block. Thenmigratewalletandgettransaction <txid>onimports_watchonlyWith this branch, the same script reports
listwallets after: ['default_wallet', 'desc']and['default_wallet', 'desc', 'enc'], and all three results areTrue.The upstream tests backported here also fail on the unfixed code (see "How Has This Been Tested?").
Why it matters
Neither bug loses funds or keys. The first one is a usability trap in a v24 feature: a typo in the passphrase makes the wallet disappear from the node, the GUI and RPC clients, with no hint that a
loadwalletis needed. The second one leaves the post-migration watch-only wallet with incomplete history and balance, and nothing reports it. Users migrating real wallets have already hit it (see #7275).What was done?
Backport of bitcoin#28868, minus one commit:
78ba0e6748d2"wallet: Reload the wallet if migration exited early": remember whether the wallet was loaded (was_loaded), define thereload_wallethelper before the early exits, and reload the wallet on the descriptor-wallet, backup-failure and passphrase-failure exits. As upstream does, the passphrase unlock moves out of theLOCK(cs_wallet)scope so the reload does not happen while the wallet lock is held.71cb28ea8cb5"test: Make sure that migration test does not rescan on reloading": adds themigrate_wallet()helper, which reloads the wallet before migrating and checks that migration does not logRescanning. Because the helper callsgetwalletinfoon the wallet first, it fails if an earlier failedmigratewalletleft the wallet unloaded. The Dash-onlytest_wallet_name_with_slashescase also goes through the helper.c62a8d03a862"wallet: Keep txs that belong to both watchonly and migrated wallets": every transaction is offered to the watch-only wallet. It is removed from the migrated wallet only if the migrated wallet does not also own it.4da76ca24725"test: Test migration of tx with both spendable and watchonly"Dash adaptations and omitted hunks:
9332c7edda79"wallet: Write bestblock to watchonly and solvable wallets". Upstream needs it because, after wallet: Reload watchonly and solvables wallets after migration bitcoin/bitcoin#28609, the watch-only and solvable wallets are created in an empty context and reloaded at the end of migration, and without a best block record they rescan on that reload. Dash has not backported wallet: Reload watchonly and solvables wallets after migration bitcoin/bitcoin#28609. Here those wallets are created withCreateWallet(context, ...), which already writes the chain tip as their best block on first run, and they are not reloaded during migration. The commit fixes nothing on Dash today and belongs with a wallet: Reload watchonly and solvables wallets after migration bitcoin/bitcoin#28609 backport.!GetLegacyScriptPubKeyMan()) and Dash's backup filename logic are kept as they are. The upstream hunk that movesreload_walletout of the success branch does not apply, because that helper came from wallet: Reload watchonly and solvables wallets after migration bitcoin/bitcoin#28609. The success path keeps its existing direct reload.AddToWallet(), because wallet: bugfix, disallow migration of invalid scripts bitcoin/bitcoin#28125 (LoadToWallet()/CopyFrom()and the shared watch-onlyWalletBatch) is not backported. Only the ownership logic changes, exactly as upstream.test_addressbookthroughself.migrate_wallet()is not carried over. That scenario was added by wallet: address book migration bug fixes bitcoin/bitcoin#28038, which Dash has not backported, so there is notest_addressbookto change. Every other scenario that upstream routes through the helper does so here as well.test_other_watchonly. The upstream context lines that check copied tx metadata come from wallet: bugfix, disallow migration of invalid scripts bitcoin/bitcoin#28125 and are not part of this change.Why this is the correct minimal fix
The wallet goes missing because the unload happens before the checks, while only the success path reloads it. Reloading on each early exit is the upstream fix, and every exit taken after the unload but before any database change is covered. Moving the checks ahead of the unload would mean running the backup and the passphrase check against the loaded instance, which is a larger restructuring than this fix needs. As upstream does, no reload is attempted when
MakeWalletDatabase()/CWallet::Create()fail (the same open would fail again) or whenMigrateToSQLite()fails (by then the original BDB file may already have been removed, and the failed-migration restore path does not run). The.legacy.bakfile written before a wrong-passphrase exit is also kept, as upstream does. The watch-only change is the upstream one-condition fix, and the rest of bitcoin#28609/bitcoin#28125 is not needed for either bug.The
migratewalletRPC first shipped in v24.0.0-rc.1 (#7275/#7277 are on v24.0.x), so this is a candidate for v24.0.x.How Has This Been Tested?
macOS arm64,
--enable-debug --enable-werror, builtdashd/dash-cli/dash-walletat each step.test/functional/wallet_migration.pywith only the 71cb28e test change, against the unfixed c14104b binary: fails intest_encrypted. After the expected wrong-passphrase errors,migrate_wallet()→getwalletinforaisesRequested wallet does not exist or is not loaded (-18). All earlier migrations pass the no-Rescanningcheck.wallet_migration.pywith the 4da76ca test change, against a binary that has 78ba0e6 but not c62a8d0: fails atwatchonly.gettransaction(watchonly_spendable_txid)withInvalid or non-wallet transaction id (-5). The new listtransactions counts before migration (6) and in the migrated wallet (2) already pass at that point.test/functional/test_runner.py wallet_migration.pypasses.test/lint/lint-python.pyandtest/lint/lint-whitespace.py: clean.clang-format-diffonly suggests re-wrapping the passphrase error strings. They keep upstream's layout to stay 1:1, andwallet.cppis not innon-backported.txt.Breaking Changes
None. After a failed
migratewallet, a wallet that was loaded beforehand is now loaded again, as it was before the call.Checklist:
🤖 Generated with Claude Code