#33112 wallet: relax external_signer flag constraints

ReviewabilityReviewsAgreementSize
#33112 Sjors wallet: relax external_signer flag constraintsReady0 (+3)Strong179 + 91 tests
  • Allow external signer wallets to keep private keys enabled and let existing wallets toggle the signer flag
  • Enables hybrid multisig and MuSig2 setups combining local hot keys with external signer devices
  • Ready to review
  • Broad support with multiple tested ACKs and all prior objections addressed
  • Unblocks hybrid hot-key and external-signer multisig workflows (PraneethGunas)
  • Verified by testing on mock signers, CLI, and GUI workflows (adyshimony, naiyoma, jeanpablojp)
  • Confirmed hardware signer fix on Ledger Flex (PraneethGunas)
  • Verified signing reduces repeated calls to the external device (fametrano)
  • 179 lines added or modified outside tests
  • 91 lines added or modified in tests
  • 136 lines removed in total
  • 11 files, 10 commits
Wallet#28 of 92
P2 · new feature
  • P2 because it unblocks hybrid multisig and MuSig2 setups combining hot keys with external signer devices
  • Users can manage joint hot and hardware-signed keys in a single wallet instead of forcing watch-only mode
  • Ranking pass: Moved down: prerequisite value for MuSig2/hardware multisig is real but speculative relative to shipped-bug fixes.
Reviewability: ReadyStalePaused
Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets
Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs.
Size: lines added or modified outside tests, then in tests; greener = smaller.

Goal

This pull request relaxes constraints on the `external_signer` wallet flag in Bitcoin Core. It removes the requirement that external signer wallets must have private keys disabled, makes the `external_signer` flag mutable via `setwalletflag` (automatically reloading the wallet when toggled), ensures blank signer wallets do not auto-import keys, and allows the wallet to sign with internal keys before delegating remaining inputs to the external signer.

Problem: Previously, the `external_signer` flag forced a wallet to be watch-only and immutable from creation, which blocked multisig or MuSig2 setups where the node holds both a hot key and relies on an external signer device.

Reviewability

Ready

Nothing known that would invalidate a review now.

Waiting on (from the thread read):

Agreement

Strong

substantive support, no open objection (rkrux, adyshimony, naiyoma, jeanpablojp, PraneethGunas, fametrano)

Thread notes: Added participant fametrano with supportive testing feedback regarding reduced signer calls. Added questions from rkrux and PraneethGunas and an interface suggestion from rkrux on error messaging. Verified existing objections were fixed and confirmed clears_with fits change for defects. / Added fametrano to participants and support after testing repeated signer call reductions; converted previous objections to clears_with=change and updated PraneethGunas's objection status to accepted; recorded

Objections, suggestions, and questions:

ReviewerTypeHarm, request, or questionStatusMust beAuthor repliedQuote
rkrux (member)question, approachAsks whether there are plans to add a test for an external signer wallet with private keys enabled in a later PR.answered · author replied that testing would be easier after MuSig2 landsansweredyes2025-08-11: 'Do you intend to add a test for an external signer wallet with private keys enabled in a later PR?'
Status from: 2025-08-15: Sjors: 'I think that'll become more relevant, and probably easier to test, after MuSig2 support lands.'
adyshimony (none)suggestion, maintenanceAdd a functional test explicitly enabling private keys upon external signer wallet creation.answered · fix pushed · naiyoma seconded the suggestion and author later added test coverageansweredby others2026-02-26: 'Maybe add a check that create the wallet with explicitly values upon creation?'
Status from: 2026-03-08: naiyoma: '+1 on testing when `disable_private_keys=False `'
rkrux (member)suggestion, interfaceUpdate the error message so it does not state private keys are disabled when an external signer is used without private keys disabled.fixed · fix pushed · author split the change into its own commit with an updated error message and testchangedyes2026-05-04: 'After the next commit, the associated error here ("Private keys are disabled for this wallet") doesn't seem correct if it's an external signer.'
Status from: 2026-05-06: Sjors: 'Indeed. I also split this change into its own commit 996064c6db85023e0f8aa4f91277ac494821292d and added a test.'
jeanpablojp (contributor)objection, correctnessToggling external_signer flag on an existing wallet leaves descriptor ScriptPubKeyMans out of sync, causing wallet operations like display address and send to fail until reloaded.fixed · fix pushed · reloading the wallet on toggle keeps SPKMs in sync; no further objectionchangedyes2026-08-25: 'The SPKM subclass is chosen when it's constructed, and toggling doesn't rebuild the existing ones, so after unloadwallet/loadwallet, with nothing else changed, both work.'
Status from: 2026-08-26: Sjors: 'it's actually easier to just have the RPC automatically reload the whole wallet for this flag. So I went with that.'
jeanpablojp (contributor)objection, correctnessToggling external_signer flag off leaves leftover external signer SPKMs, causing subsequent spends to abort in FillPSBT without a reload.fixed · fix pushed · fixed by the automatic reload on flag togglechangedyes2026-08-25: 'Imported a descriptor with the flag on, turned the flag off, imported another one, and without reloading I can't spend from either.'
Status from: 2026-08-26: Sjors: 'That should be fixed now, thanks to the reload.'
jeanpablojp (contributor)objection, safetySetting external_signer flag on a build without external signer support bricks the wallet from loading on that binary.fixed · fix pushed · added compile guard for ENABLE_EXTERNAL_SIGNER; no further objectionchangedyes2026-08-25: 'On a build with -DENABLE_EXTERNAL_SIGNER=OFF I set the flag on an ordinary wallet and it no longer loads on that binary, not even after a restart'
Status from: 2026-08-26: Sjors: 'Added a guard for completeness.'
PraneethGunas (none)objection, correctnessIn an external signer wallet with private keys enabled, FillPSBT attempts to sign hot key UTXOs using the hardware signer and fails.accepted · fix pushed · reviewer confirmed the fix on Ledger Flexchangedyes2026-08-27: 'a descriptor holding a hot key also gets an ExternalSignerScriptPubKeyMan here, and at LoadDescriptorScriptPubKeyMan. Its FillPSBT() goes straight to the device when sign=true and never falls back.'
Status from: 2026-08-28: PraneethGunas: 'Confirmed fixed on a Ledger Flex, thanks.'
PraneethGunas (none)question, approachWhat is the intended wallet layout for a multisig where one cosigner is an external signer and another is a hot key, if mixing hot and cold descriptors in one wallet is discouraged?answered · author explained the descriptor setup and key export workflowansweredyes2026-08-28: 'If mixing hot and cold in one wallet isn't encouraged, what's the intended layout for a multisig where one cosigner is an external signer and another is a hot key, one wallet per key?'
Status from: 2026-08-31: Sjors: 'Ideally there's a single multisig descriptor for which the wallet has one key, e.g. musig2(core_xprv, ledger_xpub).'

Support:

  • rkrux (member) (ACK, 2025-08-11) [read]: Agree with the intent to relax these constraints for external signer wallets.
  • rkrux (member) (ACK, 2025-09-02): re-ACK 03978530ad8dc9124307d2ffc7d64c24b784be0e [not substantive]
  • adyshimony (none) (ACK, 2026-02-26) [tested: Qt UX]: Built and ran successfully all tests. Qt UX tested on regtest with mock signer
  • naiyoma (member) (Concept ACK, 2026-03-08) [both: bitcoin-qt, cli]: Reviewed commits and tested createwallet on bitcoin-qt and on cli
  • achow101 (member) (Concept ACK, 2026-04-30): Concept ACK [not substantive]
  • jeanpablojp (contributor) (Concept ACK, 2026-08-25) [tested: signer, wallet]: Built and ran the wallet and signer tests.
  • PraneethGunas (none) (Concept ACK, 2026-08-27): This greatly helps unblock using external signers and hot keys in the same wallet for a multisig setup
  • PraneethGunas (none) (ACK, 2026-08-29): ACK f4dd05644f3eada3656f619f71ab3b94fe08ec3d, 5a7c4309da24d9dd3ff5a8f7edc3d316cd9355c0 [not substantive]
  • fametrano (contributor) (2026-10-22) [both: test/functional/mocks/signer.py, test/functional/wallet_signer.py, wallet_signer.py]: This PR also reduces repeated external signer calls: on master one signing can ask the device once per descriptor.

Participants:

  • fanquake (member, neutral, 1 statements 2025-08-01 to 2025-08-01): reported CI build failure on maybe-uninitialized compiler warning
  • maflcko (member, neutral, 2 statements 2025-08-01 to 2025-08-01): suggested fixes and nits for the CI compiler warning
  • rkrux (member, support, 12 statements 2025-08-11 to 2026-05-04): ACKed the approach and implementation, reviewed test coverage, and suggested error message fix
  • adyshimony (none, support, 4 statements 2026-02-26 to 2026-02-26): tested Qt UX and CLI workflows and gave ACK
  • naiyoma (member, support, 2 statements 2026-03-08 to 2026-03-08): tested bitcoin-qt and CLI behavior and gave Concept ACK
  • achow101 (member, support, 1 statements 2026-04-30 to 2026-04-30): Concept ACK
  • jeanpablojp (contributor, objection, 4 statements 2026-08-25 to 2026-08-25): identified descriptor SPKM mismatches on flag toggling and missing compile guards
  • PraneethGunas (none, objection, 4 statements 2026-08-27 to 2026-08-29): found that spending hot key UTXOs failed on external signers, verified the fix, and ACKed
  • fametrano (contributor, support, 1 statements 2026-10-22 to 2026-10-22): tested signer calls and noted the PR reduces repeated external signer requests

Dependencies

Enables:

  • MuSig2 multisig support with external signers

Files

132 lines under test/bench/ci.

  • src/wallet/rpc/wallet.cpp +115/-46
  • test/functional/wallet_signer.py +83/-36
  • src/wallet/wallet.cpp +19/-17
  • src/qt/createwalletdialog.cpp +8/-20
  • src/wallet/external_signer_scriptpubkeyman.cpp +24/-4
  • src/wallet/wallet.h +11/-7
  • test/functional/rpc_signer.py +0/-5
  • test/functional/test_framework/test_framework.py +5/-0
  • test/functional/wallet_avoidreuse.py +3/-0
  • src/qt/walletmodel.cpp +1/-1
  • src/wallet/external_signer_scriptpubkeyman.h +1/-0

Card

The short text the ranking pass compares across PRs.

This PR relaxes constraints on external signer wallets in Bitcoin Core by allowing private keys to remain enabled and making the external_signer flag mutable via setwalletflag. This enables hybrid setups where a wallet contains both hot keys and external signer keys, which is a required prerequisite for MuSig2 and multisig hardware signer integration. Reviewers had previously flagged issues with SPKM construction during flag toggling and hot key PSBT signing, both of which have been resolved and verified with physical hardware testing. The PR is clean and ready for final review.

Data

dossier JSON · extract JSON · model ledger, generated 2026-10-23T01:18, confidence high, input hash a0e2699caf773410
thread read through 2026-10-22 (55 statements), head ac6c932e10
labels: Wallet

Categories

Wallet

P2 · new feature (ranking pass: Moved down: prerequisite value for MuSig2/hardware multisig is real but speculative relative to shipped-bug fixes.)

Enables hybrid hot-and-cold multisig and MuSig2 setups by relaxing the external signer watch-only constraint. Reviewers note this 'greatly helps unblock using external signers and hot keys in the same wallet for a multisig setup', expanding external signer functionality significantly.

Membership: Carries the Wallet label and modifies external signer wallet logic across src/wallet/.

Factors: security/stability 0, bug 0, performance 0, user value 2, leverage 2