LCOV - code coverage report
Current view: top level - src - rest.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 267 324 82.4 %
Date: 2025-02-23 09:33:43 Functions: 16 17 94.1 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2014 The Bitcoin Core developers
       3             : // Copyright (c) 2017-2022 The PIVX Core developers
       4             : // Distributed under the MIT software license, see the accompanying
       5             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       6             : 
       7             : #include "chain.h"
       8             : #include "core_io.h"
       9             : #include "primitives/block.h"
      10             : #include "primitives/transaction.h"
      11             : #include "httpserver.h"
      12             : #include "rpc/server.h"
      13             : #include "streams.h"
      14             : #include "sync.h"
      15             : #include "txmempool.h"
      16             : #include "utilstrencodings.h"
      17             : #include "validation.h"
      18             : #include "version.h"
      19             : #include "wallet/wallet.h"
      20             : 
      21             : #include <boost/algorithm/string.hpp>
      22             : 
      23             : #include <univalue.h>
      24             : 
      25             : 
      26             : static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
      27             : 
      28             : enum RetFormat {
      29             :     RF_UNDEF,
      30             :     RF_BINARY,
      31             :     RF_HEX,
      32             :     RF_JSON,
      33             : };
      34             : 
      35             : static const struct {
      36             :     enum RetFormat rf;
      37             :     const char* name;
      38             : } rf_names[] = {
      39             :       {RF_UNDEF, ""},
      40             :       {RF_BINARY, "bin"},
      41             :       {RF_HEX, "hex"},
      42             :       {RF_JSON, "json"},
      43             : };
      44             : 
      45          49 : struct CCoin {
      46             :     uint32_t nHeight;
      47             :     CTxOut out;
      48             : 
      49           1 :     CCoin() : nHeight(0) {}
      50          18 :     explicit CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {}
      51             : 
      52           0 :     SERIALIZE_METHODS(CCoin, obj)
      53             :     {
      54           0 :         uint32_t nTxVerDummy = 0;
      55           0 :         READWRITE(nTxVerDummy, obj.nHeight, obj.out);
      56             :     }
      57             : };
      58             : 
      59             : extern void TxToJSON(CWallet* const pwallet, const CTransaction& tx, const CBlockIndex* tip, const CBlockIndex* blockindex, UniValue& entry);
      60             : extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false);
      61             : extern UniValue mempoolInfoToJSON();
      62             : extern UniValue mempoolToJSON(bool fVerbose = false);
      63             : extern UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex);
      64             : 
      65           4 : static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message)
      66             : {
      67           8 :     req->WriteHeader("Content-Type", "text/plain");
      68           4 :     req->WriteReply(status, message + "\r\n");
      69           4 :     return false;
      70             : }
      71             : 
      72          27 : static enum RetFormat ParseDataFormat(std::vector<std::string>& params, const std::string& strReq)
      73             : {
      74          27 :     boost::split(params, strReq, boost::is_any_of("."));
      75          27 :     if (params.size() > 1) {
      76          95 :         for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
      77          95 :             if (params[1] == rf_names[i].name)
      78          27 :                 return rf_names[i].rf;
      79             :     }
      80             : 
      81             :     return rf_names[0].rf;
      82             : }
      83             : 
      84           0 : static std::string AvailableDataFormatsString()
      85             : {
      86           0 :     std::string formats = "";
      87           0 :     for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
      88           0 :         if (strlen(rf_names[i].name) > 0) {
      89           0 :             formats.append(".");
      90           0 :             formats.append(rf_names[i].name);
      91           0 :             formats.append(", ");
      92             :         }
      93             : 
      94           0 :     if (formats.length() > 0)
      95           0 :         return formats.substr(0, formats.length() - 2);
      96             : 
      97           0 :     return formats;
      98             : }
      99             : 
     100          27 : static bool CheckWarmup(HTTPRequest* req)
     101             : {
     102          54 :     std::string statusmessage;
     103          27 :     if (RPCIsInWarmup(&statusmessage))
     104           0 :          return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
     105             :     return true;
     106             : }
     107             : 
     108           4 : static bool rest_headers(HTTPRequest* req,
     109             :                          const std::string& strURIPart)
     110             : {
     111           4 :     if (!CheckWarmup(req))
     112             :         return false;
     113           8 :     std::vector<std::string> params;
     114           4 :     const RetFormat rf = ParseDataFormat(params, strURIPart);
     115           4 :     std::vector<std::string> path;
     116           4 :     boost::split(path, params[0], boost::is_any_of("/"));
     117             : 
     118           4 :     if (path.size() != 2)
     119           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
     120             : 
     121           4 :     long count = strtol(path[0].c_str(), nullptr, 10);
     122           4 :     if (count < 1 || count > 2000)
     123           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
     124             : 
     125           8 :     std::string hashStr = path[1];
     126           4 :     uint256 hash;
     127           4 :     if (!ParseHashStr(hashStr, hash))
     128           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
     129             : 
     130           4 :     const CBlockIndex* tip;
     131           8 :     std::vector<const CBlockIndex *> headers;
     132           4 :     headers.reserve(count);
     133           4 :     {
     134           4 :         LOCK(cs_main);
     135           4 :         tip = chainActive.Tip();
     136           4 :         CBlockIndex* pindex = LookupBlockIndex(hash);
     137          16 :         while (pindex != nullptr && chainActive.Contains(pindex)) {
     138           8 :             headers.push_back(pindex);
     139           8 :             if (headers.size() == (unsigned long)count)
     140             :                 break;
     141           4 :             pindex = chainActive.Next(pindex);
     142             :         }
     143             :     }
     144             : 
     145           8 :     CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
     146          12 :     for (const CBlockIndex *pindex : headers) {
     147          16 :         ssHeader << pindex->GetBlockHeader();
     148             :     }
     149             : 
     150           4 :     switch (rf) {
     151           1 :     case RF_BINARY: {
     152           2 :         std::string binaryHeader = ssHeader.str();
     153           2 :         req->WriteHeader("Content-Type", "application/octet-stream");
     154           1 :         req->WriteReply(HTTP_OK, binaryHeader);
     155           1 :         return true;
     156             :     }
     157             : 
     158           1 :     case RF_HEX: {
     159           2 :         std::string strHex = HexStr(ssHeader) + "\n";
     160           2 :         req->WriteHeader("Content-Type", "text/plain");
     161           1 :         req->WriteReply(HTTP_OK, strHex);
     162           1 :         return true;
     163             :     }
     164           2 :     case RF_JSON: {
     165           2 :         UniValue jsonHeaders(UniValue::VARR);
     166           8 :         for (const CBlockIndex *pindex : headers) {
     167           6 :             jsonHeaders.push_back(blockheaderToJSON(tip, pindex));
     168             :         }
     169           4 :         std::string strJSON = jsonHeaders.write() + "\n";
     170           4 :         req->WriteHeader("Content-Type", "application/json");
     171           2 :         req->WriteReply(HTTP_OK, strJSON);
     172           2 :         return true;
     173             :     }
     174           0 :     default: {
     175           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
     176             :     }
     177             :     }
     178             : }
     179             : 
     180           5 : static bool rest_block(HTTPRequest* req,
     181             :                        const std::string& strURIPart,
     182             :                        bool showTxDetails)
     183             : {
     184           5 :     if (!CheckWarmup(req))
     185             :         return false;
     186           5 :     std::vector<std::string> params;
     187           5 :     const RetFormat rf = ParseDataFormat(params, strURIPart);
     188             : 
     189          10 :     std::string hashStr = params[0];
     190           5 :     uint256 hash;
     191           5 :     if (!ParseHashStr(hashStr, hash))
     192           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
     193             : 
     194           5 :     CBlock block;
     195           5 :     const CBlockIndex* pblockindex;
     196           5 :     const CBlockIndex* tip;
     197           5 :     {
     198           5 :         LOCK(cs_main);
     199           5 :         tip = chainActive.Tip();
     200           5 :         pblockindex = LookupBlockIndex(hash);
     201           5 :         if (!pblockindex) {
     202           0 :             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
     203             :         }
     204             : 
     205           5 :         if (!(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0)
     206           0 :             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
     207             : 
     208           5 :         if (!ReadBlockFromDisk(block, pblockindex))
     209           0 :             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
     210             :     }
     211             : 
     212          10 :     CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
     213           5 :     ssBlock << block;
     214             : 
     215           5 :     switch (rf) {
     216           1 :     case RF_BINARY: {
     217           2 :         std::string binaryBlock = ssBlock.str();
     218           2 :         req->WriteHeader("Content-Type", "application/octet-stream");
     219           1 :         req->WriteReply(HTTP_OK, binaryBlock);
     220           1 :         return true;
     221             :     }
     222             : 
     223           1 :     case RF_HEX: {
     224           2 :         std::string strHex = HexStr(ssBlock) + "\n";
     225           2 :         req->WriteHeader("Content-Type", "text/plain");
     226           1 :         req->WriteReply(HTTP_OK, strHex);
     227           1 :         return true;
     228             :     }
     229             : 
     230           3 :     case RF_JSON: {
     231           3 :         UniValue objBlock = blockToJSON(block, tip, pblockindex, showTxDetails);
     232           6 :         std::string strJSON = objBlock.write() + "\n";
     233           6 :         req->WriteHeader("Content-Type", "application/json");
     234           3 :         req->WriteReply(HTTP_OK, strJSON);
     235           3 :         return true;
     236             :     }
     237             : 
     238           0 :     default: {
     239           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     240             :     }
     241             :     }
     242             : }
     243             : 
     244           4 : static bool rest_block_extended(HTTPRequest* req, const std::string& strURIPart)
     245             : {
     246           4 :     return rest_block(req, strURIPart, true);
     247             : }
     248             : 
     249           1 : static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPart)
     250             : {
     251           1 :     return rest_block(req, strURIPart, false);
     252             : }
     253             : 
     254             : // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
     255             : UniValue getblockchaininfo(const JSONRPCRequest& request);
     256             : 
     257           1 : static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
     258             : {
     259           1 :     if (!CheckWarmup(req))
     260             :         return false;
     261           2 :     std::vector<std::string> params;
     262           1 :     const RetFormat rf = ParseDataFormat(params, strURIPart);
     263             : 
     264           1 :     switch (rf) {
     265           1 :     case RF_JSON: {
     266           2 :         JSONRPCRequest jsonRequest;
     267           1 :         jsonRequest.params = UniValue(UniValue::VARR);
     268           1 :         UniValue chainInfoObject = getblockchaininfo(jsonRequest);
     269           2 :         std::string strJSON = chainInfoObject.write() + "\n";
     270           2 :         req->WriteHeader("Content-Type", "application/json");
     271           1 :         req->WriteReply(HTTP_OK, strJSON);
     272           1 :         return true;
     273             :     }
     274           0 :     default: {
     275           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
     276             :     }
     277             :     }
     278             : }
     279             : 
     280           1 : static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
     281             : {
     282           1 :     if (!CheckWarmup(req))
     283             :         return false;
     284           2 :     std::vector<std::string> params;
     285           1 :     const RetFormat rf = ParseDataFormat(params, strURIPart);
     286             : 
     287           1 :     switch (rf) {
     288           1 :     case RF_JSON: {
     289           1 :         UniValue mempoolInfoObject = mempoolInfoToJSON();
     290             : 
     291           2 :         std::string strJSON = mempoolInfoObject.write() + "\n";
     292           2 :         req->WriteHeader("Content-Type", "application/json");
     293           1 :         req->WriteReply(HTTP_OK, strJSON);
     294           1 :         return true;
     295             :     }
     296           0 :     default: {
     297           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
     298             :     }
     299             :     }
     300             : }
     301             : 
     302           1 : static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPart)
     303             : {
     304           1 :     if (!CheckWarmup(req))
     305             :         return false;
     306           2 :     std::vector<std::string> params;
     307           1 :     const RetFormat rf = ParseDataFormat(params, strURIPart);
     308             : 
     309           1 :     switch (rf) {
     310           1 :     case RF_JSON: {
     311           1 :         UniValue mempoolObject = mempoolToJSON(true);
     312             : 
     313           2 :         std::string strJSON = mempoolObject.write() + "\n";
     314           2 :         req->WriteHeader("Content-Type", "application/json");
     315           1 :         req->WriteReply(HTTP_OK, strJSON);
     316           1 :         return true;
     317             :     }
     318           0 :     default: {
     319           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
     320             :     }
     321             :     }
     322             : }
     323             : 
     324           4 : static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
     325             : {
     326           4 :     if (!CheckWarmup(req))
     327             :         return false;
     328           4 :     std::vector<std::string> params;
     329           4 :     const RetFormat rf = ParseDataFormat(params, strURIPart);
     330             : 
     331           8 :     std::string hashStr = params[0];
     332           4 :     uint256 hash;
     333           4 :     if (!ParseHashStr(hashStr, hash))
     334           0 :         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
     335             : 
     336           4 :     CTransactionRef tx;
     337           4 :     uint256 hashBlock = uint256();
     338           4 :     if (!GetTransaction(hash, tx, hashBlock, true))
     339           0 :         return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
     340             : 
     341           8 :     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
     342           4 :     ssTx << tx;
     343             : 
     344           4 :     switch (rf) {
     345           0 :     case RF_BINARY: {
     346           0 :         std::string binaryTx = ssTx.str();
     347           0 :         req->WriteHeader("Content-Type", "application/octet-stream");
     348           0 :         req->WriteReply(HTTP_OK, binaryTx);
     349           0 :         return true;
     350             :     }
     351             : 
     352           1 :     case RF_HEX: {
     353           2 :         std::string strHex = HexStr(ssTx) + "\n";
     354           2 :         req->WriteHeader("Content-Type", "text/plain");
     355           1 :         req->WriteReply(HTTP_OK, strHex);
     356           1 :         return true;
     357             :     }
     358             : 
     359           3 :     case RF_JSON: {
     360           3 :         const CBlockIndex* pblockindex;
     361           3 :         const CBlockIndex* tip;
     362           3 :         {
     363           3 :             LOCK(cs_main);
     364           3 :             tip = chainActive.Tip();
     365           3 :             pblockindex = LookupBlockIndex(hashBlock);
     366             :         }
     367           3 :         UniValue objTx(UniValue::VOBJ);
     368           3 :         TxToJSON(nullptr, *tx, tip, pblockindex, objTx);
     369           6 :         std::string strJSON = objTx.write() + "\n";
     370           6 :         req->WriteHeader("Content-Type", "application/json");
     371           3 :         req->WriteReply(HTTP_OK, strJSON);
     372           3 :         return true;
     373             :     }
     374             : 
     375           0 :     default: {
     376           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     377             :     }
     378             :     }
     379             : }
     380             : 
     381          11 : static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
     382             : {
     383          11 :     if (!CheckWarmup(req))
     384             :         return false;
     385          22 :     std::vector<std::string> params;
     386          11 :     enum RetFormat rf = ParseDataFormat(params, strURIPart);
     387             : 
     388          11 :     std::vector<std::string> uriParts;
     389          11 :     if (params.size() > 0 && params[0].length() > 1)
     390             :     {
     391          16 :         std::string strUriParams = params[0].substr(1);
     392          16 :         boost::split(uriParts, strUriParams, boost::is_any_of("/"));
     393             :     }
     394             : 
     395             :     // throw exception in case of a empty request
     396          22 :     std::string strRequestMutable = req->ReadBody();
     397          11 :     if (strRequestMutable.length() == 0 && uriParts.size() == 0)
     398           0 :         return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Error: empty request");
     399             : 
     400          11 :     bool fInputParsed = false;
     401          11 :     bool fCheckMemPool = false;
     402          22 :     std::vector<COutPoint> vOutPoints;
     403             : 
     404             :     // parse/deserialize input
     405             :     // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
     406             : 
     407          11 :     if (uriParts.size() > 0)
     408             :     {
     409             :         //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
     410           8 :         if (uriParts[0] == "checkmempool") fCheckMemPool = true;
     411             : 
     412          49 :         for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
     413             :         {
     414          41 :             uint256 txid;
     415          41 :             int32_t nOutput;
     416          82 :             std::string strTxid = uriParts[i].substr(0, uriParts[i].find('-'));
     417          82 :             std::string strOutput = uriParts[i].substr(uriParts[i].find('-')+1);
     418             : 
     419          41 :             if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid))
     420           0 :                 return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Parse error");
     421             : 
     422          41 :             txid.SetHex(strTxid);
     423          41 :             vOutPoints.emplace_back(txid, (uint32_t)nOutput);
     424             :         }
     425             : 
     426           8 :         if (vOutPoints.size() > 0)
     427             :             fInputParsed = true;
     428             :         else
     429           2 :             return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Error: empty request");
     430             :     }
     431             : 
     432          10 :     switch (rf) {
     433           0 :     case RF_HEX: {
     434             :         // convert hex to bin, continue then with bin part
     435           0 :         std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
     436           0 :         strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
     437             :     }
     438             : 
     439           2 :     case RF_BINARY: {
     440           2 :         try {
     441             :             //deserialize only if user sent a request
     442           2 :             if (strRequestMutable.size() > 0)
     443             :             {
     444           2 :                 if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA
     445           0 :                     return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Combination of URI scheme inputs and raw post data is not allowed");
     446             : 
     447           4 :                 CDataStream oss(SER_NETWORK, PROTOCOL_VERSION);
     448           2 :                 oss << strRequestMutable;
     449           2 :                 oss >> fCheckMemPool;
     450           3 :                 oss >> vOutPoints;
     451             :             }
     452           1 :         } catch (const std::ios_base::failure& e) {
     453             :             // abort in case of unreadable binary data
     454           1 :             return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Parse error");
     455             :         }
     456             :         break;
     457             :     }
     458             : 
     459           8 :     case RF_JSON: {
     460           8 :         if (!fInputParsed)
     461           2 :             return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Error: empty request");
     462             :         break;
     463             :     }
     464           0 :     default: {
     465           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     466             :     }
     467             :     }
     468             : 
     469             :     // limit max outpoints
     470           8 :     if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS)
     471           2 :         return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
     472             : 
     473             :     // check spentness and form a bitmap (as well as a JSON capable human-readble string representation)
     474          18 :     std::vector<unsigned char> bitmap;
     475           7 :     std::vector<CCoin> outs;
     476          14 :     std::string bitmapStringRepresentation;
     477          14 :     std::vector<bool> hits;
     478           7 :     bitmap.resize((vOutPoints.size() + 7) / 8);
     479           7 :     {
     480          14 :         LOCK2(cs_main, mempool.cs);
     481             : 
     482           7 :         CCoinsView viewDummy;
     483          14 :         CCoinsViewCache view(&viewDummy);
     484             : 
     485           7 :         CCoinsViewCache& viewChain = *pcoinsTip;
     486          14 :         CCoinsViewMemPool viewMempool(&viewChain, mempool);
     487             : 
     488           7 :         if (fCheckMemPool)
     489           6 :             view.SetBackend(viewMempool); // switch cache backend to db+mempool in case user likes to query mempool
     490             : 
     491          29 :         for (size_t i = 0; i < vOutPoints.size(); i++) {
     492          22 :             bool hit = false;
     493          44 :             Coin coin;
     494          22 :             if (view.GetCoin(vOutPoints[i], coin) && !mempool.isSpent(vOutPoints[i])) {
     495          18 :                 hit = true;
     496          18 :                 outs.emplace_back(std::move(coin));
     497             :             }
     498             : 
     499          22 :             hits.push_back(hit);
     500          26 :             bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output)
     501          22 :             bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
     502             :         }
     503             :     }
     504             : 
     505           7 :     switch (rf) {
     506           1 :     case RF_BINARY: {
     507             :         // serialize data
     508             :         // use exact same output as mentioned in Bip64
     509           2 :         CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
     510           2 :         ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs;
     511           2 :         std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
     512             : 
     513           2 :         req->WriteHeader("Content-Type", "application/octet-stream");
     514           1 :         req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
     515           1 :         return true;
     516             :     }
     517             : 
     518           0 :     case RF_HEX: {
     519           0 :         CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
     520           0 :         ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs;
     521           0 :         std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
     522             : 
     523           0 :         req->WriteHeader("Content-Type", "text/plain");
     524           0 :         req->WriteReply(HTTP_OK, strHex);
     525           0 :         return true;
     526             :     }
     527             : 
     528           6 :     case RF_JSON: {
     529          12 :         UniValue objGetUTXOResponse(UniValue::VOBJ);
     530             : 
     531             :         // pack in some essentials
     532             :         // use more or less the same output as mentioned in Bip64
     533           6 :         objGetUTXOResponse.pushKV("chainHeight", chainActive.Height());
     534          18 :         objGetUTXOResponse.pushKV("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex());
     535           6 :         objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
     536             : 
     537           6 :         UniValue utxos(UniValue::VARR);
     538          24 :         for (const CCoin& coin : outs) {
     539          36 :             UniValue utxo(UniValue::VOBJ);
     540          18 :             utxo.pushKV("height", (int32_t)coin.nHeight);
     541          36 :             utxo.pushKV("value", ValueFromAmount(coin.out.nValue));
     542             : 
     543             :             // include the script in a json output
     544          36 :             UniValue o(UniValue::VOBJ);
     545          18 :             ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
     546          18 :             utxo.pushKV("scriptPubKey", o);
     547          18 :             utxos.push_back(utxo);
     548             :         }
     549           6 :         objGetUTXOResponse.pushKV("utxos", utxos);
     550             : 
     551             :         // return json string
     552          12 :         std::string strJSON = objGetUTXOResponse.write() + "\n";
     553          12 :         req->WriteHeader("Content-Type", "application/json");
     554           6 :         req->WriteReply(HTTP_OK, strJSON);
     555           6 :         return true;
     556             :     }
     557           0 :     default: {
     558           0 :         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
     559             :     }
     560             :     }
     561             : }
     562             : 
     563             : static const struct {
     564             :     const char* prefix;
     565             :     bool (*handler)(HTTPRequest* req, const std::string& strReq);
     566             : } uri_prefixes[] = {
     567             :       {"/rest/tx/", rest_tx},
     568             :       {"/rest/block/notxdetails/", rest_block_notxdetails},
     569             :       {"/rest/block/", rest_block_extended},
     570             :       {"/rest/chaininfo", rest_chaininfo},
     571             :       {"/rest/mempool/info", rest_mempool_info},
     572             :       {"/rest/mempool/contents", rest_mempool_contents},
     573             :       {"/rest/headers/", rest_headers},
     574             :       {"/rest/getutxos", rest_getutxos},
     575             : };
     576             : 
     577         371 : bool StartREST()
     578             : {
     579        3339 :     for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++)
     580        8904 :         RegisterHTTPHandler(uri_prefixes[i].prefix, false, uri_prefixes[i].handler);
     581         371 :     return true;
     582             : }
     583             : 
     584         378 : void InterruptREST()
     585             : {
     586         378 : }
     587             : 
     588         378 : void StopREST()
     589             : {
     590        3402 :     for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++)
     591        6048 :         UnregisterHTTPHandler(uri_prefixes[i].prefix, false);
     592         378 : }

Generated by: LCOV version 1.14