LCOV - code coverage report
Current view: top level - src - rest.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 3 626 0.5 %
Date: 2023-11-10 23:46:46 Functions: 0 36 0.0 %
Branches: 2 1392 0.1 %

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

Generated by: LCOV version 1.14