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