{
 "number": 36385,
 "input_hash": "594a1f443762875b",
 "model": "ledger",
 "prompt_hash": null,
 "created": "2026-10-26T01:21:43+00:00",
 "result": {
  "summary": "Fixes request body deserialization for the `/rest/getutxos` POST endpoint. Rather than writing the request string into a `DataStream` with `operator<<`, which prepended a `CompactSize` length prefix, the endpoint parses body bytes in place using `SpanReader`. The change also caps vector deserialization at `MAX_GETUTXOS_OUTPOINTS`, rejects trailing unparsed bytes with an HTTP 400 error, and fails malformed hex inputs using `TryParseHex`.",
  "problem": "Binary and hex POST requests to `/rest/getutxos` incorrectly report that valid outpoints do not exist. Serializing the body string with `operator<<` inserts a length prefix, causing the parser to interpret the length byte as the `checkmempool` flag and misalign all subsequent outpoints. In addition, an unbounded vector read could allocate memory for large declared counts before reaching the 15-outpoint validation check, and trailing or malformed data was silently accepted.",
  "card": "Fixes request body deserialization in the /rest/getutxos endpoint for binary and hex POST queries. Serializing the raw body into a DataStream with operator<< prepended a CompactSize length, misaligning parsed fields and causing valid POST queries to find no UTXOs. The change deserializes the payload in place using SpanReader, bounds vector deserialization before allocation, rejects trailing bytes, and errors on malformed hex. Functional tests are updated to ensure GET and POST requests return matching binary output.",
  "evidence": [
   "PR description: 'Binary GET and POST requests to /rest/getutxos for the same outpoints should agree. GET finds the unspent output, but POST incorrectly reports no hit.'",
   "Commit 57dc676b59: 'Writing the POST body to DataStream with operator<< serializes it as a string and prepends a CompactSize length, which the parser incorrectly reads as request fields.'"
  ],
  "scope_notes": "",
  "changed_since_previous": "No functional changes to the patch logic; the PR remains split into four commits addressing test characterization, decoder correction, vector size limits, and in-place `SpanReader` usage.",
  "thread": {
   "last_event_at": "2026-10-25T11:40:45Z",
   "events": 8,
   "head_sha": "b7622767c6e4100ea2d0ac8fae2d6c98a43d081d",
   "schema_floor": 2,
   "schema": 2
  },
  "waiting_on": [
   {
    "at": "2026-10-25",
    "id": "c:6397174832",
    "on": "author",
    "url": "https://github.com/bitcoin/bitcoin/pull/36385#issuecomment-6397174832",
    "what": "response to fametrano's suggestion to return HTTP 400 for empty outpoints"
   }
  ],
  "risk": {
   "level": "medium",
   "touches": [
    "rpc_api",
    "tests"
   ],
   "why": "A flaw in REST request parsing could cause node endpoint failures or return incorrect UTXO query results to client applications."
  },
  "discussion": {
   "author_status": "last statement processed 2026-10-25",
   "open_concerns": [
    "fametrano: return HTTP 400 'Error: empty request' when a POST body contains no outpoints"
   ],
   "resolved_concerns": [
    "b-l-u-e: POST decoder silently accepts trailing data instead of rejecting requests with unconsumed bytes (fixed by l0rinc 2026-10-04)",
    "b-l-u-e: POST /getutxos accepts odd-length hex bodies with 200 OK instead of rejecting with HTTP 400 (fixed by l0rinc 2026-10-04)"
   ]
  },
  "reviewability": {
   "state": "Paused",
   "label": "Waiting on author",
   "reason": "Open objection from fametrano with no author reply for 7 days; review would repeat what has been asked."
  },
  "agreement": {
   "state": "Strong",
   "summary": "Strong: substantive support, no open objection (Zeegaths, fametrano)",
   "reason": "Added Tested ACK and suggestion from fametrano to reject empty-outpoint POST bodies with HTTP 400.",
   "evidence": [
    "fametrano (suggestion, interface, must be answered, open): return HTTP 400 'Error: empty request' when a POST body contains no outpoints",
    "Zeegaths: support \u2014 reproduced the issue locally and verified response match on the PR branch",
    "fametrano: Tested ACK \u2014 tested 14 POST bodies in binary and hex against regtest nodes at base and PR head, verifying that all 28 match GET / 400 expectations on PR head versus 10 on base"
   ],
   "objections": [
    {
     "reviewer": "b-l-u-e",
     "association": "CONTRIBUTOR",
     "type": "objection",
     "kind": "correctness",
     "harm": "POST decoder silently accepts trailing data instead of rejecting requests with unconsumed bytes",
     "request": "",
     "question": "",
     "text": "POST decoder silently accepts trailing data instead of rejecting requests with unconsumed bytes",
     "clears_with": "change",
     "blocking": true,
     "status": "fixed",
     "status_note": "author added trailing-byte rejection and tests",
     "cleared": true,
     "author_replied": true,
     "others_replied": false,
     "fix_pushed": true,
     "replies": [
      "rc:4178091417"
     ],
     "evidence": "2026-10-02: 'seems like trailing data is silently being accepted... the decoder doesnt check that the body was consumed fully..so any bytes after the outpoint vector are ignored'",
     "url": "https://github.com/bitcoin/bitcoin/pull/36385#discussion_r4167801310",
     "id": "rc:4167801310",
     "at": "2026-10-02",
     "status_at": "2026-10-04",
     "status_by": "l0rinc",
     "resolution_evidence": "2026-10-04: l0rinc: 'added trailing-byte rejection and coverage for binary and hex POST bodies'",
     "pin": null,
     "from_schema": 2
    },
    {
     "reviewer": "b-l-u-e",
     "association": "CONTRIBUTOR",
     "type": "objection",
     "kind": "correctness",
     "harm": "POST /getutxos accepts odd-length hex bodies with 200 OK instead of rejecting with HTTP 400",
     "request": "",
     "question": "",
     "text": "POST /getutxos accepts odd-length hex bodies with 200 OK instead of rejecting with HTTP 400",
     "clears_with": "change",
     "blocking": true,
     "status": "fixed",
     "status_note": "author pushed a fix for the remaining issues and added b-l-u-e as co-author",
     "cleared": true,
     "author_replied": true,
     "others_replied": false,
     "fix_pushed": true,
     "replies": [
      "c:5985643679"
     ],
     "evidence": "2026-10-04: 'AssertionError: Expected: 400, Got: 200 (OK)'",
     "url": "https://github.com/bitcoin/bitcoin/pull/36385#issuecomment-5983327618",
     "id": "c:5983327618",
     "at": "2026-10-04",
     "status_at": "2026-10-04",
     "status_by": "l0rinc",
     "resolution_evidence": "2026-10-04: l0rinc: 'we might as well fix the remaining issues. Added @b-l-u-e as co-author.'",
     "pin": null,
     "from_schema": 2
    },
    {
     "reviewer": "fametrano",
     "association": "CONTRIBUTOR",
     "type": "suggestion",
     "kind": "interface",
     "harm": "",
     "request": "return HTTP 400 'Error: empty request' when a POST body contains no outpoints",
     "question": "",
     "text": "return HTTP 400 'Error: empty request' when a POST body contains no outpoints",
     "clears_with": "answer",
     "blocking": false,
     "status": "open",
     "status_note": "",
     "cleared": false,
     "author_replied": false,
     "others_replied": false,
     "fix_pushed": false,
     "replies": [],
     "evidence": "2026-10-25: 'Adding `if (vOutPoints.empty()) return RESTERR(req, HTTP_BAD_REQUEST, \"Error: empty request\");` before the max-outpoints check makes these a 400 too.'",
     "url": "https://github.com/bitcoin/bitcoin/pull/36385#issuecomment-6397174832",
     "id": "c:6397174832",
     "at": "2026-10-25",
     "status_at": null,
     "status_by": null,
     "resolution_evidence": "",
     "pin": null,
     "from_schema": 2
    }
   ],
   "support": [
    {
     "reviewer": "Zeegaths",
     "reason": "reproduced the issue locally and verified response match on the PR branch",
     "substantive": true,
     "verdict": "",
     "id": "c:5928408741",
     "url": "https://github.com/bitcoin/bitcoin/pull/36385#issuecomment-5928408741",
     "at": "2026-10-01",
     "association": "NONE",
     "evidence": "tested",
     "areas": []
    },
    {
     "reviewer": "fametrano",
     "reason": "tested 14 POST bodies in binary and hex against regtest nodes at base and PR head, verifying that all 28 match GET / 400 expectations on PR head versus 10 on base",
     "substantive": true,
     "verdict": "Tested ACK",
     "id": "c:6397174832",
     "url": "https://github.com/bitcoin/bitcoin/pull/36385#issuecomment-6397174832",
     "at": "2026-10-25",
     "association": "CONTRIBUTOR",
     "evidence": "both",
     "areas": [
      "interface_rest.py"
     ]
    }
   ],
   "participants": [
    {
     "login": "Zeegaths",
     "stance": "support",
     "note": "reproduced the issue and verified the fix on the PR branch",
     "association": "NONE",
     "comments": 2,
     "first": "2026-10-01",
     "last": "2026-10-02"
    },
    {
     "login": "b-l-u-e",
     "stance": "objection",
     "note": "pointed out unconsumed trailing POST bytes and found unhandled odd-length hex bodies",
     "association": "CONTRIBUTOR",
     "comments": 2,
     "first": "2026-10-02",
     "last": "2026-10-04"
    },
    {
     "login": "fametrano",
     "stance": "support",
     "note": "tACK; suggested returning HTTP 400 when a POST body contains no outpoints",
     "association": "CONTRIBUTOR",
     "comments": 1,
     "first": "2026-10-25",
     "last": "2026-10-25"
    }
   ],
   "derivation": "substantive support, no open objection (Zeegaths, fametrano)",
   "model_state": null,
   "corrections": [],
   "notes": "Added Tested ACK and suggestion from fametrano to reject empty-outpoint POST bodies with HTTP 400."
  },
  "dependencies": {
   "depends_on": [],
   "enables": []
  },
  "categories": [
   {
    "name": "rpc",
    "member": true,
    "evidence": "Matches the RPC/REST/ZMQ label and directly modifies src/rest.cpp endpoint deserialization.",
    "band": "P2",
    "reason_tag": "bug fix",
    "score": 0.68,
    "factors": {
     "bug_severity": 2,
     "leverage": 0,
     "performance": 0,
     "security_stability": 1,
     "user_value": 2
    },
    "rationale": "Fixes a data correctness bug where POST requests to `/rest/getutxos` fail to find valid unspent outputs due to improper length-prefixed stream deserialization. As noted in the PR description, 'GET finds the unspent output, but POST incorrectly reports no hit', causing false negatives for consumers of the REST endpoint while also patching an unbounded allocation risk on input outpoints."
   }
  ],
  "confidence": "high",
  "uncertainties": [],
  "needs": []
 },
 "cost_usd": 0.0,
 "source": {
  "record": "2026-10-26T01:21:43+00:00",
  "code": "2026-10-06T01:20:25+00:00",
  "judgments": 1
 }
}