LCOV - code coverage report
Current view: top level - src - rest.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 0.0 % 752 0
Test Date: 2026-08-29 05:53:03 Functions: 0.0 % 29 0
Branches: 0.0 % 1673 0

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

Generated by: LCOV version 2.5.0-full