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