Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251while (pindex != nullptr && active_chain.Contains(*pindex)) {252headers.push_back(pindex);253if (headers.size() == *parsed_count) {254break;255}256pindex = active_chain.Next(*pindex);257}258}260switch (rf) {261case RESTResponseFormat::BINARY: {262DataStream ssHeader{};263for (const CBlockIndex *pindex : headers) {264ssHeader << pindex->GetBlockHeader();265}267// Do not cache because chain extensions and reorgs can affect the response.268req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);269req->WriteHeader("Content-Type", "application/octet-stream");270req->WriteReply(HTTP_OK, ssHeader);271return true;272}274case RESTResponseFormat::HEX: {275DataStream ssHeader{};276for (const CBlockIndex *pindex : headers) {277ssHeader << pindex->GetBlockHeader();278}280std::string strHex = HexStr(ssHeader) + "\n";281req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);282req->WriteHeader("Content-Type", "text/plain");283req->WriteReply(HTTP_OK, strHex);284return true;285}286case RESTResponseFormat::JSON: {287UniValue jsonHeaders(UniValue::VARR);288for (const CBlockIndex *pindex : headers) {289jsonHeaders.push_back(blockheaderToJSON(*tip, *pindex, chainman.GetConsensus().powLimit));290}291std::string strJSON = jsonHeaders.write() + "\n";292req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);293req->WriteHeader("Content-Type", "application/json");294req->WriteReply(HTTP_OK, strJSON);295return true;296}297default: {298return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");299}300}301}303/**304* Serialize spent outputs as a list of per-transaction CTxOut lists using binary format.305*/306static void SerializeBlockUndo(DataStream& stream, const CBlockUndo& block_undo)307{308WriteCompactSize(stream, block_undo.vtxundo.size() + 1);309WriteCompactSize(stream, 0); // block_undo.vtxundo doesn't contain coinbase tx310for (const CTxUndo& tx_undo : block_undo.vtxundo) {311WriteCompactSize(stream, tx_undo.vprevout.size());312for (const Coin& coin : tx_undo.vprevout) {313coin.out.Serialize(stream);314}315}316}318/**319* Serialize spent outputs as a list of per-transaction CTxOut lists using JSON format.320*/321static void BlockUndoToJSON(const CBlockUndo& block_undo, UniValue& result)322{323result.push_back({UniValue::VARR}); // block_undo.vtxundo doesn't contain coinbase tx324for (const CTxUndo& tx_undo : block_undo.vtxundo) {325UniValue tx_prevouts(UniValue::VARR);326for (const Coin& coin : tx_undo.vprevout) {327UniValue prevout(UniValue::VOBJ);328prevout.pushKV("value", ValueFromAmount(coin.out.nValue));330UniValue script_pub_key(UniValue::VOBJ);331ScriptToUniv(coin.out.scriptPubKey, /*out=*/script_pub_key, /*include_hex=*/true, /*include_address=*/true);332prevout.pushKV("scriptPubKey", std::move(script_pub_key));334tx_prevouts.push_back(std::move(prevout));335}336result.push_back(std::move(tx_prevouts));337}338}340static bool rest_spent_txouts(const std::any& context, HTTPRequest* req, const std::string& uri_part)341{342if (!CheckWarmup(req)) {343return false;344}345std::string param;346const RESTResponseFormat rf = ParseDataFormat(param, uri_part);347std::vector<std::string> path = SplitString(param, '/');349std::string hashStr;350if (path.size() == 1) {351// path with query parameter: /rest/spenttxouts/<hash>352hashStr = path[0];353} else {354return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/spenttxouts/<hash>.<ext>");355}357auto hash{uint256::FromHex(hashStr)};358if (!hash) {359return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);360}362ChainstateManager* chainman = GetChainman(context, req);363if (!chainman) {364return false;365}367const CBlockIndex* pblockindex = WITH_LOCK(cs_main, return chainman->m_blockman.LookupBlockIndex(*hash));368if (!pblockindex) {369return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");370}372CBlockUndo block_undo;373if (pblockindex->nHeight > 0 && !chainman->m_blockman.ReadBlockUndo(block_undo, *pblockindex)) {374return RESTERR(req, HTTP_NOT_FOUND, hashStr + " undo not available");375}377switch (rf) {378case RESTResponseFormat::BINARY: {379DataStream ssSpentResponse{};380SerializeBlockUndo(ssSpentResponse, block_undo);381req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);382req->WriteHeader("Content-Type", "application/octet-stream");383req->WriteReply(HTTP_OK, ssSpentResponse);384return true;385}387case RESTResponseFormat::HEX: {388DataStream ssSpentResponse{};389SerializeBlockUndo(ssSpentResponse, block_undo);390const std::string strHex{HexStr(ssSpentResponse) + "\n"};391req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);392req->WriteHeader("Content-Type", "text/plain");393req->WriteReply(HTTP_OK, strHex);394return true;395}397case RESTResponseFormat::JSON: {398UniValue result(UniValue::VARR);399BlockUndoToJSON(block_undo, result);400std::string strJSON = result.write() + "\n";401req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);402req->WriteHeader("Content-Type", "application/json");403req->WriteReply(HTTP_OK, strJSON);404return true;405}407default: {408return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");409}410}411}413/**414* This handler is used by multiple HTTP endpoints:415* - `/block/` via `rest_block_extended()`416* - `/block/notxdetails/` via `rest_block_notxdetails()`417* - `/blockpart/` via `rest_block_part()` (doesn't support JSON response, so `tx_verbosity` is unset)418*/419static bool rest_block(const std::any& context,420HTTPRequest* req,421const std::string& uri_part,422std::optional<TxVerbosity> tx_verbosity,423std::optional<std::pair<size_t, size_t>> block_part = std::nullopt)424{425if (!CheckWarmup(req))426return false;427std::string hashStr;428const RESTResponseFormat rf = ParseDataFormat(hashStr, uri_part);430auto hash{uint256::FromHex(hashStr)};431if (!hash) {432return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);433}435FlatFilePos pos{};436const CBlockIndex* pblockindex = nullptr;437const CBlockIndex* tip = nullptr;438ChainstateManager* maybe_chainman = GetChainman(context, req);439if (!maybe_chainman) return false;440ChainstateManager& chainman = *maybe_chainman;441{442LOCK(cs_main);443tip = chainman.ActiveChain().Tip();444pblockindex = chainman.m_blockman.LookupBlockIndex(*hash);445if (!pblockindex) {446return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");447}448if (!(pblockindex->nStatus & BLOCK_HAVE_DATA)) {449if (chainman.m_blockman.IsBlockPruned(*pblockindex)) {450return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");451}452return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (not fully downloaded)");453}454pos = pblockindex->GetBlockPos();455}457const auto block_data{chainman.m_blockman.ReadRawBlock(pos, block_part)};458if (!block_data) {459switch (block_data.error()) {460case node::ReadRawError::IO: return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "I/O error reading " + hashStr);461case node::ReadRawError::BadPartRange:462assert(block_part);463return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Bad block part offset/size %d/%d for %s", block_part->first, block_part->second, hashStr));464} // no default case, so the compiler can warn about missing cases465assert(false);466}468switch (rf) {469case RESTResponseFormat::BINARY: {470req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);471req->WriteHeader("Content-Type", "application/octet-stream");472req->WriteReply(HTTP_OK, *block_data);473return true;474}476case RESTResponseFormat::HEX: {477const std::string strHex{HexStr(*block_data) + "\n"};478req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);479req->WriteHeader("Content-Type", "text/plain");480req->WriteReply(HTTP_OK, strHex);481return true;482}484case RESTResponseFormat::JSON: {485if (tx_verbosity) {486CBlock block{};487SpanReader{*block_data} >> TX_WITH_WITNESS(block);488UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, *tx_verbosity, chainman.GetConsensus().powLimit);489std::string strJSON = objBlock.write() + "\n";490req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);491req->WriteHeader("Content-Type", "application/json");492req->WriteReply(HTTP_OK, strJSON);493return true;494}495return RESTERR(req, HTTP_BAD_REQUEST, "JSON output is not supported for this request type");496}498default: {499return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");500}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.