Repository: bitcoin/bitcoin
File: src/rest.cpp (C++)

Code before:

    bool fInputParsed = false;
    bool fCheckMemPool = false;
    std::vector<COutPoint> vOutPoints;

    // parse/deserialize input
    // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...

    if (uriParts.size() > 0)
    {
        //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
        if (uriParts[0] == "checkmempool") fCheckMemPool = true;

        for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
        {
            const auto txid_out{util::Split<std::string_view>(uriParts[i], '-')};
            if (txid_out.size() != 2) {
                return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
            }
            auto output{ToIntegral<uint32_t>(txid_out.at(1))};

            if (!output || !IsHex(txid_out.at(0))) {
                return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
            }

            vOutPoints.emplace_back(TxidFromString(txid_out.at(0)), *output);
        }

        if (vOutPoints.size() > 0)
            fInputParsed = true;
        else
            return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
    }

    switch (rf) {
    case RESTResponseFormat::HEX: {
        // convert hex to bin, continue then with bin part
        std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
        strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
        [[fallthrough]];
    }

    case RESTResponseFormat::BINARY: {
        try {
            //deserialize only if user sent a request
            if (strRequestMutable.size() > 0)
            {
                if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA
                    return RESTERR(req, HTTP_BAD_REQUEST, "Combination of URI scheme inputs and raw post data is not allowed");

                DataStream oss{};

Review comment (suggestion):
Perhaps worth it to introduce the `FromHex` method you [mentioned](https://github.com/bitcoin/bitcoin/pull/30436#discussion_r1682274787) here already, as it's not much extra LoC and probably makes for a more final interface? We can just do it for `transaction_identifier` now and do `base_blob` later?

<details>
<summary>git diff on 5ac06abeb9</summary>

```diff
diff --git a/src/rest.cpp b/src/rest.cpp
index b0d0d928f6..ff06c4191f 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -792,14 +792,14 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
             if (txid_out.size() != 2) {
                 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
             }
-            Txid txid;
             auto output{ToIntegral<uint32_t>(txid_out.at(1))};
 
-            if (!txid.SetHex(txid_out.at(0)) || !output ) {
+            auto txid {Txid::FromHex(txid_out.at(0))};
+            if (!txid || !output ) {
                 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
             }
 
-            vOutPoints.emplace_back(txid, *output);
+            vOutPoints.emplace_back(txid.value(), *output);
         }
 
         if (vOutPoints.size() > 0)
diff --git a/src/util/transaction_identifier.h b/src/util/transaction_identifier.h
index c1b46ea39c..68182b6332 100644
--- a/src/util/transaction_identifier.h
+++ b/src/util/transaction_identifier.h
@@ -37,6 +37,11 @@ public:
     bool operator<(const Other& other) const { return Compare(other) < 0; }
 
     const uint256& ToUint256() const LIFETIMEBOUND { return m_wrapped; }
+    static std::optional<transaction_identifier> FromHex(std::string_view hex)
+    {
+        transaction_identifier result;
+        return result.SetHex(hex) ? std::make_optional(result) : std::nullopt;
+    }
     static transaction_identifier FromUint256(const uint256& id) { return {id}; }
 
     /** Wrapped `uint256` methods. */

```
</details>




Code after:
    std::string strRequestMutable = req->ReadBody();
    if (strRequestMutable.length() == 0 && uriParts.size() == 0)
        return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");

    bool fInputParsed = false;
    bool fCheckMemPool = false;
    std::vector<COutPoint> vOutPoints;

    // parse/deserialize input
    // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...

    if (uriParts.size() > 0)
    {
        //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
        if (uriParts[0] == "checkmempool") fCheckMemPool = true;

        for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
        {
            const auto txid_out{util::Split<std::string_view>(uriParts[i], '-')};
            if (txid_out.size() != 2) {
                return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
            }
            auto txid{Txid::FromHex(txid_out.at(0))};
            auto output{ToIntegral<uint32_t>(txid_out.at(1))};

            if (!txid || !output) {
                return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
            }

            vOutPoints.emplace_back(*txid, *output);
        }

        if (vOutPoints.size() > 0)
            fInputParsed = true;
        else
            return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
    }

    switch (rf) {
    case RESTResponseFormat::HEX: {
        // convert hex to bin, continue then with bin part
        std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
        strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
        [[fallthrough]];
    }

    case RESTResponseFormat::BINARY: {
        try {
            //deserialize only if user sent a request
            if (strRequestMutable.size() > 0)
            {