LCOV - code coverage report
Current view: top level - src - rest.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 316 385 82.1 %
Date: 2020-09-26 01:30:44 Functions: 28 32 87.5 %

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

Generated by: LCOV version 1.15