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.
2001.skip_type_check = true,2002.type_str = {"", "string or numeric"},2003}},2004{"stats", RPCArg::Type::ARR, RPCArg::DefaultHint{"all values"}, "Values to plot (see result below)",2005{2006{"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},2007{"time", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},2008},2009RPCArgOptions{.oneline_description="stats"}},2010},2011RPCResult{2012RPCResult::Type::OBJ, "", "",2013{2014{RPCResult::Type::NUM, "avgfee", /*optional=*/true, "Average fee in the block"},2015{RPCResult::Type::NUM, "avgfeerate", /*optional=*/true, "Average feerate (in satoshis per virtual byte)"},2016{RPCResult::Type::NUM, "avgtxsize", /*optional=*/true, "Average transaction size"},2017{RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The block hash (to check for potential reorgs)"},2018{RPCResult::Type::ARR_FIXED, "feerate_percentiles", /*optional=*/true, "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)",2019{2020{RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"},2021{RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"},2022{RPCResult::Type::NUM, "50th_percentile_feerate", "The 50th percentile feerate"},2023{RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"},2024{RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"},2025}},2026{RPCResult::Type::NUM, "height", /*optional=*/true, "The height of the block"},2027{RPCResult::Type::NUM, "ins", /*optional=*/true, "The number of inputs (excluding coinbase)"},2028{RPCResult::Type::NUM, "maxfee", /*optional=*/true, "Maximum fee in the block"},2029{RPCResult::Type::NUM, "maxfeerate", /*optional=*/true, "Maximum feerate (in satoshis per virtual byte)"},2030{RPCResult::Type::NUM, "maxtxsize", /*optional=*/true, "Maximum transaction size"},2031{RPCResult::Type::NUM, "medianfee", /*optional=*/true, "Truncated median fee in the block"},2032{RPCResult::Type::NUM, "mediantime", /*optional=*/true, "The block median time past"},2033{RPCResult::Type::NUM, "mediantxsize", /*optional=*/true, "Truncated median transaction size"},2034{RPCResult::Type::NUM, "minfee", /*optional=*/true, "Minimum fee in the block"},2035{RPCResult::Type::NUM, "minfeerate", /*optional=*/true, "Minimum feerate (in satoshis per virtual byte)"},2036{RPCResult::Type::NUM, "mintxsize", /*optional=*/true, "Minimum transaction size"},2037{RPCResult::Type::NUM, "outs", /*optional=*/true, "The number of outputs"},2038{RPCResult::Type::NUM, "subsidy", /*optional=*/true, "The block subsidy"},2039{RPCResult::Type::NUM, "swtotal_size", /*optional=*/true, "Total size of all segwit transactions"},2040{RPCResult::Type::NUM, "swtotal_weight", /*optional=*/true, "Total weight of all segwit transactions"},2041{RPCResult::Type::NUM, "swtxs", /*optional=*/true, "The number of segwit transactions"},2042{RPCResult::Type::NUM, "time", /*optional=*/true, "The block time"},2043{RPCResult::Type::NUM, "total_out", /*optional=*/true, "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"},2044{RPCResult::Type::NUM, "total_size", /*optional=*/true, "Total size of all non-coinbase transactions"},2045{RPCResult::Type::NUM, "total_weight", /*optional=*/true, "Total weight of all non-coinbase transactions"},2046{RPCResult::Type::NUM, "totalfee", /*optional=*/true, "The fee total"},2047{RPCResult::Type::NUM, "txs", /*optional=*/true, "The number of transactions (including coinbase)"},2048{RPCResult::Type::NUM, "utxo_increase", /*optional=*/true, "The increase/decrease in the number of unspent outputs (not discounting op_return and similar)"},2049{RPCResult::Type::NUM, "utxo_size_inc", /*optional=*/true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},2050{RPCResult::Type::NUM, "utxo_increase_actual", /*optional=*/true, "The increase/decrease in the number of unspent outputs, not counting unspendables"},2051{RPCResult::Type::NUM, "utxo_size_inc_actual", /*optional=*/true, "The increase/decrease in size for the utxo index, not counting unspendables"},2052}},2053RPCExamples{2054HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +2055HelpExampleCli("getblockstats", R"(1000 '["minfeerate","avgfeerate"]')") +2056HelpExampleRpc("getblockstats", R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"])") +2057HelpExampleRpc("getblockstats", R"(1000, ["minfeerate","avgfeerate"])")2058},2059[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue2060{2061ChainstateManager& chainman = EnsureAnyChainman(request.context);2062const CBlockIndex& pindex{*CHECK_NONFATAL(ParseHashOrHeight(request.params[0], chainman))};2064std::set<std::string> stats;2065if (!request.params[1].isNull()) {2066const UniValue stats_univalue = request.params[1].get_array();2067for (unsigned int i = 0; i < stats_univalue.size(); i++) {2068const std::string stat = stats_univalue[i].get_str();2069stats.insert(stat);2070}2071}2073const CBlock& block = GetBlockChecked(chainman.m_blockman, pindex);2074const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, pindex);2076const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)2077const bool do_mediantxsize = do_all || stats.contains("mediantxsize");2078const bool do_medianfee = do_all || stats.contains("medianfee");2079const bool do_feerate_percentiles = do_all || stats.contains("feerate_percentiles");2080const bool loop_inputs = do_all || do_medianfee || do_feerate_percentiles ||2081SetHasKeys(stats, "utxo_increase", "utxo_increase_actual", "utxo_size_inc", "utxo_size_inc_actual", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate");2082const bool loop_outputs = do_all || loop_inputs || stats.contains("total_out");2083const bool do_calculate_size = do_mediantxsize ||2084SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "swtotal_size");2085const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");2086const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight");2088CAmount maxfee = 0;2089CAmount maxfeerate = 0;2090CAmount minfee = MAX_MONEY;2091CAmount minfeerate = MAX_MONEY;2092CAmount total_out = 0;2093CAmount totalfee = 0;2094int64_t inputs = 0;2095int64_t maxtxsize = 0;2096int64_t mintxsize = MAX_BLOCK_SERIALIZED_SIZE;2097int64_t outputs = 0;2098int64_t swtotal_size = 0;2099int64_t swtotal_weight = 0;2100int64_t swtxs = 0;2101int64_t total_size = 0;2102int64_t total_weight = 0;2103int64_t utxos = 0;2104int64_t utxo_size_inc = 0;2105int64_t utxo_size_inc_actual = 0;2106std::vector<CAmount> fee_array;2107std::vector<std::pair<CAmount, int64_t>> feerate_array;2108std::vector<int64_t> txsize_array;2110for (size_t i = 0; i < block.vtx.size(); ++i) {2111const auto& tx = block.vtx.at(i);2112outputs += tx->vout.size();2114CAmount tx_total_out = 0;2115if (loop_outputs) {2116for (const CTxOut& out : tx->vout) {2117tx_total_out += out.nValue;2119uint64_t out_size{GetSerializeSize(out) + PER_UTXO_OVERHEAD};2120utxo_size_inc += out_size;2122// The Genesis block and the repeated BIP30 block coinbases don't change the UTXO2123// set counts, so they have to be excluded from the statistics2124if (pindex.nHeight == 0 || (IsBIP30Repeat(pindex) && tx->IsCoinBase())) continue;2125// Skip unspendable outputs since they are not included in the UTXO set2126if (out.scriptPubKey.IsUnspendable()) continue;2128++utxos;2129utxo_size_inc_actual += out_size;2130}2131}2133if (tx->IsCoinBase()) {2134continue;2135}2137inputs += tx->vin.size(); // Don't count coinbase's fake input2138total_out += tx_total_out; // Don't count coinbase reward2140int64_t tx_size = 0;2141if (do_calculate_size) {2143tx_size = tx->ComputeTotalSize();2144if (do_mediantxsize) {2145txsize_array.push_back(tx_size);2146}2147maxtxsize = std::max(maxtxsize, tx_size);2148mintxsize = std::min(mintxsize, tx_size);2149total_size += tx_size;2150}2152int64_t weight = 0;2153if (do_calculate_weight) {2154weight = GetTransactionWeight(*tx);2155total_weight += weight;2156}2158if (do_calculate_sw && tx->HasWitness()) {2159++swtxs;2160swtotal_size += tx_size;2161swtotal_weight += weight;2162}2164if (loop_inputs) {2165CAmount tx_total_in = 0;2166const auto& txundo = blockUndo.vtxundo.at(i - 1);2167for (const Coin& coin: txundo.vprevout) {2168const CTxOut& prevoutput = coin.out;2170tx_total_in += prevoutput.nValue;2171uint64_t prevout_size{GetSerializeSize(prevoutput) + PER_UTXO_OVERHEAD};2172utxo_size_inc -= prevout_size;2173utxo_size_inc_actual -= prevout_size;2174}2176CAmount txfee = tx_total_in - tx_total_out;2177CHECK_NONFATAL(MoneyRange(txfee));2178if (do_medianfee) {2179fee_array.push_back(txfee);2180}2181maxfee = std::max(maxfee, txfee);2182minfee = std::min(minfee, txfee);2183totalfee += txfee;2185// New feerate uses satoshis per virtual byte instead of per serialized byte2186CAmount feerate = weight ? (txfee * WITNESS_SCALE_FACTOR) / weight : 0;2187if (do_feerate_percentiles) {2188feerate_array.emplace_back(feerate, weight);2189}2190maxfeerate = std::max(maxfeerate, feerate);2191minfeerate = std::min(minfeerate, feerate);2192}2193}2195CAmount feerate_percentiles[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 };2196CalculatePercentilesByWeight(feerate_percentiles, feerate_array, total_weight);2198UniValue feerates_res(UniValue::VARR);2199for (int64_t i = 0; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {2200feerates_res.push_back(feerate_percentiles[i]);2201}2203UniValue ret_all(UniValue::VOBJ);2204ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0);2205ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte2206ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);2207ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex());2208ret_all.pushKV("feerate_percentiles", std::move(feerates_res));2209ret_all.pushKV("height", pindex.nHeight);2210ret_all.pushKV("ins", inputs);2211ret_all.pushKV("maxfee", maxfee);2212ret_all.pushKV("maxfeerate", maxfeerate);2213ret_all.pushKV("maxtxsize", maxtxsize);2214ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));2215ret_all.pushKV("mediantime", pindex.GetMedianTimePast());2216ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array));2217ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee);2218ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);2219ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize);2220ret_all.pushKV("outs", outputs);2221ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.nHeight, chainman.GetParams().GetConsensus()));2222ret_all.pushKV("swtotal_size", swtotal_size);2223ret_all.pushKV("swtotal_weight", swtotal_weight);2224ret_all.pushKV("swtxs", swtxs);2225ret_all.pushKV("time", pindex.GetBlockTime());2226ret_all.pushKV("total_out", total_out);2227ret_all.pushKV("total_size", total_size);2228ret_all.pushKV("total_weight", total_weight);2229ret_all.pushKV("totalfee", totalfee);2230ret_all.pushKV("txs", block.vtx.size());2231ret_all.pushKV("utxo_increase", outputs - inputs);2232ret_all.pushKV("utxo_size_inc", utxo_size_inc);2233ret_all.pushKV("utxo_increase_actual", utxos - inputs);2234ret_all.pushKV("utxo_size_inc_actual", utxo_size_inc_actual);2236if (do_all) {2237return ret_all;2238}2240UniValue ret(UniValue::VOBJ);2241for (const std::string& stat : stats) {2242const UniValue& value = ret_all[stat];2243if (value.isNull()) {2244throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid selected statistic '%s'", stat));2245}2246ret.pushKVEnd(stat, value);2247}2248return ret;2249},2250};