{
 "number": 34698,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/34698",
 "title": "wallet: handle MiniMiner bump fee calculation failures",
 "author": "shuv-amp",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-02-28T09:12:00Z",
 "updated_at": "2026-08-30T22:42:17Z",
 "age_days": 201,
 "draft": false,
 "labels": [
  "Wallet"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "a34afcd4366dd49b74922486bbce8a3fbd71ed83",
 "head_ref": "fix-wallet-bump-fee-error",
 "head_repo": "shuv-amp/bitcoin",
 "head_history": [
  {
   "t": "2026-03-25T04:04:42Z",
   "sha": "a241bd8fb1784796cbc3e1baf5a616161650c939"
  },
  {
   "t": "2026-03-25T07:24:44Z",
   "sha": "69b9601da8bce5c22ff39b701ad525de90346fd8"
  },
  {
   "t": "2026-05-01T09:45:06Z",
   "sha": "e0ef9387603ecdef7b11addbd80baaf1be7d9473"
  },
  {
   "t": "2026-05-02T01:09:25Z",
   "sha": "119c28b0cb7b0b3031f41ad80913ec65b51c4980"
  },
  {
   "t": "2026-08-19T21:22:55Z",
   "sha": "a34afcd4366dd49b74922486bbce8a3fbd71ed83"
  }
 ],
 "additions": 147,
 "deletions": 37,
 "changed_files": 11,
 "commit_count": 1,
 "size_bucket": "M",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {},
   "conflicts": [
    {
     "number": 35511,
     "title": "RFC: consensus: Make `CAmount` a class",
     "author": "hodlinator"
    }
   ]
  }
 },
 "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": [
   "achow101"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-08-19T21:30:02Z",
  "last_reviewer_activity": "2026-04-30T21:32:41Z",
  "last_reviewer": "achow101",
  "author_silent_days": 28,
  "waiting_on_author_days": 0,
  "days_since_update": 17
 },
 "refs": {
  "mentioned": [
   29711
  ],
  "depends_on": [],
  "fixes": [
   29711
  ],
  "linked_issues": [
   {
    "number": 29711,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "Wallet: Nondescript error message for 502nd unconfirmed transaction"
   }
  ],
  "references": [
   {
    "number": 29711,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "Wallet: Nondescript error message for 502nd unconfirmed transaction"
   }
  ],
  "conflicts": [
   35511
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/node/mini_miner.cpp",
  "src/wallet/spend.cpp"
 ],
 "body": "Fixes #29711.\n\nWhen MiniMiner cannot calculate individual bump fees, wallet funding can\nindex missing bump-fee entries and return `map::at` to users.\n\nTreat an empty bump-fee map for non-empty requested outpoints as a\ncalculation failure. Make `AvailableCoins` return\n`util::Result<CoinsResult>` so transaction creation and `sendall` can\npropagate the wallet error instead of leaking an internal exception.\n\nAdd a functional regression test in `wallet_spend_unconfirmed.py`\ncovering the large unconfirmed set scenario from #29711. The test checks\nauto-selected inputs, manually selected inputs, and `sendall`, and verifies\nthat spending succeeds again after mining a block.\n\nTested:\n- `cmake --build build`\n- `build/bin/test_bitcoin --catch_system_errors=no`\n- `build/bin/test_bitcoin --run_test=miniminer_tests --catch_system_errors=no`\n- `build/bin/test_bitcoin --run_test=spend_tests --catch_system_errors=no`\n- `build/bin/test_bitcoin --run_test=coinselector_tests/bump_fee_test --catch_system_errors=no`\n- `build/bin/test_bitcoin --run_test=wallet_tests/ListCoinsTest --catch_system_errors=no`\n- `build/bin/test_bitcoin --run_test=wallet_tests/BasicOutputTypesTest --catch_system_errors=no`\n- `python3 build/test/functional/test_runner.py wallet_spend_unconfirmed.py wallet_sendall.py wallet_send.py wallet_fundrawtransaction.py wallet_bumpfee.py wallet_create_tx.py wallet_basic.py`\n- `build/bin/bench_bitcoin -filter='WalletAvailableCoins|WalletCreateTxUseOnlyPresetInputs|WalletCreateTxUsePresetInputsAndCoinSelection' -sanity-check`",
 "commits": [
  {
   "sha": "a34afcd4366dd49b74922486bbce8a3fbd71ed83",
   "date": "2026-08-19T21:21:04Z",
   "message": "wallet: handle MiniMiner bump fee calculation failures\n\ncalculateIndividualBumpFees returns an empty map when MiniMiner cannot\ncalculate fees for requested unconfirmed outpoints, such as when\nGatherClusters hits its transaction limit. FetchSelectedInputs and\nAvailableCoins then index the map and can surface map::at to users.\n\nReturn an error when the map is empty for non-empty requested outpoints.\nMake AvailableCoins return util::Result<CoinsResult> so transaction creation\nand sendall can propagate this error. No-feerate callers keep the existing\nbehavior by unwrapping the result.\n\nThe mini_miner fuzz target asserted that the calculation is always ready,\nwhich no longer holds once that limit is hit, so handle both outcomes\nthere, and add unit coverage that hitting the limit yields no bump fees.\n\nAdd a functional regression test for the #29711 scenario. Cover\nauto-selected inputs, manually selected inputs, and sendall, and check that\nspending succeeds again after mining a block."
  }
 ],
 "timeline": [
  {
   "t": "2026-03-24T22:43:57Z",
   "kind": "review_comment",
   "who": "achow101",
   "assoc": "MEMBER",
   "path": "src/node/mini_miner.cpp",
   "commit": "5f9460692710a4d0cfef14e568bea114412bc939",
   "in_reply_to": null,
   "text": "Changing this to be `std::optional` seems a bit unnecessary to me as it's equivalently easy to check for `!empty()`. That probably reduces the diff by half."
  },
  {
   "t": "2026-03-25T02:05:26Z",
   "kind": "review_comment",
   "who": "shuv-amp",
   "assoc": "CONTRIBUTOR",
   "path": "src/node/mini_miner.cpp",
   "commit": "5f9460692710a4d0cfef14e568bea114412bc939",
   "in_reply_to": 2984747958,
   "text": "Makes sense. I'll simplify this by keeping the map return type and detecting failure based on whether entries were produced for all requested outpoints, rather than changing the interface to return `std::optional`."
  },
  {
   "t": "2026-03-25T04:04:42Z",
   "kind": "force_push",
   "who": "shuv-amp",
   "commit": "a241bd8fb1784796cbc3e1baf5a616161650c939"
  },
  {
   "t": "2026-03-25T07:24:44Z",
   "kind": "force_push",
   "who": "shuv-amp",
   "commit": "69b9601da8bce5c22ff39b701ad525de90346fd8"
  },
  {
   "t": "2026-04-30T21:32:41Z",
   "kind": "review_comment",
   "who": "achow101",
   "assoc": "MEMBER",
   "path": "src/wallet/spend.cpp",
   "commit": "69b9601da8bce5c22ff39b701ad525de90346fd8",
   "in_reply_to": null,
   "text": "I don't think this is the right way to propagate errors out of `AvailableCoins`. This is already returning `CoinsResult` which can be extended to include error codes/states, a la `ValidationState`. Or this could return a `util::Result<CoinsResult>` to contain the error message on error."
  },
  {
   "t": "2026-05-01T09:45:06Z",
   "kind": "force_push",
   "who": "shuv-amp",
   "commit": "e0ef9387603ecdef7b11addbd80baaf1be7d9473"
  },
  {
   "t": "2026-05-02T01:09:25Z",
   "kind": "force_push",
   "who": "shuv-amp",
   "commit": "119c28b0cb7b0b3031f41ad80913ec65b51c4980"
  },
  {
   "t": "2026-08-19T21:22:55Z",
   "kind": "force_push",
   "who": "shuv-amp",
   "commit": "a34afcd4366dd49b74922486bbce8a3fbd71ed83"
  },
  {
   "t": "2026-08-19T21:26:32Z",
   "kind": "review_comment",
   "who": "shuv-amp",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/spend.cpp",
   "commit": "69b9601da8bce5c22ff39b701ad525de90346fd8",
   "in_reply_to": 3170963339,
   "text": "Took the second option, `AvailableCoins` now returns `util::Result<CoinsResult>`"
  },
  {
   "t": "2026-08-19T21:30:02Z",
   "kind": "comment",
   "who": "shuv-amp",
   "assoc": "CONTRIBUTOR",
   "text": "Rebased on master.\n\nChanges since the last round of review:\n\n- `AvailableCoins` returns `util::Result<CoinsResult>` rather than reporting through an\n  out-parameter, so the error travels with the result.\n- `CreateTransactionInternal` moves the `CoinsResult` instead of copying it.\n- Added a comment at both call sites for why an empty bump-fee map means the calculation\n  gave up, and comments in the functional test for the cluster limit the scenario relies on."
  }
 ],
 "labels_log": [
  {
   "t": "2026-02-28T09:12:04Z",
   "action": "labeled",
   "label": "Wallet",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-01T23:52:12Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-05T13:10:38Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [],
 "text_chars": 3797,
 "text_tokens_estimate": 949,
 "changed_paths": [
  "src/bench/wallet_create_tx.cpp",
  "src/test/fuzz/mini_miner.cpp",
  "src/test/miniminer_tests.cpp",
  "src/wallet/interfaces.cpp",
  "src/wallet/rpc/coins.cpp",
  "src/wallet/rpc/spend.cpp",
  "src/wallet/spend.cpp",
  "src/wallet/spend.h",
  "src/wallet/test/spend_tests.cpp",
  "src/wallet/test/wallet_tests.cpp",
  "test/functional/wallet_spend_unconfirmed.py"
 ],
 "files": [
  {
   "path": "src/bench/wallet_create_tx.cpp",
   "add": 8,
   "del": 6
  },
  {
   "path": "src/test/fuzz/mini_miner.cpp",
   "add": 15,
   "del": 9
  },
  {
   "path": "src/test/miniminer_tests.cpp",
   "add": 11,
   "del": 0
  },
  {
   "path": "src/wallet/interfaces.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/wallet/rpc/coins.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/wallet/rpc/spend.cpp",
   "add": 5,
   "del": 1
  },
  {
   "path": "src/wallet/spend.cpp",
   "add": 22,
   "del": 9
  },
  {
   "path": "src/wallet/spend.h",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/wallet/test/spend_tests.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/wallet/test/wallet_tests.cpp",
   "add": 9,
   "del": 5
  },
  {
   "path": "test/functional/wallet_spend_unconfirmed.py",
   "add": 70,
   "del": 0
  }
 ],
 "test_lines": 135,
 "git": {
  "head": "a34afcd4366dd49b74922486bbce8a3fbd71ed83",
  "head_matches_backup": true,
  "base": "367b2202a496c866c485d940864171ca97c514f2",
  "commits": [
   {
    "sha": "a34afcd436",
    "subject": "wallet: handle MiniMiner bump fee calculation failures",
    "files": 11,
    "add": 147,
    "del": 37
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "fafee9503517e9c0",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}