LCOV - code coverage report
Current view: top level - src - rest.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 507 708 71.6 %
Date: 2022-04-21 14:51:19 Functions: 25 27 92.6 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 191 282 67.7 %

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

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a