LCOV - code coverage report
Current view: top level - src - rest.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 386 500 77.2 %
Date: 2021-06-29 14:35:33 Functions: 22 24 91.7 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 138 192 71.9 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2009-2010 Satoshi Nakamoto
#       2                 :            : // Copyright (c) 2009-2020 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 <chain.h>
#       7                 :            : #include <chainparams.h>
#       8                 :            : #include <core_io.h>
#       9                 :            : #include <httpserver.h>
#      10                 :            : #include <index/txindex.h>
#      11                 :            : #include <node/blockstorage.h>
#      12                 :            : #include <node/context.h>
#      13                 :            : #include <primitives/block.h>
#      14                 :            : #include <primitives/transaction.h>
#      15                 :            : #include <rpc/blockchain.h>
#      16                 :            : #include <rpc/protocol.h>
#      17                 :            : #include <rpc/server.h>
#      18                 :            : #include <streams.h>
#      19                 :            : #include <sync.h>
#      20                 :            : #include <txmempool.h>
#      21                 :            : #include <util/check.h>
#      22                 :            : #include <util/system.h>
#      23                 :            : #include <validation.h>
#      24                 :            : #include <version.h>
#      25                 :            : 
#      26                 :            : #include <any>
#      27                 :            : 
#      28                 :            : #include <boost/algorithm/string.hpp>
#      29                 :            : 
#      30                 :            : #include <univalue.h>
#      31                 :            : 
#      32                 :            : static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
#      33                 :            : 
#      34                 :            : enum class RetFormat {
#      35                 :            :     UNDEF,
#      36                 :            :     BINARY,
#      37                 :            :     HEX,
#      38                 :            :     JSON,
#      39                 :            : };
#      40                 :            : 
#      41                 :            : static const struct {
#      42                 :            :     RetFormat rf;
#      43                 :            :     const char* name;
#      44                 :            : } rf_names[] = {
#      45                 :            :       {RetFormat::UNDEF, ""},
#      46                 :            :       {RetFormat::BINARY, "bin"},
#      47                 :            :       {RetFormat::HEX, "hex"},
#      48                 :            :       {RetFormat::JSON, "json"},
#      49                 :            : };
#      50                 :            : 
#      51                 :            : struct CCoin {
#      52                 :            :     uint32_t nHeight;
#      53                 :            :     CTxOut out;
#      54                 :            : 
#      55                 :          1 :     CCoin() : nHeight(0) {}
#      56                 :          8 :     explicit CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {}
#      57                 :            : 
#      58                 :            :     SERIALIZE_METHODS(CCoin, obj)
#      59                 :          0 :     {
#      60                 :          0 :         uint32_t nTxVerDummy = 0;
#      61                 :          0 :         READWRITE(nTxVerDummy, obj.nHeight, obj.out);
#      62                 :          0 :     }
#      63                 :            : };
#      64                 :            : 
#      65                 :            : static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message)
#      66                 :          9 : {
#      67                 :          9 :     req->WriteHeader("Content-Type", "text/plain");
#      68                 :          9 :     req->WriteReply(status, message + "\r\n");
#      69                 :          9 :     return false;
#      70                 :          9 : }
#      71                 :            : 
#      72                 :            : /**
#      73                 :            :  * Get the node context.
#      74                 :            :  *
#      75                 :            :  * @param[in]  req  The HTTP request, whose status code will be set if node
#      76                 :            :  *                  context is not found.
#      77                 :            :  * @returns         Pointer to the node context or nullptr if not found.
#      78                 :            :  */
#      79                 :            : static NodeContext* GetNodeContext(const std::any& context, HTTPRequest* req)
#      80                 :          3 : {
#      81                 :          3 :     auto node_context = util::AnyPtr<NodeContext>(context);
#      82         [ -  + ]:          3 :     if (!node_context) {
#      83                 :          0 :         RESTERR(req, HTTP_INTERNAL_SERVER_ERROR,
#      84                 :          0 :                 strprintf("%s:%d (%s)\n"
#      85                 :          0 :                           "Internal bug detected: Node context not found!\n"
#      86                 :          0 :                           "You may report this issue here: %s\n",
#      87                 :          0 :                           __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
#      88                 :          0 :         return nullptr;
#      89                 :          0 :     }
#      90                 :          3 :     return node_context;
#      91                 :          3 : }
#      92                 :            : 
#      93                 :            : /**
#      94                 :            :  * Get the node context mempool.
#      95                 :            :  *
#      96                 :            :  * @param[in]  req The HTTP request, whose status code will be set if node
#      97                 :            :  *                 context mempool is not found.
#      98                 :            :  * @returns        Pointer to the mempool or nullptr if no mempool found.
#      99                 :            :  */
#     100                 :            : static CTxMemPool* GetMemPool(const std::any& context, HTTPRequest* req)
#     101                 :          7 : {
#     102                 :          7 :     auto node_context = util::AnyPtr<NodeContext>(context);
#     103 [ -  + ][ -  + ]:          7 :     if (!node_context || !node_context->mempool) {
#     104                 :          0 :         RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
#     105                 :          0 :         return nullptr;
#     106                 :          0 :     }
#     107                 :          7 :     return node_context->mempool.get();
#     108                 :          7 : }
#     109                 :            : 
#     110                 :            : static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
#     111                 :         41 : {
#     112                 :         41 :     const std::string::size_type pos = strReq.rfind('.');
#     113         [ -  + ]:         41 :     if (pos == std::string::npos)
#     114                 :          0 :     {
#     115                 :          0 :         param = strReq;
#     116                 :          0 :         return rf_names[0].rf;
#     117                 :          0 :     }
#     118                 :            : 
#     119                 :         41 :     param = strReq.substr(0, pos);
#     120                 :         41 :     const std::string suff(strReq, pos + 1);
#     121                 :            : 
#     122         [ +  - ]:        150 :     for (const auto& rf_name : rf_names) {
#     123         [ +  + ]:        150 :         if (suff == rf_name.name)
#     124                 :         41 :             return rf_name.rf;
#     125                 :        150 :     }
#     126                 :            : 
#     127                 :            :     /* If no suffix is found, return original string.  */
#     128                 :         41 :     param = strReq;
#     129                 :          0 :     return rf_names[0].rf;
#     130                 :         41 : }
#     131                 :            : 
#     132                 :            : static std::string AvailableDataFormatsString()
#     133                 :          0 : {
#     134                 :          0 :     std::string formats;
#     135         [ #  # ]:          0 :     for (const auto& rf_name : rf_names) {
#     136         [ #  # ]:          0 :         if (strlen(rf_name.name) > 0) {
#     137                 :          0 :             formats.append(".");
#     138                 :          0 :             formats.append(rf_name.name);
#     139                 :          0 :             formats.append(", ");
#     140                 :          0 :         }
#     141                 :          0 :     }
#     142                 :            : 
#     143         [ #  # ]:          0 :     if (formats.length() > 0)
#     144                 :          0 :         return formats.substr(0, formats.length() - 2);
#     145                 :            : 
#     146                 :          0 :     return formats;
#     147                 :          0 : }
#     148                 :            : 
#     149                 :            : static bool CheckWarmup(HTTPRequest* req)
#     150                 :         41 : {
#     151                 :         41 :     std::string statusmessage;
#     152         [ -  + ]:         41 :     if (RPCIsInWarmup(&statusmessage))
#     153                 :          0 :          return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
#     154                 :         41 :     return true;
#     155                 :         41 : }
#     156                 :            : 
#     157                 :            : static bool rest_headers(const std::any& context,
#     158                 :            :                          HTTPRequest* req,
#     159                 :            :                          const std::string& strURIPart)
#     160                 :          6 : {
#     161         [ -  + ]:          6 :     if (!CheckWarmup(req))
#     162                 :          0 :         return false;
#     163                 :          6 :     std::string param;
#     164                 :          6 :     const RetFormat rf = ParseDataFormat(param, strURIPart);
#     165                 :          6 :     std::vector<std::string> path;
#     166                 :          6 :     boost::split(path, param, boost::is_any_of("/"));
#     167                 :            : 
#     168         [ -  + ]:          6 :     if (path.size() != 2)
#     169                 :          0 :         return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
#     170                 :            : 
#     171                 :          6 :     long count = strtol(path[0].c_str(), nullptr, 10);
#     172 [ -  + ][ -  + ]:          6 :     if (count < 1 || count > 2000)
#     173                 :          0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
#     174                 :            : 
#     175                 :          6 :     std::string hashStr = path[1];
#     176                 :          6 :     uint256 hash;
#     177         [ -  + ]:          6 :     if (!ParseHashStr(hashStr, hash))
#     178                 :          0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
#     179                 :            : 
#     180                 :          6 :     const CBlockIndex* tip = nullptr;
#     181                 :          6 :     std::vector<const CBlockIndex *> headers;
#     182                 :          6 :     headers.reserve(count);
#     183                 :          6 :     {
#     184                 :          6 :         ChainstateManager& chainman = EnsureAnyChainman(context);
#     185                 :          6 :         LOCK(cs_main);
#     186                 :          6 :         CChain& active_chain = chainman.ActiveChain();
#     187                 :          6 :         tip = active_chain.Tip();
#     188                 :          6 :         const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
#     189 [ +  + ][ +  + ]:         10 :         while (pindex != nullptr && active_chain.Contains(pindex)) {
#     190                 :          8 :             headers.push_back(pindex);
#     191         [ +  + ]:          8 :             if (headers.size() == (unsigned long)count)
#     192                 :          4 :                 break;
#     193                 :          4 :             pindex = active_chain.Next(pindex);
#     194                 :          4 :         }
#     195                 :          6 :     }
#     196                 :            : 
#     197                 :          6 :     switch (rf) {
#     198         [ +  + ]:          1 :     case RetFormat::BINARY: {
#     199                 :          1 :         CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
#     200         [ +  + ]:          1 :         for (const CBlockIndex *pindex : headers) {
#     201                 :          1 :             ssHeader << pindex->GetBlockHeader();
#     202                 :          1 :         }
#     203                 :            : 
#     204                 :          1 :         std::string binaryHeader = ssHeader.str();
#     205                 :          1 :         req->WriteHeader("Content-Type", "application/octet-stream");
#     206                 :          1 :         req->WriteReply(HTTP_OK, binaryHeader);
#     207                 :          1 :         return true;
#     208                 :          0 :     }
#     209                 :            : 
#     210         [ +  + ]:          1 :     case RetFormat::HEX: {
#     211                 :          1 :         CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
#     212         [ +  + ]:          1 :         for (const CBlockIndex *pindex : headers) {
#     213                 :          1 :             ssHeader << pindex->GetBlockHeader();
#     214                 :          1 :         }
#     215                 :            : 
#     216                 :          1 :         std::string strHex = HexStr(ssHeader) + "\n";
#     217                 :          1 :         req->WriteHeader("Content-Type", "text/plain");
#     218                 :          1 :         req->WriteReply(HTTP_OK, strHex);
#     219                 :          1 :         return true;
#     220                 :          0 :     }
#     221         [ +  + ]:          4 :     case RetFormat::JSON: {
#     222                 :          4 :         UniValue jsonHeaders(UniValue::VARR);
#     223         [ +  + ]:          6 :         for (const CBlockIndex *pindex : headers) {
#     224                 :          6 :             jsonHeaders.push_back(blockheaderToJSON(tip, pindex));
#     225                 :          6 :         }
#     226                 :          4 :         std::string strJSON = jsonHeaders.write() + "\n";
#     227                 :          4 :         req->WriteHeader("Content-Type", "application/json");
#     228                 :          4 :         req->WriteReply(HTTP_OK, strJSON);
#     229                 :          4 :         return true;
#     230                 :          0 :     }
#     231         [ -  + ]:          0 :     default: {
#     232                 :          0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex, .json)");
#     233                 :          0 :     }
#     234                 :          6 :     }
#     235                 :          6 : }
#     236                 :            : 
#     237                 :            : static bool rest_block(const std::any& context,
#     238                 :            :                        HTTPRequest* req,
#     239                 :            :                        const std::string& strURIPart,
#     240                 :            :                        bool showTxDetails)
#     241                 :          7 : {
#     242         [ -  + ]:          7 :     if (!CheckWarmup(req))
#     243                 :          0 :         return false;
#     244                 :          7 :     std::string hashStr;
#     245                 :          7 :     const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
#     246                 :            : 
#     247                 :          7 :     uint256 hash;
#     248         [ -  + ]:          7 :     if (!ParseHashStr(hashStr, hash))
#     249                 :          0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
#     250                 :            : 
#     251                 :          7 :     CBlock block;
#     252                 :          7 :     CBlockIndex* pblockindex = nullptr;
#     253                 :          7 :     CBlockIndex* tip = nullptr;
#     254                 :          7 :     {
#     255                 :          7 :         ChainstateManager& chainman = EnsureAnyChainman(context);
#     256                 :          7 :         LOCK(cs_main);
#     257                 :          7 :         tip = chainman.ActiveChain().Tip();
#     258                 :          7 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
#     259         [ +  + ]:          7 :         if (!pblockindex) {
#     260                 :          1 :             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
#     261                 :          1 :         }
#     262                 :            : 
#     263         [ -  + ]:          6 :         if (IsBlockPruned(pblockindex))
#     264                 :          0 :             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
#     265                 :            : 
#     266         [ -  + ]:          6 :         if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
#     267                 :          0 :             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
#     268                 :          6 :     }
#     269                 :            : 
#     270                 :          6 :     switch (rf) {
#     271         [ +  + ]:          1 :     case RetFormat::BINARY: {
#     272                 :          1 :         CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
#     273                 :          1 :         ssBlock << block;
#     274                 :          1 :         std::string binaryBlock = ssBlock.str();
#     275                 :          1 :         req->WriteHeader("Content-Type", "application/octet-stream");
#     276                 :          1 :         req->WriteReply(HTTP_OK, binaryBlock);
#     277                 :          1 :         return true;
#     278                 :          0 :     }
#     279                 :            : 
#     280         [ +  + ]:          1 :     case RetFormat::HEX: {
#     281                 :          1 :         CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
#     282                 :          1 :         ssBlock << block;
#     283                 :          1 :         std::string strHex = HexStr(ssBlock) + "\n";
#     284                 :          1 :         req->WriteHeader("Content-Type", "text/plain");
#     285                 :          1 :         req->WriteReply(HTTP_OK, strHex);
#     286                 :          1 :         return true;
#     287                 :          0 :     }
#     288                 :            : 
#     289         [ +  + ]:          4 :     case RetFormat::JSON: {
#     290                 :          4 :         UniValue objBlock = blockToJSON(block, tip, pblockindex, showTxDetails);
#     291                 :          4 :         std::string strJSON = objBlock.write() + "\n";
#     292                 :          4 :         req->WriteHeader("Content-Type", "application/json");
#     293                 :          4 :         req->WriteReply(HTTP_OK, strJSON);
#     294                 :          4 :         return true;
#     295                 :          0 :     }
#     296                 :            : 
#     297         [ -  + ]:          0 :     default: {
#     298                 :          0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
#     299                 :          0 :     }
#     300                 :          6 :     }
#     301                 :          6 : }
#     302                 :            : 
#     303                 :            : static bool rest_block_extended(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
#     304                 :          6 : {
#     305                 :          6 :     return rest_block(context, req, strURIPart, true);
#     306                 :          6 : }
#     307                 :            : 
#     308                 :            : static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
#     309                 :          1 : {
#     310                 :          1 :     return rest_block(context, req, strURIPart, false);
#     311                 :          1 : }
#     312                 :            : 
#     313                 :            : // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
#     314                 :            : RPCHelpMan getblockchaininfo();
#     315                 :            : 
#     316                 :            : static bool rest_chaininfo(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
#     317                 :          1 : {
#     318         [ -  + ]:          1 :     if (!CheckWarmup(req))
#     319                 :          0 :         return false;
#     320                 :          1 :     std::string param;
#     321                 :          1 :     const RetFormat rf = ParseDataFormat(param, strURIPart);
#     322                 :            : 
#     323                 :          1 :     switch (rf) {
#     324         [ +  - ]:          1 :     case RetFormat::JSON: {
#     325                 :          1 :         JSONRPCRequest jsonRequest;
#     326                 :          1 :         jsonRequest.context = context;
#     327                 :          1 :         jsonRequest.params = UniValue(UniValue::VARR);
#     328                 :          1 :         UniValue chainInfoObject = getblockchaininfo().HandleRequest(jsonRequest);
#     329                 :          1 :         std::string strJSON = chainInfoObject.write() + "\n";
#     330                 :          1 :         req->WriteHeader("Content-Type", "application/json");
#     331                 :          1 :         req->WriteReply(HTTP_OK, strJSON);
#     332                 :          1 :         return true;
#     333                 :          0 :     }
#     334         [ -  + ]:          0 :     default: {
#     335                 :          0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
#     336                 :          0 :     }
#     337                 :          1 :     }
#     338                 :          1 : }
#     339                 :            : 
#     340                 :            : static bool rest_mempool_info(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
#     341                 :          1 : {
#     342         [ -  + ]:          1 :     if (!CheckWarmup(req))
#     343                 :          0 :         return false;
#     344                 :          1 :     const CTxMemPool* mempool = GetMemPool(context, req);
#     345         [ -  + ]:          1 :     if (!mempool) return false;
#     346                 :          1 :     std::string param;
#     347                 :          1 :     const RetFormat rf = ParseDataFormat(param, strURIPart);
#     348                 :            : 
#     349                 :          1 :     switch (rf) {
#     350         [ +  - ]:          1 :     case RetFormat::JSON: {
#     351                 :          1 :         UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool);
#     352                 :            : 
#     353                 :          1 :         std::string strJSON = mempoolInfoObject.write() + "\n";
#     354                 :          1 :         req->WriteHeader("Content-Type", "application/json");
#     355                 :          1 :         req->WriteReply(HTTP_OK, strJSON);
#     356                 :          1 :         return true;
#     357                 :          0 :     }
#     358         [ -  + ]:          0 :     default: {
#     359                 :          0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
#     360                 :          0 :     }
#     361                 :          1 :     }
#     362                 :          1 : }
#     363                 :            : 
#     364                 :            : static bool rest_mempool_contents(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
#     365                 :          1 : {
#     366         [ -  + ]:          1 :     if (!CheckWarmup(req)) return false;
#     367                 :          1 :     const CTxMemPool* mempool = GetMemPool(context, req);
#     368         [ -  + ]:          1 :     if (!mempool) return false;
#     369                 :          1 :     std::string param;
#     370                 :          1 :     const RetFormat rf = ParseDataFormat(param, strURIPart);
#     371                 :            : 
#     372                 :          1 :     switch (rf) {
#     373         [ +  - ]:          1 :     case RetFormat::JSON: {
#     374                 :          1 :         UniValue mempoolObject = MempoolToJSON(*mempool, true);
#     375                 :            : 
#     376                 :          1 :         std::string strJSON = mempoolObject.write() + "\n";
#     377                 :          1 :         req->WriteHeader("Content-Type", "application/json");
#     378                 :          1 :         req->WriteReply(HTTP_OK, strJSON);
#     379                 :          1 :         return true;
#     380                 :          0 :     }
#     381         [ -  + ]:          0 :     default: {
#     382                 :          0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
#     383                 :          0 :     }
#     384                 :          1 :     }
#     385                 :          1 : }
#     386                 :            : 
#     387                 :            : static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
#     388                 :          3 : {
#     389         [ -  + ]:          3 :     if (!CheckWarmup(req))
#     390                 :          0 :         return false;
#     391                 :          3 :     std::string hashStr;
#     392                 :          3 :     const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
#     393                 :            : 
#     394                 :          3 :     uint256 hash;
#     395         [ -  + ]:          3 :     if (!ParseHashStr(hashStr, hash))
#     396                 :          0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
#     397                 :            : 
#     398         [ -  + ]:          3 :     if (g_txindex) {
#     399                 :          0 :         g_txindex->BlockUntilSyncedToCurrentChain();
#     400                 :          0 :     }
#     401                 :            : 
#     402                 :          3 :     const NodeContext* const node = GetNodeContext(context, req);
#     403         [ -  + ]:          3 :     if (!node) return false;
#     404                 :          3 :     uint256 hashBlock = uint256();
#     405                 :          3 :     const CTransactionRef tx = GetTransaction(/* block_index */ nullptr, node->mempool.get(), hash, Params().GetConsensus(), hashBlock);
#     406         [ -  + ]:          3 :     if (!tx) {
#     407                 :          0 :         return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
#     408                 :          0 :     }
#     409                 :            : 
#     410                 :          3 :     switch (rf) {
#     411         [ -  + ]:          0 :     case RetFormat::BINARY: {
#     412                 :          0 :         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
#     413                 :          0 :         ssTx << tx;
#     414                 :            : 
#     415                 :          0 :         std::string binaryTx = ssTx.str();
#     416                 :          0 :         req->WriteHeader("Content-Type", "application/octet-stream");
#     417                 :          0 :         req->WriteReply(HTTP_OK, binaryTx);
#     418                 :          0 :         return true;
#     419                 :          0 :     }
#     420                 :            : 
#     421         [ +  + ]:          1 :     case RetFormat::HEX: {
#     422                 :          1 :         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
#     423                 :          1 :         ssTx << tx;
#     424                 :            : 
#     425                 :          1 :         std::string strHex = HexStr(ssTx) + "\n";
#     426                 :          1 :         req->WriteHeader("Content-Type", "text/plain");
#     427                 :          1 :         req->WriteReply(HTTP_OK, strHex);
#     428                 :          1 :         return true;
#     429                 :          0 :     }
#     430                 :            : 
#     431         [ +  + ]:          2 :     case RetFormat::JSON: {
#     432                 :          2 :         UniValue objTx(UniValue::VOBJ);
#     433                 :          2 :         TxToUniv(*tx, hashBlock, objTx);
#     434                 :          2 :         std::string strJSON = objTx.write() + "\n";
#     435                 :          2 :         req->WriteHeader("Content-Type", "application/json");
#     436                 :          2 :         req->WriteReply(HTTP_OK, strJSON);
#     437                 :          2 :         return true;
#     438                 :          0 :     }
#     439                 :            : 
#     440         [ -  + ]:          0 :     default: {
#     441                 :          0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
#     442                 :          0 :     }
#     443                 :          3 :     }
#     444                 :          3 : }
#     445                 :            : 
#     446                 :            : static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
#     447                 :         15 : {
#     448         [ -  + ]:         15 :     if (!CheckWarmup(req))
#     449                 :          0 :         return false;
#     450                 :         15 :     std::string param;
#     451                 :         15 :     const RetFormat rf = ParseDataFormat(param, strURIPart);
#     452                 :            : 
#     453                 :         15 :     std::vector<std::string> uriParts;
#     454         [ +  + ]:         15 :     if (param.length() > 1)
#     455                 :         12 :     {
#     456                 :         12 :         std::string strUriParams = param.substr(1);
#     457                 :         12 :         boost::split(uriParts, strUriParams, boost::is_any_of("/"));
#     458                 :         12 :     }
#     459                 :            : 
#     460                 :            :     // throw exception in case of an empty request
#     461                 :         15 :     std::string strRequestMutable = req->ReadBody();
#     462 [ +  + ][ -  + ]:         15 :     if (strRequestMutable.length() == 0 && uriParts.size() == 0)
#     463                 :          0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
#     464                 :            : 
#     465                 :         15 :     bool fInputParsed = false;
#     466                 :         15 :     bool fCheckMemPool = false;
#     467                 :         15 :     std::vector<COutPoint> vOutPoints;
#     468                 :            : 
#     469                 :            :     // parse/deserialize input
#     470                 :            :     // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
#     471                 :            : 
#     472         [ +  + ]:         15 :     if (uriParts.size() > 0)
#     473                 :         12 :     {
#     474                 :            :         //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
#     475         [ +  + ]:         12 :         if (uriParts[0] == "checkmempool") fCheckMemPool = true;
#     476                 :            : 
#     477 [ +  + ][ +  + ]:         57 :         for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
#     478                 :         45 :         {
#     479                 :         45 :             uint256 txid;
#     480                 :         45 :             int32_t nOutput;
#     481                 :         45 :             std::string strTxid = uriParts[i].substr(0, uriParts[i].find('-'));
#     482                 :         45 :             std::string strOutput = uriParts[i].substr(uriParts[i].find('-')+1);
#     483                 :            : 
#     484 [ -  + ][ -  + ]:         45 :             if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid))
#     485                 :          0 :                 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
#     486                 :            : 
#     487                 :         45 :             txid.SetHex(strTxid);
#     488                 :         45 :             vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput));
#     489                 :         45 :         }
#     490                 :            : 
#     491         [ +  + ]:         12 :         if (vOutPoints.size() > 0)
#     492                 :         11 :             fInputParsed = true;
#     493                 :          1 :         else
#     494                 :          1 :             return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
#     495                 :         14 :     }
#     496                 :            : 
#     497                 :         14 :     switch (rf) {
#     498         [ -  + ]:          0 :     case RetFormat::HEX: {
#     499                 :            :         // convert hex to bin, continue then with bin part
#     500                 :          0 :         std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
#     501                 :          0 :         strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
#     502                 :          0 :     }
#     503                 :            : 
#     504         [ +  + ]:          2 :     case RetFormat::BINARY: {
#     505                 :          2 :         try {
#     506                 :            :             //deserialize only if user sent a request
#     507         [ +  - ]:          2 :             if (strRequestMutable.size() > 0)
#     508                 :          2 :             {
#     509         [ -  + ]:          2 :                 if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA
#     510                 :          0 :                     return RESTERR(req, HTTP_BAD_REQUEST, "Combination of URI scheme inputs and raw post data is not allowed");
#     511                 :            : 
#     512                 :          2 :                 CDataStream oss(SER_NETWORK, PROTOCOL_VERSION);
#     513                 :          2 :                 oss << strRequestMutable;
#     514                 :          2 :                 oss >> fCheckMemPool;
#     515                 :          2 :                 oss >> vOutPoints;
#     516                 :          2 :             }
#     517                 :          2 :         } catch (const std::ios_base::failure&) {
#     518                 :            :             // abort in case of unreadable binary data
#     519                 :          1 :             return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
#     520                 :          1 :         }
#     521                 :          1 :         break;
#     522                 :          1 :     }
#     523                 :            : 
#     524         [ +  + ]:         12 :     case RetFormat::JSON: {
#     525         [ +  + ]:         12 :         if (!fInputParsed)
#     526                 :          1 :             return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
#     527                 :         11 :         break;
#     528                 :         11 :     }
#     529         [ -  + ]:         11 :     default: {
#     530                 :          0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
#     531                 :         12 :     }
#     532                 :         12 :     }
#     533                 :            : 
#     534                 :            :     // limit max outpoints
#     535         [ +  + ]:         12 :     if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS)
#     536                 :          1 :         return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
#     537                 :            : 
#     538                 :            :     // check spentness and form a bitmap (as well as a JSON capable human-readable string representation)
#     539                 :         11 :     std::vector<unsigned char> bitmap;
#     540                 :         11 :     std::vector<CCoin> outs;
#     541                 :         11 :     std::string bitmapStringRepresentation;
#     542                 :         11 :     std::vector<bool> hits;
#     543                 :         11 :     bitmap.resize((vOutPoints.size() + 7) / 8);
#     544                 :         11 :     ChainstateManager& chainman = EnsureAnyChainman(context);
#     545                 :         11 :     {
#     546                 :         11 :         auto process_utxos = [&vOutPoints, &outs, &hits](const CCoinsView& view, const CTxMemPool& mempool) {
#     547         [ +  + ]:         26 :             for (const COutPoint& vOutPoint : vOutPoints) {
#     548                 :         26 :                 Coin coin;
#     549 [ +  + ][ +  + ]:         26 :                 bool hit = !mempool.isSpent(vOutPoint) && view.GetCoin(vOutPoint, coin);
#     550                 :         26 :                 hits.push_back(hit);
#     551         [ +  + ]:         26 :                 if (hit) outs.emplace_back(std::move(coin));
#     552                 :         26 :             }
#     553                 :         11 :         };
#     554                 :            : 
#     555         [ +  + ]:         11 :         if (fCheckMemPool) {
#     556                 :          5 :             const CTxMemPool* mempool = GetMemPool(context, req);
#     557         [ -  + ]:          5 :             if (!mempool) return false;
#     558                 :            :             // use db+mempool as cache backend in case user likes to query mempool
#     559                 :          5 :             LOCK2(cs_main, mempool->cs);
#     560                 :          5 :             CCoinsViewCache& viewChain = chainman.ActiveChainstate().CoinsTip();
#     561                 :          5 :             CCoinsViewMemPool viewMempool(&viewChain, *mempool);
#     562                 :          5 :             process_utxos(viewMempool, *mempool);
#     563                 :          6 :         } else {
#     564                 :          6 :             LOCK(cs_main);  // no need to lock mempool!
#     565                 :          6 :             process_utxos(chainman.ActiveChainstate().CoinsTip(), CTxMemPool());
#     566                 :          6 :         }
#     567                 :            : 
#     568         [ +  + ]:         37 :         for (size_t i = 0; i < hits.size(); ++i) {
#     569                 :         26 :             const bool hit = hits[i];
#     570         [ +  + ]:         26 :             bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output)
#     571                 :         26 :             bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
#     572                 :         26 :         }
#     573                 :         11 :     }
#     574                 :            : 
#     575                 :         11 :     switch (rf) {
#     576         [ +  + ]:          1 :     case RetFormat::BINARY: {
#     577                 :            :         // serialize data
#     578                 :            :         // use exact same output as mentioned in Bip64
#     579                 :          1 :         CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
#     580                 :          1 :         ssGetUTXOResponse << chainman.ActiveChain().Height() << chainman.ActiveChain().Tip()->GetBlockHash() << bitmap << outs;
#     581                 :          1 :         std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
#     582                 :            : 
#     583                 :          1 :         req->WriteHeader("Content-Type", "application/octet-stream");
#     584                 :          1 :         req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
#     585                 :          1 :         return true;
#     586                 :          0 :     }
#     587                 :            : 
#     588         [ -  + ]:          0 :     case RetFormat::HEX: {
#     589                 :          0 :         CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
#     590                 :          0 :         ssGetUTXOResponse << chainman.ActiveChain().Height() << chainman.ActiveChain().Tip()->GetBlockHash() << bitmap << outs;
#     591                 :          0 :         std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
#     592                 :            : 
#     593                 :          0 :         req->WriteHeader("Content-Type", "text/plain");
#     594                 :          0 :         req->WriteReply(HTTP_OK, strHex);
#     595                 :          0 :         return true;
#     596                 :          0 :     }
#     597                 :            : 
#     598         [ +  + ]:         10 :     case RetFormat::JSON: {
#     599                 :         10 :         UniValue objGetUTXOResponse(UniValue::VOBJ);
#     600                 :            : 
#     601                 :            :         // pack in some essentials
#     602                 :            :         // use more or less the same output as mentioned in Bip64
#     603                 :         10 :         objGetUTXOResponse.pushKV("chainHeight", chainman.ActiveChain().Height());
#     604                 :         10 :         objGetUTXOResponse.pushKV("chaintipHash", chainman.ActiveChain().Tip()->GetBlockHash().GetHex());
#     605                 :         10 :         objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
#     606                 :            : 
#     607                 :         10 :         UniValue utxos(UniValue::VARR);
#     608         [ +  + ]:         10 :         for (const CCoin& coin : outs) {
#     609                 :          8 :             UniValue utxo(UniValue::VOBJ);
#     610                 :          8 :             utxo.pushKV("height", (int32_t)coin.nHeight);
#     611                 :          8 :             utxo.pushKV("value", ValueFromAmount(coin.out.nValue));
#     612                 :            : 
#     613                 :            :             // include the script in a json output
#     614                 :          8 :             UniValue o(UniValue::VOBJ);
#     615                 :          8 :             ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
#     616                 :          8 :             utxo.pushKV("scriptPubKey", o);
#     617                 :          8 :             utxos.push_back(utxo);
#     618                 :          8 :         }
#     619                 :         10 :         objGetUTXOResponse.pushKV("utxos", utxos);
#     620                 :            : 
#     621                 :            :         // return json string
#     622                 :         10 :         std::string strJSON = objGetUTXOResponse.write() + "\n";
#     623                 :         10 :         req->WriteHeader("Content-Type", "application/json");
#     624                 :         10 :         req->WriteReply(HTTP_OK, strJSON);
#     625                 :         10 :         return true;
#     626                 :          0 :     }
#     627         [ -  + ]:          0 :     default: {
#     628                 :          0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
#     629                 :          0 :     }
#     630                 :         11 :     }
#     631                 :         11 : }
#     632                 :            : 
#     633                 :            : static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
#     634                 :            :                        const std::string& str_uri_part)
#     635                 :          7 : {
#     636         [ -  + ]:          7 :     if (!CheckWarmup(req)) return false;
#     637                 :          7 :     std::string height_str;
#     638                 :          7 :     const RetFormat rf = ParseDataFormat(height_str, str_uri_part);
#     639                 :            : 
#     640                 :          7 :     int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785
#     641 [ +  + ][ +  + ]:          7 :     if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
#     642                 :          3 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str));
#     643                 :          3 :     }
#     644                 :            : 
#     645                 :          4 :     CBlockIndex* pblockindex = nullptr;
#     646                 :          4 :     {
#     647                 :          4 :         ChainstateManager& chainman = EnsureAnyChainman(context);
#     648                 :          4 :         LOCK(cs_main);
#     649                 :          4 :         const CChain& active_chain = chainman.ActiveChain();
#     650         [ +  + ]:          4 :         if (blockheight > active_chain.Height()) {
#     651                 :          1 :             return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
#     652                 :          1 :         }
#     653                 :          3 :         pblockindex = active_chain[blockheight];
#     654                 :          3 :     }
#     655                 :          3 :     switch (rf) {
#     656         [ +  + ]:          1 :     case RetFormat::BINARY: {
#     657                 :          1 :         CDataStream ss_blockhash(SER_NETWORK, PROTOCOL_VERSION);
#     658                 :          1 :         ss_blockhash << pblockindex->GetBlockHash();
#     659                 :          1 :         req->WriteHeader("Content-Type", "application/octet-stream");
#     660                 :          1 :         req->WriteReply(HTTP_OK, ss_blockhash.str());
#     661                 :          1 :         return true;
#     662                 :          0 :     }
#     663         [ +  + ]:          1 :     case RetFormat::HEX: {
#     664                 :          1 :         req->WriteHeader("Content-Type", "text/plain");
#     665                 :          1 :         req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n");
#     666                 :          1 :         return true;
#     667                 :          0 :     }
#     668         [ +  + ]:          1 :     case RetFormat::JSON: {
#     669                 :          1 :         req->WriteHeader("Content-Type", "application/json");
#     670                 :          1 :         UniValue resp = UniValue(UniValue::VOBJ);
#     671                 :          1 :         resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
#     672                 :          1 :         req->WriteReply(HTTP_OK, resp.write() + "\n");
#     673                 :          1 :         return true;
#     674                 :          0 :     }
#     675         [ -  + ]:          0 :     default: {
#     676                 :          0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
#     677                 :          0 :     }
#     678                 :          3 :     }
#     679                 :          3 : }
#     680                 :            : 
#     681                 :            : static const struct {
#     682                 :            :     const char* prefix;
#     683                 :            :     bool (*handler)(const std::any& context, HTTPRequest* req, const std::string& strReq);
#     684                 :            : } uri_prefixes[] = {
#     685                 :            :       {"/rest/tx/", rest_tx},
#     686                 :            :       {"/rest/block/notxdetails/", rest_block_notxdetails},
#     687                 :            :       {"/rest/block/", rest_block_extended},
#     688                 :            :       {"/rest/chaininfo", rest_chaininfo},
#     689                 :            :       {"/rest/mempool/info", rest_mempool_info},
#     690                 :            :       {"/rest/mempool/contents", rest_mempool_contents},
#     691                 :            :       {"/rest/headers/", rest_headers},
#     692                 :            :       {"/rest/getutxos", rest_getutxos},
#     693                 :            :       {"/rest/blockhashbyheight/", rest_blockhash_by_height},
#     694                 :            : };
#     695                 :            : 
#     696                 :            : void StartREST(const std::any& context)
#     697                 :          1 : {
#     698         [ +  + ]:          9 :     for (const auto& up : uri_prefixes) {
#     699                 :         41 :         auto handler = [context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); };
#     700                 :          9 :         RegisterHTTPHandler(up.prefix, false, handler);
#     701                 :          9 :     }
#     702                 :          1 : }
#     703                 :            : 
#     704                 :            : void InterruptREST()
#     705                 :        663 : {
#     706                 :        663 : }
#     707                 :            : 
#     708                 :            : void StopREST()
#     709                 :        663 : {
#     710         [ +  + ]:       5967 :     for (const auto& up : uri_prefixes) {
#     711                 :       5967 :         UnregisterHTTPHandler(up.prefix, false);
#     712                 :       5967 :     }
#     713                 :        663 : }

Generated by: LCOV version 1.14