{
 "number": 36143,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36143",
 "title": "descriptor: add CreateMultisigDescriptor()",
 "author": "rxbryan",
 "author_association": "FIRST_TIME_CONTRIBUTOR",
 "created_at": "2026-09-01T23:57:02Z",
 "updated_at": "2026-09-17T02:28:41Z",
 "age_days": 15,
 "draft": true,
 "labels": [
  "Needs rebase",
  "Descriptors"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "e0b664f14158d9257d3441410391c8d23560aa88",
 "head_ref": "create-multisig-descriptor",
 "head_repo": "rxbryan/bitcoin",
 "head_history": [],
 "additions": 203,
 "deletions": 0,
 "changed_files": 3,
 "commit_count": 2,
 "size_bucket": "M",
 "mergeable_state": "dirty",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_nack": [
     {
      "login": "Sjors",
      "url": "https://github.com/bitcoin/bitcoin/pull/36143#issuecomment-5508988125"
     }
    ]
   },
   "conflicts": [
    {
     "number": 36133,
     "title": "wallet: store multipath descriptor",
     "author": "Sjors"
    },
    {
     "number": 35377,
     "title": "wallet: Allow importing of descriptors without private keys when the wallet has the private keys",
     "author": "achow101"
    },
    {
     "number": 34861,
     "title": "wallet: Add importdescriptors interface",
     "author": "polespinasa"
    }
   ]
  }
 },
 "acks_parsed": {
  "Sjors": {
   "kind": "concept_nack",
   "hash": null,
   "t": "2026-09-02T11:44:14Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 1,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "Sjors"
  ]
 },
 "signals": {
  "needs_rebase": true,
  "ci_failed": false,
  "mergeable_state": "dirty",
  "last_author_activity": "2026-09-02T23:23:56Z",
  "last_reviewer_activity": "2026-09-03T06:27:51Z",
  "last_reviewer": "Sjors",
  "author_silent_days": 14,
  "waiting_on_author_days": 14,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [
   35645
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 35645,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "Multisig Wizard tracking issue"
   }
  ],
  "conflicts": [
   36133,
   35377,
   34861
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [],
 "body": "The multisig setup flows need to assemble a descriptor from collected cosigner keys without hand-crafting the descriptor string, which today means bash interpolation with no validation of key forms, duplicates, or threshold.\n\nCreateMultisigDescriptor() takes a threshold, a list of key expressions with origin info, and an output type, and returns a ranged, multipath wsh(sortedmulti(...)) descriptor with checksum.\n\nPart of the multisig wizard tracking issue #35645.",
 "commits": [
  {
   "sha": "9698311b2513a1f1e50310e6f75dd9872ac765b4",
   "date": "2026-09-01T23:48:08Z",
   "message": "descriptor: add CreateMultisigDescriptor()\n\nThe multisig setup flows need to assemble a descriptor from collected\ncosigner keys without hand-crafting the descriptor string, which today\nmeans bash interpolation with no validation of key forms, duplicates, or\nthreshold.\n\nCreateMultisigDescriptor() takes a threshold, a list of key expressions\nwith origin info, and an output type, and returns a ranged, multipath\nwsh(sortedmulti(...)) descriptor with checksum.\n\nParsing is done by the existing internal ParsePubkey(); the setup-specific\npolicy is applied here on top of it. Private key, missing origin\ninfo, keys that already carry a derivation, and duplicate keys are all\nrejected. The /<0;1>/* suffix is appended internally, so callers supply\nbare account xpubs only.\n\nP2TR is deferred pending a decision on the taproot internal key. Other\noutput types return an error.\n\nCovered by unit tests in descriptor_tests.cpp.\n\nPart of the multisig wizard backend tracked in #35645."
  },
  {
   "sha": "e0b664f14158d9257d3441410391c8d23560aa88",
   "date": "2026-09-02T01:28:24Z",
   "message": "descriptor: support P2TR in CreateMultisigDescriptor\n\nHardcodes `xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZnin`\n`gWLdN3zgtU6QgnecKFpJFPpdzxKrwoaZoV44qAJewsc4kX9vGaCaBExuvJH57`\nas unspendable internal key since an xpub will work better with\nhardware signers than an xonly NUMS_H point.\n\nAlthough using a hardcoded internal key raises the possibility of\nfingerprinting"
  }
 ],
 "timeline": [
  {
   "t": "2026-09-02T11:44:14Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Concept NACK, unless this is part of a larger end-to-end implementation (with some buy-in), like a GUI or RPC that actually uses this new method.\n\nBut I also think that's premature. Wallet setup is a one-off thing, and it's not obvious what the best approach is. A Python utility is more flexible. A full c++ implementation is also likely to need a huge stack of commits, see e.g. https://github.com/Sjors/bitcoin/pull/91 which only implements spending from an _existing_ multisig wallet.\n\nSo I think the better sequence is:\n\n1.introduce a Python utility that helps the user construct and import a multisig descriptor\n2. gradually improve / add RPC methods to simplify that utility and eventually make it unnecessary\n\nStep (2) is where changes like this may be useful."
  },
  {
   "t": "2026-09-02T23:23:56Z",
   "kind": "comment",
   "who": "rxbryan",
   "assoc": "NONE",
   "text": "[quoted text omitted]\nThat sequencing makes sense. Would exposing this via an RPC in the same PR address the \"nothing uses it\" concern, or do you specifically want the Python flow to exist first so the RPC surface is shaped by what that flow needs?"
  },
  {
   "t": "2026-09-03T06:27:51Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nYes, but.\n\n[quoted text omitted]\nI think that's the better yes."
  }
 ],
 "labels_log": [
  {
   "t": "2026-09-01T23:57:06Z",
   "action": "labeled",
   "label": "Descriptors",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-17T02:28:40Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [],
 "text_chars": 2961,
 "text_tokens_estimate": 740,
 "changed_paths": [
  "src/script/descriptor.cpp",
  "src/script/descriptor.h",
  "src/test/descriptor_tests.cpp"
 ],
 "files": [
  {
   "path": "src/script/descriptor.cpp",
   "add": 111,
   "del": 0
  },
  {
   "path": "src/script/descriptor.h",
   "add": 13,
   "del": 0
  },
  {
   "path": "src/test/descriptor_tests.cpp",
   "add": 79,
   "del": 0
  }
 ],
 "test_lines": 79,
 "git": {
  "head": "e0b664f14158d9257d3441410391c8d23560aa88",
  "head_matches_backup": true,
  "base": "128e5c6805a02712ee981b2ca4613dc7d57761ce",
  "commits": [
   {
    "sha": "9698311b25",
    "subject": "descriptor: add CreateMultisigDescriptor()",
    "files": 3,
    "add": 172,
    "del": 0
   },
   {
    "sha": "e0b664f141",
    "subject": "descriptor: support P2TR in CreateMultisigDescriptor",
    "files": 3,
    "add": 43,
    "del": 12
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "af0b84240f5550ca",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}