Inside Bitcoin
Opening the selected file…
Inside Bitcoin
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.
752if (auto data{blockman.ReadRawBlock(pos)}) return std::move(*data);753// Block not found on disk. This shouldn't normally happen unless the block was754// pruned right after we released the lock above.755throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");756}758static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex& blockindex)759{760CBlockUndo blockUndo;762// The Genesis block does not have undo data763if (blockindex.nHeight == 0) return blockUndo;765{766LOCK(cs_main);767CheckBlockDataAvailability(blockman, blockindex, /*check_for_undo=*/true);768}770if (!blockman.ReadBlockUndo(blockUndo, blockindex)) {771throw JSONRPCError(RPC_MISC_ERROR, "Can't read undo data from disk");772}774return blockUndo;775}777static std::vector<RPCResult> GetBlockFields(RPCResult tx_result, std::optional<std::string> elision_msg = std::nullopt)778{779auto fields = std::vector<RPCResult>{780{RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},781{RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},782{RPCResult::Type::NUM, "size", "The block size"},783{RPCResult::Type::NUM, "strippedsize", "The block size excluding witness data"},784{RPCResult::Type::NUM, "weight", "The block weight as defined in BIP 141"},785{RPCResult::Type::OBJ, "coinbase_tx", "Coinbase transaction metadata",786{787{RPCResult::Type::NUM, "version", "The coinbase transaction version"},788{RPCResult::Type::NUM, "locktime", "The coinbase transaction's locktime (nLockTime)"},789{RPCResult::Type::NUM, "sequence", "The coinbase input's sequence number (nSequence)"},790{RPCResult::Type::STR_HEX, "coinbase", "The coinbase input's script"},791{RPCResult::Type::STR_HEX, "witness", /*optional=*/true, "The coinbase input's first (and only) witness stack element, if present"},792}},793{RPCResult::Type::NUM, "height", "The block height or index"},794{RPCResult::Type::NUM, "version", "The block version"},795{RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},796{RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},797};798fields.push_back(std::move(tx_result));799fields.emplace_back(RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME);800fields.emplace_back(RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME);801fields.emplace_back(RPCResult::Type::NUM, "nonce", "The nonce");802fields.emplace_back(RPCResult::Type::STR_HEX, "bits", "nBits: compact representation of the block difficulty target");803fields.emplace_back(RPCResult::Type::STR_HEX, "target", "The difficulty target");804fields.emplace_back(RPCResult::Type::NUM, "difficulty", "The difficulty");805fields.emplace_back(RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the chain up to this block (in hex)");806fields.emplace_back(RPCResult::Type::NUM, "nTx", "The number of transactions in the block");807fields.emplace_back(RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)");808fields.emplace_back(RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)");809if (elision_msg) {810// Elide all block-level fields except the tx array (which differs per verbosity)811std::vector<RPCResult> new_fields;812new_fields.reserve(fields.size());813bool first = true;814for (const auto& f : fields) {815if (f.m_key_name == "tx") {816new_fields.push_back(f);817continue;818}819if (first) {820RPCResultOptions eopts = f.m_opts;821eopts.print_elision = *elision_msg;822new_fields.emplace_back(f, std::move(eopts));823first = false;824} else {825RPCResultOptions eopts = f.m_opts;826eopts.print_elision = HelpElisionSkip{};827new_fields.emplace_back(f, std::move(eopts));828}829}830fields = std::move(new_fields);831}832return fields;833}835static RPCMethod getblock()836{837return RPCMethod{838"getblock",839"If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"840"If verbosity is 1, returns an Object with information about block <hash>.\n"841"If verbosity is 2, returns an Object with information about block <hash> and information about each transaction.\n"842"If verbosity is 3, returns an Object with information about block <hash> and information about each transaction, including prevout information for inputs (only for unpruned blocks in the current best chain).\n",843{844{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},845{"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a JSON object, 2 for JSON object with transaction data, and 3 for JSON object with transaction data including prevout information for inputs",846RPCArgOptions{.skip_type_check = true}},847},848{849RPCResult{"for verbosity = 0", RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},850RPCResult{"for verbosity = 1", RPCResult::Type::OBJ, "", "",851GetBlockFields({RPCResult::Type::ARR, "tx", "The transaction ids",852{{RPCResult::Type::STR_HEX, "", "The transaction id"}}})},853RPCResult{"for verbosity = 2", RPCResult::Type::OBJ, "", "",854GetBlockFields({RPCResult::Type::ARR, "tx", "",855{856{RPCResult::Type::OBJ, "", "",857TxDoc({.elision_mode = ElisionMode::WithSummary,858.elision_summary = "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result",859.fee = true, .hex = true,860.fee_doc = "The transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"})},861}}, /*elision_msg=*/"Same output as verbosity = 1")},862RPCResult{"for verbosity = 3", RPCResult::Type::OBJ, "", "",863GetBlockFields({RPCResult::Type::ARR, "tx", "",864{865{RPCResult::Type::OBJ, "", "",866TxDoc({.elision_mode = ElisionMode::Silent,867.prevout = true,868.prevout_optional = true,869.fee = true,870.hex = true,871.vin_item_doc = "",872.prevout_doc = "(Only if undo information is available)",873.vin_inner_elision = "The same output as verbosity = 2"})},874}}, /*elision_msg=*/"Same output as verbosity = 2")},875},876RPCExamples{877HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")878+ HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")879},880[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue881{882uint256 hash(ParseHashV(request.params[0], "blockhash"));884int verbosity{ParseVerbosity(request.params[1], /*default_verbosity=*/1, /*allow_bool=*/true)};886const CBlockIndex* pblockindex;887const CBlockIndex* tip;888ChainstateManager& chainman = EnsureAnyChainman(request.context);889{890LOCK(cs_main);891pblockindex = chainman.m_blockman.LookupBlockIndex(hash);892tip = chainman.ActiveChain().Tip();894if (!pblockindex) {895throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");896}897}899const std::vector<std::byte> block_data{GetRawBlockChecked(chainman.m_blockman, *pblockindex)};901if (verbosity <= 0) {902return HexStr(block_data);903}905CBlock block{};906SpanReader{block_data} >> TX_WITH_WITNESS(block);908TxVerbosity tx_verbosity;909if (verbosity == 1) {910tx_verbosity = TxVerbosity::SHOW_TXID;911} else if (verbosity == 2) {912tx_verbosity = TxVerbosity::SHOW_DETAILS;913} else {914tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT;915}917return blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity, chainman.GetConsensus().powLimit);918},919};920}922//! Return height of highest block that has been pruned, or std::nullopt if no blocks have been pruned923std::optional<int> GetPruneHeight(const BlockManager& blockman, const CChain& chain) {924AssertLockHeld(::cs_main);926// Search for the last block missing block data or undo data. Don't let the927// search consider the genesis block, because the genesis block does not928// have undo data, but should not be considered pruned.929const CBlockIndex* first_block{chain[1]};930const CBlockIndex* chain_tip{chain.Tip()};932// If there are no blocks after the genesis block, or no blocks at all, nothing is pruned.933if (!first_block || !chain_tip) return std::nullopt;935// If the chain tip is pruned, everything is pruned.936if ((chain_tip->nStatus & BLOCK_HAVE_MASK) != BLOCK_HAVE_MASK) return chain_tip->nHeight;938const auto& first_unpruned{blockman.GetFirstBlock(*chain_tip, /*status_mask=*/BLOCK_HAVE_MASK, first_block)};939if (&first_unpruned == first_block) {940// All blocks between first_block and chain_tip have data, so nothing is pruned.941return std::nullopt;942}944// Block before the first unpruned block is the last pruned block.945return CHECK_NONFATAL(first_unpruned.pprev)->nHeight;946}948static RPCMethod pruneblockchain()949{950return RPCMethod{"pruneblockchain",951"Attempts to delete block and undo data up to a specified height or timestamp, if eligible for pruning.\n"952"Requires `-prune` to be enabled at startup. While pruned data may be re-fetched in some cases (e.g., via `getblockfrompeer`), local deletion is irreversible.\n",953{954{"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or to a " + UNIX_EPOCH_TIME + "\n"955" to prune blocks whose block time is at least 2 hours older than the provided timestamp."},956},957RPCResult{958RPCResult::Type::NUM, "", "Height of the last block pruned"},959RPCExamples{960HelpExampleCli("pruneblockchain", "1000")961+ HelpExampleRpc("pruneblockchain", "1000")962},963[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue964{965ChainstateManager& chainman = EnsureAnyChainman(request.context);966if (!chainman.m_blockman.IsPruneMode()) {967throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");968}970LOCK(cs_main);971Chainstate& active_chainstate = chainman.ActiveChainstate();972CChain& active_chain = active_chainstate.m_chain;974int heightParam = request.params[0].getInt<int>();975if (heightParam < 0) {976throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height.");977}979// Height value more than a billion is too high to be a block height, and980// too low to be a block time (corresponds to timestamp from Sep 2001).981if (heightParam > 1000000000) {982// Add a 2 hour buffer to include blocks which might have had old timestamps983const CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);984if (!pindex) {985throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");986}987heightParam = pindex->nHeight;988}990unsigned int height = (unsigned int) heightParam;991unsigned int chainHeight = (unsigned int) active_chain.Height();992if (chainHeight < chainman.GetParams().PruneAfterHeight()) {993throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");994} else if (height > chainHeight) {995throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height.");996} else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {997LogDebug(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.\n");998height = chainHeight - MIN_BLOCKS_TO_KEEP;999}