#36385 rest: fix getutxos POST body decoding
| Reviewability | Reviews | Agreement | Size | ||||
|---|---|---|---|---|---|---|---|
| #36385 rest: fix getutxos POST body decoding | Waiting on author | 1 | Strong | 7 + 17 tests | |||
|
|
|
|
| |||
Reviewability: ReadyStalePaused Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs. Size: lines added or modified outside tests, then in tests; greener = smaller. | |||||||
Goal
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.
Evidence of importance:
- 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.'
Risk of a mistake: medium (rpc_api, tests). A flaw in REST request parsing could cause node endpoint failures or return incorrect UTXO query results to client applications.
Changed since the previous assessment: 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.
Reviewability
Paused · Waiting on author
Open objection from fametrano with no author reply for 7 days; review would repeat what has been asked.
Waiting on (from the thread read):
- author: response to fametrano's suggestion to return HTTP 400 for empty outpoints 2026-10-25
Agreement
Strong
substantive support, no open objection (Zeegaths, fametrano)
Thread notes: Added Tested ACK and suggestion from fametrano to reject empty-outpoint POST bodies with HTTP 400.
Objections, suggestions, and questions:
| Reviewer | Type | Harm, request, or question | Status | Must be | Author replied | Quote |
|---|---|---|---|---|---|---|
| b-l-u-e (contributor) | objection, correctness | POST decoder silently accepts trailing data instead of rejecting requests with unconsumed bytes | fixed · fix pushed · author added trailing-byte rejection and tests | changed | yes | 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' Status from: 2026-10-04: l0rinc: 'added trailing-byte rejection and coverage for binary and hex POST bodies' |
| b-l-u-e (contributor) | objection, correctness | POST /getutxos accepts odd-length hex bodies with 200 OK instead of rejecting with HTTP 400 | fixed · fix pushed · author pushed a fix for the remaining issues and added b-l-u-e as co-author | changed | yes | 2026-10-04: 'AssertionError: Expected: 400, Got: 200 (OK)' Status from: 2026-10-04: l0rinc: 'we might as well fix the remaining issues. Added @b-l-u-e as co-author.' |
| fametrano (contributor) | suggestion, interface | return HTTP 400 'Error: empty request' when a POST body contains no outpoints | open | answered | no | 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.' |
Support:
- Zeegaths (none) (2026-10-01) [tested]: reproduced the issue locally and verified response match on the PR branch
- fametrano (contributor) (Tested ACK, 2026-10-25) [both: interface_rest.py]: 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
Participants:
- Zeegaths (none, support, 2 statements 2026-10-01 to 2026-10-02): reproduced the issue and verified the fix on the PR branch
- b-l-u-e (contributor, objection, 2 statements 2026-10-02 to 2026-10-04): pointed out unconsumed trailing POST bytes and found unhandled odd-length hex bodies
- fametrano (contributor, support, 1 statements 2026-10-25 to 2026-10-25): tACK; suggested returning HTTP 400 when a POST body contains no outpoints
Files
22 lines under test/bench/ci.
- test/functional/interface_rest.py +17/-5
- src/rest.cpp +7/-6
Card
The short text the ranking pass compares across PRs.
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.
Data
thread read through 2026-10-25 (8 statements), head b7622767c6
labels: RPC/REST/ZMQ
Categories
RPC / REST / ZMQ
P1 · bug fix (assessed alone as P2; Raised to P1: like #35837 it silently returns wrong data to existing callers (POST getutxos always misses), plus an unbounded allocation; same consequence class as the other P1 correctness bugs.)
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.
Membership: Matches the RPC/REST/ZMQ label and directly modifies src/rest.cpp endpoint deserialization.
Factors: security/stability 1, bug 2, performance 0, user value 2, leverage 0