{
 "number": 35747,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/35747",
 "title": "wallet: Fix FillPSBT failing to sign owned inputs when UTXOs disagree",
 "author": "nervana21",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-07-19T16:45:28Z",
 "updated_at": "2026-08-18T12:12:45Z",
 "age_days": 59,
 "draft": false,
 "labels": [
  "Wallet",
  "Needs rebase"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "2c361ed4fa2780361995165b59f1c442494bcdb2",
 "head_ref": "fix-fillpsbt",
 "head_repo": "nervana21/bitcoin",
 "head_history": [
  {
   "t": "2026-07-20T15:22:35Z",
   "sha": "0a13744b73f2abbe978294ca247abfe314e6e7de"
  },
  {
   "t": "2026-08-15T15:04:25Z",
   "sha": "2c361ed4fa2780361995165b59f1c442494bcdb2"
  }
 ],
 "additions": 93,
 "deletions": 7,
 "changed_files": 4,
 "commit_count": 2,
 "size_bucket": "S",
 "mergeable_state": "unknown",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {},
   "conflicts": [
    {
     "number": 35848,
     "title": "test: Cover `IsNull()` for PSBT, PSBTInput, PSBTOutput",
     "author": "nebula-21"
    },
    {
     "number": 35797,
     "title": "psbt: support output metadata updates before inputs are added",
     "author": "l0rinc"
    },
    {
     "number": 35569,
     "title": "Encapsulation for CTransaction",
     "author": "purpleKarrot"
    },
    {
     "number": 35069,
     "title": "Refactor keypath parser",
     "author": "pythcoiner"
    }
   ]
  }
 },
 "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": [
   "vicjuma"
  ]
 },
 "signals": {
  "needs_rebase": true,
  "ci_failed": false,
  "mergeable_state": "unknown",
  "last_author_activity": "2026-08-15T15:04:25Z",
  "last_reviewer_activity": "2026-08-03T10:15:54Z",
  "last_reviewer": "vicjuma",
  "author_silent_days": 33,
  "waiting_on_author_days": 0,
  "days_since_update": 30
 },
 "refs": {
  "mentioned": [],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [],
  "conflicts": [
   35848,
   35797,
   35569,
   35069
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/wallet/scriptpubkeyman.cpp"
 ],
 "body": "### tl;dr\n\nPreviously, the wallet could fail to sign an intended spend when an input's `non_witness_utxo` and `witness_utxo` disagreed.\nThis PR fixes `FillPSBT` by routing its UTXO-selection logic through `GetUTXO`.\n\n### Problem\n\nA PSBT input can carry both a `non_witness_utxo` and a `witness_utxo`. `GetUTXO` prefers a verified `non_witness_utxo` and does **not** fall back to `witness_utxo` when the `non_witness_utxo` is present but unusable.\n\nPreviously, `FillPSBT` selected the `SigningProvider` from the `witness_utxo` whenever it was set. When the two disagreed, `FillPSBT` loaded keys for the `witness_utxo` script while `SignPSBTInput` signed the `non_witness_utxo` spend. This could lead to an incomplete input even though the wallet owns the appropriate `non_witness_utxo` script.\n\n### Fix\n\nRoute `FillPSBT` through `GetUTXO` so it shares `SignPSBTInput`'s prefer/reject rule.\n\n### Behavior change\n\n`FillPSBT` now uses `GetUTXO` for script selection, so an input whose `non_witness_utxo` is present but unusable (out-of-range or txid-mismatched) returns `MISSING_INPUTS` instead of first selecting a script from `witness_utxo`.\n\n`SignPSBTInput` rejects the same input with `MISSING_INPUTS`, and `FillPSBT` already propagates any non-`INCOMPLETE` error for the remaining `ScriptPubKeyMan`s and inputs. The change is that the failure is detected at script selection rather than after a mistaken key lookup.\n\nWell-formed PSBTs are unaffected, since `witness_utxo` and `non_witness_utxo` resolve to the same script. Txid handling is also tightened in the selection path. A txid-mismatched `non_witness_utxo` is now rejected by `GetUTXO` rather than used for script selection when no `witness_utxo` is present.\n\n### Testing\n\n`fillpsbt_signs_despite_conflicting_witness_utxo` covers the regression. `psbt2_getutxo` pins the `GetUTXO` prefer/reject rules the fix relies on.\n\n### Follow-up\n\n`SignPSBTInput`, `PSBTInputSignedAndVerified`, and `decodepsbt` fee totaling still reimplement `GetUTXO`'s logic and risk the same divergence. Refactoring them is left for a follow-up.",
 "commits": [
  {
   "sha": "775de6e4ad7cbc8f349ec1481177c243bf724d24",
   "date": "2026-08-15T14:31:54Z",
   "message": "test, doc: Pin PSBTInput::GetUTXO prefer rules\n\nDocument and test prefer-non_witness, reject OOB/txid mismatch, and no\nwitness fallthrough when fields disagree, before migrating callers."
  },
  {
   "sha": "2c361ed4fa2780361995165b59f1c442494bcdb2",
   "date": "2026-08-15T14:38:53Z",
   "message": "wallet: Select FillPSBT SigningProvider via GetUTXO\n\nMatch SignPSBTInput by preferring a verified non_witness_utxo and\nrejecting a bad non_witness_utxo instead of falling back to a\nwitness_utxo.\n\nAdd a regression test."
  }
 ],
 "timeline": [
  {
   "t": "2026-07-20T15:22:35Z",
   "kind": "force_push",
   "who": "nervana21",
   "commit": "0a13744b73f2abbe978294ca247abfe314e6e7de"
  },
  {
   "t": "2026-08-03T10:15:54Z",
   "kind": "review_comment",
   "who": "vicjuma",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/scriptpubkeyman.cpp",
   "commit": "2c361ed4fa2780361995165b59f1c442494bcdb2",
   "in_reply_to": null,
   "text": "Why do I feel as if these checks are enough? Is there any motivation for someone intending to disagree `non_witness_utxo` and `witness_utxo`? Any risk involved? I have explored some possible paths and saw that at no particular point can this be internal."
  },
  {
   "t": "2026-08-03T15:28:45Z",
   "kind": "review_comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/scriptpubkeyman.cpp",
   "commit": "2c361ed4fa2780361995165b59f1c442494bcdb2",
   "in_reply_to": 3703059777,
   "text": "Hi vicjuma, thanks for your review!\n\n[quoted text omitted]\nFor `SignPSBTInput` they are but `FillPSBT` wasn't using that same rule for key selection though, which is the gap this PR closes.\n\n[quoted text omitted]\nIIUC, a malicious or otherwise buggy PSBT coordinator could add a faulty witness_utxo to an honest peer's already-existing non_witness_utxo. That would cause FillPSBT to choose the faulty witness_utxo for key selection instead of the desired non_witness_utxo, so signing could fail even though we owned the right input.\n\n[quoted text omitted]\nAs I see it, the risk with the old FillPSBT behavior is just that signing fails or stays incomplete. We'd never sign the wrong utxo.\n\n[quoted text omitted]\nThat's my understanding as well. I think this only corrects the external/mutated PSBT case."
  },
  {
   "t": "2026-08-15T15:04:25Z",
   "kind": "force_push",
   "who": "nervana21",
   "commit": "2c361ed4fa2780361995165b59f1c442494bcdb2"
  }
 ],
 "labels_log": [
  {
   "t": "2026-07-19T16:45:32Z",
   "action": "labeled",
   "label": "Wallet",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-19T17:45:24Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-20T16:18:22Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-15T02:46:56Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-15T15:38:17Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-18T12:12:44Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [],
 "text_chars": 3615,
 "text_tokens_estimate": 903,
 "changed_paths": [
  "src/psbt.h",
  "src/test/psbt_tests.cpp",
  "src/wallet/scriptpubkeyman.cpp",
  "src/wallet/test/psbt_wallet_tests.cpp"
 ],
 "files": [
  {
   "path": "src/psbt.h",
   "add": 5,
   "del": 0
  },
  {
   "path": "src/test/psbt_tests.cpp",
   "add": 44,
   "del": 0
  },
  {
   "path": "src/wallet/scriptpubkeyman.cpp",
   "add": 4,
   "del": 7
  },
  {
   "path": "src/wallet/test/psbt_wallet_tests.cpp",
   "add": 40,
   "del": 0
  }
 ],
 "test_lines": 84,
 "git": {
  "head": "2c361ed4fa2780361995165b59f1c442494bcdb2",
  "head_matches_backup": true,
  "base": "c90c23d388f66b7eef67f4c6f69184c088727d6a",
  "commits": [
   {
    "sha": "775de6e4ad",
    "subject": "test, doc: Pin PSBTInput::GetUTXO prefer rules",
    "files": 2,
    "add": 49,
    "del": 0
   },
   {
    "sha": "2c361ed4fa",
    "subject": "wallet: Select FillPSBT SigningProvider via GetUTXO",
    "files": 2,
    "add": 44,
    "del": 7
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "32b629f3148f15c7",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}