Skip to content

Conversation

@knst
Copy link
Collaborator

@knst knst commented Oct 22, 2025

Blocked by #6019

What was done?

Extra backports to improve support of External signer
This backports probably are not mandatory to make it works, but it improvements for CI, tests, and user experience.

How Has This Been Tested?

N/A

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 and others added 30 commits October 21, 2025 01:33
f75e0c1 doc: add external-signer.md (Sjors Provoost)
d4b0107 rpc: send: support external signer (Sjors Provoost)
245b445 rpc: signerdisplayaddress (Sjors Provoost)
7ebc7c0 wallet: ExternalSigner: add GetDescriptors method (Sjors Provoost)
fc5da52 wallet: add GetExternalSigner() (Sjors Provoost)
259f52c test: external_signer wallet flag is immutable (Sjors Provoost)
2655197 rpc: add external_signer option to createwallet (Sjors Provoost)
2700f09 rpc: signer: add enumeratesigners to list external signers (Sjors Provoost)
07b7c94 rpc: add external signer RPC files (Sjors Provoost)
8ce7767 wallet: add ExternalSignerScriptPubKeyMan (Sjors Provoost)
157ea7c wallet: add external_signer flag (Sjors Provoost)
f3e6ce7 test: add external signer test (Sjors Provoost)
8cf543f wallet: add -signer argument for external signer command (Sjors Provoost)
f7eb7ec test: framework: add skip_if_no_external_signer (Sjors Provoost)
87a9794 configure: add --enable-external-signer (Sjors Provoost)

Pull request description:

  Big picture overview in [this gist](https://gist.github.com/Sjors/29d06728c685e6182828c1ce9b74483d).

  This PR lets `bitcoind` call an arbitrary command `-signer=<cmd>`, e.g. a hardware wallet driver,  where it can fetch public keys, ask to display an address, and sign a transaction (using PSBT under the hood).

  It's design to work with https://github.com/bitcoin-core/HWI, which supports multiple hardware wallets. Any command with the same arguments and return values will work. It simplifies the manual procedure described [here](https://github.com/bitcoin-core/HWI/blob/master/docs/bitcoin-core-usage.md).

  Usage is documented in [doc/external-signer.md](
  https://github.com/Sjors/bitcoin/blob/2019/08/hww-box2/doc/external-signer.md), which also describes what protocol a different signer binary should conform to.

  Use `--enable-external-signer` to opt in, requires Boost::Process:

  ```
  Options used to compile and link:
    with wallet     = yes
    with gui / qt   = no
    external signer = yes
  ```

  It adds the following RPC methods:
  * `enumeratesigners`: asks <cmd> for a list of signers (e.g. devices) and their master key fingerprint
  * `signerdisplayaddress <address>`:  asks <cmd> to display an address

  It enhances the following RPC methods:
  * `createwallet`: takes an additional `external_signer` argument and fetches keys from device
  * `send`: automatically sends transaction to device and waits

  Usage TL&DR:
  * clone HWI repo somewhere and launch `bitcoind -signer=../HWI/hwi.py`
  * check if you can see your hardware device: `bitcoin-cli enumeratesigners`
  * create wallet and auto import keys `bitcoin-cli createwallet "hww" true true "" true true true`
  * display address on device: `bitcoin-cli signerdisplayaddress ...`
  * to spend, use `send` RPC and approve transaction on device

  Prerequisites:
  - [x] bitcoin#21127 load wallet flags before everything else
  - [x] bitcoin#21182 remove mostly pointless BOOST_PROCESS macro

  Potentially useful followups:
  - GUI support: bitcoin-core/gui#4
  - bumpfee support
  - (automatically) verify (a subset of) keys on the device after import, through message signing

ACKs for top commit:
  laanwj:
    re-ACK f75e0c1

Tree-SHA512: 7db8afd54762295c1424c3f01d8c587ec256a72f34bd5256e04b21832dabd5dc212be8ab975ae3b67de75259fd569a561491945750492f417111dc7b6641e77f
da30c1b wallet: fix doc typo in signer option (William Casarin)

Pull request description:

ACKs for top commit:
  0xB10C:
    ACK da30c1b
  darosior:
    ACK da30c1b

Tree-SHA512: 1f4ba501121101bbe94a18a0276df6a3592123548110ed5d1e4e1937b75c9a832bc3f0d6fa26bab69b3304526ef0548b2540d446e05a6402793321a34f508b09
…ion and typos

8b08d0f build, doc: Fix configure script output indentation and typos (Hennadii Stepanov)

Pull request description:

  This PR is follow up of bitcoin#16546, that breaks the `configure` script output indentation for gui/qt/qr lines:
  ```
  Options used to compile and link:
    external signer = no
    multiprocess    = no
    with libs       = yes
    with wallet     = yes
      with sqlite   = yes
      with bdb      = yes
      with gui / qt = yes
    with qr         = yes
    with zmq        = yes
    with test       = yes
  ...
  ```

  With this PR:
  ```
  Options used to compile and link:
    external signer = no
    multiprocess    = no
    with libs       = yes
    with wallet     = yes
      with sqlite   = yes
      with bdb      = yes
    with gui / qt   = yes
      with qr       = yes
    with zmq        = yes
    with test       = yes
  ...
  ```

  Also typos are fixed.

ACKs for top commit:
  Sjors:
    utACK 8b08d0f
  vasild:
    ACK 8b08d0f

Tree-SHA512: 46dfcfb754192dbcc080348781327d1714e2f9a696f3ed9252746b426e3afc628d84adb197ba3b8080eacaa6053ccac07e670998930ae92cef8ed713dd457c10
57ff5a4 doc: specify minimum HWI version (Sjors Provoost)
03308b2 rpc: don't require wallet for enumeratesigners (Sjors Provoost)

Pull request description:

  HWI just released 2.0. See https://github.com/bitcoin-core/HWI/releases/tag/2.0.0

  As of bitcoin#16546 we already rely on features that are in 2.0 and not in the previous 1.* releases:
  * `--chain` param

  This shouldn't be a problem, because HWI 2.0 has been released before we release v22.

  Misc improvements:
  * document that HWI 2.0 is required
  * drop wallet requirement for `enumeratesigners`

ACKs for top commit:
  achow101:
    Code Review ACK 57ff5a4

Tree-SHA512: 3fb6ba20894e52a116f89525a5f5a1f61d363ccd904e1cffd0e6d095640fc6d2edf0388cd6ae20f83bbc31e5f458255ec090b6e823798d426eba3e45b4336bf9
88d4d5f rpc: add help for enumeratesigners and walletdisplayaddress (Sjors Provoost)
b0db187 ci: use --enable-external-signer instead of --with-boost-process (Sjors Provoost)
b54b2e7 Move external signer out of wallet module (Sjors Provoost)

Pull request description:

  In addition, this PR enables external signer testing on CI.

  This PR moves the ExternalSigner class and RPC methods out of the wallet module.

  The `enumeratesigners` RPC can be used without a wallet since bitcoin#21417. With additional modifications external signers could be used without a wallet in general, e.g. via `signrawtransaction`.

  The `signerdisplayaddress` RPC is ranamed to `walletdisplayaddress` because it requires wallet context. A future `displayaddress` RPC call without wallet context could take a descriptor argument.

  This commit fixes a `rpc_help.py` failure when configured with `--disable-wallet`.

ACKs for top commit:
  ryanofsky:
    Code review ACK 88d4d5f
  fanquake:
    ACK 88d4d5f

Tree-SHA512: 3242a24e22313aed97eee32a520bfcb1c17495ba32a2b8e06a5e151e2611320e2da5ef35b572d84623af0a49a210d2f9377a2531250868d1a0ccf3e144352a97
c8f469c external_signer: remove ExternalSignerException (fanquake)
9e0b199 external_signer: use const where appropriate (fanquake)
aaa4e5a wallet: remove CWallet::GetExternalSigner() (fanquake)
06a0673 external_signer: remove ignore_errors from Enumerate() (fanquake)
8fdbb89 refactor: unify external wallet runtime errors (fanquake)
f4652bf refactor: add missing includes to external signer code (fanquake)
54569cc refactor: move all signer code inside ENABLE_EXTERNAL_SIGNER #ifdefs (fanquake)

Pull request description:

  These are a few followups after bitcoin#21467.

ACKs for top commit:
  Sjors:
    tACK c8f469c
  instagibbs:
    utACK bitcoin@c8f469c

Tree-SHA512: 3d5ac5df81680075e71e0e4a7595c520d746c3e37f016cf168c1e10da15541ebb1595aecaf2c08575636e9ff77d499644cae53180232b7049cfae0b923106e4e
…allet)

1c4b456 gui: send using external signer (Sjors Provoost)
24815c6 gui: wallet creation detects external signer (Sjors Provoost)
3f845ea node: add externalSigners to interface (Sjors Provoost)
62ac119 gui: display address on external signer (Sjors Provoost)
450cb40 wallet: add displayAddress to interface (Sjors Provoost)
eef8d64 gui: create wallet with external signer (Sjors Provoost)
6cdbc83 gui: add external signer path to options dialog (Sjors Provoost)

Pull request description:

  Big picture overview in [this gist](https://gist.github.com/Sjors/29d06728c685e6182828c1ce9b74483d).

  This PR adds GUI support for external signers, based on the since merged bitcoin#16546 (RPC).

  The UX isn't amazing - especially the blocking calls - but it works.

  First we adds a GUI setting for the signer script (e.g. path to HWI):

  <img width="625" alt="Schermafbeelding 2019-08-05 om 19 32 59" src="https://user-images.githubusercontent.com/10217/62483415-e1ff1680-b7b7-11e9-97ca-8d2ce54ca1cb.png">

  Then we add an external signer checkbox to the wallet creation dialog:

  <img width="374" alt="Schermafbeelding 2019-11-07 om 19 17 23" src="https://user-images.githubusercontent.com/10217/68416387-b57ee000-0194-11ea-9730-127d60273008.png">

  It's checked by default if HWI detects a device. It also grabs the name. It then creates a fresh wallet and imports the keys.

  You can verify an address on the device (blocking...):
  <img width="673" alt="Schermafbeelding 2019-08-05 om 19 29 22" src="https://user-images.githubusercontent.com/10217/62483560-43bf8080-b7b8-11e9-9902-8a036116dc4b.png">

  Sending, including coin selection, Just Works(tm) as long the device is present.

  ~External signer support is enabled by default when the GUI is configured and Boost::Process is present.~

  External signer support remains disabled by default, see bitcoin#21935.

ACKs for top commit:
  achow101:
    Code Review ACK 1c4b456
  hebasto:
    ACK 1c4b456, tested on Linux Mint 20.1 (Qt 5.12.8) with HWW `2.0.2-rc.1`.
  promag:
    Tested ACK 1c4b456 but rebased with e033ca1, with HWI 2.0.2, with Nano S and Nano X.
  meshcollider:
    re-code-review ACK 1c4b456

Tree-SHA512: 3503113c5c69d40adb6ce364d8e7cae23ce82d032a00474ba9aeb6202eb70f496ef4a6bf2e623e5171e524ad31ade7941a4e0e89539c64518aaec74f4562d86b
…n unsupported

e60cd26 Do not load external signers wallets when unsupported (Andrew Chow)

Pull request description:

  When external signer support is not compiled, do not load external signer wallets.

  Alternative to bitcoin#22168.

ACKs for top commit:
  promag:
    Tested ACK e60cd26.
  meshcollider:
    Code review ACK e60cd26

Tree-SHA512: aed2d0038f448c2f89c6b48f412b106e63c9ed20e748e69aae21fb58c33fc7e4fa73375a52372c73788669eb2b968a8da6b022c65658fa4484f5bbcf205b1b15
…e #ifdef

2f5bdcb gui: misc external signer fixes and translation hints (Sjors Provoost)
d672404 refactor: make ExternalSigner NetworkArg() and m_chain private (Sjors Provoost)
4455145 refactor: reduce #ifdef ENABLE_EXTERNAL_SIGNER usage (Sjors Provoost)
5be90c9 build: enable external signer by default (Sjors Provoost)
7d94530 refactor: clean up external_signer.h includes (Sjors Provoost)
fc0eca3 fuzz: fix fuzz binary linking order (Sjors Provoost)

Pull request description:

  This follows the introduction of GUI support in bitcoin-core/gui#4

  I don't think we should expect GUI users to self compile. This also enables external signer support by default for RPC users.

  In addition this PR reduces the number of `#ifdef ENABLE_EXTERNAL_SIGNER`, which also fixes bitcoin#21919. When compiled with `--disable-external-signer` such wallets can't be created in RPC or GUI, but they can be loaded. Attempting any action that calls HWI will trigger an error.

  Side-note: this PR may or may not (currently) break CI for the GUI repository, as explained here: bitcoin-core/gui#4 (comment)

ACKs for top commit:
  achow101:
    ACK 2f5bdcb
  hebasto:
    re-ACK 2f5bdcb

Tree-SHA512: 1b71c5a8bea2be077ee9fa33a01130c957a0cf90951d4b7b04d3d0ef826bb77e474c3963abddfef2e2c1ea99d9c72cd2302d1eb9b5fcb7ba0bd2a625f006aa05
…ocess

67669ab build: Fix Boost Process compatibility with mingw-w64 compiler (Hennadii Stepanov)

Pull request description:

  On master (9c3751a) the cross build for Win64 is broken if configured with `--enable-external-signer`:
  ```
  ...
    CXX      crypto/libbitcoin_crypto_base_a-chacha_poly_aead.o
  In file included from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handles.hpp:11,
                   from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/used_handles.hpp:17,
                   from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/async_in.hpp:20,
                   from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/async.hpp:49,
                   from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process.hpp:23,
                   from util/system.cpp:9:
  /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:208:51: error: expected ‘)’ before ‘*’ token
    208 | typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_system_query_information_p )(
        |                                    ~              ^~
        |                                                   )
  /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:223:51: error: expected ‘)’ before ‘*’ token
    223 | typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_query_object_p )(
        |                                    ~              ^~
        |                                                   )
  /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp: In function ‘boost::winapi::NTSTATUS_ boost::process::detail::windows::workaround::nt_system_query_information(boost::process::detail::windows::workaround::SYSTEM_INFORMATION_CLASS_, void*, boost::winapi::ULONG_, boost::winapi::PULONG_)’:
  /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:239:12: error: ‘nt_system_query_information_p’ does not name a type; did you mean ‘nt_system_query_information’?
    239 |     static nt_system_query_information_p f = reinterpret_cast<nt_system_query_information_p>(::boost::winapi::get_proc_address(h, "NtQuerySystemInformation"));
        |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        |            nt_system_query_information
  In file included from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handles.hpp:11,
                   from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/used_handles.hpp:17,
                   from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/async_in.hpp:20,
                   from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/async.hpp:49,
                   from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process.hpp:23,
                   from util/system.cpp:9:
  /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:241:14: error: ‘f’ was not declared in this scope
    241 |     return (*f)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
        |              ^
  /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp: In function ‘boost::winapi::BOOL_ boost::process::detail::windows::workaround::nt_query_object(boost::winapi::HANDLE_, boost::process::detail::windows::workaround::OBJECT_INFORMATION_CLASS_, void*, boost::winapi::ULONG_, boost::winapi::PULONG_)’:
  /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:253:12: error: ‘nt_query_object_p’ does not name a type; did you mean ‘nt_query_object’?
    253 |     static nt_query_object_p f = reinterpret_cast<nt_query_object_p>(::boost::winapi::get_proc_address(h, "NtQueryObject"));
        |            ^~~~~~~~~~~~~~~~~
        |            nt_query_object
  /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:255:14: error: ‘f’ was not declared in this scope
    255 |     return (*f)(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
        |              ^
  make[2]: *** [Makefile:9906: util/libbitcoin_util_a-system.o] Error 1
  make[2]: *** Waiting for unfinished jobs....
    CXX      crypto/libbitcoin_crypto_base_a-chacha20.o
  make[2]: Leaving directory '/home/hebasto/GitHub/bitcoin/src'
  make[1]: *** [Makefile:16141: all-recursive] Error 1
  make[1]: Leaving directory '/home/hebasto/GitHub/bitcoin/src'
  make: *** [Makefile:820: all-recursive] Error 1
  ```

  The upstream bug: boostorg/process#96
  Also see: https://stackoverflow.com/a/59338759

  bitcoin#22348 (comment):
  > [This commit](boostorg/process@7fc41b2), containing the `__kernel_entry` [SAL annotations](https://docs.microsoft.com/en-us/cpp/code-quality/using-sal-annotations-to-reduce-c-cpp-code-defects?view=msvc-160) was included in Boost Process as part of the `1.71.0` release, which broke support for compiling with mingw-w64 because it doesn't define the `__kernel_entry` SAL annotation (but it does define some others, i.e see [`sal.h`](https://github.com/mirror/mingw-w64/blob/master/mingw-w64-headers/include/sal.h)).
  >
  > A [commit was made](boostorg/process@d7a721e) to remove the annotations, however, it hasn't made it into either of the two Boost releases that have happened since (1.75.0 & 1.76.0). Meaning that this is currently needed for all versions of Boost process from 1.71.0 onwards.

ACKs for top commit:
  fanquake:
    ACK 67669ab - thanks for updating this.

Tree-SHA512: 5931ca1fb77ce38c042cf5a7556add024ea2386c208bf26c792a8ca4a771d97fac9802c32fa8aa2e3de1ad35f3362d8c066f0a83ee675859d226c602fd0bcf93
…abled without signers

a9b9ca8 gui: ensure external signer option remains disabled without signers (Andrew Chow)

Pull request description:

  When no external signers are available, the option to enable external signers should always be disabled. However the encrypt wallet checkbox can erroneously re-enable the external signer checkbox. To avoid this, CreateWalletDialog now stores whether signers were available during setSigners so that future calls to external_signer_checkbox->setEnabled can account for whether signers are available.

  Fixes dashpay#395

ACKs for top commit:
  hebasto:
    ACK a9b9ca8, tested on Linux Mint 20.2 (Qt 5.12.8).
  Sjors:
    tACK a9b9ca8
  jarolrod:
    ACK a9b9ca8

Tree-SHA512: 98951bcadc23fce99a66ea2d367c44360989e888c253845a767e1f7085c594562d0f099de4130f4a078c5072aa7806294097d976ee6407291f3d3c5a4a608b44
…gic (stop on first match)

d047ed7 external_signer: improve fingerprint matching logic (stop on first match) (Sebastian Falbesoner)

Pull request description:

  The fingerprint matching logic in `ExternalSigner::SignTransaction` currently always iterates all inputs of a PSBT, even after a match has already been found. I guess the reason for that is not that it was not thought of, but rather the fact that breaking out of a nested loop is simply not possible (at least not without adding ugly constructs like gotos or extra state variables).
  This PR fixes this by using `std::any_of` from C++'s standard library, see http://www.cplusplus.com/reference/algorithm/any_of/

ACKs for top commit:
  lsilva01:
    Code Review ACK bitcoin@d047ed7
  Sjors:
    utACK d047ed7
  Zero-1729:
    crACK d047ed7
  mjdietzx:
    Code review ACK d047ed7
  hebasto:
    ACK d047ed7, I have reviewed the code and it looks OK, I agree it can be merged.

Tree-SHA512: 447e7c0c6a5b5549a2c09d52e55ba4146302c1a06e4d96de11f6945d09f98c89129cba221202dff7e0718e01a83dd173b9f19b1f02b6be228978f3f6e35d8096
a032fa3 multiprocess: add interfaces::ExternalSigner class (Russell Yanofsky)

Pull request description:

  Add `interfaces::ExternalSigner` class to let signer objects be passed between processes and let signer code run in the original process where the object was created.

  ---

  This PR is part of the [process separation project](https://github.com/bitcoin/bitcoin/projects/10).

ACKs for top commit:
  laanwj:
    Concept and code review ACK a032fa3
  hebasto:
    re-ACK a032fa3

Tree-SHA512: 99a729fb3a64d010e142cc778a9f1f358e58345b77faaf2664de7d2277715d59df3352326e8f0f2a6628038670eaa4556310a549079fb28af6d2eeb05aea1460
5493e92 Check descriptors returned by external signers (sstone)

Pull request description:

  Check that descriptors returned by external signers have been parsed properly when creating a new wallet.
  See bitcoin#23627 for context.

  The problem is that parsing an invalid descriptor will return `null` which is not checked for in `CWallet::SetupDescriptorScriptPubKeyMans()`.

  I'm not completely sure what the best fix is since there several strategies for dealing with errors in the current codebase but the proposed fix is very simple and consistent with other validation checks in `CWallet::SetupDescriptorScriptPubKeyMans()`.

ACKs for top commit:
  jamesob:
    Code review ACK bitcoin@5493e92
  achow101:
    ACK 5493e92

Tree-SHA512: 63259f4aa519405a86c554b6813efdb741314bdaa18bf005b70ea8bb92a27abc6e2b65f7c584641dc257fc78a6499f42b51b5310c243e611c4663430dccf3d04
b75f4c8 RPC: Return external_signer in getwalletinfo (Kristaps Kaupe)

Pull request description:

  Add `external_signer` to the result object of `getwalletinfo` RPC which indicates whether `WALLET_FLAG_EXTERNAL_SIGNER` flag is set for the wallet.

ACKs for top commit:
  S3RK:
    utACK b75f4c8
  achow101:
    ACK b75f4c8
  prayank23:
    utACK bitcoin@b75f4c8
  brunoerg:
    utACK b75f4c8

Tree-SHA512: 066ccb97541fd4dc3d9728834645db714a3c8c93ccf29142811af4d79cfb9440a97bbb6c845434a909bc6e1775ef3737fcbb368c1f0582bc63973f6deb17a45f
…or message

7f3a6a9 wallet: Add external-signer-support specific error message (Hennadii Stepanov)

Pull request description:

  On master (5f44c5c) an attempt to load an external signer wallet using Bitcoin Core compiled without external signer support fails with the following log messages:
  ```
  2022-02-20T19:01:11Z [qt-walletctrl] Using SQLite Version 3.31.1
  2022-02-20T19:01:11Z [qt-walletctrl] Using wallet /home/hebasto/.bitcoin/testnet3/wallets/coldcard-0220
  2022-02-20T19:01:11Z [qt-walletctrl] init message: Loading wallet…
  2022-02-20T19:01:11Z [qt-walletctrl] [coldcard-0220] Error: External signer wallet being loaded without external signer support compiled
  2022-02-20T19:01:11Z [qt-walletctrl] [coldcard-0220] Releasing wallet
  ```

  While log messages are good, a message in the GUI window is completely misleading:

  ![Screenshot from 2022-02-20 20-43-46](https://user-images.githubusercontent.com/32963518/154859854-b87032e0-c428-4e11-8009-39e38200482c.png)

  This PR fixes this issue:

  ![Screenshot from 2022-02-20 21-01-18](https://user-images.githubusercontent.com/32963518/154859868-e3a2c89d-4f0f-424e-96cb-7accaa48acc0.png)

ACKs for top commit:
  achow101:
    ACK 7f3a6a9
  kristapsk:
    ACK 7f3a6a9
  brunoerg:
    crACK 7f3a6a9

Tree-SHA512: a4842751c0ca8a37ccc3ea00503678f6b712a7f53d6cbdc07ce02dcb85ca8a94890d1c2da20307be043faa347747abeba29185c88ba12edd5253bfca56531585
…rnalSigner::SignTransaction

2a22f03 parsing external signer master fingerprint string as bytes instead of caring for lower/upper case in ExternalSigner::SignTransaction (avirgovi)

Pull request description:

  Some external signers scripts may provide master fingerprint in uppercase format. In that case core will fail with `Signer fingerprint 00000000 does not match any of the inputs` as it only works with lowercase format. Even if the fingerprints match, yet one is lowercase the other uppercase.

  ExternalSigner::SignTransaction is the only place where it is needed IMO, as changing it in other places may break the communication with the external signer (i.e. enumerating with lowercase may not find the device).

ACKs for top commit:
  achow101:
    ACK 2a22f03
  theStack:
    Code-review ACK 2a22f03
  Sjors:
    utACK 2a22f03

Tree-SHA512: f3d84b83fb0b5e06c405eaf9bf20a2fa864bf4172fd4de113b80b9b9a525a76f2f8cf63031b480358b3a7666023a2aef131fb89ff50448c66df3ed541da10f99
…gner` configure option

8df063e build: Fix help string for `--enable-external-signer` configure option (Hennadii Stepanov)

Pull request description:

  This PR is a follow up of bitcoin#24065 and fixes the help string according to the actual default value https://github.com/bitcoin/bitcoin/blob/816ca01650f4cc66a61ac2f9b0f8b74cd9cd0cf8/configure.ac#L324-L327

ACKs for top commit:
  kristapsk:
    cr utACK 8df063e
  jarolrod:
    ACK 8df063e

Tree-SHA512: ad3f457a53c9238ddd8ded9efd1224e564e6cb9da8b7ff7733a11e32a7daad5c0f6c6223509218f44944a874470cb0d2447897662eaf4e78c763b30785717c50
…ationDialog

742918c qt: hide Create Unsigned button behind an expert mode option (Andrew Chow)
5c3b800 qt: Add Create Unsigned button to SendConfirmationDialog (Andrew Chow)

Pull request description:

  Instead of having different buttons or changing button behavior for making a PSBT, just have SendConfirmationDialog return whether the user wants a PSBT or a broadcasted transaction. Since this dialog is used by both the bumpFeeAction and the SendCoinsDialog, changes to both to support the different behavior is needed. They will check the return value of the SendConfirmationDialog for whether a PSBT needs to be created instead of checking whether private keys are disabled.

  Strings used in this dialog are being slightly modified to work with both private keys enabled and disabled wallets.

  Moved from bitcoin#18789

ACKs for top commit:
  jarolrod:
    ACK 742918c
  ryanofsky:
    Code review ACK 742918c. Just suggested changes since last review. Looks great!
  hebasto:
    ACK 742918c, tested on Linux Mint 20.2 (Qt 5.12.8).

Tree-SHA512: dd29f4364c7b4f15befe8fe63257b26187918786b005e0f8336183270b1a162680b93f6ced60f0285c6e607c084cc0d24950fc68a8f9c056521ede614041be66
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
…_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
… 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
knst and others added 18 commits October 23, 2025 01:01
…igning on Windows

e2ab9f8 build: disable external signer on Windows (fanquake)

Pull request description:

  This change explicitly disables support for external signing when targeting Windows and OpenBSD. The driver for this is that Boost Process uses boost::filesystem internally, when targeting Windows, which gets in the way of removing our usage of it (bitcoin#20744). While we could adjust bitcoin#20744 to still link against the Boost libs when building for Windows, that would be disappointing, as we wouldn't have cleanly removed the Boost usage we're trying too (including the build infrastructure), and, we'd be in a position where we would be building releases differently depending on the platform, which is something I want to avoid.

  After discussion with Sjors, Achow and Hebasto, this seemed like a reasonable step to move bitcoin#20744 forward (as-is). Note that support for external signing ([while already being experimental](https://github.com/bitcoin/bitcoin/blob/master/doc/external-signer.md#example-usage)), could be considered even more experimental on Windows. Also, oddly, we have external-signing [explicitly disabled in our Windows (cross-compile) CI](https://github.com/bitcoin/bitcoin/blob/807169e10b4a18324356ed6ee4d69587b96a7c70/ci/test/00_setup_env_win64.sh#L16), it's not clear why this is the case, as, if it's a feature being built into releases, it should be being built and tested in the CI which is most-like the release process.

  There is an [issue open upstream](boostorg/process#207), in regards to migrating Boost Process to std::filesystem, or having an option to use it. However there hasn't been much discussion since it was opened ~9 months ago. There is another related issue here: klemens-morgenstern/boost-process#164.

  Resolves bitcoin#24036.

ACKs for top commit:
  Sjors:
    utACK e2ab9f8
  achow101:
    ACK e2ab9f8
  kallewoof:
    utACK e2ab9f8
  hebasto:
    ACK e2ab9f8, tested on Linux Mint 20.2 (x86_64).

Tree-SHA512: 36fcfc0e1a008a8271dc76b8e12e93d3e1d1e528bf668e95a559e9f6fd7d5f031bd7a6a6bc8b9fa9d057b2cd56f9ec8838c7f74e87899bf9a6aeb787afbd112c
abc057c build: Add Boost.Process usage check (Hennadii Stepanov)

Pull request description:

  This PR adds a check that Boost.Process can be used without linking any libraries (header-only).
  Disable the functionality if that is not the case.

  Fixes bitcoin#24314.

ACKs for top commit:
  fanquake:
    ACK abc057c

Tree-SHA512: ed2a32b1f751ec6f88cc7220766edd4cdab93c1d0515c892aa3094ee8d5b13ef569830d6e7a7a00c0197b117585dc526d00d943cc99a1f8c8a66ac4e20fe2061
…d older

774323e ci: Force `--enable-external-signer` to prevent future regressions (Hennadii Stepanov)
6997885 build: Fix Boost.Process check for Boost 1.73 and older (Hennadii Stepanov)
2199ef7 build: Fix a non-portable use of `test` (Hennadii Stepanov)
d436c48 build, refactor: Replace tabs with spaces (Hennadii Stepanov)

Pull request description:

  On master (5f44c5c) Boost.Process check false fails without the `-lpthread` flag.

  ```
  $ grep -C 2 pthread_detach config.log
  /usr/bin/ld: /tmp/cczCQfQv.o: in function `boost::asio::detail::posix_global_impl<boost::asio::system_context>::~posix_global_impl()':
  conftest.cpp:(.text._ZN5boost4asio6detail17posix_global_implINS0_14system_contextEED2Ev[_ZN5boost4asio6detail17posix_global_implINS0_14system_contextEED5Ev]+0xa3): undefined reference to `pthread_join'
  /usr/bin/ld: conftest.cpp:(.text._ZN5boost4asio6detail17posix_global_implINS0_14system_contextEED2Ev[_ZN5boost4asio6detail17posix_global_implINS0_14system_contextEED5Ev]+0xc4): undefined reference to `pthread_detach'
  collect2: error: ld returned 1 exit status
  configure:26674: $? = 1
  ```

  Not required for Boost 1.74+.

ACKs for top commit:
  laanwj:
    Code review ACK 774323e, is a bugfix/workaround, seems fine to merge last minute for 23.0.

Tree-SHA512: 2a9d4b67fd8910e107af972d8c223286b7c933bc310616f86c8b6d8c903438916980fc76bd7e37f2698f6ce5361dc706cbf2933d1ac2c081bcabe1b83ca7d6b6
1d4157a build: Fix Boost.Process detection on macOS arm64 (Hennadii Stepanov)

Pull request description:

  Could be tested as follows:
  ```
  % brew install [email protected]
  % ./autogen.sh
  % ./configure --with-boost='/opt/homebrew/opt/[email protected]'
  ```

ACKs for top commit:
  promag:
    Tested ACK on 1d4157a with boost 1.76 on macOS arm64. bitcoin#24523 is required for boost 1.78.

Tree-SHA512: 7abd39a78e970ecc051e53b5923dfc31d3f0576cf4ff7fcfb9c8708c857c46a7a595ec36238def83f41158970eeee209980da4b8b70f0ff68f940a38ac9a0471
…igner

2efdfb8 gui: restore Send for external signer (Sjors Provoost)
4b5a6cd refactor: helper function signWithExternalSigner() (Sjors Provoost)
026b5b4 move-only: helper function to present PSBT (Sjors Provoost)

Pull request description:

  Fixes dashpay#551

  For the simplest use case of a wallet with one external signer and "PSBT Controls" disabled in settings (the default), the send dialog will behave the same as when using a wallet with private keys. I.e. there's no "Create Unsigned" button.

  When PSBT controls are turned on, you can now actually make a PSBT with signing it; before this PR that button would trigger a sign event and would broadcast the transaction.

  In case a multisig, the Send button will sign on the device, and then fall back to presenting a PSBT (same behavior as before dashpay#441).

  This PR starts with two refactoring commits to move some stuff into a helper function for improved readability in general, and to make the main commit easier to review.

ACKs for top commit:
  jonatack:
    utACK 2efdfb8 diff review since my last review, code re-review, rebased to current master, verified clean debug build of each commit
  luke-jr:
    utACK 2efdfb8

Tree-SHA512: e8731a0ef9e87564b2676c7b022b742d9621bba964c19dba9fd9f6961eb608737a9e1a22c0a3c8b2f2f6d583bba067606ee8392422e82082deefb20ea7b88c7c
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
532c64a build: Fix Boost.Process test for Boost 1.78 (Hennadii Stepanov)

Pull request description:

  Rebased bitcoin#24415 with Luke's suggestion.

  Fixes bitcoin#24413.

ACKs for top commit:
  hebasto:
    ACK 532c64a, tested on Mac mini (M1, 2020) + macOS Monterey 12.3 (21E230).

Tree-SHA512: 74f779695f6bbc45a2b7341a1402f747cc0d433d74825c7196cb9f156db0c0299895365f01665bd0bff12a8ebb5ea33a29b9a52f5eac0007ec35d1dca6544705
multiple conflicts in wallet/rpc/spend - haven't resolved yet

bb84b71 add tests for no recipient and using send_max while inputs are specified (ishaanam)
49090ec Add sendall RPC née sweep (Murch)
902793c Extract FinishTransaction from send() (Murch)
6d2208a Extract interpretation of fee estimation arguments (Murch)
a31d75e Elaborate error messages for outdated options (Murch)
35ed094 Extract prevention of outdated option names (Murch)

Pull request description:

  Add sendall RPC née sweep

  _Motivation_
  Currently, the wallet uses a fSubtractFeeAmount (SFFO) flag on the
  recipients objects for all forms of sending calls. According to the
  commit discussion, this flag was chiefly introduced to permit sweeping
  without manually calculating the fees of transactions. However, the flag
  leads to unintuitive behavior and makes it more complicated to test
  many wallet RPCs exhaustively. We proposed to introduce a dedicated
  `sendall` RPC with the intention to cover this functionality.

  Since the proposal, it was discovered in further discussion that our
  proposed `sendall` rpc and SFFO have subtly different scopes of
  operation.
  • sendall:
    Use _given UTXOs_ to pay a destination the remainder after fees.
  • SFFO:
    Use a _given budget_ to pay an address the remainder after fees.

  While `sendall` will simplify cases of spending a given set of
  UTXOs such as paying the value from one or more specific UTXOs, emptying
  a wallet, or burning dust, we realized that there are some cases in
  which SFFO is used to pay other parties from a limited budget,
  which can often lead to the creation of change outputs. This cannot be
  easily replicated using `sendall` as it would require manual
  computation of the appropriate change amount.

  As such, sendall cannot replace all uses of SFFO, but it still has a
  different use case and will aid in simplifying some wallet calls and
  numerous wallet tests.

  _Sendall call details_
  The proposed sendall call builds a transaction from a specific
  subset of the wallet's UTXO pool (by default all of them) and assigns
  the funds to one or more receivers. Receivers can either be specified
  with a given amount or receive an equal share of the remaining
  unassigned funds. At least one recipient must be provided without
  assigned amount to collect the remainder. The `sendall` call will
  never create change. The call has a `send_max` option that changes the
  default behavior of spending all UTXOs ("no UTXO left behind"), to
  maximizing the output amount of the transaction by skipping uneconomic
  UTXOs. The `send_max` option is incompatible with providing a specific
  set of inputs.

  ---
  Edit: Replaced OP with latest commit message to reflect my updated motivation of the proposal.

ACKs for top commit:
  achow101:
    re-ACK bb84b71

Tree-SHA512: 20aaf75d268cb4b144f5d6437d33ec7b5f989256b3daeeb768ae1e7f39dc6b962af8223c5cb42ecc72dc38cecd921c53c077bc0ec300b994e902412213dd2cc3
This reverts commit f6743de2ac690bf7025ab62ff3469ea276ec523c.
…n build-osx.md

57f3f5c doc: s/Compiler/Dependency in dependencies.md (fanquake)
bf84677 doc: cleanup wallet docs in build-osx.md (fanquake)

Pull request description:

  Re-order legacy and descriptor wallet section.
  Installing sqlite isn't required (the version pre-installed on macOS is just as good as what will be installed via `brew`).
  Remove prelude that pointlessly repeats the same info.

  Basically the macOS version of bitcoin#23446.

  Includes a small fixup from bitcoin#23565.

ACKs for top commit:
  RandyMcMillan:
    ACK 57f3f5c
  hebasto:
    ACK 57f3f5c, I have reviewed the changes and they look OK, I agree they can be merged.

Tree-SHA512: a1ca5f73aa4f4f56de747fd9669bce572c1d7d23925afb47b5d963314df1738762ea26428c040e9c706d288eb7e775227d2387a322cda065885b89c6a619314f
ba9a8e6 test: Drop unused boost workaround (Hennadii Stepanov)

Pull request description:

  This PR is a follow up of bitcoin#24065 and removes the workaround which has already been removed in other [places](https://github.com/bitcoin/bitcoin/pull/24065/files#diff-19427b0dd1a791adc728c82e88f267751ba4f1c751e19262cac03cccd2822216).

  Moreover, this workaround won't be required even if bitcoin#25696 is ever merged.

ACKs for top commit:
  fanquake:
    ACK ba9a8e6

Tree-SHA512: db19fc1550252d7a82a08f388ff6078c78452365e74b41e7bc36cbbc4d0fed9342636e8f2371bb8e78c9d11ee721d6363bcc21d11787f3aac967a6c4a9cc346f
…alSignerImpl members public, rm temporaries, simplify

BACKPORT NOTE:
do to it probably later not sure when ; due to difference in chainman()
4bedfd7 refactor: remove unneeded temporaries in node/interfaces, simplify code (Jon Atack)
b27ba16 refactor: make all NodeImpl/ChainImpl/ExternalSignerImpl members public (Jon Atack)

Pull request description:

  - Make all `NodeImpl`, `ChainImpl` and `ExternalSignerImpl` class members `public` (and document why), to be consistent in all the `*Impl` classes in `src/node/interfaces.cpp` and `src/wallet/interfaces.cpp` and to help future reviewers and contributors.

  - Remove unneeded temporaries in `NodeImpl` and `ChainImpl` methods in `src/node/interfaces.cpp` and simplify, to make the code easier to read and understand and to improve performance by avoiding unnecessary move operations.

ACKs for top commit:
  ryanofsky:
    Code review ACK 4bedfd7. Changes since last review, applying suggested style & simplifiying first commit. Also avoiding another lock in second commit.

Tree-SHA512: 112f7cad5e2838c94c5b79d61328f42fe75fdb97f401ab49eccf696fc2c6a8a0c0ee55ec974c0602acf7423f78bb82e90eb8a0cc531e1d3347f73b7c83685504
…l/ExternalSignerImpl members public, rm temporaries, simplify"

This reverts commit 659f01625868547b4c7614436908ba7aa81a39d9.
…e found

292b1a3 GetExternalSigner(): fail if multiple signers are found (amadeuszpawlik)

Pull request description:

  If there are multiple external signers, `GetExternalSigner()` will
  just pick the first one in the list. If the user has two or more
  hardware wallets connected at the same time, he might not notice this.

  This PR adds a check and fails with suitable message, forcing the user to disconnect all but one external signer, so that there is no ambiguity as to which external signer was used.

ACKs for top commit:
  Sjors:
    tACK 292b1a3
  achow101:
    ACK 292b1a3

Tree-SHA512: e2a41d3eecc607d4f94e708614bed0f3545f7abba85f300c5a5f0d3d17d72c815259734accc5ca370953eacd290f27894ba2c18016f5e9584cd50fa1ec2fbb0b
…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
1a0d8e1 build: Re-enable external signer on Windows (Hennadii Stepanov)
989451d configure: Detect compatibility of Boost.Process rather than hardcode non-Windows (Luke Dashjr)

Pull request description:

  As boostorg/process#207 has been resolved, it is possible now to re-enable external signer on Windows when cross-compiling.

  Guix build hashes:
  ```
  78f69ea7e0dbc8338981a92c0352220ccd7c2272d8cbff6a3b082a1412a935c5  guix-build-1a0d8e178c7b/output/aarch64-linux-gnu/SHA256SUMS.part
  ee17456ec818ddf5a175182508966e622573ccb518807cca43a40fa1dceda092  guix-build-1a0d8e178c7b/output/aarch64-linux-gnu/bitcoin-1a0d8e178c7b-aarch64-linux-gnu-debug.tar.gz
  5080551bde379c746cc67b10429aef33b9f9e49d2d4e21ee1c3bfd9c1c845d46  guix-build-1a0d8e178c7b/output/aarch64-linux-gnu/bitcoin-1a0d8e178c7b-aarch64-linux-gnu.tar.gz
  dfab220ce76a40bf7dcf07aab352a616a91b516503639455fe7e1b137bad3e85  guix-build-1a0d8e178c7b/output/arm-linux-gnueabihf/SHA256SUMS.part
  516ceb822571a8bd88fe107dca434ef596b1e4328ccbda1d51e1d482d3050396  guix-build-1a0d8e178c7b/output/arm-linux-gnueabihf/bitcoin-1a0d8e178c7b-arm-linux-gnueabihf-debug.tar.gz
  21325380638f817107c203b9a1aedb808d1a4a2b4041493753ca4cbf19aa4f2c  guix-build-1a0d8e178c7b/output/arm-linux-gnueabihf/bitcoin-1a0d8e178c7b-arm-linux-gnueabihf.tar.gz
  cf48ed78fcfceaeb3610ccf22326d735a129dcbf9d50b557b3de359169aefdfd  guix-build-1a0d8e178c7b/output/arm64-apple-darwin/SHA256SUMS.part
  d4d51e136148bac6a20bb3adb402c499967647736acb420bfdeb71603aba57da  guix-build-1a0d8e178c7b/output/arm64-apple-darwin/bitcoin-1a0d8e178c7b-arm64-apple-darwin-unsigned.dmg
  95bb62d24f860e08a392ddb74d5860ccf27e8baa183e6749af877d26a3bd6b0b  guix-build-1a0d8e178c7b/output/arm64-apple-darwin/bitcoin-1a0d8e178c7b-arm64-apple-darwin-unsigned.tar.gz
  68da4c92f37bb802df37141af194f47c16da1d84f77a0fbb1016013ae0338502  guix-build-1a0d8e178c7b/output/arm64-apple-darwin/bitcoin-1a0d8e178c7b-arm64-apple-darwin.tar.gz
  6704e38c2d3f11321403797598d05f062648fec6f2d76900ba250dab481e29da  guix-build-1a0d8e178c7b/output/dist-archive/bitcoin-1a0d8e178c7b.tar.gz
  64b936bc90d1e01fe8f276511edc9bb945dcebe70332aa37d3a786348443b8e7  guix-build-1a0d8e178c7b/output/powerpc64-linux-gnu/SHA256SUMS.part
  3d03532e54b6e42498ea240c86b8567e94fd462f56087b869c3d6f09e2dde878  guix-build-1a0d8e178c7b/output/powerpc64-linux-gnu/bitcoin-1a0d8e178c7b-powerpc64-linux-gnu-debug.tar.gz
  c5843d79a58b0a864fe723458dab4eee54ad11f4b1f7960975b086eeedc0d541  guix-build-1a0d8e178c7b/output/powerpc64-linux-gnu/bitcoin-1a0d8e178c7b-powerpc64-linux-gnu.tar.gz
  f861ff519bd5e3d6d5ce1646ee0a06bcef1288ddb804a4a600e4dbfe5d5be521  guix-build-1a0d8e178c7b/output/powerpc64le-linux-gnu/SHA256SUMS.part
  5f477da21980dbcf9696081903dc1ba8a3f79ce3579641d208e69a6f598c8eb9  guix-build-1a0d8e178c7b/output/powerpc64le-linux-gnu/bitcoin-1a0d8e178c7b-powerpc64le-linux-gnu-debug.tar.gz
  b3757b11c614136934158acea5139e8abd0c5c9cdfda72ae44db436f21716b33  guix-build-1a0d8e178c7b/output/powerpc64le-linux-gnu/bitcoin-1a0d8e178c7b-powerpc64le-linux-gnu.tar.gz
  1c21bdb17fe3436e685e88c62423e630fe2b3c41dd00025a99fd80d97817ac2f  guix-build-1a0d8e178c7b/output/riscv64-linux-gnu/SHA256SUMS.part
  f36ae98473f086ae8f0dc66223b5ec407d57dc4d8d45ae284401520ff5c0b273  guix-build-1a0d8e178c7b/output/riscv64-linux-gnu/bitcoin-1a0d8e178c7b-riscv64-linux-gnu-debug.tar.gz
  1603e4d0e869eb47a1dc2d26b67772d0016d90f7ba5e50d2009365cc02cb8169  guix-build-1a0d8e178c7b/output/riscv64-linux-gnu/bitcoin-1a0d8e178c7b-riscv64-linux-gnu.tar.gz
  f86ef652102f022827b70477bffa0a44008c6300cf62ca7b3595146cf2ed91ba  guix-build-1a0d8e178c7b/output/x86_64-apple-darwin/SHA256SUMS.part
  f84d435d8e4709bf29bc7ac7ed8dc6b8af4077cef05e520b468b2896ce10876a  guix-build-1a0d8e178c7b/output/x86_64-apple-darwin/bitcoin-1a0d8e178c7b-x86_64-apple-darwin-unsigned.dmg
  af2aab969b7ed7aeea0e02adbcc9e3b438086bf76b6bfc36146c53e05a27bd57  guix-build-1a0d8e178c7b/output/x86_64-apple-darwin/bitcoin-1a0d8e178c7b-x86_64-apple-darwin-unsigned.tar.gz
  32a5109ba28ab74ff66238e6a8f8a04e455ebce382a3be287df92a227818fe72  guix-build-1a0d8e178c7b/output/x86_64-apple-darwin/bitcoin-1a0d8e178c7b-x86_64-apple-darwin.tar.gz
  377462e9a96f4aba72c915dd5df5159a4301a1fa8ed0ee48faa6c71573de80c3  guix-build-1a0d8e178c7b/output/x86_64-linux-gnu/SHA256SUMS.part
  a3bf62e828d2350a483b2d16205014f66e8884597b0b72e178042a958c548336  guix-build-1a0d8e178c7b/output/x86_64-linux-gnu/bitcoin-1a0d8e178c7b-x86_64-linux-gnu-debug.tar.gz
  66cda980188ea1941a7d66c8b03c447580af33db55abe3bbe3581823ae0534a3  guix-build-1a0d8e178c7b/output/x86_64-linux-gnu/bitcoin-1a0d8e178c7b-x86_64-linux-gnu.tar.gz
  2117f0dd9baeb4d585f841592e94c088f4487bf2008b8f281d0c3ceee92ff6cc  guix-build-1a0d8e178c7b/output/x86_64-w64-mingw32/SHA256SUMS.part
  d40d5dec3287f467c42232c05d82f7fb538cda34bd2e63ff7e1876f471c3a790  guix-build-1a0d8e178c7b/output/x86_64-w64-mingw32/bitcoin-1a0d8e178c7b-win64-debug.zip
  92dcc92765fbc07b1cc8258bfa69280541e1b4553cc41fed18672c2c6931d5c0  guix-build-1a0d8e178c7b/output/x86_64-w64-mingw32/bitcoin-1a0d8e178c7b-win64-setup-unsigned.exe
  a6dd9b4d29f21d3a18cf64556cb03446ef17bf801eb6ac257b65d27cbd95080f  guix-build-1a0d8e178c7b/output/x86_64-w64-mingw32/bitcoin-1a0d8e178c7b-win64-unsigned.tar.gz
  a4022e595d955198f73530473ef8e90a708746089ee2dd27de794176873330c1  guix-build-1a0d8e178c7b/output/x86_64-w64-mingw32/bitcoin-1a0d8e178c7b-win64.zip
  ```

ACKs for top commit:
  Sjors:
    tACK 1a0d8e1
  achow101:
    ACK 1a0d8e1

Tree-SHA512: db7319259b1e1571cfab4bb3b99ae10a2f744e62757cae5059fd6f4dd6d5586eb09feb63a0c4bb07f7128b283f1dc281ed435224bc8e40da577fd4f04cde489a
… signer support available)

6b17994 doc: update OpenBSD build docs for 7.3 (external signer support available) (Sebastian Falbesoner)

Pull request description:

  With OpenBSD 7.3, the waitid(2) system call is implemented (see openbsd/src@8112871, first mentioned kernel improvement at https://www.openbsd.org/73.html).

  This means Boost.Process finally doesn't fail to compile anymore and we can remove the build hint about missing external signer support. Tested on my amd64 machine by reconfiguring / rebuilding master branch and successfully running the functional test wallet_signer.py. ✔️

ACKs for top commit:
  fanquake:
    ACK 6b17994 - haven't tested, but looks good to me.

Tree-SHA512: 5bbcecce4ced38d8221f2c906a54667e50317e9ded182554cf73bb7f2fce55a38e53730eca25f813cff1d2d65c94141eb158d40f83228d12dcf859c16a1798b9
@github-actions
Copy link

github-actions bot commented Oct 22, 2025

⚠️ Potential Merge Conflicts Detected

This PR has potential conflicts with the following open PRs:

Please coordinate with the authors of these PRs to avoid merge conflicts.

@coderabbitai
Copy link

coderabbitai bot commented Oct 22, 2025

Walkthrough

This pull request adds external signer support to Dash Core. It replaces Boost.Process configuration with an --enable-external-signer option, introduces an ExternalSigner class and ExternalSignerScriptPubKeyMan, integrates external signing into wallet descriptor setup and PSBT flows, adds RPCs (enumeratesigners, walletdisplayaddress), exposes UI controls for external signer configuration and wallet creation, and includes mocks and functional tests for valid/invalid/multi-signer scenarios. Numerous build, docs, and CI updates enable and advertise the feature.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant UI as Qt UI
participant Node as Dash Node
participant Wallet as Wallet
participant Signer as External Signer (script)
Note over UI,Node: Wallet creation with external signer
UI->>Node: createwallet(external_signer=true)
Node->>Wallet: create with WALLET_FLAG_EXTERNAL_SIGNER
Wallet->>Signer: ExternalSigner::Enumerate("--signer")
Signer-->>Wallet: [fingerprint,name] (JSON)
Wallet-->>UI: creation result/status

mermaid
sequenceDiagram
autonumber
participant UI as Send Dialog
participant Wallet as Wallet
participant Signer as External Signer (script)
participant RPC as RunCommandParseJSON
Note over UI,Wallet: PSBT-based external signing flow
UI->>Wallet: create PSBT (sign=false)
Wallet->>UI: PSBT
UI->>Signer: signtransaction --stdin --fingerprint (PSBT)
Signer->>RPC: execute signer command
RPC-->>Signer: returns JSON {psbt: ""}
Signer-->>UI: signed PSBT
UI->>Wallet: finalize and broadcast (if complete)
Wallet-->>Network: broadcast tx

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The PR title "backport: to-insert-list external signer" attempts to convey the main purpose (external signer support backports), which aligns with the changeset that is entirely focused on external signer functionality across build, test, wallet, and UI components. However, the presence of "to-insert-list" appears to be placeholder or incomplete text rather than a meaningful descriptor, making the title unclear and suggesting unfinished authoring. The actual subject matter (external signer) is clearly related to the changes, but the vague placeholder element undermines the clarity and specificity expected of a PR title.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed The PR description clearly states that the changes consist of "extra backports to improve support of External signer" and identifies them as improvements for CI, tests, and user experience. This description directly relates to the changeset, which comprises comprehensive external signer support enhancements spanning configure scripts, build system, core implementation, wallet integration, Qt GUI, and test infrastructure. Although the description is relatively high-level and does not enumerate every modification, it meaningfully communicates the purpose and scope of the changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08ca6d1 and a142e5b.

📒 Files selected for processing (3)
  • src/script/interpreter.h (1 hunks)
  • src/script/sign.cpp (1 hunks)
  • src/script/sign.h (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/script/interpreter.h
  • src/script/sign.h
  • src/script/sign.cpp
🧬 Code graph analysis (1)
src/script/sign.h (1)
src/script/sign.cpp (1)
  • DUMMY_CHECKER (364-364)
⏰ 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_tsan-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: mac-build / Build source
  • GitHub Check: win64-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: arm-linux-build / Build source
  • GitHub Check: Lint / Run linters
🔇 Additional comments (6)
src/script/interpreter.h (1)

195-198: Verified: const-correctness improvement is safe and correct.

All call sites are compatible. The only instantiation found (in src/script/sign.cpp:227) passes a non-const BaseSignatureChecker&, which safely binds to the new const BaseSignatureChecker& parameter. This is a standard C++ conversion that maintains backward compatibility while improving const-correctness.

src/script/sign.h (1)

53-54: LGTM!

The extern declaration and comment are clear. This properly exposes DUMMY_CHECKER with the same pattern used for the other dummy creators, maintaining consistency across the API.

src/script/sign.cpp (4)

346-353: LGTM!

The DummySignatureChecker is correctly placed in an anonymous namespace and implements the expected dummy behavior (accepting all signatures). The use of final and override keywords follows modern C++ best practices.


355-355: LGTM!

The const reference bound to a temporary is a valid C++ pattern. The temporary's lifetime is extended to static storage duration, ensuring it lives for the entire program. This approach maintains polymorphism while avoiding heap allocation.


357-382: LGTM!

The DummySignatureCreator correctly constructs valid DER-encoded signatures. The encoding structure is accurate:

  • Produces 71-byte signatures for (32, 32) configuration
  • Produces 72-byte signatures for (33, 32) configuration

The implementation properly handles the DER SEQUENCE structure with INTEGER components for r and s values, and appends the sighash byte.


384-385: LGTM!

Both signature creator definitions follow the same valid const-reference-to-temporary pattern. The configurations produce the expected signature sizes:

  • DUMMY_SIGNATURE_CREATOR (32, 32) → 71 bytes
  • DUMMY_MAXIMUM_SIGNATURE_CREATOR (33, 32) → 72 bytes

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: 21

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
test/functional/wallet_abandonconflict.py (1)

139-139: Use wallet RPC handle for consistency.

This line uses self.nodes[0].listtransactions() instead of alice.listtransactions(), which is inconsistent with the refactoring pattern applied throughout the rest of the file. While functionally equivalent (both access the default wallet), using the alice handle maintains consistency with the PR's objective to switch all wallet operations to wallet RPC handles.

Apply this diff:

-        txAB1_listtransactions = [d for d in self.nodes[0].listtransactions() if d['txid'] == txAB1]
+        txAB1_listtransactions = [d for d in alice.listtransactions() if d['txid'] == txAB1]
src/wallet/scriptpubkeyman.cpp (1)

1624-1649: Avoid pushing invalid entries when pool read fails.

Only append to result when ReadPool succeeds; otherwise you may return default-initialized CKeyPool.

-    while (it != std::end(*setKeyPool)) {
+    while (it != std::end(*setKeyPool)) {
         const int64_t& index = *(it);
         if (index > keypool_id) break; // set*KeyPool is ordered

         CKeyPool keypool;
-        if (batch.ReadPool(index, keypool)) { //TODO: This should be unnecessary
-            m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
-        }
+        if (batch.ReadPool(index, keypool)) { // TODO: Should be unnecessary
+            m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
+        }
         batch.ErasePool(index);
         WalletLogPrintf("keypool index %d removed\n", index);
         it = setKeyPool->erase(it);
-        result.push_back(std::move(keypool));
+        if (keypool.vchPubKey.IsValid()) {
+            result.push_back(std::move(keypool));
+        }
     }
src/qt/sendcoinsdialog.cpp (1)

796-809: External signer balance is always shown as 0.

You change the label but never assign a balance for this branch. Show watch-only (typical for external signer wallets) or regular balance.

-        if (model->wallet().hasExternalSigner()) {
-            ui->labelBalanceName->setText(tr("External balance:"));
+        if (model->wallet().hasExternalSigner()) {
+            balance = balances.watch_only_balance;
+            ui->labelBalanceName->setText(tr("External balance:"));
🧹 Nitpick comments (35)
test/functional/wallet_listtransactions.py (5)

21-24: num_nodes=3: OK, but keep restart args consistent.

Bumping to 3 is justified for cross‑node wallet checks. When you restart later, pass self.extra_args to preserve initial node config.

Apply this small change at the later restart:

-        self.start_nodes()
+        self.start_nodes(extra_args=self.extra_args)

113-118: Avoid hard‑coding wallet.dat paths; use RPC‑based cloning.

Directly copying wallet.dat is brittle (backend differences, walletdir layout like “wallets/”, multiwallet). Prefer RPCs:

  • dumpwallet on node 0 and importwallet on node 2 (no stop/start needed), or
  • backupwallet while running, then copy the backup file.

This removes assumptions about self.chain/default_wallet_name and survives backend changes.

Example replacement:

-        self.nodes[0].keypoolrefill(1000)
-        self.stop_nodes()
-        wallet0 = os.path.join(self.nodes[0].datadir, self.chain, self.default_wallet_name, "wallet.dat")
-        wallet2 = os.path.join(self.nodes[2].datadir, self.chain, self.default_wallet_name, "wallet.dat")
-        shutil.copyfile(wallet0, wallet2)
-        self.start_nodes()
+        self.nodes[0].keypoolrefill(1000)
+        dump_file = os.path.join(self.nodes[0].datadir, "w0_dump.txt")
+        self.nodes[0].dumpwallet(dump_file)
+        # Ensure wallet 2 is fresh and imports all keys/labels from node 0
+        self.nodes[2].importwallet(dump_file)

If you must keep file‑copy semantics, derive the wallet path via RPC or node attributes (e.g., walletdir) rather than constructing it manually.


118-123: Reconnect is fine; add sync for determinism.

After restarting/reconnecting, call sync_* to avoid races before subsequent sends/mines and comparisons.

         self.connect_nodes(0, 1)
         self.connect_nodes(1, 2)
         self.connect_nodes(2, 0)
+        self.sync_all()

128-140: Add chain syncs after mining to avoid flakiness across nodes.

generate() mines on a single node; peers may lag briefly. Sync before comparing listtransactions to ensure both nodes see the same chain.

         self.nodes[1].sendtoaddress(addr3, "0.001")
         self.generate(self.nodes[1], 1)
+        self.sync_blocks()
@@
         self.nodes[1].sendtoaddress(addr2, "0.001")
         self.generate(self.nodes[1], 1)
+        self.sync_blocks()
@@
         self.nodes[0].sendtoaddress(addr1, "0.001")
         self.generate(self.nodes[0], 1)
+        self.sync_blocks()

145-152: Strengthen normalization sort key to avoid duplicate‑txid ordering differences.

listtransactions can emit multiple entries with the same txid (e.g., send‑to‑self). Sorting only by txid may keep per‑txid order different across nodes, causing spurious mismatches. Include category and vout in the sort key.

-        def normalize_list(txs):
+        def normalize_list(txs):
             for tx in txs:
                 tx.pop('label', None)
                 tx.pop('time', None)
                 tx.pop('timereceived', None)
-            txs.sort(key=lambda x: x['txid'])
+            txs.sort(key=lambda x: (x['txid'], x.get('category', ''), x.get('vout', -1)))
src/wallet/init.cpp (1)

70-72: Hide -signer when feature is not compiled for consistent CLI UX.

Mirror existing patterns (e.g., -unsafesqlitesync) to avoid unknown-option noise.

 #ifdef ENABLE_EXTERNAL_SIGNER
-    argsman.AddArg("-signer=<cmd>", "External signing tool, see doc/external-signer.md", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+    argsman.AddArg("-signer=<cmd>", "External signing tool, see doc/external-signer.md", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+#else
+    argsman.AddHiddenArgs({"-signer"});
 #endif
src/qt/forms/createwalletdialog.ui (2)

112-121: Text/style polish and availability gating.

  • Use title case to match help/category naming: “External Signer”.
  • Ensure the checkbox is hidden/disabled when external signer is not compiled or no signers are detected.
-         <string>External signer</string>
+         <string>External Signer</string>

If not already handled in CreateWalletDialog code, I can add the visibility/enablement toggle based on signer enumeration.


155-155: Tab order looks fine; consider adding descriptor checkbox too (if missing).

If descriptor_checkbox isn’t already in tabstops elsewhere, add it for keyboard navigation consistency.

src/qt/walletmodel.h (1)

144-144: Consider passing string parameter by const reference.

The method signature takes std::string sAddress by value. For efficiency, consider using const std::string& sAddress to avoid unnecessary string copies, especially since the implementation only needs to read the value.

Apply this diff if you'd like to optimize the parameter passing:

-    bool displayAddress(std::string sAddress);
+    bool displayAddress(const std::string& sAddress);

Also update the implementation in src/qt/walletmodel.cpp accordingly.

src/qt/walletmodel.cpp (2)

582-592: Consider using this as the parent widget for the error dialog.

Passing nullptr as the parent to QMessageBox::critical can lead to suboptimal behavior: the dialog may not be modal to the wallet window, may appear on the wrong screen in multi-monitor setups, and lacks proper window ownership. Consider passing this or another appropriate parent widget instead.

Apply this diff:

-        QMessageBox::critical(nullptr, tr("Can't display address"), e.what());
+        QMessageBox::critical(/* appropriate parent widget */, tr("Can't display address"), e.what());

Note: You may need to refactor this method to accept a parent widget parameter, or use a pattern that ensures proper parent widget handling.


582-582: Parameter efficiency: pass by const reference.

The parameter sAddress is passed by value, which creates an unnecessary copy. Consider passing by const std::string& instead.

Apply this diff:

-bool WalletModel::displayAddress(std::string sAddress)
+bool WalletModel::displayAddress(const std::string& sAddress)
src/qt/walletcontroller.cpp (1)

296-306: Consider using a translatable string for the multi-signer error message.

The error message on line 303 uses QString::fromStdString with a hardcoded English string, while other error messages in the same flow (like line 300) use Qt's tr() for translation.

Apply this diff to make the error message translatable and consistent with the rest of the codebase:

     if (signers.size() > 1) {
-        QMessageBox::critical(nullptr, tr("Too many external signers found"), QString::fromStdString("More than one external signer found. Please connect only one at a time."));
+        QMessageBox::critical(nullptr, tr("Too many external signers found"), tr("More than one external signer found. Please connect only one at a time."));
         signers.clear();
     }
src/wallet/rpc/backup.cpp (1)

2041-2047: CoinJoin detection via string search is brittle

Parsing the descriptor string to find "/'/'/4'/0'" is fragile. Prefer a structured check (e.g., store derivation purpose/path metadata in WalletDescriptor or a helper that decodes derivation steps) to avoid false positives if formatting changes or keys are reordered.

src/interfaces/node.h (1)

160-169: Make ExternalSigner interface const-correct and defaulted

getName is an accessor; mark it const. Also default the virtual destructor for clarity.

Apply:

-class ExternalSigner
+class ExternalSigner
 {
 public:
-    virtual ~ExternalSigner() {};
+    virtual ~ExternalSigner() = default;
 
     //! Get signer display name
-    virtual std::string getName() = 0;
+    virtual std::string getName() const = 0;
 };
doc/external-signer.md (1)

1-171: Tighten Markdown (fences, prompts, style) and mention build flag

  • Add language identifiers (sh, json).
  • Drop leading “$ ” prompts or include sample outputs per MD014.
  • Minor grammar: “In order to be” → “To be”, hyphenate “BIP44/49/84-compatible”.
  • Document that builds must enable external signer support (configure with --enable-external-signer) if that’s the gating.

Apply sample edits:

-```sh
-$ dashd -signer=../HWI/hwi.py
-```
+```sh
+dashd -signer=../HWI/hwi.py
+```

-```
-$ dash-cli enumeratesigners
+```json
+{
+  "signers": [
+    { "fingerprint": "c8df832a" }
+  ]
+}
+```

-```sh
-$ dash-cli createwallet "hww" true true "" true true true
-```
+```sh
+dash-cli createwallet "hww" true true "" true true true
+```

-``` 
-$ <cmd> enumerate
+```sh
+<cmd> enumerate
 [
   { "fingerprint": "00000000" }
 ]

+Note: build with --enable-external-signer to enable this feature.

Also fix “immedidately” → “immediately” if you keep that note in code comments elsewhere (see createwalletdialog.cpp).

</blockquote></details>
<details>
<summary>src/qt/createwalletdialog.cpp (2)</summary><blockquote>

`49-65`: **External signer toggle: align blank-wallet semantics and fix typo**

- You set blank_wallet_checkbox to checked when toggled on (Line 64), implying watch-only wallets should be “blank”. In setSigners() you later force it unchecked (see below), which is inconsistent. Decide the intended default and make both paths consistent.
- Typo in comment: “immedidately” → “immediately”.



Would you like me to update both spots to a single policy (e.g., blank=false because descriptors are imported immediately)?

---

`115-134`: **setSigners(): minor polish and consistency**

- Consistency: As noted above, this forces blank_wallet_checkbox unchecked even though the “toggled” path sets it checked. Pick one behavior.
- UX: When no signers are found (else branch), consider re-enabling the other checkboxes back to their defaults to avoid a partially locked UI if setSigners() is called after prior toggling.
- API: You call signers[0]->getName(); with the proposed const qualifier, this still works.


```diff
-        ui->blank_wallet_checkbox->setEnabled(false);
-        ui->blank_wallet_checkbox->setChecked(false);
+        ui->blank_wallet_checkbox->setEnabled(false);
+        ui->blank_wallet_checkbox->setChecked(true); // or false, but keep consistent with the toggle handler
@@
     } else {
         ui->external_signer_checkbox->setEnabled(false);
+        // Optionally restore defaults:
+        ui->encrypt_wallet_checkbox->setEnabled(true);
+        ui->blank_wallet_checkbox->setEnabled(true);
+        ui->disable_privkeys_checkbox->setEnabled(true);
+        ui->descriptor_checkbox->setEnabled(true);
     }

If you prefer blank=false, update the toggle handler accordingly.

src/node/interfaces.cpp (3)

530-551: Make listExternalSigners resilient (catch enumerate errors, guard empty cmd).

Avoid throwing across the Node interface. Catch exceptions from ExternalSigner::Enumerate and return an empty list; also use command.empty() for clarity.

Apply:

     std::vector<std::unique_ptr<interfaces::ExternalSigner>> listExternalSigners() override
     {
 #ifdef ENABLE_EXTERNAL_SIGNER
-        std::vector<ExternalSigner> signers = {};
-        const std::string command = gArgs.GetArg("-signer", "");
-        if (command == "") return {};
-        ExternalSigner::Enumerate(command, signers, Params().NetworkIDString());
+        std::vector<ExternalSigner> signers;
+        const std::string command = gArgs.GetArg("-signer", "");
+        if (command.empty()) return {};
+        try {
+            ExternalSigner::Enumerate(command, signers, Params().NetworkIDString());
+        } catch (const std::exception& e) {
+            // Log and degrade gracefully
+            LogPrintf("listExternalSigners: enumerate failed: %s\n", e.what());
+            return {};
+        }
         std::vector<std::unique_ptr<interfaces::ExternalSigner>> result;
-        for (auto& signer : signers) {
+        result.reserve(signers.size());
+        for (auto& signer : signers) {
             result.emplace_back(std::make_unique<ExternalSignerImpl>(std::move(signer)));
         }
         return result;
 #else

17-17: Optional: include only when enabled.

Wrap the header include with ENABLE_EXTERNAL_SIGNER to avoid unnecessary coupling when the feature is off.

-#include <external_signer.h>
+#ifdef ENABLE_EXTERNAL_SIGNER
+#include <external_signer.h>
+#endif

402-411: Minor: keep adaptor minimal.

Consider storing just the name instead of copying the whole ::ExternalSigner, unless future methods are planned.

-class ExternalSignerImpl : public interfaces::ExternalSigner
-{
-public:
-    ExternalSignerImpl(::ExternalSigner signer) : m_signer(std::move(signer)) {}
-    std::string getName() override { return m_signer.m_name; }
-private:
-    ::ExternalSigner m_signer;
-};
+class ExternalSignerImpl : public interfaces::ExternalSigner {
+public:
+    explicit ExternalSignerImpl(std::string name) : m_name(std::move(name)) {}
+    std::string getName() override { return m_name; }
+private:
+    std::string m_name;
+};

And in the loop:

- result.emplace_back(std::make_unique<ExternalSignerImpl>(std::move(signer)));
+ result.emplace_back(std::make_unique<ExternalSignerImpl>(std::move(signer.m_name)));
src/external_signer.h (1)

27-35: API polish for performance and const-correctness.

  • Remove top-level const on by-value return.
  • Pass chain by const reference in ctor.
  • Make GetDescriptors const.
-    const std::string NetworkArg() const;
+    std::string NetworkArg() const;
@@
-    ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name);
+    ExternalSigner(const std::string& command, const std::string& chain, const std::string& fingerprint, const std::string& name);
@@
-    UniValue GetDescriptors(const int account);
+    UniValue GetDescriptors(int account) const;

Also update implementations:

-const std::string ExternalSigner::NetworkArg() const
+std::string ExternalSigner::NetworkArg() const
 {
     return " --chain " + m_chain;
 }
@@
-ExternalSigner::ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name)
+ExternalSigner::ExternalSigner(const std::string& command, const std::string& chain, const std::string& fingerprint, const std::string& name)
   : m_command(command), m_chain(chain), m_fingerprint(fingerprint), m_name(name) {}
@@
-UniValue ExternalSigner::GetDescriptors(const int account)
+UniValue ExternalSigner::GetDescriptors(int account) const

Also applies to: 54-59

src/wallet/external_signer_scriptpubkeyman.cpp (1)

64-86: Use wallet logging instead of stderr on signing failure.

Direct writes to stderr bypass logging. Prefer LogPrintf/WalletLogPrintf.

-    if(!GetExternalSigner().SignTransaction(psbt, strFailReason)) {
-        tfm::format(std::cerr, "Failed to sign: %s\n", strFailReason);
+    if (!GetExternalSigner().SignTransaction(psbt, strFailReason)) {
+        LogPrintf("%s: external signer failed: %s\n", __func__, strFailReason);
         return TransactionError::EXTERNAL_SIGNER_FAILED;
     }

Add:

+#include <logging.h>

near the includes.

test/functional/rpc_signer.py (2)

51-54: Fix Flake8 E128 under-indented continuations.

Reformat the four multi-line assert_raises_rpc_error calls.

-        assert_raises_rpc_error(-1, 'Error: restart dashd with -signer=<cmd>',
-            self.nodes[0].enumeratesigners
-        )
+        assert_raises_rpc_error(
+            -1,
+            'Error: restart dashd with -signer=<cmd>',
+            self.nodes[0].enumeratesigners,
+        )

-        assert_raises_rpc_error(-1, 'execve failed: No such file or directory',
-            self.nodes[3].enumeratesigners
-        )
+        assert_raises_rpc_error(
+            -1,
+            'execve failed: No such file or directory',
+            self.nodes[3].enumeratesigners,
+        )

-        assert_raises_rpc_error(-1, 'RunCommandParseJSON error',
-            self.nodes[1].enumeratesigners
-        )
+        assert_raises_rpc_error(
+            -1,
+            'RunCommandParseJSON error',
+            self.nodes[1].enumeratesigners,
+        )

-        assert_raises_rpc_error(-1, 'fingerprint not found',
-            self.nodes[1].enumeratesigners
-        )
+        assert_raises_rpc_error(
+            -1,
+            'fingerprint not found',
+            self.nodes[1].enumeratesigners,
+        )

Also applies to: 56-58, 62-64, 68-70


55-58: Harden missing-signer assertion across OSes.

The exact message “execve failed: No such file or directory” is Linux-specific and will differ on Windows/macOS. Match a less brittle substring or accept multiple options (e.g., “No such file or directory” or “The system cannot find the file specified.”).

src/qt/sendcoinsdialog.cpp (1)

503-518: Optional: sanitize suggested filename for PSBT saves.

Labels/addresses may contain characters invalid for filenames. Consider replacing forbidden characters (/:*?"<>| etc.) or falling back to a safe default.

src/wallet/wallet.h (1)

30-31: Guard external_signer include for non-signer builds.

Protect include behind ENABLE_EXTERNAL_SIGNER to avoid unnecessary coupling.

-#include <external_signer.h>
+#ifdef ENABLE_EXTERNAL_SIGNER
+#include <external_signer.h>
+#endif
test/functional/wallet_signer.py (5)

113-116: Fix flake8 E128/E124: align continuation and closing paren.

-        assert_raises_rpc_error(-1, 'RunCommandParseJSON error',
-            hww.walletdisplayaddress, address1
-        )
+        assert_raises_rpc_error(
+            -1,
+            'RunCommandParseJSON error',
+            hww.walletdisplayaddress,
+            address1,
+        )

128-141: Fix flake8 E122: indent the second descriptor dict.

-        result = mock_wallet.importdescriptors([{
+        result = mock_wallet.importdescriptors([
+            {
             "desc": "pkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0/*)#6k3x80k9",
             "timestamp": 0,
             "range": [0,1],
             "internal": False,
             "active": True
-        },
-        {
+            },
+            {
             "desc": "pkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/*)#tz5866xa",
             "timestamp": 0,
             "range": [0, 0],
             "internal": True,
             "active": True
-        }])
+            },
+        ])

179-180: Prefer assert_equal for clearer failures.

-        assert(hww.testmempoolaccept([mock_tx])[0]["allowed"])
+        assert_equal(hww.testmempoolaccept([mock_tx])[0]["allowed"], True)

21-26: Make signer path robust on Windows and paths with spaces.
Use sys.executable and quoting to avoid shell issues.

+import sys
+import shlex
...
-        if platform.system() == "Windows":
-            return "py " + path
-        else:
-            return path
+        if platform.system() == "Windows":
+            return f'"{sys.executable}" {shlex.quote(path)}'
+        return path

Please confirm CI on Windows still finds Python via sys.executable.

Also applies to: 29-34, 36-40


100-106: Reduce brittleness of exact address assertion.
Hardcoding the full address risks future derivation/encoding changes. Consider asserting validity and keypath properties instead.

-        assert_equal(address3, "yg8pCsowDbY7PbJMthTi4xkkmhNfcNNdHV")
+        assert_equal(address_info['hdkeypath'].startswith("m/44'/1'/0'/0/"), True)
+        assert_equal(address_info['iswitness'], False)

If the exact address is intentionally fixed by the mock, feel free to keep it.

src/wallet/scriptpubkeyman.h (1)

604-608: SetupDescriptor public hook — consider nodiscard.
Return value indicates success; marking [[nodiscard]] would prevent accidental ignore.

-    bool SetupDescriptor(std::unique_ptr<Descriptor>desc);
+    [[nodiscard]] bool SetupDescriptor(std::unique_ptr<Descriptor> desc);
src/wallet/wallet.cpp (3)

3731-3741: Avoid reusing SignatureData across SPKMs.
Stateful CanProvide() may mutate sigdata; reinitialize per iteration.

 std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script) const
 {
     std::set<ScriptPubKeyMan*> spk_mans;
-    SignatureData sigdata;
     for (const auto& spk_man_pair : m_spk_managers) {
-        if (spk_man_pair.second->CanProvide(script, sigdata)) {
+        SignatureData sigdata;
+        if (spk_man_pair.second->CanProvide(script, sigdata)) {
             spk_mans.insert(spk_man_pair.second.get());
         }
     }
     return spk_mans;
 }

3869-3897: External signer descriptor ingestion — minor hardening.
Consider logging/skipping descriptors with missing/unsupported output types; current continue silently drops them.

-                if (!desc->GetOutputType()) {
-                    continue;
-                }
+                if (!desc->GetOutputType()) {
+                    WalletLogPrintf("%s: skipping descriptor without output type: %s\n", __func__, desc_str);
+                    continue;
+                }

3965-3987: Remove unused variable and assert in IsInternalScriptPubKeyMan.
The output type is fetched but not used; trim dead code.

-    const auto& type = desc_spk_man->GetWalletDescriptor().descriptor->GetOutputType();
-    assert(type.has_value());
-
     return GetScriptPubKeyMan(/*internal=*/true) == desc_spk_man;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5ce1f0 and 08ca6d1.

📒 Files selected for processing (80)
  • ci/dash/build_src.sh (1 hunks)
  • ci/test/00_setup_env_arm.sh (1 hunks)
  • ci/test/00_setup_env_mac.sh (1 hunks)
  • ci/test/00_setup_env_mac_native_x86_64.sh (1 hunks)
  • ci/test/00_setup_env_native_asan.sh (1 hunks)
  • ci/test/00_setup_env_native_fuzz.sh (1 hunks)
  • ci/test/00_setup_env_native_multiprocess.sh (1 hunks)
  • ci/test/00_setup_env_native_nowallet.sh (1 hunks)
  • ci/test/00_setup_env_native_qt5.sh (1 hunks)
  • ci/test/00_setup_env_native_tsan.sh (1 hunks)
  • ci/test/00_setup_env_s390x.sh (1 hunks)
  • ci/test/00_setup_env_win64.sh (1 hunks)
  • configure.ac (8 hunks)
  • doc/Doxyfile.in (1 hunks)
  • doc/build-openbsd.md (1 hunks)
  • doc/build-osx.md (1 hunks)
  • doc/dependencies.md (1 hunks)
  • doc/external-signer.md (1 hunks)
  • src/Makefile.am (5 hunks)
  • src/Makefile.test.include (1 hunks)
  • src/dummywallet.cpp (1 hunks)
  • src/external_signer.cpp (1 hunks)
  • src/external_signer.h (1 hunks)
  • src/interfaces/node.h (2 hunks)
  • src/interfaces/wallet.h (2 hunks)
  • src/node/interfaces.cpp (3 hunks)
  • src/qt/createwalletdialog.cpp (4 hunks)
  • src/qt/createwalletdialog.h (2 hunks)
  • src/qt/forms/createwalletdialog.ui (2 hunks)
  • src/qt/forms/optionsdialog.ui (1 hunks)
  • src/qt/forms/receiverequestdialog.ui (1 hunks)
  • src/qt/optionsdialog.cpp (3 hunks)
  • src/qt/optionsmodel.cpp (3 hunks)
  • src/qt/optionsmodel.h (1 hunks)
  • src/qt/receiverequestdialog.cpp (1 hunks)
  • src/qt/sendcoinsdialog.cpp (8 hunks)
  • src/qt/sendcoinsdialog.h (2 hunks)
  • src/qt/walletcontroller.cpp (4 hunks)
  • src/qt/walletmodel.cpp (2 hunks)
  • src/qt/walletmodel.h (1 hunks)
  • src/qt/walletmodeltransaction.cpp (1 hunks)
  • src/qt/walletmodeltransaction.h (1 hunks)
  • src/rpc/client.cpp (1 hunks)
  • src/rpc/external_signer.cpp (1 hunks)
  • src/rpc/register.h (2 hunks)
  • src/test/system_tests.cpp (3 hunks)
  • src/util/error.cpp (1 hunks)
  • src/util/error.h (1 hunks)
  • src/util/system.cpp (3 hunks)
  • src/util/system.h (0 hunks)
  • src/wallet/external_signer_scriptpubkeyman.cpp (1 hunks)
  • src/wallet/external_signer_scriptpubkeyman.h (1 hunks)
  • src/wallet/init.cpp (1 hunks)
  • src/wallet/interfaces.cpp (2 hunks)
  • src/wallet/rpc/addresses.cpp (2 hunks)
  • src/wallet/rpc/backup.cpp (2 hunks)
  • src/wallet/rpc/spend.cpp (8 hunks)
  • src/wallet/rpc/wallet.cpp (6 hunks)
  • src/wallet/scriptpubkeyman.cpp (5 hunks)
  • src/wallet/scriptpubkeyman.h (6 hunks)
  • src/wallet/spend.cpp (2 hunks)
  • src/wallet/wallet.cpp (13 hunks)
  • src/wallet/wallet.h (8 hunks)
  • src/wallet/walletdb.cpp (1 hunks)
  • src/wallet/walletdb.h (1 hunks)
  • src/wallet/walletutil.h (1 hunks)
  • test/config.ini.in (1 hunks)
  • test/functional/mocks/invalid_signer.py (1 hunks)
  • test/functional/mocks/multi_signers.py (1 hunks)
  • test/functional/mocks/signer.py (1 hunks)
  • test/functional/rpc_fundrawtransaction.py (1 hunks)
  • test/functional/rpc_help.py (1 hunks)
  • test/functional/rpc_signer.py (1 hunks)
  • test/functional/test_framework/test_framework.py (1 hunks)
  • test/functional/test_framework/test_node.py (1 hunks)
  • test/functional/test_runner.py (1 hunks)
  • test/functional/wallet_abandonconflict.py (5 hunks)
  • test/functional/wallet_listtransactions.py (2 hunks)
  • test/functional/wallet_signer.py (1 hunks)
  • test/lint/spelling.ignore-words.txt (1 hunks)
💤 Files with no reviewable changes (1)
  • src/util/system.h
🧰 Additional context used
📓 Path-based instructions (5)
ci/**

📄 CodeRabbit inference engine (CLAUDE.md)

Unless specifically prompted, avoid making changes to the ci directory (continuous integration)

Files:

  • ci/test/00_setup_env_native_asan.sh
  • ci/test/00_setup_env_s390x.sh
  • ci/test/00_setup_env_win64.sh
  • ci/test/00_setup_env_native_multiprocess.sh
  • ci/test/00_setup_env_mac_native_x86_64.sh
  • ci/test/00_setup_env_arm.sh
  • ci/test/00_setup_env_native_nowallet.sh
  • ci/test/00_setup_env_mac.sh
  • ci/test/00_setup_env_native_fuzz.sh
  • ci/test/00_setup_env_native_qt5.sh
  • ci/dash/build_src.sh
  • ci/test/00_setup_env_native_tsan.sh
doc/**

📄 CodeRabbit inference engine (CLAUDE.md)

Unless specifically prompted, avoid making changes to the doc directory (documentation)

Files:

  • doc/build-openbsd.md
  • doc/Doxyfile.in
  • doc/external-signer.md
  • doc/dependencies.md
  • doc/build-osx.md
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/util/error.cpp
  • src/qt/optionsmodel.h
  • src/qt/createwalletdialog.h
  • src/util/system.cpp
  • src/wallet/external_signer_scriptpubkeyman.cpp
  • src/qt/optionsmodel.cpp
  • src/wallet/walletdb.h
  • src/qt/sendcoinsdialog.cpp
  • src/qt/walletmodel.h
  • src/qt/receiverequestdialog.cpp
  • src/qt/createwalletdialog.cpp
  • src/wallet/rpc/addresses.cpp
  • src/rpc/external_signer.cpp
  • src/test/system_tests.cpp
  • src/qt/sendcoinsdialog.h
  • src/wallet/rpc/wallet.cpp
  • src/util/error.h
  • src/external_signer.h
  • src/wallet/wallet.h
  • src/qt/optionsdialog.cpp
  • src/rpc/register.h
  • src/wallet/rpc/backup.cpp
  • src/wallet/spend.cpp
  • src/qt/walletmodeltransaction.cpp
  • src/wallet/scriptpubkeyman.cpp
  • src/qt/walletmodeltransaction.h
  • src/wallet/walletutil.h
  • src/qt/walletcontroller.cpp
  • src/wallet/rpc/spend.cpp
  • src/interfaces/node.h
  • src/wallet/walletdb.cpp
  • src/wallet/scriptpubkeyman.h
  • src/dummywallet.cpp
  • src/interfaces/wallet.h
  • src/wallet/init.cpp
  • src/external_signer.cpp
  • src/wallet/interfaces.cpp
  • src/wallet/wallet.cpp
  • src/node/interfaces.cpp
  • src/rpc/client.cpp
  • src/wallet/external_signer_scriptpubkeyman.h
  • src/qt/walletmodel.cpp
src/{test,wallet/test,qt/test}/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

Unit tests for C++ code should be placed in src/test/, src/wallet/test/, or src/qt/test/ and use Boost::Test or Qt 5 for GUI tests

Files:

  • src/test/system_tests.cpp
test/functional/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Functional tests should be written in Python and placed in test/functional/

Files:

  • test/functional/mocks/invalid_signer.py
  • test/functional/wallet_abandonconflict.py
  • test/functional/rpc_help.py
  • test/functional/rpc_signer.py
  • test/functional/test_runner.py
  • test/functional/rpc_fundrawtransaction.py
  • test/functional/test_framework/test_node.py
  • test/functional/mocks/signer.py
  • test/functional/test_framework/test_framework.py
  • test/functional/wallet_listtransactions.py
  • test/functional/wallet_signer.py
  • test/functional/mocks/multi_signers.py
🧬 Code graph analysis (39)
src/util/error.cpp (1)
src/util/translation.h (1)
  • Untranslated (48-48)
src/qt/createwalletdialog.h (2)
src/interfaces/node.h (2)
  • interfaces (48-411)
  • ExternalSigner (161-168)
src/qt/createwalletdialog.cpp (12)
  • setSigners (115-134)
  • setSigners (115-115)
  • isEncryptWalletChecked (141-144)
  • isEncryptWalletChecked (141-141)
  • isDisablePrivateKeysChecked (146-149)
  • isDisablePrivateKeysChecked (146-146)
  • isDescriptorWalletChecked (156-159)
  • isDescriptorWalletChecked (156-156)
  • isExternalSignerChecked (161-164)
  • isExternalSignerChecked (161-161)
  • CreateWalletDialog (17-108)
  • CreateWalletDialog (110-113)
src/util/system.cpp (1)
src/node/interfaces.cpp (1)
  • ENABLE_EXTERNAL_SIGNER (530-551)
src/wallet/external_signer_scriptpubkeyman.cpp (2)
src/wallet/wallet.cpp (6)
  • DisplayAddress (2533-2545)
  • DisplayAddress (2533-2533)
  • GetSolvingProvider (3751-3755)
  • GetSolvingProvider (3751-3751)
  • GetSolvingProvider (3757-3765)
  • GetSolvingProvider (3757-3757)
src/external_signer.cpp (4)
  • Enumerate (24-60)
  • Enumerate (24-24)
  • DisplayAddress (62-65)
  • DisplayAddress (62-62)
src/qt/sendcoinsdialog.cpp (2)
src/qt/guiutil.cpp (4)
  • setClipboard (1653-1660)
  • setClipboard (1653-1653)
  • getSaveFileName (521-559)
  • getSaveFileName (521-523)
src/qt/bitcoinunits.cpp (4)
  • formatWithUnit (125-128)
  • formatWithUnit (125-125)
  • format (80-114)
  • format (80-80)
src/qt/walletmodel.h (1)
src/qt/walletmodel.cpp (2)
  • displayAddress (582-592)
  • displayAddress (582-582)
src/qt/createwalletdialog.cpp (2)
src/node/interfaces.cpp (1)
  • ENABLE_EXTERNAL_SIGNER (530-551)
src/qt/createwalletdialog.h (1)
  • CreateWalletDialog (24-44)
src/wallet/rpc/addresses.cpp (3)
src/wallet/interfaces.cpp (18)
  • LOCK (249-258)
  • LOCK (259-262)
  • LOCK (290-294)
  • LOCK (295-299)
  • LOCK (352-360)
  • LOCK (478-490)
  • dest (209-213)
  • dest (209-209)
  • dest (222-225)
  • dest (222-222)
  • dest (226-229)
  • dest (226-226)
  • dest (230-248)
  • dest (230-233)
  • dest (263-267)
  • dest (263-263)
  • dest (268-272)
  • dest (268-268)
src/wallet/scriptpubkeyman.h (1)
  • wallet (28-253)
src/wallet/rpc/util.cpp (2)
  • GetWalletForJSONRPCRequest (55-77)
  • GetWalletForJSONRPCRequest (55-55)
src/rpc/external_signer.cpp (2)
src/node/interfaces.cpp (1)
  • ENABLE_EXTERNAL_SIGNER (530-551)
src/external_signer.cpp (2)
  • Enumerate (24-60)
  • Enumerate (24-24)
src/test/system_tests.cpp (2)
src/node/interfaces.cpp (1)
  • ENABLE_EXTERNAL_SIGNER (530-551)
src/util/system.cpp (2)
  • RunCommandParseJSON (1401-1438)
  • RunCommandParseJSON (1401-1401)
src/qt/sendcoinsdialog.h (1)
src/qt/sendcoinsdialog.cpp (6)
  • presentPSBT (489-533)
  • presentPSBT (489-489)
  • processSendCoinsReturn (820-859)
  • processSendCoinsReturn (820-820)
  • signWithExternalSigner (535-561)
  • signWithExternalSigner (535-535)
src/wallet/rpc/wallet.cpp (2)
src/wallet/rpc/addresses.cpp (2)
  • walletdisplayaddress (674-712)
  • walletdisplayaddress (674-674)
src/node/interfaces.cpp (1)
  • ENABLE_EXTERNAL_SIGNER (530-551)
src/external_signer.h (1)
src/external_signer.cpp (11)
  • ExternalSigner (17-17)
  • NetworkArg (19-22)
  • NetworkArg (19-19)
  • Enumerate (24-60)
  • Enumerate (24-24)
  • DisplayAddress (62-65)
  • DisplayAddress (62-62)
  • GetDescriptors (67-70)
  • GetDescriptors (67-67)
  • SignTransaction (72-117)
  • SignTransaction (72-72)
src/wallet/wallet.h (5)
src/wallet/wallet.cpp (19)
  • DisplayAddress (2533-2545)
  • DisplayAddress (2533-2533)
  • FillPSBT (2060-2109)
  • FillPSBT (2060-2060)
  • IsMine (1449-1453)
  • IsMine (1449-1449)
  • IsMine (1455-1459)
  • IsMine (1455-1455)
  • IsMine (1461-1469)
  • IsMine (1461-1461)
  • IsMine (1471-1478)
  • IsMine (1471-1471)
  • IsMine (1480-1491)
  • IsMine (1480-1480)
  • outpoint (1082-1082)
  • GetScriptPubKeyMans (3731-3741)
  • GetScriptPubKeyMans (3731-3731)
  • IsInternalScriptPubKeyMan (3965-3987)
  • IsInternalScriptPubKeyMan (3965-3965)
src/wallet/external_signer_scriptpubkeyman.cpp (4)
  • DisplayAddress (53-62)
  • DisplayAddress (53-53)
  • FillPSBT (65-86)
  • FillPSBT (65-65)
src/external_signer.cpp (2)
  • DisplayAddress (62-65)
  • DisplayAddress (62-62)
src/wallet/interfaces.cpp (21)
  • dest (209-213)
  • dest (209-209)
  • dest (222-225)
  • dest (222-222)
  • dest (226-229)
  • dest (226-226)
  • dest (230-248)
  • dest (230-233)
  • dest (263-267)
  • dest (263-263)
  • dest (268-272)
  • dest (268-268)
  • outpoint (397-397)
  • outpoint (397-397)
  • outpoint (398-398)
  • outpoint (398-398)
  • script (188-195)
  • script (188-188)
  • script (204-208)
  • script (204-204)
  • spk_man (214-221)
src/wallet/scriptpubkeyman.h (5)
  • ScriptPubKeyMan (170-233)
  • ScriptPubKeyMan (178-178)
  • bool (185-185)
  • bool (230-230)
  • bool (236-236)
src/qt/optionsdialog.cpp (1)
src/node/interfaces.cpp (1)
  • ENABLE_EXTERNAL_SIGNER (530-551)
test/functional/mocks/invalid_signer.py (2)
test/functional/mocks/signer.py (3)
  • perform_pre_checks (11-18)
  • enumerate (20-21)
  • getdescriptors (23-37)
test/functional/mocks/multi_signers.py (1)
  • enumerate (10-12)
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 (848-874)
  • disconnect_nodes (744-778)
test/functional/test_framework/util.py (1)
  • assert_raises_rpc_error (132-148)
src/wallet/rpc/transactions.cpp (8)
  • abandontransaction (803-842)
  • abandontransaction (803-803)
  • gettransaction (694-801)
  • gettransaction (694-694)
  • listsinceblock (554-692)
  • listsinceblock (554-554)
  • listtransactions (442-552)
  • listtransactions (442-442)
src/rpc/register.h (2)
src/rpc/external_signer.cpp (2)
  • RegisterSignerRPCCommands (61-69)
  • RegisterSignerRPCCommands (61-61)
src/node/interfaces.cpp (1)
  • ENABLE_EXTERNAL_SIGNER (530-551)
src/wallet/rpc/backup.cpp (1)
src/wallet/scriptpubkeyman.h (1)
  • wallet (28-253)
src/wallet/spend.cpp (2)
src/interfaces/wallet.h (2)
  • wallet (39-46)
  • wallet (389-389)
src/node/interfaces.cpp (2)
  • coins (998-998)
  • coins (998-998)
test/functional/rpc_help.py (2)
test/functional/test_framework/test_framework.py (2)
  • is_external_signer_compiled (1115-1117)
  • is_zmq_compiled (1135-1137)
test/functional/test_framework/util.py (1)
  • assert_equal (69-74)
test/functional/rpc_signer.py (4)
test/functional/test_framework/test_framework.py (1)
  • skip_if_no_external_signer (1106-1109)
test/functional/test_framework/util.py (2)
  • assert_equal (69-74)
  • assert_raises_rpc_error (132-148)
test/functional/wallet_signer.py (5)
  • mock_signer_path (21-26)
  • set_test_params (42-48)
  • skip_test_if_missing_module (50-52)
  • set_mock_result (54-56)
  • clear_mock_result (58-59)
src/rpc/external_signer.cpp (2)
  • enumeratesigners (16-59)
  • enumeratesigners (16-16)
src/qt/walletmodeltransaction.h (1)
src/qt/walletmodeltransaction.cpp (2)
  • setWtx (29-32)
  • setWtx (29-29)
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)
src/interfaces/node.h (2)
src/external_signer.cpp (1)
  • ExternalSigner (17-17)
src/external_signer.h (1)
  • ExternalSigner (18-64)
src/wallet/walletdb.cpp (1)
src/node/interfaces.cpp (1)
  • ENABLE_EXTERNAL_SIGNER (530-551)
src/wallet/scriptpubkeyman.h (3)
src/wallet/scriptpubkeyman.cpp (6)
  • MarkUnusedAddresses (305-340)
  • MarkUnusedAddresses (305-305)
  • MarkUnusedAddresses (2019-2045)
  • MarkUnusedAddresses (2019-2019)
  • MarkReserveKeysAsUsed (1624-1649)
  • MarkReserveKeysAsUsed (1624-1624)
src/wallet/walletutil.h (1)
  • WalletDescriptor (70-103)
src/wallet/external_signer_scriptpubkeyman.cpp (2)
  • SetupDescriptor (17-40)
  • SetupDescriptor (17-17)
src/interfaces/wallet.h (2)
src/qt/walletmodel.cpp (2)
  • displayAddress (582-592)
  • displayAddress (582-582)
src/wallet/interfaces.cpp (12)
  • dest (209-213)
  • dest (209-209)
  • dest (222-225)
  • dest (222-222)
  • dest (226-229)
  • dest (226-226)
  • dest (230-248)
  • dest (230-233)
  • dest (263-267)
  • dest (263-263)
  • dest (268-272)
  • dest (268-268)
src/wallet/init.cpp (2)
src/node/interfaces.cpp (1)
  • ENABLE_EXTERNAL_SIGNER (530-551)
src/dummywallet.cpp (1)
  • argsman (31-31)
test/functional/test_framework/test_node.py (1)
src/wallet/rpc/wallet.cpp (2)
  • createwallet (615-713)
  • createwallet (615-615)
src/external_signer.cpp (4)
src/external_signer.h (1)
  • ExternalSigner (18-64)
src/util/system.cpp (2)
  • RunCommandParseJSON (1401-1438)
  • RunCommandParseJSON (1401-1401)
src/wallet/wallet.cpp (6)
  • DisplayAddress (2533-2545)
  • DisplayAddress (2533-2533)
  • SignTransaction (2026-2043)
  • SignTransaction (2026-2026)
  • SignTransaction (2045-2058)
  • SignTransaction (2045-2045)
src/wallet/external_signer_scriptpubkeyman.cpp (2)
  • DisplayAddress (53-62)
  • DisplayAddress (53-53)
test/functional/mocks/signer.py (2)
test/functional/mocks/invalid_signer.py (3)
  • perform_pre_checks (11-18)
  • enumerate (20-21)
  • getdescriptors (23-39)
test/functional/mocks/multi_signers.py (1)
  • enumerate (10-12)
src/wallet/wallet.cpp (5)
src/wallet/interfaces.cpp (26)
  • txout (463-467)
  • txout (463-463)
  • txout (473-477)
  • txout (473-473)
  • dest (209-213)
  • dest (209-209)
  • dest (222-225)
  • dest (222-222)
  • dest (226-229)
  • dest (226-226)
  • dest (230-248)
  • dest (230-233)
  • dest (263-267)
  • dest (263-263)
  • dest (268-272)
  • dest (268-268)
  • script (188-195)
  • script (188-188)
  • script (204-208)
  • script (204-204)
  • LOCK (249-258)
  • LOCK (259-262)
  • LOCK (290-294)
  • LOCK (295-299)
  • LOCK (352-360)
  • LOCK (478-490)
src/wallet/scriptpubkeyman.cpp (9)
  • IsMine (165-177)
  • IsMine (165-165)
  • IsMine (179-183)
  • IsMine (179-179)
  • IsMine (1828-1835)
  • IsMine (1828-1828)
  • IsMine (1837-1841)
  • IsMine (1837-1837)
  • id (1684-1684)
src/wallet/scriptpubkeyman.h (2)
  • IsMine (180-180)
  • IsMine (181-181)
src/wallet/external_signer_scriptpubkeyman.cpp (4)
  • DisplayAddress (53-62)
  • DisplayAddress (53-53)
  • GetExternalSigner (42-51)
  • GetExternalSigner (42-42)
src/external_signer.cpp (2)
  • DisplayAddress (62-65)
  • DisplayAddress (62-62)
src/node/interfaces.cpp (1)
src/external_signer.cpp (2)
  • Enumerate (24-60)
  • Enumerate (24-24)
src/wallet/external_signer_scriptpubkeyman.h (4)
src/wallet/scriptpubkeyman.h (6)
  • wallet (28-253)
  • DescriptorScriptPubKeyMan (528-650)
  • WalletStorage (34-246)
  • bool (185-185)
  • bool (230-230)
  • bool (236-236)
src/wallet/external_signer_scriptpubkeyman.cpp (8)
  • SetupDescriptor (17-40)
  • SetupDescriptor (17-17)
  • GetExternalSigner (42-51)
  • GetExternalSigner (42-42)
  • DisplayAddress (53-62)
  • DisplayAddress (53-53)
  • FillPSBT (65-86)
  • FillPSBT (65-65)
src/wallet/wallet.cpp (4)
  • DisplayAddress (2533-2545)
  • DisplayAddress (2533-2533)
  • FillPSBT (2060-2109)
  • FillPSBT (2060-2060)
src/wallet/scriptpubkeyman.cpp (4)
  • FillPSBT (743-787)
  • FillPSBT (743-743)
  • FillPSBT (2314-2381)
  • FillPSBT (2314-2314)
test/functional/wallet_listtransactions.py (4)
test/functional/test_framework/test_framework.py (2)
  • stop_nodes (684-692)
  • start_nodes (660-678)
test/functional/test_framework/util.py (2)
  • assert_equal (69-74)
  • assert_raises_rpc_error (132-148)
src/wallet/rpc/addresses.cpp (6)
  • keypoolrefill (263-305)
  • keypoolrefill (263-263)
  • getnewaddress (17-56)
  • getnewaddress (17-17)
  • getaddressinfo (418-563)
  • getaddressinfo (418-418)
src/wallet/rpc/transactions.cpp (2)
  • listtransactions (442-552)
  • listtransactions (442-442)
test/functional/wallet_signer.py (6)
test/functional/test_framework/test_framework.py (9)
  • set_test_params (415-417)
  • set_test_params (1473-1475)
  • skip_test_if_missing_module (423-425)
  • skip_test_if_missing_module (1477-1478)
  • skip_if_no_external_signer (1106-1109)
  • run_test (494-496)
  • run_test (1480-1482)
  • restart_node (694-697)
  • is_bdb_compiled (1147-1149)
test/functional/rpc_signer.py (6)
  • mock_signer_path (21-26)
  • set_test_params (28-36)
  • skip_test_if_missing_module (38-39)
  • set_mock_result (41-43)
  • clear_mock_result (45-46)
  • run_test (48-73)
src/wallet/rpc/wallet.cpp (14)
  • res (591-591)
  • createwallet (615-713)
  • createwallet (615-615)
  • getwalletinfo (152-276)
  • getwalletinfo (152-152)
  • getnewaddress (1098-1098)
  • getaddressinfo (1097-1097)
  • result (308-308)
  • result (766-766)
  • result (1088-1088)
  • walletdisplayaddress (1108-1108)
  • walletcreatefundedpsbt (1151-1151)
  • walletprocesspsbt (1150-1150)
  • send (1149-1149)
test/functional/test_framework/test_node.py (2)
  • createwallet (884-889)
  • get_wallet_rpc (363-369)
src/wallet/rpc/addresses.cpp (10)
  • getnewaddress (17-56)
  • getnewaddress (17-17)
  • getaddressinfo (418-563)
  • getaddressinfo (418-418)
  • result (254-254)
  • result (707-707)
  • walletdisplayaddress (674-712)
  • walletdisplayaddress (674-674)
  • dest (382-382)
  • dest (382-382)
src/wallet/rpc/spend.cpp (11)
  • result (636-636)
  • result (730-730)
  • result (903-903)
  • result (998-998)
  • result (1127-1127)
  • walletcreatefundedpsbt (1009-1134)
  • walletcreatefundedpsbt (1009-1009)
  • walletprocesspsbt (930-1007)
  • walletprocesspsbt (930-930)
  • send (737-928)
  • send (737-737)
test/functional/mocks/multi_signers.py (2)
test/functional/mocks/invalid_signer.py (1)
  • enumerate (20-21)
test/functional/mocks/signer.py (1)
  • enumerate (20-21)
src/qt/walletmodel.cpp (1)
src/wallet/interfaces.cpp (28)
  • dest (209-213)
  • dest (209-209)
  • dest (222-225)
  • dest (222-222)
  • dest (226-229)
  • dest (226-226)
  • dest (230-248)
  • dest (230-233)
  • dest (263-267)
  • dest (263-263)
  • dest (268-272)
  • dest (268-268)
  • m_wallet (157-160)
  • m_wallet (165-165)
  • m_wallet (174-174)
  • m_wallet (175-175)
  • m_wallet (181-181)
  • m_wallet (182-182)
  • m_wallet (442-445)
  • m_wallet (446-449)
  • m_wallet (521-521)
  • m_wallet (522-522)
  • m_wallet (523-523)
  • m_wallet (524-524)
  • m_wallet (525-525)
  • m_wallet (526-526)
  • m_wallet (531-531)
  • m_wallet (591-591)
🪛 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)

test/functional/rpc_signer.py

[error] 52-52: continuation line under-indented for visual indent

(E128)


[error] 57-57: continuation line under-indented for visual indent

(E128)


[error] 63-63: continuation line under-indented for visual indent

(E128)


[error] 69-69: continuation line under-indented for visual indent

(E128)

test/functional/mocks/signer.py

[error] 96-96: indentation is not a multiple of 4

(E111)

test/functional/wallet_signer.py

[error] 115-115: continuation line under-indented for visual indent

(E128)


[error] 116-116: closing bracket does not match visual indentation

(E124)


[error] 135-135: continuation line missing indentation or outdented

(E122)

test/functional/mocks/multi_signers.py

[error] 12-12: continuation line under-indented for visual indent

(E128)

🪛 LanguageTool
doc/external-signer.md

[style] ~70-~70: Consider a more concise word here.
Context: ...ue, "txid": } ``` ## Signer API In order to be compatible with Dash Core any signer...

(IN_ORDER_TO_PREMIUM)


[grammar] ~167-~167: Use a hyphen to join words.
Context: ... support address types, in a BIP44/49/84 compatible manner. The `walletdisplayad...

(QB_NEW_EN_HYPHEN)

doc/build-osx.md

[grammar] ~87-~87: Ensure spelling is correct
Context: ...descriptor wallets. macOS ships with a useable sqlite package, meaning you don't nee...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
doc/external-signer.md

14-14: Dollar signs used before commands without showing output

(MD014, commands-show-output)


25-25: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


40-40: Dollar signs used before commands without showing output

(MD014, commands-show-output)


48-48: Dollar signs used before commands without showing output

(MD014, commands-show-output)


49-49: Dollar signs used before commands without showing output

(MD014, commands-show-output)


59-59: Dollar signs used before commands without showing output

(MD014, commands-show-output)


80-80: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


98-98: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


115-115: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


122-122: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


141-141: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


147-147: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

doc/build-osx.md

83-83: Heading levels should only increment by one level at a time
Expected: h5; Actual: h6

(MD001, heading-increment)

🪛 Ruff (0.14.1)
test/functional/mocks/invalid_signer.py

20-20: Unused function argument: args

(ARG001)

test/functional/wallet_abandonconflict.py

120-120: Test for membership should be not in

Convert to not in

(E713)

test/functional/mocks/signer.py

20-20: Unused function argument: args

(ARG001)

test/functional/test_framework/test_framework.py

1109-1109: Avoid specifying long messages outside the exception class

(TRY003)

test/functional/mocks/multi_signers.py

10-10: Unused function argument: args

(ARG001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build container / Build container

fi

BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$DEPENDS_DIR/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib"
BITCOIN_CONFIG_ALL="---enable-external-signer -disable-dependency-tracking --prefix=$DEPENDS_DIR/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix the malformed configure flag (three dashes instead of two).

The configure flag has three leading dashes (---enable-external-signer) instead of the standard two dashes (--enable-external-signer). This syntax error will cause the configure script to either fail or silently ignore the flag, preventing external signer support from being enabled.

Apply this diff:

-BITCOIN_CONFIG_ALL="---enable-external-signer -disable-dependency-tracking --prefix=$DEPENDS_DIR/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib"
+BITCOIN_CONFIG_ALL="--enable-external-signer --disable-dependency-tracking --prefix=$DEPENDS_DIR/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib"

Note: Also fixed -disable-dependency-tracking to use double dashes for consistency.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
BITCOIN_CONFIG_ALL="---enable-external-signer -disable-dependency-tracking --prefix=$DEPENDS_DIR/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib"
BITCOIN_CONFIG_ALL="--enable-external-signer --disable-dependency-tracking --prefix=$DEPENDS_DIR/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib"
🤖 Prompt for AI Agents
In ci/dash/build_src.sh around line 36, the configure flags are malformed:
'---enable-external-signer' uses three dashes and '-disable-dependency-tracking'
uses a single dash; update them to the standard GNU-style flags by replacing
'---enable-external-signer' with '--enable-external-signer' and
'-disable-dependency-tracking' with '--disable-dependency-tracking' so the
configure script correctly recognizes both options.

Comment on lines +1589 to +1590
LDFLAGS="$LDFLAGS $PTHREAD_CFLAGS"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Link probe uses PTHREAD_CFLAGS instead of PTHREAD_LIBS.

This can cause link failures on platforms requiring -lpthread at link time. Use PTHREAD_LIBS for LDFLAGS.

-  dnl Boost 1.73 and older require the following workaround.
-  LDFLAGS="$LDFLAGS $PTHREAD_CFLAGS"
+  dnl Boost 1.73 and older require the following workaround.
+  LDFLAGS="$LDFLAGS $PTHREAD_LIBS"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
LDFLAGS="$LDFLAGS $PTHREAD_CFLAGS"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
LDFLAGS="$LDFLAGS $PTHREAD_LIBS"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
🤖 Prompt for AI Agents
In configure.ac around lines 1589-1590, LDFLAGS is being appended with
PTHREAD_CFLAGS (compiler flags) instead of PTHREAD_LIBS (linker flags); change
the append to use $PTHREAD_LIBS so the link probe gets the proper linker flags
(e.g., -lpthread) — replace usage of PTHREAD_CFLAGS in the LDFLAGS assignment
with PTHREAD_LIBS and ensure quotes/spacing match the surrounding code.

Comment on lines +31 to +58
for (UniValue signer : result.getValues()) {
// Check for error
const UniValue& error = signer.find_value("error");
if (!error.isNull()) {
if (!error.isStr()) {
throw std::runtime_error(strprintf("'%s' error", command));
}
throw std::runtime_error(strprintf("'%s' error: %s", command, error.getValStr()));
}
// Check if fingerprint is present
const UniValue& fingerprint = signer.find_value("fingerprint");
if (fingerprint.isNull()) {
throw std::runtime_error(strprintf("'%s' received invalid response, missing signer fingerprint", command));
}
const std::string fingerprintStr = fingerprint.get_str();
// Skip duplicate signer
bool duplicate = false;
for (const ExternalSigner& signer : signers) {
if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
}
if (duplicate) break;
std::string name = "";
const UniValue& model_field = signer.find_value("model");
if (model_field.isStr() && model_field.getValStr() != "") {
name += model_field.getValStr();
}
signers.push_back(ExternalSigner(command, chain, fingerprintStr, name));
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Avoid shadowing and unnecessary copies in enumerate loop.

Use const references and distinct names for clarity.

-    for (UniValue signer : result.getValues()) {
+    for (const UniValue& entry : result.getValues()) {
@@
-        const UniValue& error = signer.find_value("error");
+        const UniValue& error = entry.find_value("error");
@@
-        const UniValue& fingerprint = signer.find_value("fingerprint");
+        const UniValue& fingerprint = entry.find_value("fingerprint");
@@
-        for (const ExternalSigner& signer : signers) {
-            if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
+        for (const ExternalSigner& existing : signers) {
+            if (existing.m_fingerprint == fingerprintStr) {
+                duplicate = true;
+                break;
+            }
         }
@@
-        const UniValue& model_field = signer.find_value("model");
+        const UniValue& model_field = entry.find_value("model");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (UniValue signer : result.getValues()) {
// Check for error
const UniValue& error = signer.find_value("error");
if (!error.isNull()) {
if (!error.isStr()) {
throw std::runtime_error(strprintf("'%s' error", command));
}
throw std::runtime_error(strprintf("'%s' error: %s", command, error.getValStr()));
}
// Check if fingerprint is present
const UniValue& fingerprint = signer.find_value("fingerprint");
if (fingerprint.isNull()) {
throw std::runtime_error(strprintf("'%s' received invalid response, missing signer fingerprint", command));
}
const std::string fingerprintStr = fingerprint.get_str();
// Skip duplicate signer
bool duplicate = false;
for (const ExternalSigner& signer : signers) {
if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
}
if (duplicate) break;
std::string name = "";
const UniValue& model_field = signer.find_value("model");
if (model_field.isStr() && model_field.getValStr() != "") {
name += model_field.getValStr();
}
signers.push_back(ExternalSigner(command, chain, fingerprintStr, name));
}
for (const UniValue& entry : result.getValues()) {
// Check for error
const UniValue& error = entry.find_value("error");
if (!error.isNull()) {
if (!error.isStr()) {
throw std::runtime_error(strprintf("'%s' error", command));
}
throw std::runtime_error(strprintf("'%s' error: %s", command, error.getValStr()));
}
// Check if fingerprint is present
const UniValue& fingerprint = entry.find_value("fingerprint");
if (fingerprint.isNull()) {
throw std::runtime_error(strprintf("'%s' received invalid response, missing signer fingerprint", command));
}
const std::string fingerprintStr = fingerprint.get_str();
// Skip duplicate signer
bool duplicate = false;
for (const ExternalSigner& existing : signers) {
if (existing.m_fingerprint == fingerprintStr) {
duplicate = true;
break;
}
}
if (duplicate) break;
std::string name = "";
const UniValue& model_field = entry.find_value("model");
if (model_field.isStr() && model_field.getValStr() != "") {
name += model_field.getValStr();
}
signers.push_back(ExternalSigner(command, chain, fingerprintStr, name));
}

Comment on lines +47 to +52
bool duplicate = false;
for (const ExternalSigner& signer : signers) {
if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
}
if (duplicate) break;
std::string name = "";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Duplicate handling aborts enumeration prematurely.

Using break on duplicate fingerprint stops processing remaining entries. Use continue to skip only the duplicate.

-        if (duplicate) break;
+        if (duplicate) continue;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bool duplicate = false;
for (const ExternalSigner& signer : signers) {
if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
}
if (duplicate) break;
std::string name = "";
bool duplicate = false;
for (const ExternalSigner& signer : signers) {
if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
}
if (duplicate) continue;
std::string name = "";
🤖 Prompt for AI Agents
In src/external_signer.cpp around lines 47 to 52, the loop uses break when a
duplicate fingerprint is found which aborts the entire enumeration; change the
control flow to skip only the current entry by replacing the break with continue
so the loop keeps processing remaining signers, ensuring duplicate remains
detection stays the same and no other entries are prematurely skipped.

Comment on lines +87 to +90
if (!std::any_of(psbtx.inputs.begin(), psbtx.inputs.end(), matches_signer_fingerprint)) {
error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str());
return false;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t include full PSBT in error strings.

The current error leaks base64 PSBT, which is sensitive. Keep the message minimal.

-        error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str());
+        error = "Signer fingerprint " + m_fingerprint + " does not match any PSBT input.";
🤖 Prompt for AI Agents
In src/external_signer.cpp around lines 87 to 90, the error string currently
appends the full base64 PSBT (sensitive data); remove the
EncodeBase64(ssTx.str()) portion and replace the message with a minimal,
non-sensitive description (for example: indicate the signer fingerprint
mismatched and optionally include non-sensitive metadata such as the PSBT input
count or PSBT txid/hash if available), then return false as before.

Comment on lines +93 to +96
if not sys.stdin.isatty():
buffer = sys.stdin.read()
if buffer and buffer.rstrip() != "":
sys.argv.extend(buffer.rstrip().split(" "))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the indentation on line 96.

Line 96 uses 3 spaces instead of a multiple of 4, violating PEP 8 style guidelines.

Apply this diff:

 if not sys.stdin.isatty():
     buffer = sys.stdin.read()
     if buffer and buffer.rstrip() != "":
-       sys.argv.extend(buffer.rstrip().split(" "))
+        sys.argv.extend(buffer.rstrip().split(" "))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not sys.stdin.isatty():
buffer = sys.stdin.read()
if buffer and buffer.rstrip() != "":
sys.argv.extend(buffer.rstrip().split(" "))
if not sys.stdin.isatty():
buffer = sys.stdin.read()
if buffer and buffer.rstrip() != "":
sys.argv.extend(buffer.rstrip().split(" "))
🧰 Tools
🪛 Flake8 (7.3.0)

[error] 96-96: indentation is not a multiple of 4

(E111)

🤖 Prompt for AI Agents
In test/functional/mocks/signer.py around lines 93 to 96, the third line in the
if-block (currently using 3-space indentation) violates PEP 8; re-indent that
line to use a multiple of 4 spaces (align it with the other block lines, i.e.,
replace the 3-space indent with 4 spaces) so the if-block uses consistent
4-space indentation.

Comment on lines +108 to 115
if self.is_external_signer_compiled():
components.append('Signer')

if self.is_zmq_compiled():
components.append('Zmq')

assert_equal(titles, components)
assert_equal(titles, sorted(components))

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make the category comparison truly order‑insensitive.

Sort titles as well, not just components.

-        assert_equal(titles, sorted(components))
+        assert_equal(sorted(titles), sorted(components))
🤖 Prompt for AI Agents
In test/functional/rpc_help.py around lines 108 to 115, the comparison only
sorts the components list but not titles, which makes the assertion
order-sensitive; update the assertion to compare both lists in a deterministic
order by sorting titles as well (e.g., assert_equal(sorted(titles),
sorted(components))) so the comparison is truly order-insensitive.

nAB = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txAB1)["details"] if tx_out["amount"] == Decimal("14.99998"))
nAB = next(tx_out["vout"] for tx_out in alice.gettransaction(txAB1)["details"] if tx_out["amount"] == Decimal("14.99998"))

#Create a child tx spending AB1 and C
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix comment formatting.

The comment should have a space after the # symbol per PEP 8.

Apply this diff:

-        #Create a child tx spending AB1 and C
+        # Create a child tx spending AB1 and C
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#Create a child tx spending AB1 and C
# Create a child tx spending AB1 and C
🧰 Tools
🪛 Flake8 (7.3.0)

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

(E265)

🤖 Prompt for AI Agents
In test/functional/wallet_abandonconflict.py around line 79, the inline comment
"#Create a child tx spending AB1 and C" does not follow PEP 8; add a space after
the '#' so it reads "# Create a child tx spending AB1 and C". Update only the
comment text to include the leading space and keep the rest of the line
unchanged.

assert_equal(balances['untrusted_pending'] + balances['trusted'], newbalance)
# Also shouldn't show up in listunspent
assert not txABC2 in [utxo["txid"] for utxo in self.nodes[0].listunspent(0)]
assert not txABC2 in [utxo["txid"] for utxo in alice.listunspent(0)]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use idiomatic membership test.

Replace not txABC2 in with txABC2 not in for better readability per PEP 8.

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)]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert not txABC2 in [utxo["txid"] for utxo in alice.listunspent(0)]
assert txABC2 not in [utxo["txid"] for utxo in alice.listunspent(0)]
🧰 Tools
🪛 Flake8 (7.3.0)

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

(E713)

🪛 Ruff (0.14.1)

120-120: Test for membership should be not in

Convert to not in

(E713)

🤖 Prompt for AI Agents
In test/functional/wallet_abandonconflict.py around line 120, replace the
non-idiomatic membership test "not txABC2 in ..." with the PEP 8 preferred
"txABC2 not in ...": update the assertion to use "txABC2 not in [utxo['txid']
for utxo in alice.listunspent(0)]" so the test reads more clearly and follows
Python style guidelines.

Comment on lines +124 to +126
addr1 = self.nodes[0].getnewaddress("pizza1", 'legacy')
addr2 = self.nodes[0].getnewaddress("pizza2", 'p2sh-segwit')
addr3 = self.nodes[0].getnewaddress("pizza3", 'bech32')
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Incorrect RPC usage: Dash getnewaddress does not accept address_type; bech32/p2sh‑segwit are unsupported.

Dash Core getnewaddress only takes an optional label and returns base58 addresses; there is no address_type argument (and Dash does not use bech32). This test will fail with “too many parameters” or invalid type. Update to call getnewaddress with just the label.

-        addr1 = self.nodes[0].getnewaddress("pizza1", 'legacy')
-        addr2 = self.nodes[0].getnewaddress("pizza2", 'p2sh-segwit')
-        addr3 = self.nodes[0].getnewaddress("pizza3", 'bech32')
+        addr1 = self.nodes[0].getnewaddress("pizza1")
+        addr2 = self.nodes[0].getnewaddress("pizza2")
+        addr3 = self.nodes[0].getnewaddress("pizza3")

Evidence: Dash Core wallet RPC docs list only a single label parameter and base58 result; no address_type is documented. Also, the provided src/wallet/rpc/addresses.cpp snippet shows a single parameter “label”. (docs.dash.org)


🏁 Script executed:

#!/bin/bash
# Verify Dash's RPC signature in-tree to avoid relying on assumptions.
rg -nP 'RPCHelpMan\s+getnewaddress\s*\(\)' -n -C3 --no-heading src | sed -n '1,120p'

Length of output: 1266


I need to see the full getnewaddress() function implementation to verify the parameter signature.


---

🏁 Script executed:

```shell
#!/bin/bash
# Extract the full getnewaddress() function definition to see all parameters
sed -n '17,100p' src/wallet/rpc/addresses.cpp

Length of output: 3069


Fix incorrect getnewaddress RPC calls: remove address_type parameter.

The Dash RPC getnewaddress() function definition (src/wallet/rpc/addresses.cpp:17-56) accepts only a single optional label parameter. The test code incorrectly passes a second address_type argument ('legacy', 'p2sh-segwit', 'bech32'), which Dash does not support and will cause the RPC call to fail.

-        addr1 = self.nodes[0].getnewaddress("pizza1", 'legacy')
-        addr2 = self.nodes[0].getnewaddress("pizza2", 'p2sh-segwit')
-        addr3 = self.nodes[0].getnewaddress("pizza3", 'bech32')
+        addr1 = self.nodes[0].getnewaddress("pizza1")
+        addr2 = self.nodes[0].getnewaddress("pizza2")
+        addr3 = self.nodes[0].getnewaddress("pizza3")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
addr1 = self.nodes[0].getnewaddress("pizza1", 'legacy')
addr2 = self.nodes[0].getnewaddress("pizza2", 'p2sh-segwit')
addr3 = self.nodes[0].getnewaddress("pizza3", 'bech32')
addr1 = self.nodes[0].getnewaddress("pizza1")
addr2 = self.nodes[0].getnewaddress("pizza2")
addr3 = self.nodes[0].getnewaddress("pizza3")
🤖 Prompt for AI Agents
In test/functional/wallet_listtransactions.py around lines 124 to 126, the calls
to getnewaddress pass an unsupported second address_type argument ('legacy',
'p2sh-segwit', 'bech32'); Dash's getnewaddress RPC only accepts an optional
label. Remove the second argument from each call so each invocation is just
getnewaddress("label") (e.g., getnewaddress("pizza1")), leaving label usage
intact and avoiding the invalid parameter that causes the RPC to fail.

…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants