{
 "number": 36344,
 "input_hash": "a5a354ea7fe29e0a",
 "model": "ledger",
 "prompt_hash": null,
 "created": "2026-10-31T01:17:32+00:00",
 "result": {
  "summary": "Rejects duplicate inputs in the `sendall` RPC by tracking seen outpoints and throwing an `RPC_INVALID_PARAMETER` error if a duplicate is found. This prevents constructing and committing invalid transactions when an outpoint is specified more than once in the `inputs` option.",
  "problem": "`sendall` adds the value of each specified input to the total input amount without verifying uniqueness. Supplying duplicate UTXOs creates a consensus-invalid transaction spending the same prevout multiple times; while the RPC reports success, the transaction fails mempool acceptance (`bad-txns-inputs-duplicate`) and locks the UTXOs in the wallet with negative balance accounting until abandoned.",
  "card": "Reject duplicate inputs in the sendall RPC with an RPC_INVALID_PARAMETER error. Previously, passing duplicate UTXOs to sendall caused the wallet to sum input values multiple times and generate consensus-invalid transactions (bad-txns-inputs-duplicate). These transactions failed mempool acceptance but were committed to the wallet, locking coins and displaying negative balances until abandoned. The fix mirrors duplicate checks in other RPCs and adds functional test coverage.",
  "evidence": [
   "The PR description demonstrates on regtest that passing the same UTXO twice creates a tx with duplicate inputs and double the payout, returns complete: true, is rejected by testmempoolaccept with bad-txns-inputs-duplicate, and causes getbalances to show nonmempool: -1.0 until abandoned.",
   "A functional test in test/functional/wallet_sendall.py confirms the RPC now throws RPC_INVALID_PARAMETER and leaves wallet balance and transaction counts untouched."
  ],
  "scope_notes": "",
  "changed_since_previous": "No functional or structural change; appears to be a rebase or commit metadata update.",
  "thread": {
   "last_event_at": "2026-10-30T19:26:28Z",
   "events": 7,
   "head_sha": "1beefce3519348662d833734cadf01a23d60d83e",
   "schema_floor": 2,
   "schema": 2
  },
  "waiting_on": [
   {
    "at": null,
    "id": "",
    "on": "nothing",
    "url": null,
    "what": "all reviews addressed and PR has ACKs"
   }
  ],
  "risk": {
   "level": "medium",
   "touches": [
    "wallet",
    "rpc_api"
   ],
   "why": "A flaw in input validation during transaction construction could reject valid UTXO sets or fail to properly construct spend transactions in the wallet RPC."
  },
  "discussion": {
   "author_status": "last statement processed 2026-10-30",
   "open_concerns": [],
   "resolved_concerns": [
    "l0rinc: assert the affected wallet state instead of an assertion that passes before and after, demonstrating the full state transition (accepted by l0rinc 2026-10-30)",
    "l0rinc: prefer std::unordered_set<COutPoint, SaltedOutpointHasher> with reserve for linear work and lower overhead on large sweeps (accepted by l0rinc 2026-10-30)"
   ]
  },
  "reviewability": {
   "state": "Ready",
   "label": "Ready",
   "reason": "Nothing known that would invalidate a review now."
  },
  "agreement": {
   "state": "Strong",
   "summary": "Strong: substantive support, no open objection (l0rinc, molnard, l0rinc)",
   "reason": "Added Tested ACK from molnard and full ACK from l0rinc. Recorded l0rinc's container nit as an accepted suggestion since they approved and stated they are fine with it as-is.",
   "evidence": [
    "achow101: ACK",
    "l0rinc: Concept ACK \u2014 The RPC-error assertion catches the missing rejection, but we should cover the reported wallet-state correction also - see inline suggestion.",
    "molnard: Tested ACK \u2014 Ran wallet_sendall.py on win; passes with the fix and fails with No exception raised when check is removed",
    "l0rinc: ACK \u2014 Verified state transition using characterization test in wallet_sendall.py"
   ],
   "objections": [
    {
     "reviewer": "l0rinc",
     "association": "MEMBER",
     "type": "suggestion",
     "kind": "approach",
     "harm": "",
     "request": "assert the affected wallet state instead of an assertion that passes before and after, demonstrating the full state transition",
     "question": "",
     "text": "assert the affected wallet state instead of an assertion that passes before and after, demonstrating the full state transition",
     "clears_with": "answer",
     "blocking": false,
     "status": "accepted",
     "status_note": "l0rinc verified the state transition test and ACKed the push",
     "cleared": true,
     "author_replied": true,
     "others_replied": false,
     "fix_pushed": true,
     "replies": [
      "rc:4387759956"
     ],
     "evidence": "2026-10-18: 'The previous line correctly fails without the fix, but this assertion would pass before and after because the invalid transaction never enters the mempool - we should assert the affected wallet state '",
     "url": "https://github.com/bitcoin/bitcoin/pull/36344#discussion_r4305956514",
     "id": "rc:4305956514",
     "at": "2026-10-18",
     "status_at": "2026-10-30",
     "status_by": "l0rinc",
     "resolution_evidence": "2026-10-30: l0rinc: 'I like 1 beef, ACK 1beefce3519348662d833734cadf01a23d60d83e'",
     "pin": null,
     "from_schema": 2
    },
    {
     "reviewer": "l0rinc",
     "association": "MEMBER",
     "type": "suggestion",
     "kind": "approach",
     "harm": "",
     "request": "prefer std::unordered_set<COutPoint, SaltedOutpointHasher> with reserve for linear work and lower overhead on large sweeps",
     "question": "",
     "text": "prefer std::unordered_set<COutPoint, SaltedOutpointHasher> with reserve for linear work and lower overhead on large sweeps",
     "clears_with": "answer",
     "blocking": false,
     "status": "accepted",
     "status_note": "reviewer explicitly noted they are fine with the implementation as-is",
     "cleared": true,
     "author_replied": false,
     "others_replied": false,
     "fix_pushed": false,
     "replies": [],
     "evidence": "2026-10-30: 'Nit: I\u2019d prefer `std::unordered_set<COutPoint, SaltedOutpointHasher>` with `reserve(rawTx.vin.size())` here.'",
     "url": "https://github.com/bitcoin/bitcoin/pull/36344#discussion_r4427661311",
     "id": "rc:4427661311",
     "at": "2026-10-30",
     "status_at": "2026-10-30",
     "status_by": "l0rinc",
     "resolution_evidence": "2026-10-30: l0rinc: 'Left a nit, also fine with it as-is.'",
     "pin": null,
     "from_schema": 2
    }
   ],
   "support": [
    {
     "reviewer": "achow101",
     "reason": "",
     "substantive": false,
     "verdict": "ACK",
     "id": "c:5900949316",
     "url": "https://github.com/bitcoin/bitcoin/pull/36344#issuecomment-5900949316",
     "at": "2026-09-29",
     "association": "MEMBER",
     "evidence": "none",
     "areas": []
    },
    {
     "reviewer": "l0rinc",
     "reason": "The RPC-error assertion catches the missing rejection, but we should cover the reported wallet-state correction also - see inline suggestion.",
     "substantive": true,
     "verdict": "Concept ACK",
     "id": "r:5561688631",
     "url": "https://github.com/bitcoin/bitcoin/pull/36344#pullrequestreview-5561688631",
     "at": "2026-10-18",
     "association": "MEMBER",
     "evidence": "read",
     "areas": []
    },
    {
     "reviewer": "molnard",
     "reason": "Ran wallet_sendall.py on win; passes with the fix and fails with No exception raised when check is removed",
     "substantive": true,
     "verdict": "Tested ACK",
     "id": "r:5713521792",
     "url": "https://github.com/bitcoin/bitcoin/pull/36344#pullrequestreview-5713521792",
     "at": "2026-10-30",
     "association": "NONE",
     "evidence": "tested",
     "areas": [
      "wallet_sendall.py"
     ]
    },
    {
     "reviewer": "l0rinc",
     "reason": "Verified state transition using characterization test in wallet_sendall.py",
     "substantive": true,
     "verdict": "ACK",
     "id": "r:5717619419",
     "url": "https://github.com/bitcoin/bitcoin/pull/36344#pullrequestreview-5717619419",
     "at": "2026-10-30",
     "association": "MEMBER",
     "evidence": "both",
     "areas": [
      "test/functional/wallet_sendall.py"
     ]
    }
   ],
   "participants": [
    {
     "login": "achow101",
     "stance": "support",
     "note": "ACKed the commit hash",
     "association": "MEMBER",
     "comments": 1,
     "first": "2026-09-29",
     "last": "2026-09-29"
    },
    {
     "login": "l0rinc",
     "stance": "support",
     "note": "ACKed 1beefce after verifying state transition in tests; left an optional nit",
     "association": "MEMBER",
     "comments": 4,
     "first": "2026-10-18",
     "last": "2026-10-30"
    },
    {
     "login": "molnard",
     "stance": "support",
     "note": "Tested ACK on Windows",
     "association": "NONE",
     "comments": 1,
     "first": "2026-10-30",
     "last": "2026-10-30"
    }
   ],
   "derivation": "substantive support, no open objection (l0rinc, molnard, l0rinc)",
   "model_state": null,
   "corrections": [],
   "notes": "Added Tested ACK from molnard and full ACK from l0rinc. Recorded l0rinc's container nit as an accepted suggestion since they approved and stated they are fine with it as-is."
  },
  "dependencies": {
   "depends_on": [],
   "enables": []
  },
  "categories": [
   {
    "name": "wallet",
    "member": true,
    "evidence": "Has the Wallet label and modifies `src/wallet/rpc/spend.cpp` and `test/functional/wallet_sendall.py`.",
    "band": "P3",
    "reason_tag": "bug fix",
    "score": 0.45,
    "factors": {
     "bug_severity": 1,
     "leverage": 0,
     "performance": 0,
     "security_stability": 1,
     "user_value": 1
    },
    "rationale": "Fixes a bug where `sendall` creates consensus-invalid transactions and leaves the wallet with negative balance accounting when duplicate inputs are passed. While not a permanent loss of funds, it resolves confusing failures and bad wallet state requiring manual abandonment."
   }
  ],
  "confidence": "high",
  "uncertainties": [],
  "needs": []
 },
 "cost_usd": 0.0,
 "source": {
  "record": "2026-10-31T01:17:32+00:00",
  "code": "2026-10-29T01:26:17+00:00",
  "judgments": 1
 }
}