LCOV - code coverage report
Current view: top level - src - rest.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 79.7 % 710 566
Test Date: 2025-07-01 05:14:16 Functions: 96.4 % 28 27
Branches: 44.3 % 1320 585

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

Generated by: LCOV version 2.0-1