LCOV - code coverage report
Current view: top level - src - rest.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 4 625 0.6 %
Date: 2023-09-26 12:08:55 Functions: 4 38 10.5 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2022 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include <rest.h>
       7             : 
       8             : #include <blockfilter.h>
       9             : #include <chain.h>
      10             : #include <chainparams.h>
      11             : #include <core_io.h>
      12             : #include <httpserver.h>
      13             : #include <index/blockfilterindex.h>
      14             : #include <index/txindex.h>
      15             : #include <node/blockstorage.h>
      16             : #include <node/context.h>
      17           2 : #include <primitives/block.h>
      18           2 : #include <primitives/transaction.h>
      19             : #include <rpc/blockchain.h>
      20             : #include <rpc/mempool.h>
      21             : #include <rpc/protocol.h>
      22             : #include <rpc/server.h>
      23             : #include <rpc/server_util.h>
      24             : #include <streams.h>
      25             : #include <sync.h>
      26             : #include <txmempool.h>
      27           2 : #include <util/any.h>
      28             : #include <util/check.h>
      29             : #include <validation.h>
      30             : #include <version.h>
      31             : 
      32             : #include <any>
      33             : #include <string>
      34             : 
      35             : #include <univalue.h>
      36             : 
      37             : using node::GetTransaction;
      38             : using node::NodeContext;
      39             : 
      40             : static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
      41             : static constexpr unsigned int MAX_REST_HEADERS_RESULTS = 2000;
      42             : 
      43             : static const struct {
      44             :     RESTResponseFormat rf;
      45             :     const char* name;
      46             : } rf_names[] = {
      47             :       {RESTResponseFormat::UNDEF, ""},
      48             :       {RESTResponseFormat::BINARY, "bin"},
      49             :       {RESTResponseFormat::HEX, "hex"},
      50             :       {RESTResponseFormat::JSON, "json"},
      51             : };
      52             : 
      53             : struct CCoin {
      54             :     uint32_t nHeight;
      55             :     CTxOut out;
      56             : 
      57             :     CCoin() : nHeight(0) {}
      58           0 :     explicit CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {}
      59             : 
      60           0 :     SERIALIZE_METHODS(CCoin, obj)
      61             :     {
      62           0 :         uint32_t nTxVerDummy = 0;
      63           0 :         READWRITE(nTxVerDummy, obj.nHeight, obj.out);
      64           0 :     }
      65             : };
      66             : 
      67           0 : static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message)
      68             : {
      69           0 :     req->WriteHeader("Content-Type", "text/plain");
      70           0 :     req->WriteReply(status, message + "\r\n");
      71           0 :     return false;
      72           0 : }
      73             : 
      74           2 : /**
      75             :  * Get the node context.
      76             :  *
      77             :  * @param[in]  req  The HTTP request, whose status code will be set if node
      78             :  *                  context is not found.
      79             :  * @returns         Pointer to the node context or nullptr if not found.
      80             :  */
      81           0 : static NodeContext* GetNodeContext(const std::any& context, HTTPRequest* req)
      82             : {
      83           0 :     auto node_context = util::AnyPtr<NodeContext>(context);
      84           0 :     if (!node_context) {
      85           0 :         RESTERR(req, HTTP_INTERNAL_SERVER_ERROR,
      86           0 :                 strprintf("%s:%d (%s)\n"
      87             :                           "Internal bug detected: Node context not found!\n"
      88             :                           "You may report this issue here: %s\n",
      89           0 :                           __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
      90           0 :         return nullptr;
      91             :     }
      92           0 :     return node_context;
      93           0 : }
      94             : 
      95             : /**
      96             :  * Get the node context mempool.
      97             :  *
      98             :  * @param[in]  req The HTTP request, whose status code will be set if node
      99             :  *                 context mempool is not found.
     100             :  * @returns        Pointer to the mempool or nullptr if no mempool found.
     101             :  */
     102           0 : static CTxMemPool* GetMemPool(const std::any& context, HTTPRequest* req)
     103             : {
     104           0 :     auto node_context = util::AnyPtr<NodeContext>(context);
     105           0 :     if (!node_context || !node_context->mempool) {
     106           0 :         RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
     107           0 :         return nullptr;
     108             :     }
     109           0 :     return node_context->mempool.get();
     110           0 : }
     111             : 
     112             : /**
     113             :  * Get the node context chainstatemanager.
     114             :  *
     115             :  * @param[in]  req The HTTP request, whose status code will be set if node
     116             :  *                 context chainstatemanager is not found.
     117             :  * @returns        Pointer to the chainstatemanager or nullptr if none found.
     118             :  */
     119           0 : static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
     120             : {
     121           0 :     auto node_context = util::AnyPtr<NodeContext>(context);
     122           0 :     if (!node_context || !node_context->chainman) {
     123           0 :         RESTERR(req, HTTP_INTERNAL_SERVER_ERROR,
     124           0 :                 strprintf("%s:%d (%s)\n"
     125             :                           "Internal bug detected: Chainman disabled or instance not found!\n"
     126             :                           "You may report this issue here: %s\n",
     127           0 :                           __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
     128           0 :         return nullptr;
     129             :     }
     130           0 :     return node_context->chainman.get();
     131           0 : }
     132             : 
     133           0 : RESTResponseFormat ParseDataFormat(std::string& param, const std::string& strReq)
     134             : {
     135             :     // Remove query string (if any, separated with '?') as it should not interfere with
     136             :     // parsing param and data format
     137           0 :     param = strReq.substr(0, strReq.rfind('?'));
     138           0 :     const std::string::size_type pos_format{param.rfind('.')};
     139             : 
     140             :     // No format string is found
     141           0 :     if (pos_format == std::string::npos) {
     142           0 :         return rf_names[0].rf;
     143             :     }
     144             : 
     145             :     // Match format string to available formats
     146           0 :     const std::string suffix(param, pos_format + 1);
     147           0 :     for (const auto& rf_name : rf_names) {
     148           0 :         if (suffix == rf_name.name) {
     149           0 :             param.erase(pos_format);
     150           0 :             return rf_name.rf;
     151             :         }
     152             :     }
     153             : 
     154             :     // If no suffix is found, return RESTResponseFormat::UNDEF and original string without query string
     155           0 :     return rf_names[0].rf;
     156           0 : }
     157             : 
     158           0 : static std::string AvailableDataFormatsString()
     159             : {
     160           0 :     std::string formats;
     161           0 :     for (const auto& rf_name : rf_names) {
     162           0 :         if (strlen(rf_name.name) > 0) {
     163           0 :             formats.append(".");
     164           0 :             formats.append(rf_name.name);
     165           0 :             formats.append(", ");
     166           0 :         }
     167             :     }
     168             : 
     169           0 :     if (formats.length() > 0)
     170           0 :         return formats.substr(0, formats.length() - 2);
     171             : 
     172           0 :     return formats;
     173           0 : }
     174             : 
     175           0 : static bool CheckWarmup(HTTPRequest* req)
     176             : {
     177           0 :     std::string statusmessage;
     178           0 :     if (RPCIsInWarmup(&statusmessage))
     179           0 :          return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
     180           0 :     return true;
     181           0 : }
     182             : 
     183           0 : static bool rest_headers(const std::any& context,
     184             :                          HTTPRequest* req,
     185             :                          const std::string& strURIPart)
     186             : {
     187           0 :     if (!CheckWarmup(req))
     188           0 :         return false;
     189           0 :     std::string param;
     190           0 :     const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
     191           0 :     std::vector<std::string> path = SplitString(param, '/');
     192             : 
     193           0 :     std::string raw_count;
     194           0 :     std::string hashStr;
     195           0 :     if (path.size() == 2) {
     196             :         // deprecated path: /rest/headers/<count>/<hash>
     197           0 :         hashStr = path[1];
     198           0 :         raw_count = path[0];
     199           0 :     } else if (path.size() == 1) {
     200             :         // new path with query parameter: /rest/headers/<hash>?count=<count>
     201           0 :         hashStr = path[0];
     202             :         try {
     203           0 :             raw_count = req->GetQueryParameter("count").value_or("5");
     204           0 :         } catch (const std::runtime_error& e) {
     205           0 :             return RESTERR(req, HTTP_BAD_REQUEST, e.what());
     206           0 :         }
     207           0 :     } else {
     208           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/headers/<hash>.<ext>?count=<count>");
     209             :     }
     210             : 
     211           0 :     const auto parsed_count{ToIntegral<size_t>(raw_count)};
     212           0 :     if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
     213           0 :         return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count));
     214             :     }
     215             : 
     216           0 :     uint256 hash;
     217           0 :     if (!ParseHashStr(hashStr, hash))
     218           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
     219             : 
     220           0 :     const CBlockIndex* tip = nullptr;
     221           0 :     std::vector<const CBlockIndex*> headers;
     222           0 :     headers.reserve(*parsed_count);
     223             :     {
     224           0 :         ChainstateManager* maybe_chainman = GetChainman(context, req);
     225           0 :         if (!maybe_chainman) return false;
     226           0 :         ChainstateManager& chainman = *maybe_chainman;
     227           0 :         LOCK(cs_main);
     228           0 :         CChain& active_chain = chainman.ActiveChain();
     229           0 :         tip = active_chain.Tip();
     230           0 :         const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
     231           0 :         while (pindex != nullptr && active_chain.Contains(pindex)) {
     232           0 :             headers.push_back(pindex);
     233           0 :             if (headers.size() == *parsed_count) {
     234           0 :                 break;
     235             :             }
     236           0 :             pindex = active_chain.Next(pindex);
     237             :         }
     238           0 :     }
     239             : 
     240           0 :     switch (rf) {
     241             :     case RESTResponseFormat::BINARY: {
     242           0 :         DataStream ssHeader{};
     243           0 :         for (const CBlockIndex *pindex : headers) {
     244           0 :             ssHeader << pindex->GetBlockHeader();
     245             :         }
     246             : 
     247           0 :         std::string binaryHeader = ssHeader.str();
     248           0 :         req->WriteHeader("Content-Type", "application/octet-stream");
     249           0 :         req->WriteReply(HTTP_OK, binaryHeader);
     250           0 :         return true;
     251           0 :     }
     252             : 
     253             :     case RESTResponseFormat::HEX: {
     254           0 :         DataStream ssHeader{};
     255           0 :         for (const CBlockIndex *pindex : headers) {
     256           0 :             ssHeader << pindex->GetBlockHeader();
     257             :         }
     258             : 
     259           0 :         std::string strHex = HexStr(ssHeader) + "\n";
     260           0 :         req->WriteHeader("Content-Type", "text/plain");
     261           0 :         req->WriteReply(HTTP_OK, strHex);
     262           0 :         return true;
     263           0 :     }
     264             :     case RESTResponseFormat::JSON: {
     265           0 :         UniValue jsonHeaders(UniValue::VARR);
     266           0 :         for (const CBlockIndex *pindex : headers) {
     267           0 :             jsonHeaders.push_back(blockheaderToJSON(tip, pindex));
     268             :         }
     269           0 :         std::string strJSON = jsonHeaders.write() + "\n";
     270           0 :         req->WriteHeader("Content-Type", "application/json");
     271           0 :         req->WriteReply(HTTP_OK, strJSON);
     272           0 :         return true;
     273           0 :     }
     274             :     default: {
     275           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     276             :     }
     277             :     }
     278           0 : }
     279             : 
     280           0 : static bool rest_block(const std::any& context,
     281             :                        HTTPRequest* req,
     282             :                        const std::string& strURIPart,
     283             :                        TxVerbosity tx_verbosity)
     284             : {
     285           0 :     if (!CheckWarmup(req))
     286           0 :         return false;
     287           0 :     std::string hashStr;
     288           0 :     const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart);
     289             : 
     290           0 :     uint256 hash;
     291           0 :     if (!ParseHashStr(hashStr, hash))
     292           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
     293             : 
     294           0 :     CBlock block;
     295           0 :     const CBlockIndex* pblockindex = nullptr;
     296           0 :     const CBlockIndex* tip = nullptr;
     297           0 :     ChainstateManager* maybe_chainman = GetChainman(context, req);
     298           0 :     if (!maybe_chainman) return false;
     299           0 :     ChainstateManager& chainman = *maybe_chainman;
     300             :     {
     301           0 :         LOCK(cs_main);
     302           0 :         tip = chainman.ActiveChain().Tip();
     303           0 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
     304           0 :         if (!pblockindex) {
     305           0 :             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
     306             :         }
     307             : 
     308           0 :         if (chainman.m_blockman.IsBlockPruned(pblockindex))
     309           0 :             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
     310             : 
     311           0 :     }
     312             : 
     313           0 :     if (!chainman.m_blockman.ReadBlockFromDisk(block, *pblockindex)) {
     314           0 :         return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
     315             :     }
     316             : 
     317           0 :     switch (rf) {
     318             :     case RESTResponseFormat::BINARY: {
     319           0 :         CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
     320           0 :         ssBlock << block;
     321           0 :         std::string binaryBlock = ssBlock.str();
     322           0 :         req->WriteHeader("Content-Type", "application/octet-stream");
     323           0 :         req->WriteReply(HTTP_OK, binaryBlock);
     324           0 :         return true;
     325           0 :     }
     326             : 
     327             :     case RESTResponseFormat::HEX: {
     328           0 :         CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
     329           0 :         ssBlock << block;
     330           0 :         std::string strHex = HexStr(ssBlock) + "\n";
     331           0 :         req->WriteHeader("Content-Type", "text/plain");
     332           0 :         req->WriteReply(HTTP_OK, strHex);
     333           0 :         return true;
     334           0 :     }
     335             : 
     336             :     case RESTResponseFormat::JSON: {
     337           0 :         UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip, pblockindex, tx_verbosity);
     338           0 :         std::string strJSON = objBlock.write() + "\n";
     339           0 :         req->WriteHeader("Content-Type", "application/json");
     340           0 :         req->WriteReply(HTTP_OK, strJSON);
     341           0 :         return true;
     342           0 :     }
     343             : 
     344             :     default: {
     345           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     346             :     }
     347             :     }
     348           0 : }
     349             : 
     350           0 : static bool rest_block_extended(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
     351             : {
     352           0 :     return rest_block(context, req, strURIPart, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
     353             : }
     354             : 
     355           0 : static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
     356             : {
     357           0 :     return rest_block(context, req, strURIPart, TxVerbosity::SHOW_TXID);
     358             : }
     359             : 
     360           0 : static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
     361             : {
     362           0 :     if (!CheckWarmup(req)) return false;
     363             : 
     364           0 :     std::string param;
     365           0 :     const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
     366             : 
     367           0 :     std::vector<std::string> uri_parts = SplitString(param, '/');
     368           0 :     std::string raw_count;
     369           0 :     std::string raw_blockhash;
     370           0 :     if (uri_parts.size() == 3) {
     371             :         // deprecated path: /rest/blockfilterheaders/<filtertype>/<count>/<blockhash>
     372           0 :         raw_blockhash = uri_parts[2];
     373           0 :         raw_count = uri_parts[1];
     374           0 :     } else if (uri_parts.size() == 2) {
     375             :         // new path with query parameter: /rest/blockfilterheaders/<filtertype>/<blockhash>?count=<count>
     376           0 :         raw_blockhash = uri_parts[1];
     377             :         try {
     378           0 :             raw_count = req->GetQueryParameter("count").value_or("5");
     379           0 :         } catch (const std::runtime_error& e) {
     380           0 :             return RESTERR(req, HTTP_BAD_REQUEST, e.what());
     381           0 :         }
     382           0 :     } else {
     383           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<blockhash>.<ext>?count=<count>");
     384             :     }
     385             : 
     386           0 :     const auto parsed_count{ToIntegral<size_t>(raw_count)};
     387           0 :     if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
     388           0 :         return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count));
     389             :     }
     390             : 
     391           0 :     uint256 block_hash;
     392           0 :     if (!ParseHashStr(raw_blockhash, block_hash)) {
     393           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + raw_blockhash);
     394             :     }
     395             : 
     396             :     BlockFilterType filtertype;
     397           0 :     if (!BlockFilterTypeByName(uri_parts[0], filtertype)) {
     398           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Unknown filtertype " + uri_parts[0]);
     399             :     }
     400             : 
     401           0 :     BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
     402           0 :     if (!index) {
     403           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]);
     404             :     }
     405             : 
     406           0 :     std::vector<const CBlockIndex*> headers;
     407           0 :     headers.reserve(*parsed_count);
     408             :     {
     409           0 :         ChainstateManager* maybe_chainman = GetChainman(context, req);
     410           0 :         if (!maybe_chainman) return false;
     411           0 :         ChainstateManager& chainman = *maybe_chainman;
     412           0 :         LOCK(cs_main);
     413           0 :         CChain& active_chain = chainman.ActiveChain();
     414           0 :         const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block_hash);
     415           0 :         while (pindex != nullptr && active_chain.Contains(pindex)) {
     416           0 :             headers.push_back(pindex);
     417           0 :             if (headers.size() == *parsed_count)
     418           0 :                 break;
     419           0 :             pindex = active_chain.Next(pindex);
     420             :         }
     421           0 :     }
     422             : 
     423           0 :     bool index_ready = index->BlockUntilSyncedToCurrentChain();
     424             : 
     425           0 :     std::vector<uint256> filter_headers;
     426           0 :     filter_headers.reserve(*parsed_count);
     427           0 :     for (const CBlockIndex* pindex : headers) {
     428           0 :         uint256 filter_header;
     429           0 :         if (!index->LookupFilterHeader(pindex, filter_header)) {
     430           0 :             std::string errmsg = "Filter not found.";
     431             : 
     432           0 :             if (!index_ready) {
     433           0 :                 errmsg += " Block filters are still in the process of being indexed.";
     434           0 :             } else {
     435           0 :                 errmsg += " This error is unexpected and indicates index corruption.";
     436             :             }
     437             : 
     438           0 :             return RESTERR(req, HTTP_NOT_FOUND, errmsg);
     439           0 :         }
     440           0 :         filter_headers.push_back(filter_header);
     441             :     }
     442             : 
     443           0 :     switch (rf) {
     444             :     case RESTResponseFormat::BINARY: {
     445           0 :         DataStream ssHeader{};
     446           0 :         for (const uint256& header : filter_headers) {
     447           0 :             ssHeader << header;
     448             :         }
     449             : 
     450           0 :         std::string binaryHeader = ssHeader.str();
     451           0 :         req->WriteHeader("Content-Type", "application/octet-stream");
     452           0 :         req->WriteReply(HTTP_OK, binaryHeader);
     453           0 :         return true;
     454           0 :     }
     455             :     case RESTResponseFormat::HEX: {
     456           0 :         DataStream ssHeader{};
     457           0 :         for (const uint256& header : filter_headers) {
     458           0 :             ssHeader << header;
     459             :         }
     460             : 
     461           0 :         std::string strHex = HexStr(ssHeader) + "\n";
     462           0 :         req->WriteHeader("Content-Type", "text/plain");
     463           0 :         req->WriteReply(HTTP_OK, strHex);
     464           0 :         return true;
     465           0 :     }
     466             :     case RESTResponseFormat::JSON: {
     467           0 :         UniValue jsonHeaders(UniValue::VARR);
     468           0 :         for (const uint256& header : filter_headers) {
     469           0 :             jsonHeaders.push_back(header.GetHex());
     470             :         }
     471             : 
     472           0 :         std::string strJSON = jsonHeaders.write() + "\n";
     473           0 :         req->WriteHeader("Content-Type", "application/json");
     474           0 :         req->WriteReply(HTTP_OK, strJSON);
     475           0 :         return true;
     476           0 :     }
     477             :     default: {
     478           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     479             :     }
     480             :     }
     481           0 : }
     482             : 
     483           0 : static bool rest_block_filter(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
     484             : {
     485           0 :     if (!CheckWarmup(req)) return false;
     486             : 
     487           0 :     std::string param;
     488           0 :     const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
     489             : 
     490             :     // request is sent over URI scheme /rest/blockfilter/filtertype/blockhash
     491           0 :     std::vector<std::string> uri_parts = SplitString(param, '/');
     492           0 :     if (uri_parts.size() != 2) {
     493           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilter/<filtertype>/<blockhash>");
     494             :     }
     495             : 
     496           0 :     uint256 block_hash;
     497           0 :     if (!ParseHashStr(uri_parts[1], block_hash)) {
     498           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + uri_parts[1]);
     499             :     }
     500             : 
     501             :     BlockFilterType filtertype;
     502           0 :     if (!BlockFilterTypeByName(uri_parts[0], filtertype)) {
     503           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Unknown filtertype " + uri_parts[0]);
     504             :     }
     505             : 
     506           0 :     BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
     507           0 :     if (!index) {
     508           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]);
     509             :     }
     510             : 
     511             :     const CBlockIndex* block_index;
     512             :     bool block_was_connected;
     513             :     {
     514           0 :         ChainstateManager* maybe_chainman = GetChainman(context, req);
     515           0 :         if (!maybe_chainman) return false;
     516           0 :         ChainstateManager& chainman = *maybe_chainman;
     517           0 :         LOCK(cs_main);
     518           0 :         block_index = chainman.m_blockman.LookupBlockIndex(block_hash);
     519           0 :         if (!block_index) {
     520           0 :             return RESTERR(req, HTTP_NOT_FOUND, uri_parts[1] + " not found");
     521             :         }
     522           0 :         block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS);
     523           0 :     }
     524             : 
     525           0 :     bool index_ready = index->BlockUntilSyncedToCurrentChain();
     526             : 
     527           0 :     BlockFilter filter;
     528           0 :     if (!index->LookupFilter(block_index, filter)) {
     529           0 :         std::string errmsg = "Filter not found.";
     530             : 
     531           0 :         if (!block_was_connected) {
     532           0 :             errmsg += " Block was not connected to active chain.";
     533           0 :         } else if (!index_ready) {
     534           0 :             errmsg += " Block filters are still in the process of being indexed.";
     535           0 :         } else {
     536           0 :             errmsg += " This error is unexpected and indicates index corruption.";
     537             :         }
     538             : 
     539           0 :         return RESTERR(req, HTTP_NOT_FOUND, errmsg);
     540           0 :     }
     541             : 
     542           0 :     switch (rf) {
     543             :     case RESTResponseFormat::BINARY: {
     544           0 :         DataStream ssResp{};
     545           0 :         ssResp << filter;
     546             : 
     547           0 :         std::string binaryResp = ssResp.str();
     548           0 :         req->WriteHeader("Content-Type", "application/octet-stream");
     549           0 :         req->WriteReply(HTTP_OK, binaryResp);
     550           0 :         return true;
     551           0 :     }
     552             :     case RESTResponseFormat::HEX: {
     553           0 :         DataStream ssResp{};
     554           0 :         ssResp << filter;
     555             : 
     556           0 :         std::string strHex = HexStr(ssResp) + "\n";
     557           0 :         req->WriteHeader("Content-Type", "text/plain");
     558           0 :         req->WriteReply(HTTP_OK, strHex);
     559           0 :         return true;
     560           0 :     }
     561             :     case RESTResponseFormat::JSON: {
     562           0 :         UniValue ret(UniValue::VOBJ);
     563           0 :         ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
     564           0 :         std::string strJSON = ret.write() + "\n";
     565           0 :         req->WriteHeader("Content-Type", "application/json");
     566           0 :         req->WriteReply(HTTP_OK, strJSON);
     567           0 :         return true;
     568           0 :     }
     569             :     default: {
     570           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     571             :     }
     572             :     }
     573           0 : }
     574             : 
     575             : // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
     576             : RPCHelpMan getblockchaininfo();
     577             : 
     578           0 : static bool rest_chaininfo(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
     579             : {
     580           0 :     if (!CheckWarmup(req))
     581           0 :         return false;
     582           0 :     std::string param;
     583           0 :     const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
     584             : 
     585           0 :     switch (rf) {
     586             :     case RESTResponseFormat::JSON: {
     587           0 :         JSONRPCRequest jsonRequest;
     588           0 :         jsonRequest.context = context;
     589           0 :         jsonRequest.params = UniValue(UniValue::VARR);
     590           0 :         UniValue chainInfoObject = getblockchaininfo().HandleRequest(jsonRequest);
     591           0 :         std::string strJSON = chainInfoObject.write() + "\n";
     592           0 :         req->WriteHeader("Content-Type", "application/json");
     593           0 :         req->WriteReply(HTTP_OK, strJSON);
     594           0 :         return true;
     595           0 :     }
     596             :     default: {
     597           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
     598             :     }
     599             :     }
     600           0 : }
     601             : 
     602             : 
     603             : RPCHelpMan getdeploymentinfo();
     604             : 
     605           0 : static bool rest_deploymentinfo(const std::any& context, HTTPRequest* req, const std::string& str_uri_part)
     606             : {
     607           0 :     if (!CheckWarmup(req)) return false;
     608             : 
     609           0 :     std::string hash_str;
     610           0 :     const RESTResponseFormat rf = ParseDataFormat(hash_str, str_uri_part);
     611             : 
     612           0 :     switch (rf) {
     613             :     case RESTResponseFormat::JSON: {
     614           0 :         JSONRPCRequest jsonRequest;
     615           0 :         jsonRequest.context = context;
     616           0 :         jsonRequest.params = UniValue(UniValue::VARR);
     617             : 
     618           0 :         if (!hash_str.empty()) {
     619           0 :             uint256 hash;
     620           0 :             if (!ParseHashStr(hash_str, hash)) {
     621           0 :                 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hash_str);
     622             :             }
     623             : 
     624           0 :             const ChainstateManager* chainman = GetChainman(context, req);
     625           0 :             if (!chainman) return false;
     626           0 :             if (!WITH_LOCK(::cs_main, return chainman->m_blockman.LookupBlockIndex(ParseHashV(hash_str, "blockhash")))) {
     627           0 :                 return RESTERR(req, HTTP_BAD_REQUEST, "Block not found");
     628             :             }
     629             : 
     630           0 :             jsonRequest.params.push_back(hash_str);
     631           0 :         }
     632             : 
     633           0 :         req->WriteHeader("Content-Type", "application/json");
     634           0 :         req->WriteReply(HTTP_OK, getdeploymentinfo().HandleRequest(jsonRequest).write() + "\n");
     635           0 :         return true;
     636           0 :     }
     637             :     default: {
     638           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
     639             :     }
     640             :     }
     641             : 
     642           0 : }
     643             : 
     644           0 : static bool rest_mempool(const std::any& context, HTTPRequest* req, const std::string& str_uri_part)
     645             : {
     646           0 :     if (!CheckWarmup(req))
     647           0 :         return false;
     648             : 
     649           0 :     std::string param;
     650           0 :     const RESTResponseFormat rf = ParseDataFormat(param, str_uri_part);
     651           0 :     if (param != "contents" && param != "info") {
     652           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/mempool/<info|contents>.json");
     653             :     }
     654             : 
     655           0 :     const CTxMemPool* mempool = GetMemPool(context, req);
     656           0 :     if (!mempool) return false;
     657             : 
     658           0 :     switch (rf) {
     659             :     case RESTResponseFormat::JSON: {
     660           0 :         std::string str_json;
     661           0 :         if (param == "contents") {
     662           0 :             std::string raw_verbose;
     663             :             try {
     664           0 :                 raw_verbose = req->GetQueryParameter("verbose").value_or("true");
     665           0 :             } catch (const std::runtime_error& e) {
     666           0 :                 return RESTERR(req, HTTP_BAD_REQUEST, e.what());
     667           0 :             }
     668           0 :             if (raw_verbose != "true" && raw_verbose != "false") {
     669           0 :                 return RESTERR(req, HTTP_BAD_REQUEST, "The \"verbose\" query parameter must be either \"true\" or \"false\".");
     670             :             }
     671           0 :             std::string raw_mempool_sequence;
     672             :             try {
     673           0 :                 raw_mempool_sequence = req->GetQueryParameter("mempool_sequence").value_or("false");
     674           0 :             } catch (const std::runtime_error& e) {
     675           0 :                 return RESTERR(req, HTTP_BAD_REQUEST, e.what());
     676           0 :             }
     677           0 :             if (raw_mempool_sequence != "true" && raw_mempool_sequence != "false") {
     678           0 :                 return RESTERR(req, HTTP_BAD_REQUEST, "The \"mempool_sequence\" query parameter must be either \"true\" or \"false\".");
     679             :             }
     680           0 :             const bool verbose{raw_verbose == "true"};
     681           0 :             const bool mempool_sequence{raw_mempool_sequence == "true"};
     682           0 :             if (verbose && mempool_sequence) {
     683           0 :                 return RESTERR(req, HTTP_BAD_REQUEST, "Verbose results cannot contain mempool sequence values. (hint: set \"verbose=false\")");
     684             :             }
     685           0 :             str_json = MempoolToJSON(*mempool, verbose, mempool_sequence).write() + "\n";
     686           0 :         } else {
     687           0 :             str_json = MempoolInfoToJSON(*mempool).write() + "\n";
     688             :         }
     689             : 
     690           0 :         req->WriteHeader("Content-Type", "application/json");
     691           0 :         req->WriteReply(HTTP_OK, str_json);
     692           0 :         return true;
     693           0 :     }
     694             :     default: {
     695           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
     696             :     }
     697             :     }
     698           0 : }
     699             : 
     700           0 : static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
     701             : {
     702           0 :     if (!CheckWarmup(req))
     703           0 :         return false;
     704           0 :     std::string hashStr;
     705           0 :     const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart);
     706             : 
     707           0 :     uint256 hash;
     708           0 :     if (!ParseHashStr(hashStr, hash))
     709           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
     710             : 
     711           0 :     if (g_txindex) {
     712           0 :         g_txindex->BlockUntilSyncedToCurrentChain();
     713           0 :     }
     714             : 
     715           0 :     const NodeContext* const node = GetNodeContext(context, req);
     716           0 :     if (!node) return false;
     717           0 :     uint256 hashBlock = uint256();
     718           0 :     const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, node->mempool.get(), hash, hashBlock, node->chainman->m_blockman);
     719           0 :     if (!tx) {
     720           0 :         return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
     721             :     }
     722             : 
     723           0 :     switch (rf) {
     724             :     case RESTResponseFormat::BINARY: {
     725           0 :         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
     726           0 :         ssTx << tx;
     727             : 
     728           0 :         std::string binaryTx = ssTx.str();
     729           0 :         req->WriteHeader("Content-Type", "application/octet-stream");
     730           0 :         req->WriteReply(HTTP_OK, binaryTx);
     731           0 :         return true;
     732           0 :     }
     733             : 
     734             :     case RESTResponseFormat::HEX: {
     735           0 :         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
     736           0 :         ssTx << tx;
     737             : 
     738           0 :         std::string strHex = HexStr(ssTx) + "\n";
     739           0 :         req->WriteHeader("Content-Type", "text/plain");
     740           0 :         req->WriteReply(HTTP_OK, strHex);
     741           0 :         return true;
     742           0 :     }
     743             : 
     744             :     case RESTResponseFormat::JSON: {
     745           0 :         UniValue objTx(UniValue::VOBJ);
     746           0 :         TxToUniv(*tx, /*block_hash=*/hashBlock, /*entry=*/ objTx);
     747           0 :         std::string strJSON = objTx.write() + "\n";
     748           0 :         req->WriteHeader("Content-Type", "application/json");
     749           0 :         req->WriteReply(HTTP_OK, strJSON);
     750           0 :         return true;
     751           0 :     }
     752             : 
     753             :     default: {
     754           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     755             :     }
     756             :     }
     757           0 : }
     758             : 
     759           0 : static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
     760             : {
     761           0 :     if (!CheckWarmup(req))
     762           0 :         return false;
     763           0 :     std::string param;
     764           0 :     const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
     765             : 
     766           0 :     std::vector<std::string> uriParts;
     767           0 :     if (param.length() > 1)
     768             :     {
     769           0 :         std::string strUriParams = param.substr(1);
     770           0 :         uriParts = SplitString(strUriParams, '/');
     771           0 :     }
     772             : 
     773             :     // throw exception in case of an empty request
     774           0 :     std::string strRequestMutable = req->ReadBody();
     775           0 :     if (strRequestMutable.length() == 0 && uriParts.size() == 0)
     776           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
     777             : 
     778           0 :     bool fInputParsed = false;
     779           0 :     bool fCheckMemPool = false;
     780           0 :     std::vector<COutPoint> vOutPoints;
     781             : 
     782             :     // parse/deserialize input
     783             :     // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
     784             : 
     785           0 :     if (uriParts.size() > 0)
     786             :     {
     787             :         //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
     788           0 :         if (uriParts[0] == "checkmempool") fCheckMemPool = true;
     789             : 
     790           0 :         for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
     791             :         {
     792           0 :             uint256 txid;
     793             :             int32_t nOutput;
     794           0 :             std::string strTxid = uriParts[i].substr(0, uriParts[i].find('-'));
     795           0 :             std::string strOutput = uriParts[i].substr(uriParts[i].find('-')+1);
     796             : 
     797           0 :             if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid))
     798           0 :                 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
     799             : 
     800           0 :             txid.SetHex(strTxid);
     801           0 :             vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput));
     802           0 :         }
     803             : 
     804           0 :         if (vOutPoints.size() > 0)
     805           0 :             fInputParsed = true;
     806             :         else
     807           0 :             return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
     808           0 :     }
     809             : 
     810           0 :     switch (rf) {
     811             :     case RESTResponseFormat::HEX: {
     812             :         // convert hex to bin, continue then with bin part
     813           0 :         std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
     814           0 :         strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
     815             :         [[fallthrough]];
     816           0 :     }
     817             : 
     818             :     case RESTResponseFormat::BINARY: {
     819             :         try {
     820             :             //deserialize only if user sent a request
     821           0 :             if (strRequestMutable.size() > 0)
     822             :             {
     823           0 :                 if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA
     824           0 :                     return RESTERR(req, HTTP_BAD_REQUEST, "Combination of URI scheme inputs and raw post data is not allowed");
     825             : 
     826           0 :                 DataStream oss{};
     827           0 :                 oss << strRequestMutable;
     828           0 :                 oss >> fCheckMemPool;
     829           0 :                 oss >> vOutPoints;
     830           0 :             }
     831           0 :         } catch (const std::ios_base::failure&) {
     832             :             // abort in case of unreadable binary data
     833           0 :             return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
     834           0 :         }
     835           0 :         break;
     836             :     }
     837             : 
     838             :     case RESTResponseFormat::JSON: {
     839           0 :         if (!fInputParsed)
     840           0 :             return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
     841           0 :         break;
     842             :     }
     843             :     default: {
     844           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     845             :     }
     846             :     }
     847             : 
     848             :     // limit max outpoints
     849           0 :     if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS)
     850           0 :         return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
     851             : 
     852             :     // check spentness and form a bitmap (as well as a JSON capable human-readable string representation)
     853           0 :     std::vector<unsigned char> bitmap;
     854           0 :     std::vector<CCoin> outs;
     855           0 :     std::string bitmapStringRepresentation;
     856           0 :     std::vector<bool> hits;
     857           0 :     bitmap.resize((vOutPoints.size() + 7) / 8);
     858           0 :     ChainstateManager* maybe_chainman = GetChainman(context, req);
     859           0 :     if (!maybe_chainman) return false;
     860           0 :     ChainstateManager& chainman = *maybe_chainman;
     861             :     decltype(chainman.ActiveHeight()) active_height;
     862           0 :     uint256 active_hash;
     863             :     {
     864           0 :         auto process_utxos = [&vOutPoints, &outs, &hits, &active_height, &active_hash, &chainman](const CCoinsView& view, const CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(chainman.GetMutex()) {
     865           0 :             for (const COutPoint& vOutPoint : vOutPoints) {
     866           0 :                 Coin coin;
     867           0 :                 bool hit = (!mempool || !mempool->isSpent(vOutPoint)) && view.GetCoin(vOutPoint, coin);
     868           0 :                 hits.push_back(hit);
     869           0 :                 if (hit) outs.emplace_back(std::move(coin));
     870           0 :             }
     871           0 :             active_height = chainman.ActiveHeight();
     872           0 :             active_hash = chainman.ActiveTip()->GetBlockHash();
     873           0 :         };
     874             : 
     875           0 :         if (fCheckMemPool) {
     876           0 :             const CTxMemPool* mempool = GetMemPool(context, req);
     877           0 :             if (!mempool) return false;
     878             :             // use db+mempool as cache backend in case user likes to query mempool
     879           0 :             LOCK2(cs_main, mempool->cs);
     880           0 :             CCoinsViewCache& viewChain = chainman.ActiveChainstate().CoinsTip();
     881           0 :             CCoinsViewMemPool viewMempool(&viewChain, *mempool);
     882           0 :             process_utxos(viewMempool, mempool);
     883           0 :         } else {
     884           0 :             LOCK(cs_main);
     885           0 :             process_utxos(chainman.ActiveChainstate().CoinsTip(), nullptr);
     886           0 :         }
     887             : 
     888           0 :         for (size_t i = 0; i < hits.size(); ++i) {
     889           0 :             const bool hit = hits[i];
     890           0 :             bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output)
     891           0 :             bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
     892           0 :         }
     893             :     }
     894             : 
     895           0 :     switch (rf) {
     896             :     case RESTResponseFormat::BINARY: {
     897             :         // serialize data
     898             :         // use exact same output as mentioned in Bip64
     899           0 :         DataStream ssGetUTXOResponse{};
     900           0 :         ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
     901           0 :         std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
     902             : 
     903           0 :         req->WriteHeader("Content-Type", "application/octet-stream");
     904           0 :         req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
     905           0 :         return true;
     906           0 :     }
     907             : 
     908             :     case RESTResponseFormat::HEX: {
     909           0 :         DataStream ssGetUTXOResponse{};
     910           0 :         ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
     911           0 :         std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
     912             : 
     913           0 :         req->WriteHeader("Content-Type", "text/plain");
     914           0 :         req->WriteReply(HTTP_OK, strHex);
     915           0 :         return true;
     916           0 :     }
     917             : 
     918             :     case RESTResponseFormat::JSON: {
     919           0 :         UniValue objGetUTXOResponse(UniValue::VOBJ);
     920             : 
     921             :         // pack in some essentials
     922             :         // use more or less the same output as mentioned in Bip64
     923           0 :         objGetUTXOResponse.pushKV("chainHeight", active_height);
     924           0 :         objGetUTXOResponse.pushKV("chaintipHash", active_hash.GetHex());
     925           0 :         objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
     926             : 
     927           0 :         UniValue utxos(UniValue::VARR);
     928           0 :         for (const CCoin& coin : outs) {
     929           0 :             UniValue utxo(UniValue::VOBJ);
     930           0 :             utxo.pushKV("height", (int32_t)coin.nHeight);
     931           0 :             utxo.pushKV("value", ValueFromAmount(coin.out.nValue));
     932             : 
     933             :             // include the script in a json output
     934           0 :             UniValue o(UniValue::VOBJ);
     935           0 :             ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
     936           0 :             utxo.pushKV("scriptPubKey", o);
     937           0 :             utxos.push_back(utxo);
     938           0 :         }
     939           0 :         objGetUTXOResponse.pushKV("utxos", utxos);
     940             : 
     941             :         // return json string
     942           0 :         std::string strJSON = objGetUTXOResponse.write() + "\n";
     943           0 :         req->WriteHeader("Content-Type", "application/json");
     944           0 :         req->WriteReply(HTTP_OK, strJSON);
     945           0 :         return true;
     946           0 :     }
     947             :     default: {
     948           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     949             :     }
     950             :     }
     951           0 : }
     952             : 
     953           0 : static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
     954             :                        const std::string& str_uri_part)
     955             : {
     956           0 :     if (!CheckWarmup(req)) return false;
     957           0 :     std::string height_str;
     958           0 :     const RESTResponseFormat rf = ParseDataFormat(height_str, str_uri_part);
     959             : 
     960           0 :     int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785
     961           0 :     if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
     962           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str));
     963             :     }
     964             : 
     965           0 :     CBlockIndex* pblockindex = nullptr;
     966             :     {
     967           0 :         ChainstateManager* maybe_chainman = GetChainman(context, req);
     968           0 :         if (!maybe_chainman) return false;
     969           0 :         ChainstateManager& chainman = *maybe_chainman;
     970           0 :         LOCK(cs_main);
     971           0 :         const CChain& active_chain = chainman.ActiveChain();
     972           0 :         if (blockheight > active_chain.Height()) {
     973           0 :             return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
     974             :         }
     975           0 :         pblockindex = active_chain[blockheight];
     976           0 :     }
     977           0 :     switch (rf) {
     978             :     case RESTResponseFormat::BINARY: {
     979           0 :         DataStream ss_blockhash{};
     980           0 :         ss_blockhash << pblockindex->GetBlockHash();
     981           0 :         req->WriteHeader("Content-Type", "application/octet-stream");
     982           0 :         req->WriteReply(HTTP_OK, ss_blockhash.str());
     983           0 :         return true;
     984           0 :     }
     985             :     case RESTResponseFormat::HEX: {
     986           0 :         req->WriteHeader("Content-Type", "text/plain");
     987           0 :         req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n");
     988           0 :         return true;
     989             :     }
     990             :     case RESTResponseFormat::JSON: {
     991           0 :         req->WriteHeader("Content-Type", "application/json");
     992           0 :         UniValue resp = UniValue(UniValue::VOBJ);
     993           0 :         resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
     994           0 :         req->WriteReply(HTTP_OK, resp.write() + "\n");
     995           0 :         return true;
     996           0 :     }
     997             :     default: {
     998           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     999             :     }
    1000             :     }
    1001           0 : }
    1002             : 
    1003             : static const struct {
    1004             :     const char* prefix;
    1005             :     bool (*handler)(const std::any& context, HTTPRequest* req, const std::string& strReq);
    1006             : } uri_prefixes[] = {
    1007             :       {"/rest/tx/", rest_tx},
    1008             :       {"/rest/block/notxdetails/", rest_block_notxdetails},
    1009             :       {"/rest/block/", rest_block_extended},
    1010             :       {"/rest/blockfilter/", rest_block_filter},
    1011             :       {"/rest/blockfilterheaders/", rest_filter_header},
    1012             :       {"/rest/chaininfo", rest_chaininfo},
    1013             :       {"/rest/mempool/", rest_mempool},
    1014             :       {"/rest/headers/", rest_headers},
    1015             :       {"/rest/getutxos", rest_getutxos},
    1016             :       {"/rest/deploymentinfo/", rest_deploymentinfo},
    1017             :       {"/rest/deploymentinfo", rest_deploymentinfo},
    1018             :       {"/rest/blockhashbyheight/", rest_blockhash_by_height},
    1019             : };
    1020             : 
    1021           0 : void StartREST(const std::any& context)
    1022             : {
    1023           0 :     for (const auto& up : uri_prefixes) {
    1024           0 :         auto handler = [context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); };
    1025           0 :         RegisterHTTPHandler(up.prefix, false, handler);
    1026           0 :     }
    1027           0 : }
    1028             : 
    1029           0 : void InterruptREST()
    1030             : {
    1031           0 : }
    1032             : 
    1033           0 : void StopREST()
    1034             : {
    1035           0 :     for (const auto& up : uri_prefixes) {
    1036           0 :         UnregisterHTTPHandler(up.prefix, false);
    1037             :     }
    1038           0 : }

Generated by: LCOV version 1.14