LCOV - code coverage report
Current view: top level - src - rest.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 78.5 % 638 501
Test Date: 2024-07-07 05:05:19 Functions: 95.8 % 24 23
Branches: 43.8 % 1218 534

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

Generated by: LCOV version 2.0-1