{
 "number": 33112,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/33112",
 "title": "wallet: relax external_signer flag constraints",
 "author": "Sjors",
 "author_association": "MEMBER",
 "created_at": "2025-08-01T10:11:18Z",
 "updated_at": "2026-09-17T09:57:27Z",
 "age_days": 412,
 "draft": false,
 "labels": [
  "Wallet"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "ac6c932e1012940d0814249330c10a1ea560225f",
 "head_ref": "2025/07/external-signer-relax",
 "head_repo": "Sjors/bitcoin",
 "head_history": [
  {
   "t": "2025-08-01T11:49:18Z",
   "sha": "3909b0fe1b03e2a3f50bfe7e1734f69721c6c124"
  },
  {
   "t": "2025-08-01T12:55:25Z",
   "sha": "5763b827349e145ef4d92a4a3d2acda361dfc5f8"
  },
  {
   "t": "2025-08-01T13:00:04Z",
   "sha": "a9734039a7a34e38145927f02b891685f96ab9e8"
  },
  {
   "t": "2025-09-01T07:34:11Z",
   "sha": "03978530ad8dc9124307d2ffc7d64c24b784be0e"
  },
  {
   "t": "2026-02-17T07:58:48Z",
   "sha": "7d3fc167362abaaea27b9bc626e4b22b13b1f52b"
  },
  {
   "t": "2026-05-01T10:35:27Z",
   "sha": "8e7ea9997293df451e98e39300fc75b83329c08e"
  },
  {
   "t": "2026-05-06T09:31:09Z",
   "sha": "f1766c606889d9766c66238b43fb20f45beae48f"
  },
  {
   "t": "2026-05-29T08:46:23Z",
   "sha": "ec9c7672c4bcc98553381c9eb3eac4d4b5016742"
  },
  {
   "t": "2026-06-23T15:07:43Z",
   "sha": "10aa22325dc52400c7d12a291f597fb2d88c96ac"
  },
  {
   "t": "2026-08-06T12:39:28Z",
   "sha": "eba3bb99aac6fdf4f973ab8181eb03507b058905"
  },
  {
   "t": "2026-08-17T12:22:25Z",
   "sha": "b11fdb5483a4fa452cd319faf994adc4e93177ad"
  },
  {
   "t": "2026-08-20T09:08:27Z",
   "sha": "2e39d5c032e3f703d529ef23189fa2e7f3112d1c"
  },
  {
   "t": "2026-08-24T17:53:43Z",
   "sha": "5c4ff9ef2c52b017004c7f77095ccd307b8b8ba5"
  },
  {
   "t": "2026-08-26T13:37:35Z",
   "sha": "36cb7e24b268248db2bc619f6210f392b02f9545"
  },
  {
   "t": "2026-08-26T15:38:25Z",
   "sha": "8e5c9007afa8ef95bba5bffb4599348c939163d2"
  },
  {
   "t": "2026-08-28T07:20:19Z",
   "sha": "5a7c4309da24d9dd3ff5a8f7edc3d316cd9355c0"
  },
  {
   "t": "2026-09-14T14:12:58Z",
   "sha": "ac6c932e1012940d0814249330c10a1ea560225f"
  }
 ],
 "additions": 270,
 "deletions": 136,
 "changed_files": 11,
 "commit_count": 10,
 "size_bucket": "L",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "naiyoma",
      "url": "https://github.com/bitcoin/bitcoin/pull/33112#pullrequestreview-3911509874"
     },
     {
      "login": "achow101",
      "url": "https://github.com/bitcoin/bitcoin/pull/33112#issuecomment-4356463021"
     },
     {
      "login": "jeanpablojp",
      "url": "https://github.com/bitcoin/bitcoin/pull/33112#pullrequestreview-5023710744"
     }
    ],
    "stale_ack": [
     {
      "login": "rkrux",
      "url": "https://github.com/bitcoin/bitcoin/pull/33112#pullrequestreview-3176217358"
     },
     {
      "login": "adyshimony",
      "url": "https://github.com/bitcoin/bitcoin/pull/33112#pullrequestreview-3863869508"
     },
     {
      "login": "PraneethGunas",
      "url": "https://github.com/bitcoin/bitcoin/pull/33112#issuecomment-5459194925"
     }
    ]
   },
   "conflicts": [
    {
     "number": 36257,
     "title": "qa: assert_equals -> assert_true/assert_false",
     "author": "hodlinator"
    },
    {
     "number": 36114,
     "title": "wallet: harden external signer psbt processing, revamp mock",
     "author": "Sjors"
    },
    {
     "number": 35358,
     "title": "external signer: verify PSBT is reliable after signing it",
     "author": "brunoerg"
    }
   ]
  }
 },
 "acks_parsed": {
  "rkrux": {
   "kind": "ack",
   "hash": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "t": "2025-09-02T11:59:50Z",
   "stale": true
  },
  "adyshimony": {
   "kind": "ack",
   "hash": "7d3fc167362a",
   "t": "2026-02-26T23:12:48Z",
   "stale": true
  },
  "naiyoma": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-03-08T14:15:00Z",
   "stale": false
  },
  "achow101": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-04-30T22:02:34Z",
   "stale": false
  },
  "jeanpablojp": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-08-25T20:23:59Z",
   "stale": false
  },
  "PraneethGunas": {
   "kind": "ack",
   "hash": null,
   "t": "2026-08-29T00:31:55Z",
   "stale": true
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 3,
  "concept_ack": 3,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 1,
  "changes_requested": 0,
  "distinct_reviewers": [
   "PraneethGunas",
   "achow101",
   "adyshimony",
   "fanquake",
   "jeanpablojp",
   "maflcko",
   "naiyoma",
   "rkrux"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-14T14:13:07Z",
  "last_reviewer_activity": "2026-08-29T00:31:55Z",
  "last_reviewer": "PraneethGunas",
  "author_silent_days": 3,
  "waiting_on_author_days": 0,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [
   28333,
   33765,
   35424,
   35445,
   35852,
   36114
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 28333,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-05-28",
    "title": "wallet: Construct ScriptPubKeyMans with all data rather than loaded progressively"
   },
   {
    "number": 33765,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-05-20",
    "title": "doc: update interface, --stdin flag, `signtx` (#31005)"
   },
   {
    "number": 35424,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-06-23",
    "title": "doc, wallet: align external signer documentation, reject sendtoaddress/sendmany"
   },
   {
    "number": 35445,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-09-09",
    "title": "wallet, descriptor: Revert `StringType::COMPAT` for Miniscript expressions and drop the concept of a Descriptor ID that can be validated"
   },
   {
    "number": 35852,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-08-14",
    "title": "scripted-diff: Use inline const(expr) over static constexpr in headers"
   },
   {
    "number": 36114,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "wallet: harden external signer psbt processing, revamp mock"
   }
  ],
  "conflicts": [
   36257,
   36114,
   35358
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/qt/walletmodel.cpp",
  "src/wallet/rpc/spend.cpp",
  "src/wallet/rpc/wallet.cpp",
  "src/wallet/wallet.cpp",
  "src/wallet/wallet.h",
  "test/functional/wallet_signer.py",
  "test/functional/wallet_signer_musig2.py"
 ],
 "body": "The `external_signer` indicates that an external signer device may be called via [HWI](https://github.com/bitcoin-core/HWI) or [equivalent](https://github.com/bitcoin/bitcoin/blob/master/doc/external-signer.md#signer-api) application.\n\nWhen it was initially introduced some additional constraints were placed on wallets with this flag: it had to be a descriptor wallet and watch-only. Also the flag could not be added or removed later.\n\nThe constraints aren't a problem for the main supported and documented use case of connecting a single hardware wallet and using it just like a normal single sig Bitcoin Core wallet.\n\nBut they get in the way of MuSig2 support, see https://github.com/Sjors/bitcoin/pull/91.\n\nThis pull request drops the following constraints:\n\n- `disable_private_keys` is no longer mandatory (but still default)\n- `external_signer` flag is now mutable\n\nChanging the `external_signer` flag reloads the wallet so that its descriptor ScriptPubKeyMans are recreated using the new setting.\n\nAdditionally it does the following:\n\n- make the `blank` option for `createwallet` consistent with regular wallets by _not_ importing keys from the connected signer\n- avoid going through `createTransaction` for external signers (otherwise the GUI breaks)\n- create an `ExternalSignerScriptPubKeyMan` when importing a descriptor into an external signer wallet, instead of requiring an additional reload\n\nThis should have no noticeable effect on the default single sig use case described above.\n\nFinally we add test coverage for spending from an imported hot key descriptor in an external signer wallet. The wallet signs with its own keys before involving the external signer.",
 "commits": [
  {
   "sha": "989560e4ea6f870ff325b40d1016f1dfbdae8f05",
   "date": "2026-09-14T14:08:29Z",
   "message": "wallet: don't import external keys at creation if blank\n\nThere's no need to treat external signer wallets different in this\nregard. When the user sets the 'blank' flag, don't generate or\nimport keys.\n\nFor multisig setups that involve an external signer, it may be useful\nto start from a blank wallet and manually import descriptors."
  },
  {
   "sha": "e89e14472b6ea6aa7ed8cc31221d00562d75caf1",
   "date": "2026-09-14T14:08:29Z",
   "message": "wallet: avoid signing via createTransaction() with external signer\n\nExternal signer enabled wallets should always use the process PSBT flow.\nAvoid going through CreateTransaction.\n\nThis has no effect until a later commit where WALLET_FLAG_EXTERNAL_SIGNER\nno longer implies WALLET_FLAG_DISABLE_PRIVATE_KEYS. Without this change\nsigning with the GUI would break for external signers with private keys\nenabled."
  },
  {
   "sha": "3db1fe88a00b2aa5c39e9a289df6188cd2833916",
   "date": "2026-09-14T14:08:29Z",
   "message": "wallet: make watch-only optional for external signer\n\nBefore this change the external_signer flag required the wallet to be watch-only.\nThis precludes multisig setups in which we hold a hot key.\n\nRemove this as a requirement, but disable private keys by default. This leaves\nthe typical (and only documented) use case of a single external signer unaffected."
  },
  {
   "sha": "cbfadd1becca5b4d37e79b112d7504b314f7b89d",
   "date": "2026-09-14T14:08:29Z",
   "message": "wallet: make external_signer flag mutable\n\nWith the removal of legacy wallets and the relaxing of restrictions\nin the previous commit, it's no longer a problem to toggle this flag."
  },
  {
   "sha": "04658f852ad9879988058c843a7aa8ede1c68dc2",
   "date": "2026-09-14T14:08:29Z",
   "message": "wallet: extract load and unload wallet RPC helpers\n\nA later commit reuses these helpers to reload a wallet. This does not change behavior."
  },
  {
   "sha": "3ae8d8e82898f653d3c5e8510fb7caac05ae0353",
   "date": "2026-09-14T14:08:29Z",
   "message": "wallet: report whether flag changes require reload\n\nA later commit uses this signal to reload the wallet after changing\nflags that affect in-memory state. Existing callers ignore the return\nvalue, so this does not change behavior."
  },
  {
   "sha": "6c62c7f211c6eff819a539ab5871f50acb671aae",
   "date": "2026-09-14T14:08:29Z",
   "message": "wallet: reload wallet when external signer flag changes\n\nHave setwalletflag unload and reload the wallet when a flag setter\nreports this is needed. Reuse the normal wallet loading path to\nrecreate descriptor ScriptPubKeyMans.\n\nDocument which flags trigger a reload and warn users to avoid\nconcurrent wallet RPC clients while changing them."
  },
  {
   "sha": "fdf50c963c6e54b5c4b5e78d35ff294ae106efbf",
   "date": "2026-09-14T14:08:29Z",
   "message": "test: move mock signer path helper to the test framework\n\nBoth rpc_signer.py and wallet_signer.py defined identical\nmock_signer_path() helpers; the upcoming wallet_signer_musig2.py test\nneeds the same helper. Move it to BitcoinTestFramework."
  },
  {
   "sha": "97daa62e84d817b6f6fb80795401db8e43767b42",
   "date": "2026-09-14T14:10:12Z",
   "message": "wallet: upgrade to ExternalSignerScriptPubKeyMan in AddWalletDescriptor\n\nCWallet::AddWalletDescriptor created a plain DescriptorScriptPubKeyMan\neven when WALLET_FLAG_EXTERNAL_SIGNER was set. Create the\nexternal-signer variant immediately so newly imported descriptors can\nuse address display and signing without requiring an unload/reload\ncycle."
  },
  {
   "sha": "ac6c932e1012940d0814249330c10a1ea560225f",
   "date": "2026-09-14T14:10:12Z",
   "message": "wallet: sign with own keys before using the external signer\n\nExternalSignerScriptPubKeyMan::FillPSBT went straight to the external\nsigner whenever sign is set. Now that a signer wallet can hold private\nkeys, a descriptor with a hot key gets an ExternalSignerScriptPubKeyMan\nas well, and its signature was never made.\n\nLet the base class fill and sign first, and only involve the signer if\nan input that belongs to this descriptor is still unsigned."
  }
 ],
 "timeline": [
  {
   "t": "2025-08-01T10:31:30Z",
   "kind": "comment",
   "who": "fanquake",
   "assoc": "MEMBER",
   "text": "https://cirrus-ci.com/task/6572167942373376?logs=ci#L1621:\n```bash\n[06:14:38.327] /ci_container_base/src/wallet/rpc/wallet.cpp: In function \u2018wallet::createwallet()::<lambda(const RPCHelpMan&, const JSONRPCRequest&)>\u2019:\n[06:14:38.327] /ci_container_base/src/wallet/rpc/wallet.cpp:420:45: error: \u2018*(unsigned char*)((char*)&disable_private_keys + offsetof(std::optional<bool>,std::optional<bool>::<unnamed>.std::_Optional_base<bool, true, true>::<unnamed>))\u2019 may be used uninitialized in this function [-Werror=maybe-uninitialized]\n[06:14:38.327]   420 |     if (disable_private_keys.has_value() && *disable_private_keys) {\n[06:14:38.327]       |                                             ^~~~~~~~~~~~~~~~~~~~~\n[06:14:38.327] cc1plus: all warnings being treated as errors\n[06:14:38.328] gmake[2]: *** [src/wallet/CMakeFiles/bitcoin_wallet.dir/build.make:370: src/wallet/CMakeFiles/bitcoin_wallet.dir/rpc/wallet.cpp.o] Error 1\n```"
  },
  {
   "t": "2025-08-01T11:01:25Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/wallet/rpc/wallet.cpp",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "in_reply_to": null,
   "text": "nit: Would be good to use named args, while touching this: `self.MaybeArg<bool>(\"disable_private_keys\")`"
  },
  {
   "t": "2025-08-01T11:02:41Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/wallet/rpc/wallet.cpp",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "in_reply_to": null,
   "text": "not sure if this fixes the `maybe-uninitialized` false-positive from gcc, but you can try `    if (disable_private_keys.has_value() && disable_private_keys.value()) {`, or add `-Wno-error=maybe-uninitialized` to the ci task config."
  },
  {
   "t": "2025-08-01T11:07:55Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/wallet/rpc/wallet.cpp",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "in_reply_to": 2247694580,
   "text": "That looks much nicer indeed."
  },
  {
   "t": "2025-08-01T11:48:40Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/wallet/rpc/wallet.cpp",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "in_reply_to": 2247696728,
   "text": "I'll try `value_or(false)`"
  },
  {
   "t": "2025-08-01T11:49:18Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "3909b0fe1b03e2a3f50bfe7e1734f69721c6c124"
  },
  {
   "t": "2025-08-01T12:55:25Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "5763b827349e145ef4d92a4a3d2acda361dfc5f8"
  },
  {
   "t": "2025-08-01T12:56:59Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Added 7beb338a0d4343a622236876c7d63d56bf7039e3 _wallet: avoid createTransaction() with signer_ to prevent breaking GUI signing for private key enabled external signer wallets (even when they don't have other keys)."
  },
  {
   "t": "2025-08-01T13:00:04Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "a9734039a7a34e38145927f02b891685f96ab9e8"
  },
  {
   "t": "2025-08-11T14:00:44Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "in_reply_to": null,
   "text": "In 2faf30612350fd90d9d3c44d1bb7b055addc8331 \"wallet: don't import external keys at creation if blank\"\n\nNit in commit message:\n```diff\n- For multisig setups than involve an external signer\n+ For multisig setups that involve an external signer\n```"
  },
  {
   "t": "2025-08-11T14:12:42Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "src/qt/walletmodel.cpp",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "in_reply_to": null,
   "text": "In 7beb338a0d4343a622236876c7d63d56bf7039e3 \"wallet: avoid createTransaction() with signer\"\n\nNit in commit message:\n```diff\n- wallet: avoid createTransaction() with signer\n+ wallet: avoid signing via createTransaction() with external signer\n```"
  },
  {
   "t": "2025-08-11T14:18:20Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer.py",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "in_reply_to": null,
   "text": "In 08f7813f536c242b7a4b65d01cfbc73601846a25 \"wallet: make watch-only optional for external signer\"\n\nThis comment can stay?"
  },
  {
   "t": "2025-08-11T14:31:59Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer.py",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "in_reply_to": null,
   "text": "In 08f7813f536c242b7a4b65d01cfbc73601846a25 \"wallet: make watch-only optional for external signer\"\n\nDo you intend to add a test for an external signer wallet with private keys enabled in a later PR?"
  },
  {
   "t": "2025-08-11T14:34:54Z",
   "kind": "review",
   "who": "rkrux",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "a9734039a7a34e38145927f02b891685f96ab9e8",
   "text": "ACK a9734039a7a34e38145927f02b891685f96ab9e8\n\nAgree with the intent to relax these constraints for external signer wallets."
  },
  {
   "t": "2025-08-15T06:23:21Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer.py",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "in_reply_to": 2266969608,
   "text": "I think that'll become more relevant, and probably easier to test, after MuSig2 support lands."
  },
  {
   "t": "2025-09-01T07:34:11Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e"
  },
  {
   "t": "2025-09-01T07:34:15Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer.py",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "in_reply_to": 2266923450,
   "text": "Brought it back."
  },
  {
   "t": "2025-09-01T07:34:34Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased and addressed nits."
  },
  {
   "t": "2025-09-02T11:59:50Z",
   "kind": "review",
   "who": "rkrux",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "03978530ad8dc9124307d2ffc7d64c24b784be0e",
   "text": "re-ACK 03978530ad8dc9124307d2ffc7d64c24b784be0e\n\n```\ngit range-diff a973403...0397853\n```"
  },
  {
   "t": "2026-02-17T07:58:48Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "7d3fc167362abaaea27b9bc626e4b22b13b1f52b"
  },
  {
   "t": "2026-02-26T22:50:09Z",
   "kind": "review_comment",
   "who": "adyshimony",
   "assoc": "NONE",
   "path": "test/functional/wallet_signer.py",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": null,
   "text": "Maybe add a check that create the wallet with explicitly values upon creation?\n\n```\n# check that private keys can be explicitly enabled for external signer wallets\nself.nodes[1].createwallet(wallet_name='hww_hot', external_signer=True, disable_private_keys=False)\nhww_hot = self.nodes[1].get_wallet_rpc('hww_hot')\nassert_equal(hww_hot.getwalletinfo()[\"external_signer\"], True)\nassert_equal(hww_hot.getwalletinfo()[\"private_keys_enabled\"], True)\n```"
  },
  {
   "t": "2026-02-26T22:53:49Z",
   "kind": "review_comment",
   "who": "adyshimony",
   "assoc": "NONE",
   "path": "test/functional/wallet_signer.py",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": null,
   "text": "Check the private_keys_enabled is true by default for this case:\n\n`assert_equal(not_hww.getwalletinfo()[\"private_keys_enabled\"], True)`"
  },
  {
   "t": "2026-02-26T23:03:24Z",
   "kind": "review_comment",
   "who": "adyshimony",
   "assoc": "NONE",
   "path": "test/functional/wallet_signer.py",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": null,
   "text": "Make sure those paths for external signer are raising errors:\n\n```\n# when external_signer enabled, sendtoaddress/sendmany path is disabled.\nassert_raises_rpc_error(-4, \"Error: Private keys are disabled for this wallet\",\n    not_hww.sendtoaddress, self.nodes[0].getnewaddress(), 0.01)\nassert_raises_rpc_error(-4, \"Error: Private keys are disabled for this wallet\",\n    not_hww.sendmany, \"\", {self.nodes[0].getnewaddress(): 0.01})\n```"
  },
  {
   "t": "2026-02-26T23:12:48Z",
   "kind": "review",
   "who": "adyshimony",
   "assoc": "NONE",
   "state": "COMMENTED",
   "commit": "7d3fc167362abaaea27b9bc626e4b22b13b1f52b",
   "text": "ACK 7d3fc167362a\n\nBuilt and ran successfully all tests.\n\nQt UX tested on regtest with mock signer:\n\n- Create Wallet defaults with signer detected are correct.\n\n- Checkbox interactions behave as expected when toggling external_signer.\n\n- getwalletinfo for default external signer wallet shows external_signer=true, private_keys_enabled=false.\n\n- Creating with external_signer=true and disable_private_keys manually unchecked gives external_signer=true, private_keys_enabled=true.\n\n- setwalletflag \"external_signer\" set to true/false succeeds and is reflected in getwalletinfo."
  },
  {
   "t": "2026-03-08T13:56:09Z",
   "kind": "review_comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer.py",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": 2861678919,
   "text": "+1 on testing when `disable_private_keys=False `"
  },
  {
   "t": "2026-03-08T14:15:00Z",
   "kind": "review",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "7d3fc167362abaaea27b9bc626e4b22b13b1f52b",
   "text": "Concept ACK\n\nReviewed\n3e57405bf6d1b2d75cf831537f364ec3c6092614\nb990dbb504fd1b140332ca7c13f92673f74f5735\n0ba76bf0a75dc7e60803dcb1e85a8492bb9397eb\n\nand tested 500fc7a0bddee2ed7b59a8228e13637ccebd5e31 createwallet on `bitcoin-qt`\n\nBefore this pr\ndisable_private_keys is always checked and  grayed out, so there's no way to uncheck it\nexternal signer always watch-only, no exceptions\n\nand on cli\n\n```\n\"private_keys_enabled\": false,\n\"flags\": [\n    \"last_hardened_xpub_cached\",\n    \"disable_private_keys\",\n    \"descriptor_wallet\",\n    \"external_signer\"\n  ],\n\n```\nAfter PR,\ndisabled_private_keys is checked by default, but can be unchecked\n\nif left checked:\n\"disable_private_keys\" in flags\n\"private_keys_enabled\": false\nsame as before\n\nand when Unchecked\n\"disable_private_keys\" absent\n\"private_keys_enabled\": true\nnew state\n\nand on cli\n\n```\n\"descriptors\": true,\n  \"external_signer\": true,\n  \"blank\": false,\n  \"birthtime\": 1772971109,\n  \"flags\": [\n    \"last_hardened_xpub_cached\",\n    \"descriptor_wallet\",\n    \"external_signer\"\n  ],\n\n```"
  },
  {
   "t": "2026-04-30T22:02:34Z",
   "kind": "comment",
   "who": "achow101",
   "assoc": "MEMBER",
   "text": "Concept ACK"
  },
  {
   "t": "2026-05-01T10:35:27Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "8e7ea9997293df451e98e39300fc75b83329c08e"
  },
  {
   "t": "2026-05-01T10:35:58Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased for silent merge conflict with https://github.com/bitcoin/bitcoin/pull/34049.\n\nI've been testing this again in light of https://github.com/Sjors/bitcoin/pull/91, which also helped refresh my memory.\n\nI dropped the first documentation commit 3e57405bf6d1b2d75cf831537f364ec3c6092614, because #33765 is doing a more thorough job there.\n\nAdded test coverage, including a new `wallet_signer_musig2.py`. This only imports a `musig(hot wallet, external signer)` descriptor for now. Spending from it requires too many other changes that are better left for a followup."
  },
  {
   "t": "2026-05-04T13:36:23Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "c05e9c13266f5004b1345cd1a967d2d57a4e9ae6",
   "in_reply_to": null,
   "text": "In c05e9c13266f5004b1345cd1a967d2d57a4e9ae6 \"wallet: don't import external keys at creation if blank\"\n\nRedundant parenthesis.\n\n```diff\ndiff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp\nindex 747f8ef1e0..b905b9800f 100644\n--- a/src/wallet/wallet.cpp\n+++ b/src/wallet/wallet.cpp\n@@ -2910,7 +2910,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri\n             // Fetch keys from an external signer; or\n              (wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) ||\n             // Generate them, unless private keys are disabled\n-            !(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS)))\n+            !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS))\n         ) {\n             walletInstance->SetupDescriptorScriptPubKeyMans();\n         }\n\n```"
  },
  {
   "t": "2026-05-04T13:50:23Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer.py",
   "commit": "34c447940fad77dfb291016390da80d8e4d80bb6",
   "in_reply_to": null,
   "text": "In 34c447940fad77dfb291016390da80d8e4d80bb6 \"wallet: make external_signer flag mutable\"\n\nNit: the wallet name can be updated now - s/not_hww/not_hww_initially\n\n```diff\ndiff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py\nindex 6a730bd66d..bb05bac0be 100755\n--- a/test/functional/wallet_signer.py\n+++ b/test/functional/wallet_signer.py\n@@ -74,11 +74,11 @@ class WalletSignerTest(BitcoinTestFramework):\n         assert_equal(hww.getwalletinfo()[\"private_keys_enabled\"], False)\n\n         # Flag can be set afterwards\n-        self.nodes[1].createwallet(wallet_name='not_hww', external_signer=False)\n-        not_hww = self.nodes[1].get_wallet_rpc('not_hww')\n-        assert_equal(not_hww.getwalletinfo()[\"external_signer\"], False)\n-        not_hww.setwalletflag(\"external_signer\", True)\n-        assert_equal(not_hww.getwalletinfo()[\"external_signer\"], True)\n+        self.nodes[1].createwallet(wallet_name='not_hww_initially', external_signer=False)\n+        not_hww_initially = self.nodes[1].get_wallet_rpc('not_hww_initially')\n+        assert_equal(not_hww_initially.getwalletinfo()[\"external_signer\"], False)\n+        not_hww_initially.setwalletflag(\"external_signer\", True)\n+        assert_equal(not_hww_initially.getwalletinfo()[\"external_signer\"], True)\n\n         self.set_mock_result(self.nodes[1], '0 {\"invalid json\"}')\n         assert_raises_rpc_error(-1, 'Unable to parse JSON',\n```"
  },
  {
   "t": "2026-05-04T13:54:38Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "src/wallet/rpc/spend.cpp",
   "commit": "e8a54514ddf0599ede28d5b8ab3f0e2b3f27372a",
   "in_reply_to": null,
   "text": "In e8a54514ddf0599ede28d5b8ab3f0e2b3f27372a \"wallet: avoid signing via createTransaction() with external signer\"\n\n[quoted text omitted]\nno longer implies WALLET_FLAG_DISABLE_PRIVATE_KEYS.\n\nAfter the next commit, the associated error here (\"Private keys are disabled for this wallet\") doesn't seem correct if it's an external signer."
  },
  {
   "t": "2026-05-04T14:19:18Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer_musig2.py",
   "commit": "8e7ea9997293df451e98e39300fc75b83329c08e",
   "in_reply_to": null,
   "text": "This is duplicated with wallet_signer.py. Can't these be a part of wallet_signer.py?"
  },
  {
   "t": "2026-05-04T14:20:16Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer_musig2.py",
   "commit": "8e7ea9997293df451e98e39300fc75b83329c08e",
   "in_reply_to": null,
   "text": "2 nodes don't seem to be required, only one (self.nodes[1]) is used."
  },
  {
   "t": "2026-05-04T14:25:16Z",
   "kind": "review",
   "who": "rkrux",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "8e7ea9997293df451e98e39300fc75b83329c08e",
   "text": "Code review at 8e7ea9997293df451e98e39300fc75b83329c08e"
  },
  {
   "t": "2026-05-06T08:52:32Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer_musig2.py",
   "commit": "8e7ea9997293df451e98e39300fc75b83329c08e",
   "in_reply_to": 3182188461,
   "text": "The MuSig2 test is going to grow by quite a lot, at least in my current (very rough) draft: https://github.com/Sjors/bitcoin/blob/2025/06/musig2-power/test/functional/wallet_signer_musig2.py\n\nBut I can move these helpers to the test framework. Done in 0b3ddcd516301a790afcf387ffdbcb7878ca4eb5."
  },
  {
   "t": "2026-05-06T09:31:09Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "f1766c606889d9766c66238b43fb20f45beae48f"
  },
  {
   "t": "2026-05-06T09:31:23Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/wallet/rpc/spend.cpp",
   "commit": "e8a54514ddf0599ede28d5b8ab3f0e2b3f27372a",
   "in_reply_to": 3182035442,
   "text": "Indeed. I also split this change into its own commit 996064c6db85023e0f8aa4f91277ac494821292d and added a test."
  },
  {
   "t": "2026-05-06T09:31:32Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer.py",
   "commit": "34c447940fad77dfb291016390da80d8e4d80bb6",
   "in_reply_to": 3182004172,
   "text": "Done"
  },
  {
   "t": "2026-05-06T09:32:20Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "test/functional/wallet_signer_musig2.py",
   "commit": "8e7ea9997293df451e98e39300fc75b83329c08e",
   "in_reply_to": 3182194328,
   "text": "Fixed."
  },
  {
   "t": "2026-05-06T09:34:00Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased (just in case) and addressed @w0xlt's feedback. In particular this adds 0b3ddcd516301a790afcf387ffdbcb7878ca4eb5 to move `mock_signer_path` to the test framework and split e8a54514ddf0599ede28d5b8ab3f0e2b3f27372a into 3359674e80e74c5c3566f3207eed3681333d1940 (GUI) and 996064c6db85023e0f8aa4f91277ac494821292d (RPC)."
  },
  {
   "t": "2026-05-29T08:46:23Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "ec9c7672c4bcc98553381c9eb3eac4d4b5016742"
  },
  {
   "t": "2026-05-29T08:46:29Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased after #28333."
  },
  {
   "t": "2026-06-23T15:07:43Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "10aa22325dc52400c7d12a291f597fb2d88c96ac"
  },
  {
   "t": "2026-06-23T15:07:51Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased after #35424 absorbed a5eb9e13f258c001b3096a61cd6420830cd63bc2."
  },
  {
   "t": "2026-08-06T12:39:28Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "eba3bb99aac6fdf4f973ab8181eb03507b058905"
  },
  {
   "t": "2026-08-17T12:22:25Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "b11fdb5483a4fa452cd319faf994adc4e93177ad"
  },
  {
   "t": "2026-08-17T12:22:26Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased after #35852."
  },
  {
   "t": "2026-08-20T09:08:27Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "2e39d5c032e3f703d529ef23189fa2e7f3112d1c"
  },
  {
   "t": "2026-08-24T17:53:43Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "5c4ff9ef2c52b017004c7f77095ccd307b8b8ba5"
  },
  {
   "t": "2026-08-24T17:53:56Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Added `wallet: upgrade to ExternalSignerScriptPubKeyMan in AddWalletDescriptor` and removed reload workaround from the test."
  },
  {
   "t": "2026-08-25T20:23:59Z",
   "kind": "review",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "5c4ff9ef2c52b017004c7f77095ccd307b8b8ba5",
   "text": "Concept ACK\n\nBuilt and ran the wallet and signer tests."
  },
  {
   "t": "2026-08-25T20:23:59Z",
   "kind": "review_comment",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/wallet.h",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": null,
   "text": "Took a watch-only wallet that already had the device's descriptors and set the flag. Right after, `walletdisplayaddress` fails with `There is no ScriptPubKeyManager for this address` and `send` returns a PSBT without calling the device. 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. The `AddWalletDescriptor` commit already handles this on the import path. Worth doing the same on toggle, or forcing a reload?"
  },
  {
   "t": "2026-08-25T20:23:59Z",
   "kind": "review_comment",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/wallet.cpp",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": null,
   "text": "Imported a descriptor with the flag on, turned the flag off, imported another one, and without reloading I can't spend from either. Log excerpt:\n\n```\n  -- flag is OFF, wallet NOT reloaded\n    spend the utxo imported while flag was ON    RAISED  External signer failed to sign (-25)\n    spend the utxo imported while flag was OFF   RAISED  External signer failed to sign (-25)\n  -- same wallet, after unload+load, flag still OFF\n    spend the utxo imported while flag was ON    OK\n    spend the utxo imported while flag was OFF   OK\n```\n\nThe second one is what got me, since it was imported after I turned the flag off. `FillPSBT` returns on the first error, so the leftover external signer SPKM aborts the whole fill. On the previous head, `2e39d5c032`, all four succeeded."
  },
  {
   "t": "2026-08-25T20:23:59Z",
   "kind": "review_comment",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/wallet.h",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": null,
   "text": "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, with `External signer wallet being loaded without external signer support compiled`. Since unsetting the flag requires loading the wallet, the simplest way back is a binary built with support. On the merge base the same call returns `Wallet flag is immutable: external_signer`. There's no `ENABLE_EXTERNAL_SIGNER` guard here or in `setwalletflag`, would one make sense?"
  },
  {
   "t": "2026-08-26T13:37:35Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "36cb7e24b268248db2bc619f6210f392b02f9545"
  },
  {
   "t": "2026-08-26T13:37:36Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.h",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": 3857009395,
   "text": "I tried to implement having the wallet reload all descriptors, but it's actually easier to just have the RPC automatically reload the whole wallet for this flag. So I went with that."
  },
  {
   "t": "2026-08-26T13:37:41Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": 3857009403,
   "text": "That should be fixed now, thanks to the reload."
  },
  {
   "t": "2026-08-26T13:37:45Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.h",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": 3857009408,
   "text": "Added a guard for completeness."
  },
  {
   "t": "2026-08-26T15:38:25Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "8e5c9007afa8ef95bba5bffb4599348c939163d2"
  },
  {
   "t": "2026-08-27T23:42:24Z",
   "kind": "comment",
   "who": "PraneethGunas",
   "assoc": "NONE",
   "text": "Concept ACK. This greatly helps unblock using external signers and hot keys in the same wallet for a multisig setup"
  },
  {
   "t": "2026-08-27T23:53:41Z",
   "kind": "review_comment",
   "who": "PraneethGunas",
   "assoc": "NONE",
   "path": "src/wallet/wallet.cpp",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": null,
   "text": "Now that 034375fc9c allows private keys in a signer wallet, 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.\n\nTested on a Ledger Flex, regtest. Spending a hot key UTXO gives:\n\n  error code: -25, External signer failed to sign\n  debug.log: Signer fingerprint 42036eee does not match any of the inputs:\n\nShould SPKM selection be per descriptor, based on whether the wallet holds the key, rather than per wallet?"
  },
  {
   "t": "2026-08-28T07:20:19Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "5a7c4309da24d9dd3ff5a8f7edc3d316cd9355c0"
  },
  {
   "t": "2026-08-28T07:20:45Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "- added handling for hot descriptors, see https://github.com/bitcoin/bitcoin/pull/33112#discussion_r3876752265\n- dropped `test: add MuSig2 external signer wallet test` to trim the scope a bit."
  },
  {
   "t": "2026-08-28T07:20:47Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": 3876752265,
   "text": "Don't do that :-)\n\nI added 5a7c4309da24d9dd3ff5a8f7edc3d316cd9355c0 to have the wallet first sign with its own keys, and only try the external signer if the result is unsigned.\n\nHowever we shouldn't encourage users to put hot and cold descriptors into the same wallet, so this isn't documented."
  },
  {
   "t": "2026-08-28T11:28:20Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": 3876752265,
   "text": "While working on this, I realized our external signer mock can't properly test these more complicated scenarios. That's only going to get worse with MuSig2, so I opened #36114 to rewrite it."
  },
  {
   "t": "2026-08-28T19:40:02Z",
   "kind": "review_comment",
   "who": "PraneethGunas",
   "assoc": "NONE",
   "path": "src/wallet/wallet.cpp",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": 3876752265,
   "text": "Confirmed fixed on a Ledger Flex, thanks. If mixing hot and cold in one wallet\nisn't encouraged, what's the intended layout for a multisig where one cosigner is\nan external signer and another is a hot key, one wallet per key?"
  },
  {
   "t": "2026-08-29T00:31:55Z",
   "kind": "comment",
   "who": "PraneethGunas",
   "assoc": "NONE",
   "text": "ACK [f4dd056](https://github.com/bitcoin/bitcoin/pull/33112/commits/f4dd05644f3eada3656f619f71ab3b94fe08ec3d), [5a7c430](https://github.com/bitcoin/bitcoin/pull/33112/commits/5a7c4309da24d9dd3ff5a8f7edc3d316cd9355c0)"
  },
  {
   "t": "2026-08-31T08:57:53Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f",
   "in_reply_to": 3876752265,
   "text": "Ideally there's a single multisig descriptor for which the wallet has one key, e.g. `musig2(core_xprv, ledger_xpub)`. So you start with a blank wallet, with an `usused()` descriptor, then call `gethdkey` to export the xprv for `m/87'/0'/0'`, get the device xpub, construct the descriptor and then import it. Having to copy an xprv around is not ideal, so https://github.com/Sjors/bitcoin/pull/91 contains a commit that lets you use the xpub instead, and the wallet figures it out on import."
  },
  {
   "t": "2026-09-14T14:12:58Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "ac6c932e1012940d0814249330c10a1ea560225f"
  },
  {
   "t": "2026-09-14T14:13:07Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased after #35445."
  }
 ],
 "labels_log": [
  {
   "t": "2025-08-01T10:11:21Z",
   "action": "labeled",
   "label": "Wallet",
   "who": "DrahtBot"
  },
  {
   "t": "2025-08-01T11:49:30Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-08-01T16:01:55Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-10T17:31:56Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-17T10:01:01Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-28T20:19:22Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-29T09:22:36Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-29T09:37:42Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-29T17:34:02Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-14T17:54:35Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-17T16:39:03Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-26T15:13:09Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-26T21:11:43Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-09T11:53:20Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-14T15:54:07Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-05-01T10:35:46Z",
   "kind": "renamed",
   "who": "Sjors",
   "from": "wallet: relax external_signer flag constraints",
   "to": "wallet: relax external_signer flag constraints, add musig2 test (partial)"
  },
  {
   "t": "2026-08-26T19:37:43Z",
   "kind": "closed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-26T19:37:51Z",
   "kind": "reopened",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-28T11:55:53Z",
   "kind": "renamed",
   "who": "Sjors",
   "from": "wallet: relax external_signer flag constraints, add musig2 test (partial)",
   "to": "wallet: relax external_signer flag constraints"
  }
 ],
 "text_chars": 18955,
 "text_tokens_estimate": 4738,
 "changed_paths": [
  "src/qt/createwalletdialog.cpp",
  "src/qt/walletmodel.cpp",
  "src/wallet/external_signer_scriptpubkeyman.cpp",
  "src/wallet/external_signer_scriptpubkeyman.h",
  "src/wallet/rpc/wallet.cpp",
  "src/wallet/wallet.cpp",
  "src/wallet/wallet.h",
  "test/functional/rpc_signer.py",
  "test/functional/test_framework/test_framework.py",
  "test/functional/wallet_avoidreuse.py",
  "test/functional/wallet_signer.py"
 ],
 "files": [
  {
   "path": "src/qt/createwalletdialog.cpp",
   "add": 8,
   "del": 20
  },
  {
   "path": "src/qt/walletmodel.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/wallet/external_signer_scriptpubkeyman.cpp",
   "add": 24,
   "del": 4
  },
  {
   "path": "src/wallet/external_signer_scriptpubkeyman.h",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/wallet/rpc/wallet.cpp",
   "add": 115,
   "del": 46
  },
  {
   "path": "src/wallet/wallet.cpp",
   "add": 19,
   "del": 17
  },
  {
   "path": "src/wallet/wallet.h",
   "add": 11,
   "del": 7
  },
  {
   "path": "test/functional/rpc_signer.py",
   "add": 0,
   "del": 5
  },
  {
   "path": "test/functional/test_framework/test_framework.py",
   "add": 5,
   "del": 0
  },
  {
   "path": "test/functional/wallet_avoidreuse.py",
   "add": 3,
   "del": 0
  },
  {
   "path": "test/functional/wallet_signer.py",
   "add": 83,
   "del": 36
  }
 ],
 "test_lines": 132,
 "git": {
  "head": "ac6c932e1012940d0814249330c10a1ea560225f",
  "head_matches_backup": true,
  "base": "76343a09f8025f20ac6ec8dad073132e70ef2b0f",
  "commits": [
   {
    "sha": "989560e4ea",
    "subject": "wallet: don't import external keys at creation if blank",
    "files": 2,
    "add": 11,
    "del": 4
   },
   {
    "sha": "e89e14472b",
    "subject": "wallet: avoid signing via createTransaction() with external signer",
    "files": 1,
    "add": 1,
    "del": 1
   },
   {
    "sha": "3db1fe88a0",
    "subject": "wallet: make watch-only optional for external signer",
    "files": 4,
    "add": 41,
    "del": 42
   },
   {
    "sha": "cbfadd1bec",
    "subject": "wallet: make external_signer flag mutable",
    "files": 4,
    "add": 18,
    "del": 7
   },
   {
    "sha": "04658f852a",
    "subject": "wallet: extract load and unload wallet RPC helpers",
    "files": 1,
    "add": 56,
    "del": 36
   },
   {
    "sha": "3ae8d8e828",
    "subject": "wallet: report whether flag changes require reload",
    "files": 2,
    "add": 18,
    "del": 11
   },
   {
    "sha": "6c62c7f211",
    "subject": "wallet: reload wallet when external signer flag changes",
    "files": 2,
    "add": 43,
    "del": 5
   },
   {
    "sha": "fdf50c963c",
    "subject": "test: move mock signer path helper to the test framework",
    "files": 3,
    "add": 11,
    "del": 28
   },
   {
    "sha": "97daa62e84",
    "subject": "wallet: upgrade to ExternalSignerScriptPubKeyMan in AddWalletDescriptor",
    "files": 4,
    "add": 34,
    "del": 1
   },
   {
    "sha": "ac6c932e10",
    "subject": "wallet: sign with own keys before using the external signer",
    "files": 2,
    "add": 40,
    "del": 4
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "c77c6b539373dd1f",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}