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 #if defined(HAVE_CONFIG_H) 7 #include <config/bitcoin-config.h> 8 #endif 9 10 #include <rest.h> 11 12 #include <blockfilter.h> 13 #include <chain.h> 14 #include <chainparams.h> 15 #include <core_io.h> 16 #include <flatfile.h> 17 #include <httpserver.h> 18 #include <index/blockfilterindex.h> 19 #include <index/txindex.h> 20 #include <node/blockstorage.h> 21 #include <node/context.h> 22 #include <primitives/block.h> 23 #include <primitives/transaction.h> 24 #include <rpc/blockchain.h> 25 #include <rpc/mempool.h> 26 #include <rpc/protocol.h> 27 #include <rpc/server.h> 28 #include <rpc/server_util.h> 29 #include <streams.h> 30 #include <sync.h> 31 #include <txmempool.h> 32 #include <util/any.h> 33 #include <util/check.h> 34 #include <util/strencodings.h> 35 #include <validation.h> 36 37 #include <any> 38 #include <vector> 39 40 #include <univalue.h> 41 42 using node::GetTransaction; 43 using node::NodeContext; 44 45 static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once 46 static constexpr unsigned int MAX_REST_HEADERS_RESULTS = 2000; 47 48 static const struct { 49 RESTResponseFormat rf; 50 const char* name; 51 } rf_names[] = { 52 {RESTResponseFormat::UNDEF, ""}, 53 {RESTResponseFormat::BINARY, "bin"}, 54 {RESTResponseFormat::HEX, "hex"}, 55 {RESTResponseFormat::JSON, "json"}, 56 }; 57 58 struct CCoin { 59 uint32_t nHeight; 60 CTxOut out; 61 62 CCoin() : nHeight(0) {} 63 explicit CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {} 64 65 SERIALIZE_METHODS(CCoin, obj) 66 { 67 uint32_t nTxVerDummy = 0; 68 READWRITE(nTxVerDummy, obj.nHeight, obj.out); 69 } 70 }; 71 72 static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message) 73 { 74 req->WriteHeader("Content-Type", "text/plain"); 75 req->WriteReply(status, message + "\r\n"); 76 return false; 77 } 78 79 /** 80 * Get the node context. 81 * 82 * @param[in] req The HTTP request, whose status code will be set if node 83 * context is not found. 84 * @returns Pointer to the node context or nullptr if not found. 85 */ 86 static NodeContext* GetNodeContext(const std::any& context, HTTPRequest* req) 87 { 88 auto node_context = util::AnyPtr<NodeContext>(context); 89 if (!node_context) { 90 RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, 91 strprintf("%s:%d (%s)\n" 92 "Internal bug detected: Node context not found!\n" 93 "You may report this issue here: %s\n", 94 __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT)); 95 return nullptr; 96 } 97 return node_context; 98 } 99 100 /** 101 * Get the node context mempool. 102 * 103 * @param[in] req The HTTP request, whose status code will be set if node 104 * context mempool is not found. 105 * @returns Pointer to the mempool or nullptr if no mempool found. 106 */ 107 static CTxMemPool* GetMemPool(const std::any& context, HTTPRequest* req) 108 { 109 auto node_context = util::AnyPtr<NodeContext>(context); 110 if (!node_context || !node_context->mempool) { 111 RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found"); 112 return nullptr; 113 } 114 return node_context->mempool.get(); 115 } 116 117 /** 118 * Get the node context chainstatemanager. 119 * 120 * @param[in] req The HTTP request, whose status code will be set if node 121 * context chainstatemanager is not found. 122 * @returns Pointer to the chainstatemanager or nullptr if none found. 123 */ 124 static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req) 125 { 126 auto node_context = util::AnyPtr<NodeContext>(context); 127 if (!node_context || !node_context->chainman) { 128 RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, 129 strprintf("%s:%d (%s)\n" 130 "Internal bug detected: Chainman disabled or instance not found!\n" 131 "You may report this issue here: %s\n", 132 __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT)); 133 return nullptr; 134 } 135 return node_context->chainman.get(); 136 } 137 138 RESTResponseFormat ParseDataFormat(std::string& param, const std::string& strReq) 139 { 140 // Remove query string (if any, separated with '?') as it should not interfere with 141 // parsing param and data format 142 param = strReq.substr(0, strReq.rfind('?')); 143 const std::string::size_type pos_format{param.rfind('.')}; 144 145 // No format string is found 146 if (pos_format == std::string::npos) { 147 return rf_names[0].rf; 148 } 149 150 // Match format string to available formats 151 const std::string suffix(param, pos_format + 1); 152 for (const auto& rf_name : rf_names) { 153 if (suffix == rf_name.name) { 154 param.erase(pos_format); 155 return rf_name.rf; 156 } 157 } 158 159 // If no suffix is found, return RESTResponseFormat::UNDEF and original string without query string 160 return rf_names[0].rf; 161 } 162 163 static std::string AvailableDataFormatsString() 164 { 165 std::string formats; 166 for (const auto& rf_name : rf_names) { 167 if (strlen(rf_name.name) > 0) { 168 formats.append("."); 169 formats.append(rf_name.name); 170 formats.append(", "); 171 } 172 } 173 174 if (formats.length() > 0) 175 return formats.substr(0, formats.length() - 2); 176 177 return formats; 178 } 179 180 static bool CheckWarmup(HTTPRequest* req) 181 { 182 std::string statusmessage; 183 if (RPCIsInWarmup(&statusmessage)) 184 return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage); 185 return true; 186 } 187 188 static bool rest_headers(const std::any& context, 189 HTTPRequest* req, 190 const std::string& strURIPart) 191 { 192 if (!CheckWarmup(req)) 193 return false; 194 std::string param; 195 const RESTResponseFormat rf = ParseDataFormat(param, strURIPart); 196 std::vector<std::string> path = SplitString(param, '/'); 197 198 std::string raw_count; 199 std::string hashStr; 200 if (path.size() == 2) { 201 // deprecated path: /rest/headers/<count>/<hash> 202 hashStr = path[1]; 203 raw_count = path[0]; 204 } else if (path.size() == 1) { 205 // new path with query parameter: /rest/headers/<hash>?count=<count> 206 hashStr = path[0]; 207 try { 208 raw_count = req->GetQueryParameter("count").value_or("5"); 209 } catch (const std::runtime_error& e) { 210 return RESTERR(req, HTTP_BAD_REQUEST, e.what()); 211 } 212 } else { 213 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/headers/<hash>.<ext>?count=<count>"); 214 } 215 216 const auto parsed_count{ToIntegral<size_t>(raw_count)}; 217 if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) { 218 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)); 219 } 220 221 uint256 hash; 222 if (!ParseHashStr(hashStr, hash)) 223 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); 224 225 const CBlockIndex* tip = nullptr; 226 std::vector<const CBlockIndex*> headers; 227 headers.reserve(*parsed_count); 228 { 229 ChainstateManager* maybe_chainman = GetChainman(context, req); 230 if (!maybe_chainman) return false; 231 ChainstateManager& chainman = *maybe_chainman; 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 std::string binaryHeader = ssHeader.str(); 253 req->WriteHeader("Content-Type", "application/octet-stream"); 254 req->WriteReply(HTTP_OK, binaryHeader); 255 return true; 256 } 257 258 case RESTResponseFormat::HEX: { 259 DataStream ssHeader{}; 260 for (const CBlockIndex *pindex : headers) { 261 ssHeader << pindex->GetBlockHeader(); 262 } 263 264 std::string strHex = HexStr(ssHeader) + "\n"; 265 req->WriteHeader("Content-Type", "text/plain"); 266 req->WriteReply(HTTP_OK, strHex); 267 return true; 268 } 269 case RESTResponseFormat::JSON: { 270 UniValue jsonHeaders(UniValue::VARR); 271 for (const CBlockIndex *pindex : headers) { 272 jsonHeaders.push_back(blockheaderToJSON(*tip, *pindex)); 273 } 274 std::string strJSON = jsonHeaders.write() + "\n"; 275 req->WriteHeader("Content-Type", "application/json"); 276 req->WriteReply(HTTP_OK, strJSON); 277 return true; 278 } 279 default: { 280 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); 281 } 282 } 283 } 284 285 static bool rest_block(const std::any& context, 286 HTTPRequest* req, 287 const std::string& strURIPart, 288 TxVerbosity tx_verbosity) 289 { 290 if (!CheckWarmup(req)) 291 return false; 292 std::string hashStr; 293 const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart); 294 295 uint256 hash; 296 if (!ParseHashStr(hashStr, hash)) 297 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); 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 (chainman.m_blockman.IsBlockPruned(*pblockindex)) { 313 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)"); 314 } 315 pos = pblockindex->GetBlockPos(); 316 } 317 318 std::vector<uint8_t> block_data{}; 319 if (!chainman.m_blockman.ReadRawBlockFromDisk(block_data, pos)) { 320 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); 321 } 322 323 switch (rf) { 324 case RESTResponseFormat::BINARY: { 325 const std::string binaryBlock{block_data.begin(), block_data.end()}; 326 req->WriteHeader("Content-Type", "application/octet-stream"); 327 req->WriteReply(HTTP_OK, binaryBlock); 328 return true; 329 } 330 331 case RESTResponseFormat::HEX: { 332 const std::string strHex{HexStr(block_data) + "\n"}; 333 req->WriteHeader("Content-Type", "text/plain"); 334 req->WriteReply(HTTP_OK, strHex); 335 return true; 336 } 337 338 case RESTResponseFormat::JSON: { 339 CBlock block{}; 340 DataStream block_stream{block_data}; 341 block_stream >> TX_WITH_WITNESS(block); 342 UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity); 343 std::string strJSON = objBlock.write() + "\n"; 344 req->WriteHeader("Content-Type", "application/json"); 345 req->WriteReply(HTTP_OK, strJSON); 346 return true; 347 } 348 349 default: { 350 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); 351 } 352 } 353 } 354 355 static bool rest_block_extended(const std::any& context, HTTPRequest* req, const std::string& strURIPart) 356 { 357 return rest_block(context, req, strURIPart, TxVerbosity::SHOW_DETAILS_AND_PREVOUT); 358 } 359 360 static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, const std::string& strURIPart) 361 { 362 return rest_block(context, req, strURIPart, TxVerbosity::SHOW_TXID); 363 } 364 365 static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& strURIPart) 366 { 367 if (!CheckWarmup(req)) return false; 368 369 std::string param; 370 const RESTResponseFormat rf = ParseDataFormat(param, strURIPart); 371 372 std::vector<std::string> uri_parts = SplitString(param, '/'); 373 std::string raw_count; 374 std::string raw_blockhash; 375 if (uri_parts.size() == 3) { 376 // deprecated path: /rest/blockfilterheaders/<filtertype>/<count>/<blockhash> 377 raw_blockhash = uri_parts[2]; 378 raw_count = uri_parts[1]; 379 } else if (uri_parts.size() == 2) { 380 // new path with query parameter: /rest/blockfilterheaders/<filtertype>/<blockhash>?count=<count> 381 raw_blockhash = uri_parts[1]; 382 try { 383 raw_count = req->GetQueryParameter("count").value_or("5"); 384 } catch (const std::runtime_error& e) { 385 return RESTERR(req, HTTP_BAD_REQUEST, e.what()); 386 } 387 } else { 388 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<blockhash>.<ext>?count=<count>"); 389 } 390 391 const auto parsed_count{ToIntegral<size_t>(raw_count)}; 392 if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) { 393 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)); 394 } 395 396 uint256 block_hash; 397 if (!ParseHashStr(raw_blockhash, block_hash)) { 398 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + raw_blockhash); 399 } 400 401 BlockFilterType filtertype; 402 if (!BlockFilterTypeByName(uri_parts[0], filtertype)) { 403 return RESTERR(req, HTTP_BAD_REQUEST, "Unknown filtertype " + uri_parts[0]); 404 } 405 406 BlockFilterIndex* index = GetBlockFilterIndex(filtertype); 407 if (!index) { 408 return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]); 409 } 410 411 std::vector<const CBlockIndex*> headers; 412 headers.reserve(*parsed_count); 413 { 414 ChainstateManager* maybe_chainman = GetChainman(context, req); 415 if (!maybe_chainman) return false; 416 ChainstateManager& chainman = *maybe_chainman; 417 LOCK(cs_main); 418 CChain& active_chain = chainman.ActiveChain(); 419 const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block_hash); 420 while (pindex != nullptr && active_chain.Contains(pindex)) { 421 headers.push_back(pindex); 422 if (headers.size() == *parsed_count) 423 break; 424 pindex = active_chain.Next(pindex); 425 } 426 } 427 428 bool index_ready = index->BlockUntilSyncedToCurrentChain(); 429 430 std::vector<uint256> filter_headers; 431 filter_headers.reserve(*parsed_count); 432 for (const CBlockIndex* pindex : headers) { 433 uint256 filter_header; 434 if (!index->LookupFilterHeader(pindex, filter_header)) { 435 std::string errmsg = "Filter not found."; 436 437 if (!index_ready) { 438 errmsg += " Block filters are still in the process of being indexed."; 439 } else { 440 errmsg += " This error is unexpected and indicates index corruption."; 441 } 442 443 return RESTERR(req, HTTP_NOT_FOUND, errmsg); 444 } 445 filter_headers.push_back(filter_header); 446 } 447 448 switch (rf) { 449 case RESTResponseFormat::BINARY: { 450 DataStream ssHeader{}; 451 for (const uint256& header : filter_headers) { 452 ssHeader << header; 453 } 454 455 std::string binaryHeader = ssHeader.str(); 456 req->WriteHeader("Content-Type", "application/octet-stream"); 457 req->WriteReply(HTTP_OK, binaryHeader); 458 return true; 459 } 460 case RESTResponseFormat::HEX: { 461 DataStream ssHeader{}; 462 for (const uint256& header : filter_headers) { 463 ssHeader << header; 464 } 465 466 std::string strHex = HexStr(ssHeader) + "\n"; 467 req->WriteHeader("Content-Type", "text/plain"); 468 req->WriteReply(HTTP_OK, strHex); 469 return true; 470 } 471 case RESTResponseFormat::JSON: { 472 UniValue jsonHeaders(UniValue::VARR); 473 for (const uint256& header : filter_headers) { 474 jsonHeaders.push_back(header.GetHex()); 475 } 476 477 std::string strJSON = jsonHeaders.write() + "\n"; 478 req->WriteHeader("Content-Type", "application/json"); 479 req->WriteReply(HTTP_OK, strJSON); 480 return true; 481 } 482 default: { 483 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); 484 } 485 } 486 } 487 488 static bool rest_block_filter(const std::any& context, HTTPRequest* req, const std::string& strURIPart) 489 { 490 if (!CheckWarmup(req)) return false; 491 492 std::string param; 493 const RESTResponseFormat rf = ParseDataFormat(param, strURIPart); 494 495 // request is sent over URI scheme /rest/blockfilter/filtertype/blockhash 496 std::vector<std::string> uri_parts = SplitString(param, '/'); 497 if (uri_parts.size() != 2) { 498 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilter/<filtertype>/<blockhash>"); 499 } 500 501 uint256 block_hash; 502 if (!ParseHashStr(uri_parts[1], block_hash)) { 503 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + uri_parts[1]); 504 } 505 506 BlockFilterType filtertype; 507 if (!BlockFilterTypeByName(uri_parts[0], filtertype)) { 508 return RESTERR(req, HTTP_BAD_REQUEST, "Unknown filtertype " + uri_parts[0]); 509 } 510 511 BlockFilterIndex* index = GetBlockFilterIndex(filtertype); 512 if (!index) { 513 return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]); 514 } 515 516 const CBlockIndex* block_index; 517 bool block_was_connected; 518 { 519 ChainstateManager* maybe_chainman = GetChainman(context, req); 520 if (!maybe_chainman) return false; 521 ChainstateManager& chainman = *maybe_chainman; 522 LOCK(cs_main); 523 block_index = chainman.m_blockman.LookupBlockIndex(block_hash); 524 if (!block_index) { 525 return RESTERR(req, HTTP_NOT_FOUND, uri_parts[1] + " not found"); 526 } 527 block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS); 528 } 529 530 bool index_ready = index->BlockUntilSyncedToCurrentChain(); 531 532 BlockFilter filter; 533 if (!index->LookupFilter(block_index, filter)) { 534 std::string errmsg = "Filter not found."; 535 536 if (!block_was_connected) { 537 errmsg += " Block was not connected to active chain."; 538 } else if (!index_ready) { 539 errmsg += " Block filters are still in the process of being indexed."; 540 } else { 541 errmsg += " This error is unexpected and indicates index corruption."; 542 } 543 544 return RESTERR(req, HTTP_NOT_FOUND, errmsg); 545 } 546 547 switch (rf) { 548 case RESTResponseFormat::BINARY: { 549 DataStream ssResp{}; 550 ssResp << filter; 551 552 std::string binaryResp = ssResp.str(); 553 req->WriteHeader("Content-Type", "application/octet-stream"); 554 req->WriteReply(HTTP_OK, binaryResp); 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 uint256 hash; 625 if (!ParseHashStr(hash_str, 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(ParseHashV(hash_str, "blockhash")))) { 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 uint256 hash; 713 if (!ParseHashStr(hashStr, hash)) 714 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); 715 716 if (g_txindex) { 717 g_txindex->BlockUntilSyncedToCurrentChain(); 718 } 719 720 const NodeContext* const node = GetNodeContext(context, req); 721 if (!node) return false; 722 uint256 hashBlock = uint256(); 723 const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, node->mempool.get(), hash, hashBlock, node->chainman->m_blockman); 724 if (!tx) { 725 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); 726 } 727 728 switch (rf) { 729 case RESTResponseFormat::BINARY: { 730 DataStream ssTx; 731 ssTx << TX_WITH_WITNESS(tx); 732 733 std::string binaryTx = ssTx.str(); 734 req->WriteHeader("Content-Type", "application/octet-stream"); 735 req->WriteReply(HTTP_OK, binaryTx); 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 int32_t nOutput; 798 std::string strTxid = uriParts[i].substr(0, uriParts[i].find('-')); 799 std::string strOutput = uriParts[i].substr(uriParts[i].find('-')+1); 800 801 if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid)) 802 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error"); 803 804 vOutPoints.emplace_back(TxidFromString(strTxid), (uint32_t)nOutput); 805 } 806 807 if (vOutPoints.size() > 0) 808 fInputParsed = true; 809 else 810 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); 811 } 812 813 switch (rf) { 814 case RESTResponseFormat::HEX: { 815 // convert hex to bin, continue then with bin part 816 std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable); 817 strRequestMutable.assign(strRequestV.begin(), strRequestV.end()); 818 [[fallthrough]]; 819 } 820 821 case RESTResponseFormat::BINARY: { 822 try { 823 //deserialize only if user sent a request 824 if (strRequestMutable.size() > 0) 825 { 826 if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA 827 return RESTERR(req, HTTP_BAD_REQUEST, "Combination of URI scheme inputs and raw post data is not allowed"); 828 829 DataStream oss{}; 830 oss << strRequestMutable; 831 oss >> fCheckMemPool; 832 oss >> vOutPoints; 833 } 834 } catch (const std::ios_base::failure&) { 835 // abort in case of unreadable binary data 836 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error"); 837 } 838 break; 839 } 840 841 case RESTResponseFormat::JSON: { 842 if (!fInputParsed) 843 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); 844 break; 845 } 846 default: { 847 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); 848 } 849 } 850 851 // limit max outpoints 852 if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS) 853 return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size())); 854 855 // check spentness and form a bitmap (as well as a JSON capable human-readable string representation) 856 std::vector<unsigned char> bitmap; 857 std::vector<CCoin> outs; 858 std::string bitmapStringRepresentation; 859 std::vector<bool> hits; 860 bitmap.resize((vOutPoints.size() + 7) / 8); 861 ChainstateManager* maybe_chainman = GetChainman(context, req); 862 if (!maybe_chainman) return false; 863 ChainstateManager& chainman = *maybe_chainman; 864 decltype(chainman.ActiveHeight()) active_height; 865 uint256 active_hash; 866 { 867 auto process_utxos = [&vOutPoints, &outs, &hits, &active_height, &active_hash, &chainman](const CCoinsView& view, const CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(chainman.GetMutex()) { 868 for (const COutPoint& vOutPoint : vOutPoints) { 869 Coin coin; 870 bool hit = (!mempool || !mempool->isSpent(vOutPoint)) && view.GetCoin(vOutPoint, coin); 871 hits.push_back(hit); 872 if (hit) outs.emplace_back(std::move(coin)); 873 } 874 active_height = chainman.ActiveHeight(); 875 active_hash = chainman.ActiveTip()->GetBlockHash(); 876 }; 877 878 if (fCheckMemPool) { 879 const CTxMemPool* mempool = GetMemPool(context, req); 880 if (!mempool) return false; 881 // use db+mempool as cache backend in case user likes to query mempool 882 LOCK2(cs_main, mempool->cs); 883 CCoinsViewCache& viewChain = chainman.ActiveChainstate().CoinsTip(); 884 CCoinsViewMemPool viewMempool(&viewChain, *mempool); 885 process_utxos(viewMempool, mempool); 886 } else { 887 LOCK(cs_main); 888 process_utxos(chainman.ActiveChainstate().CoinsTip(), nullptr); 889 } 890 891 for (size_t i = 0; i < hits.size(); ++i) { 892 const bool hit = hits[i]; 893 bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output) 894 bitmap[i / 8] |= ((uint8_t)hit) << (i % 8); 895 } 896 } 897 898 switch (rf) { 899 case RESTResponseFormat::BINARY: { 900 // serialize data 901 // use exact same output as mentioned in Bip64 902 DataStream ssGetUTXOResponse{}; 903 ssGetUTXOResponse << active_height << active_hash << bitmap << outs; 904 std::string ssGetUTXOResponseString = ssGetUTXOResponse.str(); 905 906 req->WriteHeader("Content-Type", "application/octet-stream"); 907 req->WriteReply(HTTP_OK, ssGetUTXOResponseString); 908 return true; 909 } 910 911 case RESTResponseFormat::HEX: { 912 DataStream ssGetUTXOResponse{}; 913 ssGetUTXOResponse << active_height << active_hash << bitmap << outs; 914 std::string strHex = HexStr(ssGetUTXOResponse) + "\n"; 915 916 req->WriteHeader("Content-Type", "text/plain"); 917 req->WriteReply(HTTP_OK, strHex); 918 return true; 919 } 920 921 case RESTResponseFormat::JSON: { 922 UniValue objGetUTXOResponse(UniValue::VOBJ); 923 924 // pack in some essentials 925 // use more or less the same output as mentioned in Bip64 926 objGetUTXOResponse.pushKV("chainHeight", active_height); 927 objGetUTXOResponse.pushKV("chaintipHash", active_hash.GetHex()); 928 objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation); 929 930 UniValue utxos(UniValue::VARR); 931 for (const CCoin& coin : outs) { 932 UniValue utxo(UniValue::VOBJ); 933 utxo.pushKV("height", (int32_t)coin.nHeight); 934 utxo.pushKV("value", ValueFromAmount(coin.out.nValue)); 935 936 // include the script in a json output 937 UniValue o(UniValue::VOBJ); 938 ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true); 939 utxo.pushKV("scriptPubKey", o); 940 utxos.push_back(utxo); 941 } 942 objGetUTXOResponse.pushKV("utxos", utxos); 943 944 // return json string 945 std::string strJSON = objGetUTXOResponse.write() + "\n"; 946 req->WriteHeader("Content-Type", "application/json"); 947 req->WriteReply(HTTP_OK, strJSON); 948 return true; 949 } 950 default: { 951 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); 952 } 953 } 954 } 955 956 static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req, 957 const std::string& str_uri_part) 958 { 959 if (!CheckWarmup(req)) return false; 960 std::string height_str; 961 const RESTResponseFormat rf = ParseDataFormat(height_str, str_uri_part); 962 963 int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785 964 if (!ParseInt32(height_str, &blockheight) || blockheight < 0) { 965 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str)); 966 } 967 968 CBlockIndex* pblockindex = nullptr; 969 { 970 ChainstateManager* maybe_chainman = GetChainman(context, req); 971 if (!maybe_chainman) return false; 972 ChainstateManager& chainman = *maybe_chainman; 973 LOCK(cs_main); 974 const CChain& active_chain = chainman.ActiveChain(); 975 if (blockheight > active_chain.Height()) { 976 return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range"); 977 } 978 pblockindex = active_chain[blockheight]; 979 } 980 switch (rf) { 981 case RESTResponseFormat::BINARY: { 982 DataStream ss_blockhash{}; 983 ss_blockhash << pblockindex->GetBlockHash(); 984 req->WriteHeader("Content-Type", "application/octet-stream"); 985 req->WriteReply(HTTP_OK, ss_blockhash.str()); 986 return true; 987 } 988 case RESTResponseFormat::HEX: { 989 req->WriteHeader("Content-Type", "text/plain"); 990 req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n"); 991 return true; 992 } 993 case RESTResponseFormat::JSON: { 994 req->WriteHeader("Content-Type", "application/json"); 995 UniValue resp = UniValue(UniValue::VOBJ); 996 resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex()); 997 req->WriteReply(HTTP_OK, resp.write() + "\n"); 998 return true; 999 } 1000 default: { 1001 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); 1002 } 1003 } 1004 } 1005 1006 static const struct { 1007 const char* prefix; 1008 bool (*handler)(const std::any& context, HTTPRequest* req, const std::string& strReq); 1009 } uri_prefixes[] = { 1010 {"/rest/tx/", rest_tx}, 1011 {"/rest/block/notxdetails/", rest_block_notxdetails}, 1012 {"/rest/block/", rest_block_extended}, 1013 {"/rest/blockfilter/", rest_block_filter}, 1014 {"/rest/blockfilterheaders/", rest_filter_header}, 1015 {"/rest/chaininfo", rest_chaininfo}, 1016 {"/rest/mempool/", rest_mempool}, 1017 {"/rest/headers/", rest_headers}, 1018 {"/rest/getutxos", rest_getutxos}, 1019 {"/rest/deploymentinfo/", rest_deploymentinfo}, 1020 {"/rest/deploymentinfo", rest_deploymentinfo}, 1021 {"/rest/blockhashbyheight/", rest_blockhash_by_height}, 1022 }; 1023 1024 void StartREST(const std::any& context) 1025 { 1026 for (const auto& up : uri_prefixes) { 1027 auto handler = [context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); }; 1028 RegisterHTTPHandler(up.prefix, false, handler); 1029 } 1030 } 1031 1032 void InterruptREST() 1033 { 1034 } 1035 1036 void StopREST() 1037 { 1038 for (const auto& up : uri_prefixes) { 1039 UnregisterHTTPHandler(up.prefix, false); 1040 } 1041 }