{
 "number": 36697,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36697",
 "title": "wallet: Add exportaddresskey RPC for descriptor wallets",
 "author": "jaromil",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-10-29T20:21:40Z",
 "updated_at": "2026-10-30T16:41:06Z",
 "description_hash": "d73649c1abc8b806",
 "age_days": 3,
 "draft": false,
 "labels": [
  "Wallet",
  "Needs rebase"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "64f57fe253b60849ee44ab2d4b62ea5cc2602933",
 "head_ref": "wallet/exportaddresskey",
 "head_repo": "jaromil/bitcoin",
 "head_history": [
  {
   "t": "2026-10-29T20:53:33Z",
   "sha": "d58917aab0acad951494398a7318aa483e95634d"
  },
  {
   "t": "2026-10-29T21:11:21Z",
   "sha": "fe4ff1ee1f3856e24a3824e57e8b1b33364f8b25"
  },
  {
   "t": "2026-10-29T21:29:29Z",
   "sha": "3b45f8b6585fae60609751164b574bbdd5cc7dd3"
  },
  {
   "t": "2026-10-29T21:59:53Z",
   "sha": "e2903a769a22633c74c29442559188816e42be71"
  },
  {
   "t": "2026-10-30T06:19:40Z",
   "sha": "4f64a4d7ad3f095ae29497aea3a9c4744a6c1c8c"
  }
 ],
 "additions": 3609,
 "deletions": 0,
 "changed_files": 22,
 "commit_count": 8,
 "size_bucket": "XL",
 "mergeable_state": "dirty",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {},
   "conflicts": []
  }
 },
 "acks_parsed": {},
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": []
 },
 "signals": {
  "needs_rebase": true,
  "ci_failed": false,
  "mergeable_state": "dirty",
  "last_author_activity": "2026-10-30T16:24:07Z",
  "last_reviewer_activity": null,
  "last_reviewer": null,
  "author_silent_days": 2,
  "waiting_on_author_days": 0,
  "days_since_update": 2
 },
 "refs": {
  "mentioned": [],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [],
 "body": "Adds `exportaddresskey \"address\" ( \"key_type\" )` to export locally available private keys for an address in a descriptor wallet. Callers can obtain private keys without identifying the descriptor index and reproducing key derivation outside Core.\n\nThe RPC has two modes:\n\n- `wallet` (default) returns the original wallet keys, with their public keys and roles.\n- `taproot_output` returns effective Taproot key-path keys using the known output commitment. Direct output keys are returned without another tweak. MuSig2 participant secrets are never combined into an aggregate private key.\n\nSupported constructions include single-key addresses, recognized P2SH/P2WSH scripts, multisig, Miniscript, and Taproot, including MuSig2 participants. A locally available key can be exported even when the wallet cannot satisfy all spending conditions.\n\nLookup is limited to existing wallet metadata and known derivation indices. Encrypted wallets must be unlocked. Missing spending data, unavailable keys, and inconsistent wallet data have distinct errors.\n\nThe result contains hexadecimal private scalars, not WIF. Exported keys are not a wallet backup; scripts, cosigners, or other spending data may still be needed. Reused keys and MuSig2 participants may control other outputs.\n\nIncludes key, descriptor, wallet manager, wallet RPC, and console-history tests. Functional coverage reconstructs scripts independently and verifies spending with exported keys, partial ownership, authorization, inconsistent metadata, and absence of wallet or nonce mutation.\n\nThe initial single-key implementation was ported from [Bitcoin Roots](https://plan-b.foundation/bitcoin-roots) and now extended to cover more use-cases. Initial motivation was the lack of `dumppriv` since v30 and the need for it in the making of the [Quietproof](https://plan-b.foundation/quietproof) client.",
 "commits": [
  {
   "sha": "e6d9e8424f2163a6c11f5303fb3d0429625ee6aa",
   "date": "2026-10-29T21:58:59Z",
   "message": "test: synchronize blocks in test_uncompressed_pubkey\n\nWait for confirmation blocks to reach both nodes before building or testing their spends. Otherwise block relay can lag behind the test, causing missing-input or stale-tip failures."
  },
  {
   "sha": "7778c8cbd5e24a0acb22b08999a3f94cd2fca1a8",
   "date": "2026-10-29T21:58:59Z",
   "message": "wallet: add exportaddresskey RPC\n\nPort address-specific private key export from Bitcoin Roots, adapting it to Core's RPCMethod API. Preserve wallet authorization, exact destination checks, console history filtering, and focused regression coverage.\n\nSource: fad94e12f7b830e820c9f722a8545bffc809f2d5 (Jaromil), with destination comparison fix 59185c317e."
  },
  {
   "sha": "a09a3cc56887ca44b338870b3ffb846c99462fba",
   "date": "2026-10-30T06:18:56Z",
   "message": "wallet: structure address key export results"
  },
  {
   "sha": "10dc6e401cbc3f8210abd4fdba726a0fae3a2e25",
   "date": "2026-10-30T06:18:56Z",
   "message": "wallet: export keys for script-based addresses"
  },
  {
   "sha": "b43ea1716f91aec0119ee718273b8f5f0dd04a84",
   "date": "2026-10-30T06:18:56Z",
   "message": "wallet: support Taproot address key export"
  },
  {
   "sha": "f3b48dd89174bd991add17695aee5d97561059d5",
   "date": "2026-10-30T06:18:56Z",
   "message": "wallet: verify address key export failure and security contracts"
  },
  {
   "sha": "4f64a4d7ad3f095ae29497aea3a9c4744a6c1c8c",
   "date": "2026-10-30T06:18:56Z",
   "message": "wallet: trim address key export includes"
  },
  {
   "sha": "64f57fe253b60849ee44ab2d4b62ea5cc2602933",
   "date": "2026-10-30T16:24:07Z",
   "message": "wallet: preserve stored key identities in address key export\n\nFind stored keys of either parity when descriptors reload in x-only form. Accept x-only representations of stored HD roots during private derivation checks while retaining exact full-key verification for exported scalars.\n\nCover both parities, wallet reload and encryption, and HD roots used as internal or leaf keys. Clarify that a direct address can identify an HD master private key."
  }
 ],
 "timeline": [
  {
   "t": "2026-10-29T20:53:33Z",
   "kind": "force_push",
   "who": "jaromil",
   "commit": "d58917aab0acad951494398a7318aa483e95634d",
   "id": "push:d58917aab0acad951494398a7318aa483e95634d"
  },
  {
   "t": "2026-10-29T21:11:21Z",
   "kind": "force_push",
   "who": "jaromil",
   "commit": "fe4ff1ee1f3856e24a3824e57e8b1b33364f8b25",
   "id": "push:fe4ff1ee1f3856e24a3824e57e8b1b33364f8b25"
  },
  {
   "t": "2026-10-29T21:29:29Z",
   "kind": "force_push",
   "who": "jaromil",
   "commit": "3b45f8b6585fae60609751164b574bbdd5cc7dd3",
   "id": "push:3b45f8b6585fae60609751164b574bbdd5cc7dd3"
  },
  {
   "t": "2026-10-29T21:59:53Z",
   "kind": "force_push",
   "who": "jaromil",
   "commit": "e2903a769a22633c74c29442559188816e42be71",
   "id": "push:e2903a769a22633c74c29442559188816e42be71"
  },
  {
   "t": "2026-10-30T06:19:40Z",
   "kind": "force_push",
   "who": "jaromil",
   "commit": "4f64a4d7ad3f095ae29497aea3a9c4744a6c1c8c",
   "id": "push:4f64a4d7ad3f095ae29497aea3a9c4744a6c1c8c"
  }
 ],
 "labels_log": [
  {
   "t": "2026-10-29T20:21:44Z",
   "action": "labeled",
   "label": "Wallet",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-29T20:54:50Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-30T07:22:55Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-30T16:41:05Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-10-29T20:46:34Z",
   "kind": "convert_to_draft",
   "who": "jaromil"
  },
  {
   "t": "2026-10-30T08:31:44Z",
   "kind": "ready_for_review",
   "who": "jaromil"
  }
 ],
 "text_chars": 3196,
 "text_tokens_estimate": 799,
 "changed_paths": [
  "doc/release-notes-exportaddresskey.md",
  "src/key.cpp",
  "src/key.h",
  "src/qt/rpcconsole.cpp",
  "src/qt/test/rpcnestedtests.cpp",
  "src/script/descriptor.cpp",
  "src/script/descriptor.h",
  "src/test/key_tests.cpp",
  "src/test/rpc_tests.cpp",
  "src/wallet/export.cpp",
  "src/wallet/export.h",
  "src/wallet/rpc/backup.cpp",
  "src/wallet/rpc/wallet.cpp",
  "src/wallet/scriptpubkeyman.cpp",
  "src/wallet/scriptpubkeyman.h",
  "src/wallet/test/scriptpubkeyman_tests.cpp",
  "src/wallet/test/wallet_rpc_tests.cpp",
  "src/wallet/test/walletload_tests.cpp",
  "src/wallet/types.h",
  "test/functional/p2p_segwit.py",
  "test/functional/test_runner.py",
  "test/functional/wallet_exportaddresskey.py"
 ],
 "files": [
  {
   "path": "doc/release-notes-exportaddresskey.md",
   "add": 77,
   "del": 0
  },
  {
   "path": "src/key.cpp",
   "add": 11,
   "del": 0
  },
  {
   "path": "src/key.h",
   "add": 3,
   "del": 0
  },
  {
   "path": "src/qt/rpcconsole.cpp",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/qt/test/rpcnestedtests.cpp",
   "add": 16,
   "del": 0
  },
  {
   "path": "src/script/descriptor.cpp",
   "add": 117,
   "del": 0
  },
  {
   "path": "src/script/descriptor.h",
   "add": 31,
   "del": 0
  },
  {
   "path": "src/test/key_tests.cpp",
   "add": 47,
   "del": 0
  },
  {
   "path": "src/test/rpc_tests.cpp",
   "add": 12,
   "del": 0
  },
  {
   "path": "src/wallet/export.cpp",
   "add": 260,
   "del": 0
  },
  {
   "path": "src/wallet/export.h",
   "add": 31,
   "del": 0
  },
  {
   "path": "src/wallet/rpc/backup.cpp",
   "add": 134,
   "del": 0
  },
  {
   "path": "src/wallet/rpc/wallet.cpp",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/wallet/scriptpubkeyman.cpp",
   "add": 138,
   "del": 0
  },
  {
   "path": "src/wallet/scriptpubkeyman.h",
   "add": 17,
   "del": 0
  },
  {
   "path": "src/wallet/test/scriptpubkeyman_tests.cpp",
   "add": 1381,
   "del": 0
  },
  {
   "path": "src/wallet/test/wallet_rpc_tests.cpp",
   "add": 121,
   "del": 0
  },
  {
   "path": "src/wallet/test/walletload_tests.cpp",
   "add": 3,
   "del": 0
  },
  {
   "path": "src/wallet/types.h",
   "add": 2,
   "del": 0
  },
  {
   "path": "test/functional/p2p_segwit.py",
   "add": 4,
   "del": 0
  },
  {
   "path": "test/functional/test_runner.py",
   "add": 1,
   "del": 0
  },
  {
   "path": "test/functional/wallet_exportaddresskey.py",
   "add": 1200,
   "del": 0
  }
 ],
 "test_lines": 2785,
 "git": {
  "head": "64f57fe253b60849ee44ab2d4b62ea5cc2602933",
  "head_matches_backup": true,
  "base": "6ed5aad06111177c39f9b9f07a52634476380ae1",
  "patch_id": "ff0dfc2120af636b",
  "commits": [
   {
    "sha": "e6d9e8424f",
    "subject": "test: synchronize blocks in test_uncompressed_pubkey",
    "files": 1,
    "add": 4,
    "del": 0
   },
   {
    "sha": "7778c8cbd5",
    "subject": "wallet: add exportaddresskey RPC",
    "files": 7,
    "add": 315,
    "del": 0
   },
   {
    "sha": "a09a3cc568",
    "subject": "wallet: structure address key export results",
    "files": 11,
    "add": 459,
    "del": 50
   },
   {
    "sha": "10dc6e401c",
    "subject": "wallet: export keys for script-based addresses",
    "files": 11,
    "add": 753,
    "del": 37
   },
   {
    "sha": "b43ea1716f",
    "subject": "wallet: support Taproot address key export",
    "files": 15,
    "add": 1780,
    "del": 102
   },
   {
    "sha": "f3b48dd891",
    "subject": "wallet: verify address key export failure and security contracts",
    "files": 6,
    "add": 431,
    "del": 23
   },
   {
    "sha": "4f64a4d7ad",
    "subject": "wallet: trim address key export includes",
    "files": 3,
    "add": 5,
    "del": 2
   },
   {
    "sha": "64f57fe253",
    "subject": "wallet: preserve stored key identities in address key export",
    "files": 3,
    "add": 78,
    "del": 2
   }
  ],
  "patch_truncated": true
 },
 "patch_id": "ff0dfc2120af636b",
 "input_hash": "58348bae58d4e113",
 "extracted_at": "2026-11-02T01:09:49+00:00"
}