* This handler is used by multiple HTTP endpoints: * - `/block/` via `rest_block_extended()` * - `/block/notxdetails/` via `rest_block_notxdetails()` * - `/blockpart/` via `rest_block_part()` (doesn't support JSON response, so `tx_verbosity` is unset) */
| 388 | * - `/blockpart/` via `rest_block_part()` (doesn't support JSON response, so `tx_verbosity` is unset) |
| 389 | */ |
| 390 | static bool rest_block(const std::any& context, |
| 391 | HTTPRequest* req, |
| 392 | const std::string& uri_part, |
| 393 | std::optional<TxVerbosity> tx_verbosity, |
| 394 | std::optional<std::pair<size_t, size_t>> block_part = std::nullopt) |
| 395 | { |
| 396 | if (!CheckWarmup(req)) |
| 397 | return false; |
| 398 | std::string hashStr; |
| 399 | const RESTResponseFormat rf = ParseDataFormat(hashStr, uri_part); |
| 400 | |
| 401 | auto hash{uint256::FromHex(hashStr)}; |
| 402 | if (!hash) { |
| 403 | return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); |
| 404 | } |
| 405 | |
| 406 | FlatFilePos pos{}; |
| 407 | const CBlockIndex* pblockindex = nullptr; |
| 408 | const CBlockIndex* tip = nullptr; |
| 409 | ChainstateManager* maybe_chainman = GetChainman(context, req); |
| 410 | if (!maybe_chainman) return false; |
| 411 | ChainstateManager& chainman = *maybe_chainman; |
| 412 | { |
| 413 | LOCK(cs_main); |
| 414 | tip = chainman.ActiveChain().Tip(); |
| 415 | pblockindex = chainman.m_blockman.LookupBlockIndex(*hash); |
| 416 | if (!pblockindex) { |
| 417 | return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); |
| 418 | } |
| 419 | if (!(pblockindex->nStatus & BLOCK_HAVE_DATA)) { |
| 420 | if (chainman.m_blockman.IsBlockPruned(*pblockindex)) { |
| 421 | return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)"); |
| 422 | } |
| 423 | return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (not fully downloaded)"); |
| 424 | } |
| 425 | pos = pblockindex->GetBlockPos(); |
| 426 | } |
| 427 | |
| 428 | const auto block_data{chainman.m_blockman.ReadRawBlock(pos, block_part)}; |
| 429 | if (!block_data) { |
| 430 | switch (block_data.error()) { |
| 431 | case node::ReadRawError::IO: return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "I/O error reading " + hashStr); |
| 432 | case node::ReadRawError::BadPartRange: |
| 433 | assert(block_part); |
| 434 | return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Bad block part offset/size %d/%d for %s", block_part->first, block_part->second, hashStr)); |
| 435 | } // no default case, so the compiler can warn about missing cases |
| 436 | assert(false); |
| 437 | } |
| 438 | |
| 439 | switch (rf) { |
| 440 | case RESTResponseFormat::BINARY: { |
| 441 | req->WriteHeader("Content-Type", "application/octet-stream"); |
| 442 | req->WriteReply(HTTP_OK, *block_data); |
| 443 | return true; |
| 444 | } |
| 445 | |
| 446 | case RESTResponseFormat::HEX: { |
| 447 | const std::string strHex{HexStr(*block_data) + "\n"}; |
no test coverage detected