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