Skip to content

Conversation

@knst
Copy link
Collaborator

@knst knst commented Dec 1, 2025

What was done?

More backports to improve support of External signer.
This PR have mostly backports related to tests and RPC

listdescriptors returns descriptors now sorted. It's not a breaking change, because before they has been returned in "undefined" sorting order.

How Has This Been Tested?

Run unit & functional tests

Breaking Changes

N/A

Checklist:

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

@knst knst added this to the 23.1 milestone Dec 1, 2025
@github-actions
Copy link

github-actions bot commented Dec 1, 2025

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

@coderabbitai
Copy link

coderabbitai bot commented Dec 1, 2025

Walkthrough

This PR makes widespread wallet and script API changes: const-correctness for signature checkers; replaces global dummy signature objects with reference-bound dummy creators; changes ScriptPubKeyMan lookup from single-manager to multi-manager (GetScriptPubKeyMans) and adds IsInternalScriptPubKeyMan; changes several methods to return vectors (MarkUnusedAddresses, MarkReserveKeysAsUsed) and introduces WalletDestination; makes AddToWalletIfInvolvingMe and DisplayAddress iterate managers and handle destinations/internal flags; refactors fund/selection logic to explicitly handle internal vs external inputs; unifies funding RPC docs via FundTxDoc; alters listdescriptors to collect, sort, and emit descriptor metadata; and updates many tests for the new behaviors.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Areas requiring extra attention:

  • src/wallet/scriptpubkeyman.h / src/wallet/scriptpubkeyman.cpp: changed return types and added WalletDestination struct — check inheritance, callers, and ABI/API impacts.
  • src/wallet/wallet.h / src/wallet/wallet.cpp: multi-manager lookup, removed single-manager API, new IsMine(IsOutPoint) and IsInternalScriptPubKeyMan — review all call sites and locking invariants (cs_wallet).
  • src/wallet/wallet.cpp (AddToWalletIfInvolvingMe / DisplayAddress): iteration over managers, inference of dest.internal, and automatic address book updates.
  • src/wallet/rpc/backup.cpp: two-phase collection-and-sort logic in listdescriptors — verify sorting, mnemonic handling, and field emission.
  • src/wallet/spend.cpp and src/wallet/spend.cpp (SelectCoins/FundTransaction): explicit external vs internal input handling, error paths for missing external UTXOs, and input size estimation for external inputs.
  • src/script/sign.cpp and src/script/sign.h: reference-binding of DUMMY_CHECKER and new DummySignatureCreator instances — confirm object lifetimes and linkage semantics.
  • Tests: wallet_abandonconflict.py, wallet_listtransactions.py, wallet_listdescriptors.py, rpc_fundrawtransaction.py — ensure expectations align and multi-wallet/test-node changes are correct.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.25% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the PR as a series of backports improving external signer support with focus on RPC and tests, directly matching the changeset content.
Description check ✅ Passed The description is related to the changeset, explaining backports for external signer improvements, test/RPC changes, and the listdescriptors sorting behavior mentioned in the changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
test/functional/wallet_abandonconflict.py (1)

120-120: Optional: simplify membership test for lint (not in vs not X in)

Line 120 currently uses assert not txABC2 in [...], which flake8/ruff flag as E713. You can make this more idiomatic (and silence the lint) without changing behavior:

-        assert not txABC2 in [utxo["txid"] for utxo in alice.listunspent(0)]
+        assert txABC2 not in [utxo["txid"] for utxo in alice.listunspent(0)]
src/wallet/rpc/spend.cpp (1)

358-382: FundTxDoc helper cleanly deduplicates funding RPC docs

Centralizing the conf_target / estimate_mode / solving_data argument descriptions in FundTxDoc() reduces duplication and keeps the shared funding‑related RPC docs in sync without affecting behavior.

src/wallet/rpc/backup.cpp (1)

1989-2107: Deterministic listdescriptors output and extended metadata look consistent

The two‑phase collection into WalletDescInfo, lexicographic sort by descriptor, and emission of optional internal, range, next(_index), coinjoin, and mnemonic fields are internally consistent and align with the stated behavior change (sorted descriptors and extra metadata). Note that coinjoin/mnemonic fields are only emitted for ranged descriptors via info.range.has_value(), which matches current usage where only keypool‑managed (ranged) BIP39 descriptor sets carry mnemonics; if you ever introduce non‑ranged mnemonic descriptors, you may want to move those fields outside the if (info.range) block or document this assumption.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35c7990 and ac5bc23.

📒 Files selected for processing (18)
  • src/node/interfaces.cpp (1 hunks)
  • src/script/interpreter.h (1 hunks)
  • src/script/sign.cpp (1 hunks)
  • src/script/sign.h (1 hunks)
  • src/wallet/rpc/addresses.cpp (1 hunks)
  • src/wallet/rpc/backup.cpp (2 hunks)
  • src/wallet/rpc/spend.cpp (7 hunks)
  • src/wallet/scriptpubkeyman.cpp (5 hunks)
  • src/wallet/scriptpubkeyman.h (5 hunks)
  • src/wallet/spend.cpp (2 hunks)
  • src/wallet/test/wallet_tests.cpp (1 hunks)
  • src/wallet/wallet.cpp (5 hunks)
  • src/wallet/wallet.h (3 hunks)
  • test/functional/rpc_fundrawtransaction.py (1 hunks)
  • test/functional/wallet_abandonconflict.py (5 hunks)
  • test/functional/wallet_listdescriptors.py (1 hunks)
  • test/functional/wallet_listtransactions.py (2 hunks)
  • test/lint/spelling.ignore-words.txt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.{cpp,h,hpp,cc}

📄 CodeRabbit inference engine (CLAUDE.md)

Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1

Files:

  • src/script/sign.h
  • src/wallet/test/wallet_tests.cpp
  • src/wallet/scriptpubkeyman.cpp
  • src/script/interpreter.h
  • src/wallet/wallet.h
  • src/wallet/spend.cpp
  • src/wallet/rpc/spend.cpp
  • src/node/interfaces.cpp
  • src/wallet/rpc/addresses.cpp
  • src/wallet/wallet.cpp
  • src/script/sign.cpp
  • src/wallet/scriptpubkeyman.h
  • src/wallet/rpc/backup.cpp
src/{test,wallet/test}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework

Files:

  • src/wallet/test/wallet_tests.cpp
src/wallet/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Wallet implementation must use Berkeley DB and SQLite

Files:

  • src/wallet/test/wallet_tests.cpp
  • src/wallet/scriptpubkeyman.cpp
  • src/wallet/wallet.h
  • src/wallet/spend.cpp
  • src/wallet/rpc/spend.cpp
  • src/wallet/rpc/addresses.cpp
  • src/wallet/wallet.cpp
  • src/wallet/scriptpubkeyman.h
  • src/wallet/rpc/backup.cpp
test/functional/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Functional tests in test/functional/ must be written in Python (minimum version specified in .python-version) and depend on dashd and dash-node

Files:

  • test/functional/wallet_abandonconflict.py
  • test/functional/wallet_listdescriptors.py
  • test/functional/rpc_fundrawtransaction.py
  • test/functional/wallet_listtransactions.py
src/node/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

NodeContext must be extended with Dash-specific managers for each subsystem during initialization

Files:

  • src/node/interfaces.cpp
🧠 Learnings (11)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: knst
Repo: dashpay/dash PR: 6916
File: src/univalue/include/univalue.h:81-88
Timestamp: 2025-10-25T07:08:51.918Z
Learning: For backport PRs from bitcoin/bitcoin, bitcoin-core/gui, etc., backported changes should match the original upstream PRs even if they appear strange, modify vendored code, or seem to violate coding guidelines. Still flag genuine issues like bugs, undefined behavior, crashes, compilation errors, or linter failures.
Learnt from: knst
Repo: dashpay/dash PR: 6871
File: contrib/guix/libexec/build.sh:358-360
Timestamp: 2025-10-05T20:38:28.457Z
Learning: In the Dash repository, when backporting code from Bitcoin Core, typos and minor issues in comments should be kept as-is to reduce merge conflicts in future backports, even if they remain unfixed in Bitcoin Core's master branch.
Learnt from: knst
Repo: dashpay/dash PR: 6883
File: src/rpc/rawtransaction.cpp:1088-1125
Timestamp: 2025-10-13T12:37:12.357Z
Learning: In backport pull requests (especially from Bitcoin Core), treat "moved" or refactored code as out-of-scope for content-level review. Focus validation on verifying that code is moved correctly: no fields added, no fields removed, no fields reordered, and no unexpected changes beyond whitespace adjustments. Pre-existing issues in the upstream code should be preserved to maintain fidelity to the original implementation.
Learnt from: PastaPastaPasta
Repo: dashpay/dash PR: 6804
File: src/qt/proposalwizard.cpp:40-42
Timestamp: 2025-08-11T17:16:36.654Z
Learning: In the Dash repository, when a PR adds new files that are not from Bitcoin backports, these files must be added to the list in test/util/data/non-backported.txt. This applies to newly created files like qt/proposalwizard.{h,cpp} and forms/proposalwizard.ui. Limited exemptions may exist for subtrees and similar cases.
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
Learnt from: kwvg
Repo: dashpay/dash PR: 6529
File: src/wallet/rpcwallet.cpp:3002-3003
Timestamp: 2025-02-14T15:19:17.218Z
Learning: The `GetWallet()` function calls in `src/wallet/rpcwallet.cpp` are properly validated with null checks that throw appropriate RPC errors, making additional validation unnecessary.
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.

Applied to files:

  • src/wallet/test/wallet_tests.cpp
  • src/wallet/scriptpubkeyman.cpp
  • src/wallet/wallet.h
  • src/wallet/rpc/spend.cpp
  • src/wallet/rpc/addresses.cpp
  • test/functional/rpc_fundrawtransaction.py
  • src/wallet/scriptpubkeyman.h
  • src/wallet/rpc/backup.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{test,wallet/test}/**/*.{cpp,h} : Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework

Applied to files:

  • src/wallet/test/wallet_tests.cpp
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.

Applied to files:

  • src/wallet/test/wallet_tests.cpp
  • test/functional/rpc_fundrawtransaction.py
📚 Learning: 2025-02-14T15:19:17.218Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6529
File: src/wallet/rpcwallet.cpp:3002-3003
Timestamp: 2025-02-14T15:19:17.218Z
Learning: The `GetWallet()` function calls in `src/wallet/rpcwallet.cpp` are properly validated with null checks that throw appropriate RPC errors, making additional validation unnecessary.

Applied to files:

  • src/wallet/test/wallet_tests.cpp
  • src/wallet/wallet.h
  • src/wallet/rpc/addresses.cpp
  • src/wallet/wallet.cpp
📚 Learning: 2025-06-20T23:32:16.225Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6726
File: test/functional/rpc_createmultisig.py:120-120
Timestamp: 2025-06-20T23:32:16.225Z
Learning: In the rpc_createmultisig.py test, the checkbalances() function correctly excludes 9 blocks from the balance calculation: 8 blocks from do_multisig() calls (2 blocks per call × 4 calls) plus 1 block from checkbalances() itself.

Applied to files:

  • test/functional/wallet_abandonconflict.py
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Applied to files:

  • src/wallet/scriptpubkeyman.cpp
  • src/wallet/wallet.h
  • src/wallet/spend.cpp
  • src/wallet/scriptpubkeyman.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/coinjoin/**/*.{cpp,h} : CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs

Applied to files:

  • src/wallet/spend.cpp
  • src/wallet/scriptpubkeyman.h
📚 Learning: 2025-02-06T14:34:30.466Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.

Applied to files:

  • src/wallet/spend.cpp
📚 Learning: 2025-09-02T07:34:28.226Z
Learnt from: knst
Repo: dashpay/dash PR: 6834
File: test/functional/wallet_mnemonicbits.py:50-51
Timestamp: 2025-09-02T07:34:28.226Z
Learning: CJ (CoinJoin) descriptors with derivation path "9'/1" are intentionally inactive in descriptor wallets, while regular internal/external descriptors with different derivation paths remain active.

Applied to files:

  • src/wallet/scriptpubkeyman.h
  • src/wallet/rpc/backup.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/wallet/**/*.{cpp,h} : Wallet implementation must use Berkeley DB and SQLite

Applied to files:

  • src/wallet/scriptpubkeyman.h
🧬 Code graph analysis (9)
src/script/sign.h (1)
src/script/sign.cpp (1)
  • DUMMY_CHECKER (364-364)
src/wallet/test/wallet_tests.cpp (1)
src/rpc/request.cpp (1)
  • error (58-58)
test/functional/wallet_abandonconflict.py (4)
test/functional/test_framework/test_node.py (1)
  • get_wallet_rpc (363-369)
test/functional/test_framework/test_framework.py (2)
  • sync_mempools (834-860)
  • disconnect_nodes (730-764)
test/functional/test_framework/util.py (1)
  • assert_raises_rpc_error (132-148)
src/wallet/rpc/transactions.cpp (4)
  • abandontransaction (803-842)
  • abandontransaction (803-803)
  • gettransaction (694-801)
  • gettransaction (694-694)
src/wallet/wallet.h (2)
src/wallet/wallet.cpp (13)
  • IsMine (1484-1488)
  • IsMine (1484-1484)
  • IsMine (1490-1494)
  • IsMine (1490-1490)
  • IsMine (1496-1504)
  • IsMine (1496-1496)
  • IsMine (1506-1513)
  • IsMine (1506-1506)
  • IsMine (1515-1526)
  • IsMine (1515-1515)
  • outpoint (1094-1094)
  • IsInternalScriptPubKeyMan (4005-4027)
  • IsInternalScriptPubKeyMan (4005-4005)
src/wallet/scriptpubkeyman.h (5)
  • ScriptPubKeyMan (170-233)
  • ScriptPubKeyMan (178-178)
  • bool (185-185)
  • bool (230-230)
  • bool (236-236)
test/functional/rpc_fundrawtransaction.py (2)
test/functional/test_framework/util.py (1)
  • assert_raises_rpc_error (132-148)
src/wallet/rpc/spend.cpp (2)
  • fundrawtransaction (554-644)
  • fundrawtransaction (554-554)
test/functional/wallet_listtransactions.py (2)
src/wallet/rpc/addresses.cpp (4)
  • getnewaddress (17-56)
  • getnewaddress (17-17)
  • getaddressinfo (418-563)
  • getaddressinfo (418-418)
src/wallet/rpc/transactions.cpp (2)
  • listtransactions (442-552)
  • listtransactions (442-442)
src/wallet/wallet.cpp (4)
src/wallet/scriptpubkeyman.cpp (8)
  • IsMine (165-177)
  • IsMine (165-165)
  • IsMine (179-183)
  • IsMine (179-179)
  • IsMine (1854-1861)
  • IsMine (1854-1854)
  • IsMine (1863-1867)
  • IsMine (1863-1863)
src/wallet/scriptpubkeyman.h (2)
  • IsMine (180-180)
  • IsMine (181-181)
src/wallet/wallet.h (1)
  • cs_wallet (604-604)
src/wallet/external_signer_scriptpubkeyman.cpp (2)
  • GetExternalSigner (42-51)
  • GetExternalSigner (42-42)
src/wallet/scriptpubkeyman.h (1)
src/wallet/scriptpubkeyman.cpp (6)
  • MarkUnusedAddresses (309-344)
  • MarkUnusedAddresses (309-309)
  • MarkUnusedAddresses (2045-2071)
  • MarkUnusedAddresses (2045-2045)
  • MarkReserveKeysAsUsed (1650-1675)
  • MarkReserveKeysAsUsed (1650-1650)
src/wallet/rpc/backup.cpp (2)
src/wallet/hdchain.h (1)
  • BIP32_PURPOSE_FEATURE (145-145)
src/script/descriptor.cpp (1)
  • nullopt (656-656)
🪛 Flake8 (7.3.0)
test/functional/wallet_abandonconflict.py

[error] 79-79: block comment should start with '# '

(E265)


[error] 120-120: test for membership should be 'not in'

(E713)

🪛 Ruff (0.14.6)
test/functional/wallet_abandonconflict.py

120-120: Test for membership should be not in

Convert to not in

(E713)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: mac-build / Build source
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: arm-linux-build / Build source
  • GitHub Check: win64-build / Build source
  • GitHub Check: Lint / Run linters
  • GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends
🔇 Additional comments (30)
src/node/interfaces.cpp (1)

583-589: Minor documentation fix approved.

This comment correction (spelling fix and clarity improvement) is a standard backport improvement. The change has no functional impact and aligns with typical upstream Bitcoin PR cleanups.

test/functional/wallet_abandonconflict.py (2)

35-175: Multi‑wallet alice/bob refactor and wallet‑scoped RPC usage look sound

The changes to introduce alice and bob wallet RPC handles, route balance/tx calls through alice, and re‑acquire alice after each restart_node() are consistent and preserve the original test semantics. Balance math, use of getbalances()['mine'], and the abandon/unabandon flows all line up correctly with the expected confirmation and mempool states.


177-235: Receiver‑side (bob) walletconflicts coverage is correct and well‑scoped

The new bob wallet flow—explicit loadwallet("bob"), paying the double‑spend to bob, and then asserting confirmations == -1 / walletconflicts for txAB1 plus a single confirmed double‑spend entry—matches the intended conflict behavior from the receiver’s perspective and doesn’t interfere with the existing sender‑side checks.

src/wallet/rpc/addresses.cpp (1)

509-546: Multi-ScriptPubKeyMan handling in getaddressinfo looks correct

Using GetScriptPubKeyMans(scriptPubKey) and picking the first manager, then reusing spk_man both for parent_desc and metadata, is null‑safe and consistent with the new multi‑manager API. No issues spotted here.

test/functional/wallet_listtransactions.py (3)

6-21: Imports and node topology updates are appropriate for new scenarios

Adding os, shutil, and assert_raises_rpc_error matches later usage, and bumping self.num_nodes to 3 is required for the wallet copy onto node2. extra_args is already derived from self.num_nodes, so startup semantics remain consistent.


105-161: Externally generated address test is sound

The new run_externally_generated_address_test correctly:

  • Refills the keypool before copying wallet.dat to node2.
  • Stops nodes before file operations and restarts them with the same datadirs.
  • Reconnects the 3‑node topology explicitly.
  • Normalizes listtransactions by stripping label/time fields and sorting by txid before comparing node0 vs node2.
  • Verifies that address labels remain only on the node that actually generated the addresses.

This matches expected external‑signer / multi‑wallet semantics.


162-167: listtransactions parameter validation coverage is adequate

run_invalid_parameters_test exercises:

  • Empty label → specific RPC error.
  • "*" as a valid label wildcard.
  • Negative count and skip (“from”) arguments.

The error codes/messages align with the listtransactions RPC implementation, so this adds useful regression coverage.

src/wallet/spend.cpp (2)

528-547: Preset input sizing correctly incorporates external signing provider

The updated preset‑input loop now:

  • Uses wallet metadata when the input is in mapWallet.
  • Falls back to coin_control.GetExternalOutput when it is not.
  • If input_bytes is still -1, computes it via coin_control.m_external_provider.

This ensures fee estimation works for externally provided inputs while still rejecting unsignable inputs (input_bytes == -1) early.


1168-1177: FundTransaction internal vs external input handling is clearer and safer

Classifying inputs as:

  • Internal when wallet.IsMine(outPoint) is true (wallet‑owned/watched).
  • External when not mine but present in the UTXO set, via coinControl.SelectExternal.
  • Failing with a clear error if the external UTXO cannot be found,

removes the previous heuristic and makes external‑signer flows and error reporting more explicit.

src/wallet/scriptpubkeyman.cpp (3)

309-344: Legacy MarkUnusedAddresses’ new return value is consistent with keypool semantics

Returning std::vector<WalletDestination> and populating it from the keys removed by MarkReserveKeysAsUsed (with PKHash and fInternal) gives callers precise knowledge of which addresses were actually consumed while preserving existing keypool‑topup and metadata‑update behavior.


1650-1675: MarkReserveKeysAsUsed correctly tracks and returns consumed keypool entries

Refactoring MarkReserveKeysAsUsed to:

  • Determine the correct internal vs external pool.
  • Remove all entries up to keypool_id from the chosen set and DB.
  • Erase m_pool_key_to_index mappings.
  • Accumulate the corresponding CKeyPool records in a vector,

provides the data needed by MarkUnusedAddresses without changing observable keypool behavior.


2045-2071: Descriptor MarkUnusedAddresses matches descriptor wallet expectations

For descriptor wallets, MarkUnusedAddresses:

  • Detects used scripts via IsMine(script) / m_map_script_pub_keys.
  • Advances next_index up to the used index, expanding from the descriptor cache per index.
  • Collects WalletDestination entries with std::nullopt for the internal flag (as expected for single‑descriptor managers).
  • Calls TopUp() afterwards to replenish the keypool.

This is in line with the descriptor keypool model; the unused block_time parameter is benign here.

src/wallet/wallet.h (2)

808-818: Outpoint-level IsMine overload is a sensible addition

Declaring isminetype IsMine(const COutPoint& outpoint) const (with cs_wallet lock requirement) aligns with the implementation in wallet.cpp and simplifies callers like FundTransaction that work directly with COutPoints.


1000-1004: Multi-manager and internal-SPK-man helpers are well-factored

The new APIs:

  • GetScriptPubKeyMans(const CScript& script) const for multi‑manager lookups, and
  • std::optional<bool> IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const to query internal/external status for active descriptor managers,

provide the right abstractions for the RPC and spending code to reason about multiple script managers without leaking internal details.

Also applies to: 1068-1075

test/lint/spelling.ignore-words.txt (1)

14-14: Add of “keypair” to ignore list is appropriate

This just prevents spurious spelling lint failures for a common cryptographic term; no behavior impact.

src/wallet/test/wallet_tests.cpp (1)

853-853: ZapSelectTx local error type change is benign

Switching the unused local from bilingual_str to std::string does not affect test behavior and keeps the code aligned with upstream.

src/script/interpreter.h (1)

195-212: Const‑correctness improvement in DeferringSignatureChecker

Storing and accepting a const BaseSignatureChecker& is consistent with how checkers are used (no mutation) and works with existing callers; behavior is unchanged.

src/wallet/rpc/spend.cpp (2)

570-597: fundrawtransaction options now reuse shared funding docs

Using Cat<...>(..., FundTxDoc()) for the options object keeps the documented snake_case keys (including solving_data) consistent with other funding RPCs and with the parsing in FundTransaction, with no runtime change.


763-797: send and walletcreatefundedpsbt option docs aligned via FundTxDoc

Extending both RPCs’ options argument lists with FundTxDoc() makes the help text for conf_target, estimate_mode, and solving_data uniform across funding calls, matching the shared FundTransaction implementation.

Also applies to: 1047-1069

src/script/sign.cpp (1)

345-385: Dummy checker/creator refactor is consistent and well‑scoped

The new DUMMY_CHECKER and DummySignatureCreator implementations produce valid, fixed‑size dummy DER signatures (71/72 bytes) behind const interface references, matching their use for size estimation and solvability checks without changing script semantics.

src/script/sign.h (1)

53-58: Expose DUMMY_CHECKER alongside dummy signature creators

Adding extern const BaseSignatureChecker& DUMMY_CHECKER; keeps the header in sync with sign.cpp and makes the shared dummy checker available to callers in the same way as the dummy signature creators.

test/functional/wallet_listdescriptors.py (1)

60-63: Good addition to assert lexicographic ordering of descriptors

The new assertion that desc strings equal their sorted order directly verifies the intended deterministic sorting behavior of listdescriptors without overconstraining other fields.

test/functional/rpc_fundrawtransaction.py (1)

389-394: Updated error expectation matches more specific external‑input failure

Switching the expected message in test_invalid_input to "Unable to find UTXO for external input" reflects the new, clearer error path for a vin pointing to a non‑existent UTXO, while keeping “Insufficient funds” for real balance shortfalls.

src/wallet/wallet.cpp (5)

1143-1158: Multi‑manager address marking and address‑book backfill look correct

The new loop over GetScriptPubKeyMans(txout.scriptPubKey) plus MarkUnusedAddresses and WalletDestination.internal handling cleanly supports multiple SPK managers. Inferring internal via IsInternalScriptPubKeyMan only when not provided by the manager, and only auto‑adding non‑internal destinations to the address book when they’re absent, gives the desired behavior for restored backups and newly detected receive addresses without mislabeling change or CoinJoin paths.


1515-1526: New IsMine(const COutPoint&) helper is safe and well‑scoped

The helper checks for a known wallet transaction and bounds‑checks the output index before delegating to IsMine(CTxOut), so there’s no risk of out‑of‑range access and it reuses existing ownership logic.


2573-2585: DisplayAddress correctly routes through external signer ScriptPubKeyMans

Switching to GetScriptPubKeyMans(scriptPubKey) and then dynamic_casting to ExternalSignerScriptPubKeyMan ensures this RPC only uses external‑signer managers that can actually handle the script, and immediately returns the first successful DisplayAddress result. Non‑external SPK managers are ignored, and a false return cleanly signals no capable signer was found.


3771-3781: GetScriptPubKeyMans(const CScript&) generalization looks sound

Collecting all SPK managers whose CanProvide returns true for a script and returning them in a std::set<ScriptPubKeyMan*> is consistent with the existing GetSolvingProvider pattern. Reusing a single SignatureData instance here matches that pattern and is safe, since CanProvide is used as a capability check rather than depending on prior contents.


4005-4027: IsInternalScriptPubKeyMan correctly classifies active descriptor managers

The helper returns std::nullopt for legacy wallets and non‑active SPK managers, and for descriptor wallets asserts that active managers have a defined output type before comparing against the internal manager pointer. This matches the import/activation constraints (e.g., combo descriptors and CoinJoin descriptors are not activated) and gives a reliable internal/external flag for use by higher‑level code like AddToWalletIfInvolvingMe and listdescriptors.

src/wallet/scriptpubkeyman.h (2)

150-161: WalletDestination and path type enum are a good fit for new address metadata

Introducing WalletDestination { CTxDestination dest; std::optional<bool> internal; } provides a simple way to pipe both the address and (optionally) its internal/external classification back to wallet‑level code. Together with the existing PathDerivationType (including DIP0009_CoinJoin for CoinJoin paths), this matches how descriptor managers distinguish external, internal, and CoinJoin derivation paths in the rest of the wallet.


197-205: MarkUnusedAddresses/MarkReserveKeysAsUsed API changes align with callers

Changing ScriptPubKeyMan::MarkUnusedAddresses to return std::vector<WalletDestination> and LegacyScriptPubKeyMan::MarkReserveKeysAsUsed to return std::vector<CKeyPool> matches the implementations in scriptpubkeyman.cpp and the new usage in CWallet::AddToWalletIfInvolvingMe. Legacy SPKMs can report concrete destinations and their fInternal flag, while descriptor SPKMs can return destinations with internal left unset for wallet‑level inference. The documented behavior (“all keys up to and including the provided key”) also matches the backported logic.

Also applies to: 353-354, 499-507, 597-598

@UdjinM6 UdjinM6 added the RPC Some notable changes to RPC params/behaviour/descriptions label Dec 4, 2025
MarcoFalke and others added 12 commits December 5, 2025 19:25
BACKPORT NOTE:
changes from script/standard is excluded due to not backported yet taproot

184d453 script, doc: spelling update (Jon Atack)

Pull request description:

  Clears out the report from `test/lint/lint-spelling.sh` and touches up the leftover nits in bitcoin#22166 (review). Happy to add any others people are aware of.

ACKs for top commit:
  MarcoFalke:
    cr ACK 184d453
  Sjors:
    utACK 184d453

Tree-SHA512: 3b0ef6bd5ff227363b0bda79eeb66763151c74f607bc3a2a7bfe7823e3eef196587bccfe639e714e8e27b918ba57e8317eda06f225143c32c736685087dbcd24
fafff13 doc: Extract FundTxDoc (MarcoFalke)

Pull request description:

  No need to duplicate the documentation for the same field(s) three times.

  Fix that by de-duplicating it for the fields: conf_target, estimate_mode, replaceable, and solving_data.

  Can be reviewed with `--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space`.

ACKs for top commit:
  fanquake:
    ACK fafff13

Tree-SHA512: 098ddad3904b80b24c9e7b57ca8e807a6ccc3899eac2c9986d71ba3873c2b580bbb95f2fdfbf94b2db02f81c7b0ebf438a90324c23389b7b968ca85ae8475373
… to the address book

3d71d16 test: listtranscations with externally generated addresses (S3RK)
d045664 Add to spends only transcations from me (S3RK)
9f3a622 Automatically add labels to detected receiving addresses (S3RK)
c1b99c0 Return used destinations from ScriptPubKeyMan::MarkUnusedAddresses (S3RK)
03840c2 Add CWallet::IsInternalScriptPubKeyMan (S3RK)
456e350 wallet: resolve ambiguity of two ScriptPubKey managers providing same script (S3RK)

Pull request description:

  This PR fixes certain use-cases when **send-to-self** transactions are missing from `listtransactions` output.

  1. When a receiving address is generated externally to the wallet
  (e.g. same wallet running on two nodes, or by 3rd party from xpub)
  2. When restoring backup with lost metadata, but keypool gap is not exceeded yet

  When the block is connected or tx added to mempool we already mark used keys. This PR extends this logic to determine whether the destination is a receiving one and if yes add it to the address book with empty label.

  Works both for legacy and descriptors wallets.
  - For legacy it uses the internal flag from the keypool entry. Caveat: because we don't know which script type would be used we add all possible destinations for such keys.
  - For descriptor wallets it uses internal flag for the script pub key manager. Caveat: it only works for active descriptors.

  fixes bitcoin#19856
  fixes bitcoin#20293

ACKs for top commit:
  laanwj:
    Code review ACK 3d71d16

Tree-SHA512: 03fafd5548ead0c4ffe9ebcc9eb2849f1d2fa7270fda4166419b86877d4e57dcf04460e465fbb9c90b42031f3c05d1b83f1b67a9f82c2a42980825ed1e7b52e6
4e1cb90 test: fix: remove outdated TestNode.generate calls (James O'Beirne)

Pull request description:

  Currently failing on CI. After this change the test itself still fails,
  but at least it's apparently for a non-incidental reason.

ACKs for top commit:
  meshcollider:
    ACK 4e1cb90
  theStack:
    Tested ACK 4e1cb90

Tree-SHA512: 5e7059d334d571ca92f250d298292ce1653da8257cbfb218d28cc9c5816c21c718c36482da31fcaf78e0714cc9b67ff04b91405e820accaf4d8321a354af9441
…_listtransactions.py

0ba98ed test: remove unneeded sync_all() calls in wallet_listtransactions.py (Sebastian Falbesoner)

Pull request description:

  This is a small follow-up to bitcoin#23659. The `self.sync_all()` calls after generating blocks can be removed, since that happens automatically per default by the test framework's generate function (if no explicit sync_fun is passed).
  On the course of touching the file, imports are sorted and the grammar of a log message is fixed.

ACKs for top commit:
  fanquake:
    ACK 0ba98ed - thanks for following up.
  shaavan:
    ACK 0ba98ed

Tree-SHA512: 451e733865dcb1e424d90289c8c89272837a9af6fd4b77d6c60728c84524d9c792d684b7e601b02a0efda67231183c42dd9040d96214ac7d9473b2808cabe73f
…ameters

c27bba9 test: check for invalid listtransactions RPC parameters (Sebastian Falbesoner)

Pull request description:

  This PR adds missing test coverage for RPC errors that are thrown if invalid parameters are passed to `listtransactions`:

  https://github.com/bitcoin/bitcoin/blob/887796a5ffcbafcd281b920f8d55fcb6e8347584/src/wallet/rpc/transactions.cpp#L508

  https://github.com/bitcoin/bitcoin/blob/887796a5ffcbafcd281b920f8d55fcb6e8347584/src/wallet/rpc/transactions.cpp#L524

  https://github.com/bitcoin/bitcoin/blob/887796a5ffcbafcd281b920f8d55fcb6e8347584/src/wallet/rpc/transactions.cpp#L526

ACKs for top commit:
  shaavan:
    ACK c27bba9

Tree-SHA512: e5a23590186b4d9663261ff6cea52ac45e9bf2f2ef693c22b3452bb07af9b800fdabc2a94fd2852c686c28214a496d7afe296e41831759f2182feac2482635d0
…are also in the wallet

ef8e2a5 tests: Test that external inputs of txs in wallet is handled correctly (Andrew Chow)
eb87963 wallet: Try estimating input size with external data if wallet fails (Andrew Chow)
a537d7a wallet: SelectExternal actually external inputs (Andrew Chow)
f2d00bf wallet: Add CWallet::IsMine(COutPoint) (Andrew Chow)

Pull request description:

  if a transaction is being funded that has an external input, and that input's parent is also in the wallet, we will fail to detect that and fail to fund the transaction. In order to correctly detect such inputs, we need to be doing `IsMine` on all specified inputs in order to use `Select` and `SelectExternal` correctly. Additionally `SelectCoins` needs to call `CalculateMaximumSignedInputSize` with the correct parameters which depends on whether the wallet is able to solve for the input. Because there are some situations where the wallet could find an external input to belong to it (e.g. watching an address - unable to solve, but will be ISMINE_WATCHONLY), instead of switching which `CalculateMaximumSignedInputSize` to use, we should call the one that uses the wallet, and if that fails, try again with the one that uses external solving data.

  Also adds a test for this case.

ACKs for top commit:
  instagibbs:
    ACK bitcoin@ef8e2a5
  furszy:
    ACK ef8e2a5
  ishaanam:
    reACK ef8e2a5

Tree-SHA512: a43c4aefeed4605f33a36ce87ebb916e2c153fea6d415b02c9a89275e84a7e3bf12840b33c296d2d2bde46350390da48d9262f9567338e3f21d5936aae4caa1e
…external inputs

BACKPORT NOTE:
Dash have no bumpfee functionality.
This PR is backported just to keep in sync with bitcoin's codebase and it includes minor refactorings

c3b099a wallet, tests: Test bumpfee's max input weight calculation (Andrew Chow)
116a620 Make DUMMY_CHECKER availble outside of script/sign.cpp (Andrew Chow)
ff63832 test, bumpfee: Check that psbtbumpfee can bump txs with external inputs (Andrew Chow)
1bc8106 bumpfee: be able to bump fee of a tx with external inputs (Andrew Chow)
31dd3dc bumpfee: Clear scriptSigs and scriptWitnesses before calculated max size (Andrew Chow)
a0c3afb bumpfee: extract weights of external inputs when bumping fee (Andrew Chow)
612f1e4 bumpfee: Calculate fee by looking up UTXOs (Andrew Chow)

Pull request description:

  This PR allows `psbtbumpfee` to return a PSBT for transactions that contain external inputs. This does not work for bumping in the GUI nor `bumpfee` because these need private keys available to sign and send the transaction. But `psbtbumpfee` returns a psbt, so it is fine to not be able to sign.

  In order to correctly estimate the size of the inputs for coin selection, the fee bumper will use the size of the inputs of the transaction being bumped. Because the sizes of signatures are not guaranteed, for external inputs, the fee bumper will verify the scripts with a special SignatureChecker which will compute the weight of all of the signatures in that input, and compute their weights if those signatures were maximally sized. This allows the fee bumper to obtain a max size estimate for each external input.

  Builds on bitcoin#23201 as it relies on the ability to pass weights in to coin selection.

  Closes bitcoin#23189

ACKs for top commit:
  ishaanam:
    reACK c3b099a
  t-bast:
    Re-ran my tests agains bitcoin@c3b099a, ACK

Tree-SHA512: 40016ec52d351430977579cfa2694c7e6764f42c9ce09d3a6f1753b767f86053f296d9de988248df033be6d725d67badbf2a5ef82c8ace23c61487729b7691e5
5099624 rpc: sort listdescriptors result (Sjors Provoost)

Pull request description:

  This puts receive and change descriptors directly below each other.

  The change would be simpler if `UniValue` arrays were sortable.

ACKs for top commit:
  achow101:
    ACK 5099624
  S3RK:
    reACK 5099624
  furszy:
    utACK 5099624
  w0xlt:
    reACK bitcoin@5099624

Tree-SHA512: 71246a48ba6f97c3e7c76ee32ff9e958227a14ca5a6eec638215dbfee57264d4e918ea5837f4d030eddc9c797c93df1791ddd55b5a499522ce2a35bcf380670b
…rings are sorted

810c3dc doc, rpc: mention that `listdescriptors` result is sorted by string representation (Sebastian Falbesoner)
d99af86 test: check that `listdescriptors` descriptor strings are sorted (Sebastian Falbesoner)

Pull request description:

  This small PR adds a test for the change introduced in PR bitcoin#25931 ("rpc: sort listdescriptors result", commit 5099624). The correctness of the test can easily be verified by commenting out the `std::sort` call in the `listdescriptors` RPC implementation:
  ```diff
  diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
  index 09c74ea..3ed1a69b26 100644
  --- a/src/wallet/rpc/backup.cpp
  +++ b/src/wallet/rpc/backup.cpp
  @@ -1829,9 +1829,11 @@ RPCHelpMan listdescriptors()
           });
       }

  +    /*
       std::sort(wallet_descriptors.begin(), wallet_descriptors.end(), [](const auto& a, const auto& b) {
           return a.descriptor < b.descriptor;
       });
  +    */

       UniValue descriptors(UniValue::VARR);
       for (const WalletDescInfo& info : wallet_descriptors) {

  ```
  leading to a fail of the functional test `wallet_listdescriptors.py`.

ACKs for top commit:
  jarolrod:
    ACK 810c3dc
  aureleoules:
    ACK 810c3dc

Tree-SHA512: 31770e3149b8a0251ecfa8662a2270c149f778eb910985f48a91d6a5d288b7b1c2244f9f1b798ebe3f1aa9f0b935cb4d6f12d5d28f78bcde3c4a61af76d11d0a
3ee6d07 test: add more wallet conflicts assertions (S3RK)
3b98bf9 Revert "Add to spends only transcations from me" (S3RK)

Pull request description:

  This reverts commit d045664 from bitcoin#22929.

  This commit was based on invalid assumption that `mapTxSpends` should contain only outgoing txs and broke wallet conflicts feature.

ACKs for top commit:
  achow101:
    ACK 3ee6d07

Tree-SHA512: bf5a77ced6bac57d5eb85771d9189c53e1edc295d179ed5a1bdce18e365794a9101b4cecf35387b27f67260db3b47f7214e7876e490494529b748cceeb95632d
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
test/functional/wallet_abandonconflict.py (2)

79-79: Add space after # in comment.

Static analysis reports a formatting issue: block comments should have a space after #.

Apply this diff:

-        #Create a child tx spending AB1 and C
+        # Create a child tx spending AB1 and C

120-120: Prefer not in operator for membership test.

Python style guidelines recommend x not in y over not x in y for better readability.

Apply this diff:

-        assert not txABC2 in [utxo["txid"] for utxo in alice.listunspent(0)]
+        assert txABC2 not in [utxo["txid"] for utxo in alice.listunspent(0)]
src/wallet/scriptpubkeyman.cpp (1)

315-350: Minor robustness nit in legacy keypool usage reporting

The refactor of LegacyScriptPubKeyMan::MarkUnusedAddresses / MarkReserveKeysAsUsed to return affected destinations and CKeyPool entries is functionally fine and matches the new wallet‑level APIs. One small robustness concern: in MarkReserveKeysAsUsed, result.push_back(std::move(keypool)); is executed even when batch.ReadPool(index, keypool) fails, so result may contain default‑initialized CKeyPool objects on DB read errors. That’s only reachable under wallet DB corruption and won’t affect normal operation, but if you ever want to harden this path, you could gate the push on a successful ReadPool to avoid propagating dummy entries. Given this is a straight backport, leaving it as‑is to stay aligned with upstream is also reasonable.

Also applies to: 1656-1681

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac5bc23 and d59ce85.

📒 Files selected for processing (18)
  • src/node/interfaces.cpp (1 hunks)
  • src/script/interpreter.h (1 hunks)
  • src/script/sign.cpp (1 hunks)
  • src/script/sign.h (1 hunks)
  • src/wallet/rpc/addresses.cpp (1 hunks)
  • src/wallet/rpc/backup.cpp (2 hunks)
  • src/wallet/rpc/spend.cpp (7 hunks)
  • src/wallet/scriptpubkeyman.cpp (5 hunks)
  • src/wallet/scriptpubkeyman.h (5 hunks)
  • src/wallet/spend.cpp (2 hunks)
  • src/wallet/test/wallet_tests.cpp (1 hunks)
  • src/wallet/wallet.cpp (5 hunks)
  • src/wallet/wallet.h (3 hunks)
  • test/functional/rpc_fundrawtransaction.py (1 hunks)
  • test/functional/wallet_abandonconflict.py (5 hunks)
  • test/functional/wallet_listdescriptors.py (1 hunks)
  • test/functional/wallet_listtransactions.py (2 hunks)
  • test/lint/spelling.ignore-words.txt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • test/functional/rpc_fundrawtransaction.py
  • test/lint/spelling.ignore-words.txt
  • src/wallet/test/wallet_tests.cpp
  • test/functional/wallet_listdescriptors.py
  • src/node/interfaces.cpp
  • src/script/sign.h
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{cpp,h,hpp,cc}

📄 CodeRabbit inference engine (CLAUDE.md)

Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1

Files:

  • src/wallet/rpc/spend.cpp
  • src/script/interpreter.h
  • src/wallet/spend.cpp
  • src/wallet/wallet.h
  • src/script/sign.cpp
  • src/wallet/rpc/backup.cpp
  • src/wallet/scriptpubkeyman.cpp
  • src/wallet/scriptpubkeyman.h
  • src/wallet/wallet.cpp
  • src/wallet/rpc/addresses.cpp
src/wallet/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Wallet implementation must use Berkeley DB and SQLite

Files:

  • src/wallet/rpc/spend.cpp
  • src/wallet/spend.cpp
  • src/wallet/wallet.h
  • src/wallet/rpc/backup.cpp
  • src/wallet/scriptpubkeyman.cpp
  • src/wallet/scriptpubkeyman.h
  • src/wallet/wallet.cpp
  • src/wallet/rpc/addresses.cpp
test/functional/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Functional tests in test/functional/ must be written in Python (minimum version specified in .python-version) and depend on dashd and dash-node

Files:

  • test/functional/wallet_abandonconflict.py
  • test/functional/wallet_listtransactions.py
🧠 Learnings (9)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: knst
Repo: dashpay/dash PR: 6916
File: src/univalue/include/univalue.h:81-88
Timestamp: 2025-10-25T07:08:51.918Z
Learning: For backport PRs from bitcoin/bitcoin, bitcoin-core/gui, etc., backported changes should match the original upstream PRs even if they appear strange, modify vendored code, or seem to violate coding guidelines. Still flag genuine issues like bugs, undefined behavior, crashes, compilation errors, or linter failures.
Learnt from: knst
Repo: dashpay/dash PR: 6871
File: contrib/guix/libexec/build.sh:358-360
Timestamp: 2025-10-05T20:38:28.457Z
Learning: In the Dash repository, when backporting code from Bitcoin Core, typos and minor issues in comments should be kept as-is to reduce merge conflicts in future backports, even if they remain unfixed in Bitcoin Core's master branch.
Learnt from: knst
Repo: dashpay/dash PR: 6883
File: src/rpc/rawtransaction.cpp:1088-1125
Timestamp: 2025-10-13T12:37:12.357Z
Learning: In backport pull requests (especially from Bitcoin Core), treat "moved" or refactored code as out-of-scope for content-level review. Focus validation on verifying that code is moved correctly: no fields added, no fields removed, no fields reordered, and no unexpected changes beyond whitespace adjustments. Pre-existing issues in the upstream code should be preserved to maintain fidelity to the original implementation.
Learnt from: PastaPastaPasta
Repo: dashpay/dash PR: 6804
File: src/qt/proposalwizard.cpp:40-42
Timestamp: 2025-08-11T17:16:36.654Z
Learning: In the Dash repository, when a PR adds new files that are not from Bitcoin backports, these files must be added to the list in test/util/data/non-backported.txt. This applies to newly created files like qt/proposalwizard.{h,cpp} and forms/proposalwizard.ui. Limited exemptions may exist for subtrees and similar cases.
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.
Learnt from: kwvg
Repo: dashpay/dash PR: 6529
File: src/wallet/rpcwallet.cpp:3002-3003
Timestamp: 2025-02-14T15:19:17.218Z
Learning: The `GetWallet()` function calls in `src/wallet/rpcwallet.cpp` are properly validated with null checks that throw appropriate RPC errors, making additional validation unnecessary.
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.

Applied to files:

  • src/wallet/rpc/spend.cpp
  • src/wallet/wallet.h
  • src/wallet/rpc/backup.cpp
  • src/wallet/scriptpubkeyman.cpp
  • src/wallet/scriptpubkeyman.h
  • src/wallet/wallet.cpp
  • src/wallet/rpc/addresses.cpp
📚 Learning: 2025-06-20T23:32:16.225Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6726
File: test/functional/rpc_createmultisig.py:120-120
Timestamp: 2025-06-20T23:32:16.225Z
Learning: In the rpc_createmultisig.py test, the checkbalances() function correctly excludes 9 blocks from the balance calculation: 8 blocks from do_multisig() calls (2 blocks per call × 4 calls) plus 1 block from checkbalances() itself.

Applied to files:

  • test/functional/wallet_abandonconflict.py
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Applied to files:

  • src/wallet/spend.cpp
  • src/wallet/scriptpubkeyman.h
  • src/wallet/wallet.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/coinjoin/**/*.{cpp,h} : CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs

Applied to files:

  • src/wallet/spend.cpp
  • src/wallet/scriptpubkeyman.h
📚 Learning: 2025-02-06T14:34:30.466Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.

Applied to files:

  • src/wallet/spend.cpp
📚 Learning: 2025-02-14T15:19:17.218Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6529
File: src/wallet/rpcwallet.cpp:3002-3003
Timestamp: 2025-02-14T15:19:17.218Z
Learning: The `GetWallet()` function calls in `src/wallet/rpcwallet.cpp` are properly validated with null checks that throw appropriate RPC errors, making additional validation unnecessary.

Applied to files:

  • src/wallet/wallet.h
  • src/wallet/wallet.cpp
  • src/wallet/rpc/addresses.cpp
📚 Learning: 2025-09-02T07:34:28.226Z
Learnt from: knst
Repo: dashpay/dash PR: 6834
File: test/functional/wallet_mnemonicbits.py:50-51
Timestamp: 2025-09-02T07:34:28.226Z
Learning: CJ (CoinJoin) descriptors with derivation path "9'/1" are intentionally inactive in descriptor wallets, while regular internal/external descriptors with different derivation paths remain active.

Applied to files:

  • src/wallet/rpc/backup.cpp
  • src/wallet/scriptpubkeyman.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/wallet/**/*.{cpp,h} : Wallet implementation must use Berkeley DB and SQLite

Applied to files:

  • src/wallet/scriptpubkeyman.h
🧬 Code graph analysis (7)
test/functional/wallet_abandonconflict.py (5)
test/functional/test_framework/test_node.py (1)
  • get_wallet_rpc (363-369)
src/wallet/test/wallet_tests.cpp (1)
  • getnewaddress (43-43)
test/functional/test_framework/test_framework.py (1)
  • sync_mempools (834-860)
test/functional/test_framework/util.py (1)
  • assert_raises_rpc_error (132-148)
src/wallet/rpc/transactions.cpp (2)
  • abandontransaction (805-844)
  • abandontransaction (805-805)
src/wallet/wallet.h (2)
src/wallet/wallet.cpp (13)
  • IsMine (1483-1487)
  • IsMine (1483-1483)
  • IsMine (1489-1493)
  • IsMine (1489-1489)
  • IsMine (1495-1503)
  • IsMine (1495-1495)
  • IsMine (1505-1512)
  • IsMine (1505-1505)
  • IsMine (1514-1525)
  • IsMine (1514-1514)
  • outpoint (1093-1093)
  • GetScriptPubKeyMans (3766-3776)
  • GetScriptPubKeyMans (3766-3766)
src/wallet/scriptpubkeyman.h (5)
  • ScriptPubKeyMan (170-233)
  • ScriptPubKeyMan (178-178)
  • bool (185-185)
  • bool (230-230)
  • bool (236-236)
src/wallet/rpc/backup.cpp (2)
src/wallet/hdchain.h (1)
  • BIP32_PURPOSE_FEATURE (145-145)
src/script/descriptor.cpp (1)
  • nullopt (656-656)
src/wallet/scriptpubkeyman.cpp (1)
src/script/descriptor.cpp (3)
  • assert (767-771)
  • dest (693-703)
  • nullopt (656-656)
src/wallet/scriptpubkeyman.h (2)
src/wallet/scriptpubkeyman.cpp (6)
  • MarkUnusedAddresses (315-350)
  • MarkUnusedAddresses (315-315)
  • MarkUnusedAddresses (2047-2073)
  • MarkUnusedAddresses (2047-2047)
  • MarkReserveKeysAsUsed (1656-1681)
  • MarkReserveKeysAsUsed (1656-1656)
src/wallet/walletdb.h (1)
  • WalletBatch (143-256)
src/wallet/wallet.cpp (1)
src/wallet/scriptpubkeyman.cpp (8)
  • IsMine (168-180)
  • IsMine (168-168)
  • IsMine (182-186)
  • IsMine (182-182)
  • IsMine (1856-1863)
  • IsMine (1856-1856)
  • IsMine (1865-1869)
  • IsMine (1865-1865)
src/wallet/rpc/addresses.cpp (1)
src/wallet/interfaces.cpp (1)
  • spk_man (216-223)
🪛 Flake8 (7.3.0)
test/functional/wallet_abandonconflict.py

[error] 79-79: block comment should start with '# '

(E265)


[error] 120-120: test for membership should be 'not in'

(E713)

🪛 Ruff (0.14.7)
test/functional/wallet_abandonconflict.py

120-120: Test for membership should be not in

Convert to not in

(E713)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: linux64_multiprocess-build / Build source
  • GitHub Check: mac-build / Build source
  • GitHub Check: arm-linux-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: win64-build / Build source
🔇 Additional comments (31)
test/functional/wallet_abandonconflict.py (3)

35-38: LGTM! Well-structured dual-wallet test setup.

The introduction of separate wallet handles for alice (sender) and bob (receiver) enables comprehensive testing of wallet conflicts from both perspectives.


177-178: LGTM! Correct wallet reload after node restart.

The explicit loadwallet("bob") call is necessary because bob is not the default wallet. After node restarts, only the default wallet (alice) is automatically loaded.


221-230: LGTM! Excellent receiver-side wallet conflict verification.

The added test coverage properly validates that wallet conflicts are correctly tracked from bob's (receiver's) perspective, ensuring both sender and receiver wallets maintain consistent conflict information.

src/wallet/rpc/backup.cpp (5)

1990-1990: Documentation accurately reflects the new sorted output behavior.


2031-2041: Well-structured intermediate type for collecting descriptor data.

The struct appropriately uses std::optional for conditionally-present fields and SecureString for sensitive mnemonic data.


2064-2065: CoinJoin detection via derivation path pattern matching.

The detection relies on finding the pattern /<BIP32_PURPOSE_FEATURE>'/<ExtCoinType>'/4'/0' in the descriptor string. While string-based matching is somewhat fragile compared to inspecting the descriptor structure directly, this approach works correctly for the current descriptor format.


2081-2083: Correct lexicographic sorting provides deterministic output.

The sorting ensures consistent ordering across calls, which is noted in the PR objectives as a non-breaking change since previous ordering was undefined.


2086-2110: Emission loop correctly outputs all descriptor fields in documented order.

Key observations:

  • Mnemonic fields are conditionally emitted only when mnemonic is non-empty (addresses past review feedback)
  • Range end is correctly converted from exclusive (range_end) to inclusive (range_end - 1) for display
  • Both next and next_index fields are emitted for backward compatibility as documented in the RPC help
src/wallet/rpc/spend.cpp (4)

358-382: FundTxDoc helper correctly centralizes common funding argument docs

The helper’s argument names and structure (conf_target, estimate_mode, solving_data.pubkeys/scripts/descriptors) line up with what FundTransaction already parses and uses, so this de-duplication keeps the RPC help consistent without changing behavior. Based on learnings, this matches the intended Bitcoin backport pattern.


569-595: Refactoring fundrawtransaction options docs to use FundTxDoc looks consistent

The Cat<std::vector<RPCArg>> composition preserves all previously documented options keys while adding the shared conf_target, estimate_mode, and solving_data docs via FundTxDoc(). This aligns the help text with the options actually consumed by FundTransaction and avoids duplication.


762-796: send() options now share the unified funding docs and remain compatible with existing logic

Reusing FundTxDoc() here correctly documents conf_target, estimate_mode, and solving_data inside the options object, matching how send() merges top‑level arguments into options before calling FundTransaction. The dual-path handling (positional vs options) is enforced by the existing conflict checks, so this is a pure help-text consolidation.


1045-1068: walletcreatefundedpsbt gains consistent docs for shared funding options

Attaching FundTxDoc() to the options object cleanly adds documentation for conf_target, estimate_mode, and solving_data that FundTransaction already understands, without touching the established mixed‑case keys. This improves help coverage while keeping the backport faithful to upstream.

src/script/sign.cpp (2)

345-356: DummySignatureChecker global reference is safe and consistent with upstream pattern

Binding DUMMY_CHECKER as const BaseSignatureChecker& to a DummySignatureChecker() temporary is OK: the temporary’s lifetime is extended to static duration, and the dynamic type remains DummySignatureChecker, so virtual dispatch works correctly. This matches the upstream dummy-checker pattern and avoids extra global objects.

Based on learnings, this looks like a faithful Bitcoin Core backport, so no local refactor is suggested.


357-385: DummySignatureCreator DER layout and globals look correct

DummySignatureCreator::CreateSig sizes vchSig as m_r_len + m_s_len + 7 and only writes up to index 6 + m_r_len + m_s_len, so no out-of-bounds access. The constructed bytes form a valid DER SEQUENCE of two INTEGERs, with the final byte used for SIGHASH_ALL. The two globals

  • DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(32, 32) and
  • DUMMY_MAXIMUM_SIGNATURE_CREATOR = DummySignatureCreator(33, 32)

are also safe: both are const BaseSignatureCreator& bound directly to temporaries, so their lifetimes are extended for the whole program, and they rely on DUMMY_CHECKER only at call time (not during static init).

Based on learnings, this aligns with the Bitcoin Core backport and should not be altered locally.

src/script/interpreter.h (1)

195-199: Const-correctness for DeferringSignatureChecker is coherent

Switching m_checker and the constructor to const BaseSignatureChecker& matches how checkers are used elsewhere (only via const methods) and aligns with VerifyScript/EvalScript signatures. Existing callers that had a non-const checker still bind fine by implicit conversion to const, so this is a safe tightening of the API.

test/functional/wallet_listtransactions.py (1)

7-16: New listtransactions functional coverage looks consistent with upstream behavior

The extra node, wallet.dat cloning, normalization helper, and invalid-parameter assertions all look correct and in line with the external-signer backports. The tests exercise keypool gap handling, cross-node wallet consistency, label persistence, and RPC argument validation without introducing Dash-specific divergences.

Also applies to: 21-25, 105-168

src/wallet/rpc/addresses.cpp (1)

509-546: Multi‑ScriptPubKeyMan handling in getaddressinfo is correct

Using GetScriptPubKeyMans(scriptPubKey) with a best‑effort selection of the first manager, then downcasting to DescriptorScriptPubKeyMan for parent_desc and reusing the same spk_man for metadata, is internally consistent and matches the intended multi‑SPKM design. No obvious correctness or lifetime issues here.

src/wallet/spend.cpp (2)

525-569: Preset input sizing now correctly supports external solving providers

The revised preset‑input loop in SelectCoins first uses wallet solving data when available, then falls back to coin_control.m_external_provider for inputs not in the wallet or not solvable by it. Aborting when input_bytes remains -1 avoids fee underestimation for unsignable presets. This matches the external signer use case without regressing existing behavior.


1166-1177: FundTransaction internal vs external input classification is well‑defined

Classifying each vin by wallet.IsMine(outPoint) and otherwise consulting findCoins to either fail with a clear error or call coinControl.SelectExternal is a clean separation between wallet‑owned and external UTXOs. It ensures external inputs have their CTxOut metadata registered for later sizing and signing, and introduces a sensible error when a referenced external UTXO cannot be found.

src/wallet/wallet.h (1)

808-818: Wallet public API extensions are coherent with multi‑SPKM design

Adding IsMine(const COutPoint&), exposing GetScriptPubKeyMans(const CScript&), and introducing IsInternalScriptPubKeyMan cleanly support the new multi‑manager and internal/external semantics used elsewhere in this PR. Signatures and lock annotations are consistent with existing implementations.

Also applies to: 1000-1002, 1068-1072

src/wallet/scriptpubkeyman.cpp (1)

2047-2073: Descriptor MarkUnusedAddresses correctly returns affected destinations

For descriptor wallets, DescriptorScriptPubKeyMan::MarkUnusedAddresses now returns a std::vector<WalletDestination> by expanding cached descriptors from next_index up to the detected used index and marking each derived destination as affected (with internal left as std::nullopt). This mirrors the legacy behavior at the descriptor level and integrates cleanly with the higher‑level wallet logic that consumes these destinations.

src/wallet/wallet.cpp (5)

1141-1157: Correct use of multi‑SPKMan MarkUnusedAddresses and address‑book inference

The new loop over GetScriptPubKeyMans(txout.scriptPubKey) and consumption of MarkUnusedAddresses results is consistent: it correctly infers internal via IsInternalScriptPubKeyMan(spk_man) when unset, skips undetermined destinations, and auto-adds external receiving addresses to the address book only when not already present. This matches the intended descriptor/external‑signer behavior while keeping DIP0009/CoinJoin managers out of the address book via the active‑SPKMan check in IsInternalScriptPubKeyMan.


1514-1525: New IsMine(const COutPoint&) helper is safe and bounded

The helper safely looks up the wallet tx and bounds‑checks outpoint.n before delegating to IsMine(const CTxOut&), and enforces cs_wallet being held via AssertLockHeld. This is a straightforward, correct addition to the ownership helpers.


2571-2580: DisplayAddress now correctly considers all matching ScriptPubKeyMans

Iterating GetScriptPubKeyMans(scriptPubKey) and selecting the first ExternalSignerScriptPubKeyMan is the right generalization from the previous single‑manager lookup. It avoids false negatives when the relevant external‑signer SPKMan isn’t the default external manager while preserving previous behavior (returning false when none apply).


3766-3776: GetScriptPubKeyMans(script) implementation is consistent with CanProvide semantics

Using a local SignatureData sigdata and selecting all managers where CanProvide(script, sigdata) is true correctly returns the set of SPKMs that can solve the script. This aligns with how other helpers (e.g. GetSolvingProvider) drive CanProvide and fits the new callers.


4000-4022: IsInternalScriptPubKeyMan correctly classifies active descriptor managers

The helper appropriately:

  • Returns std::nullopt for legacy wallets.
  • Restricts classification to active SPKMs only.
  • Requires a DescriptorScriptPubKeyMan (including external‑signer subclasses) and asserts it has an output type.
  • Determines “internal” by pointer equality with GetScriptPubKeyMan(/*internal=*/true).

This yields reliable internal flags for descriptor wallets and lets callers safely skip non‑classifiable managers (e.g. inactive CoinJoin descriptors), matching the intended behavior for WalletDestination.internal.

src/wallet/scriptpubkeyman.h (5)

150-155: WalletDestination struct is minimal and fits call‑sites

WalletDestination cleanly encapsulates the destination plus an optional internal flag. This matches how MarkUnusedAddresses implementations populate it (legacy setting fInternal, descriptors using std::nullopt) and how wallet code later infers / consumes the flag.


197-205: Base MarkUnusedAddresses signature and docs align with new usage

Changing MarkUnusedAddresses to return std::vector<WalletDestination> and documenting that it returns “all of the addresses affected” matches the implementations and the new caller in AddToWalletIfInvolvingMe. The default implementation returning {} is safe for SPKMs that don’t participate.


353-353: LegacyScriptPubKeyMan override matches contract

The override of MarkUnusedAddresses in LegacyScriptPubKeyMan correctly adopts the new signature, and (per the .cpp snippet) returns concrete WalletDestination entries tagged with keypool.fInternal, which the wallet later uses to decide whether to add addresses to the book. This is consistent and preserves legacy semantics.


499-507: MarkReserveKeysAsUsed return type and docs reflect new behavior

Updating MarkReserveKeysAsUsed to return std::vector<CKeyPool> and documenting that it returns “all affected keys” matches the provided implementation, which now exposes the consumed keypool entries so callers (like MarkUnusedAddresses) can derive all possible destinations. Signature and comment are coherent.


597-597: DescriptorScriptPubKeyMan::MarkUnusedAddresses override matches interface

The descriptor SPKMan override uses the new std::vector<WalletDestination> return type and (per the .cpp snippet) fills it with derived destinations and internal = std::nullopt, which fits the wallet‑side inference via IsInternalScriptPubKeyMan. The declaration is consistent with the base class and implementation.

Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

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

utACK d59ce85

@PastaPastaPasta PastaPastaPasta merged commit 8f71cc2 into dashpay:develop Dec 8, 2025
62 of 69 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RPC Some notable changes to RPC params/behaviour/descriptions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants