Repository navigation
fix(wallet): authenticate unlock deadline extensions - #7838
PastaPastaPasta merged 1 commit into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review. Walkthrough
Priority: ⬆️ High Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to No actionable merge-blocking risk is established; the change is mergeable after normal checks. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ Final review complete — no blockers (commit 86ba039) · triage: low · Phase 2 only (queue backlog) |
thepastaclaw
left a comment
There was a problem hiding this comment.
Final validation — Phase 1 + Phase 2
At head d2bd1a2, the wallet change authenticates unlock extensions before returning success and preserves the existing mixing-mode and RPC timer behavior. No blocking defect was identified; one non-blocking concern remains about the new regression test's real-time timing margins. Verification was static only: the supplied CI snapshot shows successful formatting and metadata checks, but container builds remain queued and no completed build or test results are available.
🟡 1 suggestion(s)
Review provenance
Source: reviewer 1: glm-5.3-flash (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: dash-core-commit-history); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
lowbygpt-6.1-sol(effort low) — The diff is a small, contained change to CWallet::Unlock passphrase authentication with focused regression tests, not a large or intricate key-handling change meeting the critical bar. - Phase 1 reviewers:
glm-5.3-flash— general (completed, effort high); agentphase1-reviewer - Phase 1 model:
glm-5.3-flash— zai quota: 5h 90% left, weekly 94% left; passed overgemini-3.8-flash-high(antigravity below 15% reserve: weekly 15% left, 5h 100% left) - Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— dash-core-commit-history (completed, effort medium); 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_encryption.py`:
- [SUGGESTION] test/functional/wallet_encryption.py:36-51: Give the new real-time relock assertions more scheduling margin
The initial 3-second unlock must cover two wrong-passphrase key derivations, several RPC round trips, and the first signing operation. The extension scenario then sleeps for 3 seconds against a 5-second timer, leaving less than 2 seconds for the successful signing RPC. These timers use real libevent time, and --timeout-factor does not scale either the wallet deadlines or these sleeps, so a loaded or instrumented worker can relock the wallet before an operation that the test expects to succeed. The expiry assertions also allow only about one second for the timer callback to run. Increase the unlock durations and corresponding sleeps together, preserving the checks on both sides of the original and replacement deadlines. This is a scheduling robustness concern, not an observed failure in this review; repository history in 7513a30d87 independently confirms sanitizer-related wallet timeout flakes.
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 1 + Phase 2
Verified the complete diff at 13e6e8e, including wallet unlock callers, master-key validation, RPC timer handling, and functional-test timeout scaling; no blocking correctness issues were found. The prior scheduling-margin concern is fixed, and the only remaining suggestion is to consolidate the test correction into its introducing commit. This was a static review without builds or test execution; build and validation checks were still queued or pending in the supplied CI snapshot.
🟡 1 suggestion(s)
1 finding(s) not shown inline (the lines are not part of this PR's diff)
🟡 Suggestion: Fold the regression timing correction into the original fix
<commit:13e6e8ed49>:1
Commit 13e6e8e only changes timeout scaling, unlock windows, and expiry waits in test_unlock_authentication, introduced immediately beforehand by d2bd1a2. It revises the same regression scenario rather than adding independently scoped coverage. CONTRIBUTING.md's "Squashing Commits" section identifies repeated changes to the same lines as fixup commits suitable for consolidation. Fold this follow-up into d2bd1a2 so the permanent history presents the authentication fix with its final regression test instead of an intermediate test followed by its timing correction. This is a non-blocking history suggestion, not a defect in the final code.
source: gpt-6.1-sol (phase2-reviewer: dash-core-commit-history)
Review provenance
Source: reviewer 1: glm-5.3-flash (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: dash-core-commit-history); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 5: 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:
lowbygpt-6.1-sol(effort low) — The diff is a small, contained authentication fix in CWallet::Unlock with focused regression coverage, not a large or intricate change despite touching key handling. - Phase 1 reviewers:
glm-5.3-flash— general (completed, effort high); agentphase1-reviewer - Phase 1 model:
glm-5.3-flash— zai quota: 5h 88% left, weekly 89% left; passed overgemini-3.8-flash-high(antigravity below 15% reserve: weekly 15% left, 5h 100% left) - Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
- Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— dash-core-commit-history (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— general (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— dash-core-commit-history (completed, effort medium); 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 `<commit:13e6e8ed49>`:
- [SUGGESTION] <commit:13e6e8ed49>:1: Fold the regression timing correction into the original fix
Commit 13e6e8ed49 only changes timeout scaling, unlock windows, and expiry waits in test_unlock_authentication, introduced immediately beforehand by d2bd1a2673. It revises the same regression scenario rather than adding independently scoped coverage. CONTRIBUTING.md's "Squashing Commits" section identifies repeated changes to the same lines as fixup commits suitable for consolidation. Fold this follow-up into d2bd1a2673 so the permanent history presents the authentication fix with its final regression test instead of an intermediate test followed by its timing correction. This is a non-blocking history suggestion, not a defect in the final code.
|
The timing concern is resolved at 🤖 Posted autonomously by Codex on behalf of pasta. |
CWallet::Unlock(SecureString) returned true without checking the passphrase when the wallet was already fully unlocked. walletpassphrase treats that as authentication and replaces the relock timer, so any nonempty wrong passphrase could keep a full unlock alive past the owner's deadline. Derive and decrypt on every request, as Bitcoin Core does. When a master key is already held, also require the decrypted candidate to match it. The already-unlocked path skips CheckDecryptionKey, which a blank wallet passes trivially, so a wrong passphrase that happens to produce valid padding would otherwise succeed. An authenticated request on a fully unlocked wallet stays a no-op. The RPC timer and mixing-mode handling are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
13e6e8e to
86ba039
Compare
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 2 only (queue backlog)
At exact head 86ba039, the complete diff correctly authenticates encrypted-wallet unlock requests before accepting deadline updates, while preserving authenticated no-op success, mixing-only upgrades, and unencrypted-wallet behavior; no actionable in-scope defects were found. Both prior suggestions are addressed: the range contains one cohesive commit, and the replacement regression checks wrong-passphrase rejection and deadline preservation without the added real-time expiry scenarios. This was static verification only; the supplied exact-head CI snapshot shows successful formatting and merge checks, with container builds and title checks still queued.
🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)
Review provenance
Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); 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:
lowbygpt-6.1-sol(effort low) — The diff is a small, contained authentication fix in CWallet::Unlock with a focused regression test, not a large or intricate change despite touching key handling. - Phase 1 reviewers: not run (skipped for throughput: 12 PRs queued, above the 10 limit)
- Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
- Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— dash-core-commit-history (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— general (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— dash-core-commit-history (completed, effort medium); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.
No unresolved findings remain from the prior review on this head.
…ollow-up fixes for lately added tests. 99cdb0b test: follow-up for #7812: use less fixture to test quorum dkgstatus (Konstantin Akimov) 0288ca9 docs: be more explicit about "scope", "scenario", "case" when writing tests. (Konstantin Akimov) 8a620db test: follow-up for #7838: replace brand new scenario to extra check (Konstantin Akimov) 4a19742 test: follow-up for #7821: check if wallet is locked after failed upgrade instead creating brand-new scenario (Konstantin Akimov) 1435bef test: follow-up for #7829: replace brand-new scenario to just extra check for mixing-unlock (Konstantin Akimov) 8a67e4d test: RPC importelectrumwallet have functional tests, removed an exception (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented #7829, #7821, #7838, #7812, added brand-new scenarious to functional tests while it could tested by extra check in existing scenarious. ## What was done? Updated guidance for agents about writing new tests. Follow-up fixes for #7829, #7821, #7838 by replacing scenarious to single check. Removed an exception for `importelectrumwallet` from list of un-tested RPCs For #7812: - Rotation test. The existing check after the DKG finishes now asserts 0 for each masternode. It no longer sums the stale debug sessions. The PR's two added scenarios are removed. - rpc_quorum.py. It now mines to the next DKG start block while the DKG spork is still off. Then it asserts the masternode's active_dkgs is above 0, before turning the spork on. The PR's separate scenario is removed. - test_framework.py. The helper that only the removed scenarios used is gone. ## How Has This Been Tested? Run functional tests. ## Breaking Changes N/A ## Checklist: - [x] I have performed a self-review of my own code - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ Top commit has no ACKs. Tree-SHA512: 625e2792ad4a848fa9a70dc65109846dae822e87fe0a8a116c7ce8bac224c7a35179bde6751915391d2d1b1bcab32eb1e28eaeaa4e54a5ff777d5c3f997924ba
86ba039 fix(wallet): authenticate unlock deadline extensions (pasta) Pull request description: ## Issue being fixed or feature implemented `CWallet::Unlock(SecureString)` returned `true` without checking the passphrase whenever the encrypted wallet was already fully unlocked. `walletpassphrase` treats that result as authentication and replaces the relock deadline and timer. So while a wallet is fully unlocked, a caller allowed to use `walletpassphrase` could keep it unlocked past the owner's deadline with any nonempty wrong passphrase. Spending or exporting keys after the original deadline still needs those RPC permissions too. A locked or mixing-only wallet cannot be unlocked this way, and RPC authentication is not bypassed. The early return is Dash-specific (added in 78c5ca5, blocked in 0.12.1, reintroduced by ff5540b in 0.16.0.0). Bitcoin Core decrypts the master key on every `Unlock`. ## What was done? - Removed the early return. Every request on an encrypted wallet now derives the key from the passphrase and decrypts the master key, as Bitcoin Core does. - When a master key is already held (full or mixing-only unlock), the decrypted candidate must match it, compared with `TimingResistantEqual`. A mismatch moves on to the next master-key entry. - This matters because the already-unlocked path skips `CheckDecryptionKey`, and a blank wallet passes that check trivially. Without the comparison, a wrong passphrase that happens to produce valid padding would still be accepted. - An authenticated request on a fully unlocked wallet is a no-op success. The metadata and descriptor-cache upgrades are not repeated. - The mixing-only → full upgrade path is unchanged, and so are the RPC timer logic, full → mixing rejection and the unencrypted-wallet result. ## How Has This Been Tested? macOS arm64, depends build. - **Regression test.** `wallet_encryption.py` now ends with one deterministic check under the mocktime the test already sets: with the wallet fully unlocked for 100 seconds, `walletpassphrase` with a wrong passphrase and a 1000-second timeout fails with `-14`, and `unlocked_until` does not change. Against an unpatched `wallet.cpp` the check fails ("No exception raised"). With the fix it passes. - `wallet_encryption.py --legacy-wallet` and `wallet_encryption.py --descriptors` both pass. - `test_dash --run_test=bip39_tests,wallet_crypto_tests` passes, and `lint-python.py` is clean. - All `Unlock(SecureString)` callers were checked: Qt unlock dialog and wallet controller, createwallet, EncryptWallet, migration and upgradetohd. Limits: - The valid-padding mismatch guard was reviewed in the source, not fault-injected. A wrong passphrase gets past decrypt padding only about 1 in 256 times, so a functional test cannot exercise the guard deterministically. - A *locked* blank encrypted wallet still accepts about 1 in 256 wrong passphrases. That gap already existed and Bitcoin Core has it too. Closing it needs a stored key check, so it is out of scope here. ## Breaking Changes None. ## 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: 18f0978db2da05fb2b658b83c0796fafc758859f27cc777d9f7b85196339f0824f128e94ad552feca7b903a3f387de61957e2900ae2995dace349cea9536b190 (cherry picked from commit 2630930)
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) Pull request description: ## Issue being fixed or feature implemented Backports for v24.0.0-rc.4, on top of `v24.0.x` at the v24.0.0-rc.3 tag (0364ed6, #7808). ## What was done? Cherry-picked (`git cherry-pick -m1 -x`) every develop merge since rc.3 that carries `backport-candidate-24.0.x`, plus eight unlabeled fixes picked after an audit of every other develop merge since rc.3 (marked † below). They are in develop merge order: - #7814 fix(qt): restore HTML escaping of send confirmation recipients † - #7851 fix(rpc): correct masternode status help for top-level service field - #7816 fix: check credit pool amount accumulation - #7828 fix: omit recovery words from invalid mnemonic errors † - #7822 fix: reject malformed detached quorum share batches † - #7810 fix(rpc): handle unavailable masternode payment inputs † - #7838 fix(wallet): authenticate unlock deadline extensions - #7792 fix: treat missing snapshots during evodb repair as repair errors - #7821 fix: relock encrypted wallets after HD upgrade failures - #7834 fix: validate Electrum import input boundaries - #7818 fix: return REST errors for unavailable deployment data - #7829 fix: preserve wallet permissions when changing passphrases - #7820 fix: hold chainstate lock during CoinJoin input validation - #7841 fix: authenticate cached InstantSend proof bytes - #7831 fix: bound governance object signatures and reset the governance cache - #7521 fix: update masternode rate limit on failed governance trigger path - #7866 fix: stop `masternode payments` at the genesis block - #7867 fix: refuse masternode list RPCs for blocks outside the active chain † - #7871 backport!: bitcoin#27217: wallet: Replace use of purpose strings with an enum - #7876 fix(consensus): capture operator key scheme changes with unchanged bytes in state diffs - #7875 docs(rpc): clarify getchainlockbyheight availability † - #7839 fix: release InstantSend tracking after mempool eviction - #7827 backport: partial bitcoin#35984 - #7824 fix: retain initialized DKG sessions during inventory reads - #7836 backport: partial bitcoin#25812, bitcoin#29855 - #7782 fix(wallet): persist pending-observation coin locks once the record becomes readable - #7881 fix: refuse to sign shared registrar updates that move the funder's coins † - #7858 fix: do not InstantSend-lock provider transactions - #7817 backport: partial bitcoin#34156 restore cleanup ownership - #7894 test: fix feature_pruning.py restart arguments † (needed because #7792 makes the evodb repair fatal, which the old restart arguments trip) - #7893 fix(net): announce sporks to new peers instead of requesting them during mnsync - 4a19742 (from #7859) test: follow-up for #7821: check if wallet is locked after failed upgrade instead creating brand-new scenario. Test-only, picked as its commit. Without it, #7821's `test_failed_upgrade()` expects RPC error -1 for a 257-byte mnemonic passphrase, which #7842's new precheck rejects with -8 first, so `wallet_upgradetohd.py --legacy-wallet` fails. On develop, #7859 replaced that scenario before #7842 merged. The rest of #7859 is test cleanup and docs and is left out. - #7842 fix(wallet): reject mnemonic passphrases that seed derivation truncates - #7857 fix: mine a pending ProUpServTx before a key change of its masternode - #7846 fix: treat pending owner keys and Platform node IDs as one conflict namespace - #7895 test: cover retroactive provider tx refusal, fix wording - #7909 feat(mining): build the masternode list incrementally during block assembly - #7537 fix: data races and check-then-act races in the CoinJoin server - #7888 fix(consensus): canonical ExtNetInfo encoding and strict netInfo decoding 34 of the 39 picks change exactly the same lines as their develop merges. #7888 and #7782 needed test-only changes (see testing), and three others needed adaptation, described in their commit messages: - **#7893** was merged to develop twice: f99212b with an earlier revision of its commit, then 72a5997 with the final one, so develop now announces sporks twice in the VERACK handler (a develop fix is open separately). Here the PR's final commit 45acdf3 is picked once instead of either merge. In `src/node/sync_manager.cpp`, this branch still sent `GETSPORKS` from the mnsync tick on every network, because #7847, which limited it to test chains, is not included. The request is removed entirely, as on develop. Mainnet spork values are hardcoded in `CSporkManager::GetSporkValue`, so the mainnet request had no effect. - **#7857** and **#7909**: develop folded `CChainstateHelper` into `Chainstate` (d5c1bd7), which is not on this branch, so the new code goes through `ChainHelper().special_tx` / `m_chain_helper.special_tx` instead of `m_special_tx`. The neighbouring develop-only `CSpecialTxProcessor::GetDeterministicMNListHash` is left out. #7888 tightens consensus rules for ExtAddr provider transactions behind V24, so I checked it against testnet. V24 is not active there yet (the tip's CbTx is still version 3), so the chain has no ExtAddr ProTxs the new rules could reject. rc.3's own serializer always writes canonical, sorted purposes, so every ProTx it builds passes the new rules. Mixed rc.3/rc.4 networks could only split on a hand-crafted non-canonical ExtAddr ProTx mined after V24 activates. So testnet miners and masternodes should move to rc.4 before V24 is signalled. Unlabeled develop fixes since rc.3 that were audited and left out: - Worth considering before the final release: #7869 (startup block replay after an unclean shutdown asserts on a quorum commitment, so recovery needs `-reindex-chainstate`; not a v24 regression), #7833 (gate signing intake before quorum retrieval, DoS hardening), #7682 (intermittent `relic_conf.h` parallel-build failure). - Left for 24.1: #7812, #7826, #7844, #7847, #7850, #7868, plus the routine upstream backports (#7780, #7806, #7830, #7855, #7856, #7862, #7863, #7865, #7883, #7884, #7898, #7906), refactors (#7805, #7878, #7880), and CI, docs and test-only changes. One CI-only commit comes last. It takes only the `ci/test` hunks of 6f71fa5 (#7901), which make `ci/test/00_setup_env_native_{multiprocess,tsan}.sh` honor `DEPENDS_ARCH`. Pull requests into `v24.0.x` run develop's workflow files (`pull_request_target`), and since #7901 those check the depends cache on x86_64 runners and pass the aarch64 build arch in `DEPENDS_ARCH`. Without the hunks, `linux64_multiprocess / Build depends` fails on any depends cache miss (`Build runner computes HOST=aarch64-linux-gnu but check-cache used x86_64-pc-linux-gnu`). Without `DEPENDS_ARCH` the scripts still use `uname -m`, so this branch's own push CI is unchanged. 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.4` tag. ## How Has This Been Tested? Built and tested at 6c02b2b on macOS arm64 against depends, with `--enable-debug --enable-werror --disable-hardening`. - `make check`: all unit test suites pass, including `test_dash-qt`. - Functional tests, all passing: `feature_evo_shares_activation.py`, `feature_governance_objects.py`, `feature_llmq_signing.py` (plain and `--spork21`), `feature_masternode_shares.py`, `feature_sporks.py`, `interface_rest.py`, `p2p_instantsend.py`, `p2p_quorum_data.py`, `rpc_masternode.py`, `rpc_psbt.py`, `rpc_quorum.py`, `wallet_backup.py`, `wallet_dump.py`, `wallet_encryption.py`, `wallet_labels.py` and `wallet_upgradetohd.py`, each with `--descriptors` and `--legacy-wallet` where it supports them. - Each pick's changed lines were compared with its develop merge (or, for #7893, its PR commit). They are identical except for the #7893, #7857 and #7909 adaptations above, and the #7888 test adaptation below. - A local macOS arm64 debug build (`--enable-debug --enable-werror`) compiled most of the tree and stopped on a single error in `src/test/evo_netinfo_tests.cpp`. #7888's new test used `CNetAddr::V2()`, the serialization-parameter API from bitcoin#25284, which this branch lacks. The pick now writes that entry through an `OverrideStream` with `ADDRV2_FORMAT`, the way `NetInfoEntry::Serialize` does on this branch (see its commit message). - #7782's new unit test reloads the wallet with develop's three-argument `CWallet` constructor. On this branch the constructor still takes the CoinJoin loader and an `ArgsManager`, so the test passes them the way its fixture already does (see the commit message). - With both test fixes, a full local `make` (`dashd`, `dash-qt`, `test_dash`, `test_dash-qt`) completes with no errors. - `feature_pruning.py` is an extended test that CI does not run. #7894 is included because #7792 makes its restart path fail. ## Breaking Changes Carried over from the picked PRs: - #7876: rc.1 to rc.3 nodes that connected an affected block stored a wrong masternode list diff for it. Those nodes need `-reindex`; `-reindex-chainstate` is not enough for nodes that already marked the following block invalid. - #7888: stricter ExtAddr `netInfo` decoding and validation behind V24 (see above). The RFC1918 check now applies even with `-allowprivatenet`. - #7858: provider transactions no longer get InstantSend locks. Software that waits for an islock on a ProTx should wait for a ChainLock or confirmations instead. - #7857: a `ProUpServTx` spending an output of a pending operator key change, asset lock, asset unlock or MNHF signal is rejected from the mempool until that transaction confirms. - #7831: governance object signatures over 96 bytes are rejected, and the governance cache is discarded and re-synced on first start (`doc/release-notes-7831.md`). - #7871: address purposes are limited to `send`, `receive` and `refund`. Wallets with other purpose strings load with warnings, and `listlabels` errors on an unknown purpose (`doc/release-notes-27217.md`). - #7842: creating an HD wallet with a mnemonic passphrase over 248 bytes fails unless `-allow-deprecated-long-mnemonics` is set, and over 256 bytes always fails (`doc/release-notes-7842.md`). - #7792: a node whose evodb is missing a masternode list snapshot now stops at startup and asks for `-reindex`, and `evodb repair` reports it in `repairErrors`. - #7867: `protx info <proTxHash> <blockHash>`, `protx listdiff` and `masternode payments <blockHash>` return an error for blocks outside the active chain. - #7881: `protx shared_combine` on a `ProUpSharedRegTx` is refused unless its inputs are unlocked coins of a single address of the calling wallet, change returns to that address, and the fee is within `-maxtxfee`. - #7836: PSBTs referencing nonexistent previous outputs fail to deserialize (RPC error -22). - #7834: negative Electrum-import rescan indices are rejected. - #7893: on this branch, mainnet nodes no longer send `GETSPORKS` during mnsync (no effect, mainnet spork values are hardcoded). ## 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: 7de9535ca51620f4ddf5a46df2c3341a5e5fdd2263606f83f7db6b576c31ff7b82e1b285bf66988ac4b2729a9060513c45710135f61d7fd5c147b670d5f405dc
Issue being fixed or feature implemented
CWallet::Unlock(SecureString)returnedtruewithout checking the passphrase whenever the encrypted wallet was already fully unlocked.walletpassphrasetreats that result as authentication and replaces the relock deadline and timer. So while a wallet is fully unlocked, a caller allowed to usewalletpassphrasecould keep it unlocked past the owner's deadline with any nonempty wrong passphrase. Spending or exporting keys after the original deadline still needs those RPC permissions too. A locked or mixing-only wallet cannot be unlocked this way, and RPC authentication is not bypassed.The early return is Dash-specific (added in 78c5ca5, blocked in 0.12.1, reintroduced by ff5540b in 0.16.0.0). Bitcoin Core decrypts the master key on every
Unlock.What was done?
TimingResistantEqual. A mismatch moves on to the next master-key entry.CheckDecryptionKey, and a blank wallet passes that check trivially. Without the comparison, a wrong passphrase that happens to produce valid padding would still be accepted.How Has This Been Tested?
macOS arm64, depends build.
wallet_encryption.pynow ends with one deterministic check under the mocktime the test already sets: with the wallet fully unlocked for 100 seconds,walletpassphrasewith a wrong passphrase and a 1000-second timeout fails with-14, andunlocked_untildoes not change. Against an unpatchedwallet.cppthe check fails ("No exception raised"). With the fix it passes.wallet_encryption.py --legacy-walletandwallet_encryption.py --descriptorsboth pass.test_dash --run_test=bip39_tests,wallet_crypto_testspasses, andlint-python.pyis clean.Unlock(SecureString)callers were checked: Qt unlock dialog and wallet controller, createwallet, EncryptWallet, migration and upgradetohd.Limits:
Breaking Changes
None.
Checklist:
🤖 Generated with Claude Code