diff --git a/doc/release-notes.md b/doc/release-notes.md index ddc8eec38..23e65b8c5 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,5 +1,6 @@ Bitcoin ABC version 0.19.12 is now available from: This release includes the following features and fixes: + - Add the `getblockstats` RPC to get statistics on a block or a block range. diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index debbbdf30..54d6e5065 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1,1967 +1,2288 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include // boost::thread::interrupt #include #include #include struct CUpdatedBlock { uint256 hash; int height; }; static Mutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock; /** * Calculate the difficulty for a given block index. */ double GetDifficulty(const CBlockIndex *blockindex) { assert(blockindex); int nShift = (blockindex->nBits >> 24) & 0xff; double dDiff = double(0x0000ffff) / double(blockindex->nBits & 0x00ffffff); while (nShift < 29) { dDiff *= 256.0; nShift++; } while (nShift > 29) { dDiff /= 256.0; nShift--; } return dDiff; } static int ComputeNextBlockAndDepth(const CBlockIndex *tip, const CBlockIndex *blockindex, const CBlockIndex *&next) { next = tip->GetAncestor(blockindex->nHeight + 1); if (next && next->pprev == blockindex) { return tip->nHeight - blockindex->nHeight + 1; } next = nullptr; return blockindex == tip ? 1 : -1; } UniValue blockheaderToJSON(const CBlockIndex *tip, const CBlockIndex *blockindex) { UniValue result(UniValue::VOBJ); result.pushKV("hash", blockindex->GetBlockHash().GetHex()); const CBlockIndex *pnext; int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext); result.pushKV("confirmations", confirmations); result.pushKV("height", blockindex->nHeight); result.pushKV("version", blockindex->nVersion); result.pushKV("versionHex", strprintf("%08x", blockindex->nVersion)); result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex()); result.pushKV("time", int64_t(blockindex->nTime)); result.pushKV("mediantime", int64_t(blockindex->GetMedianTimePast())); result.pushKV("nonce", uint64_t(blockindex->nNonce)); result.pushKV("bits", strprintf("%08x", blockindex->nBits)); result.pushKV("difficulty", GetDifficulty(blockindex)); result.pushKV("chainwork", blockindex->nChainWork.GetHex()); if (blockindex->pprev) { result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); } if (pnext) { result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); } return result; } UniValue blockToJSON(const CBlock &block, const CBlockIndex *tip, const CBlockIndex *blockindex, bool txDetails) { UniValue result(UniValue::VOBJ); result.pushKV("hash", blockindex->GetBlockHash().GetHex()); const CBlockIndex *pnext; int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext); result.pushKV("confirmations", confirmations); result.pushKV( "size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)); result.pushKV("height", blockindex->nHeight); result.pushKV("version", block.nVersion); result.pushKV("versionHex", strprintf("%08x", block.nVersion)); result.pushKV("merkleroot", block.hashMerkleRoot.GetHex()); UniValue txs(UniValue::VARR); for (const auto &tx : block.vtx) { if (txDetails) { UniValue objTx(UniValue::VOBJ); TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags()); txs.push_back(objTx); } else { txs.push_back(tx->GetId().GetHex()); } } result.pushKV("tx", txs); result.pushKV("time", block.GetBlockTime()); result.pushKV("mediantime", int64_t(blockindex->GetMedianTimePast())); result.pushKV("nonce", uint64_t(block.nNonce)); result.pushKV("bits", strprintf("%08x", block.nBits)); result.pushKV("difficulty", GetDifficulty(blockindex)); result.pushKV("chainwork", blockindex->nChainWork.GetHex()); if (blockindex->pprev) { result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); } if (pnext) { result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); } return result; } static UniValue getblockcount(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getblockcount\n" "\nReturns the number of blocks in the longest blockchain.\n" "\nResult:\n" "n (numeric) The current block count\n" "\nExamples:\n" + HelpExampleCli("getblockcount", "") + HelpExampleRpc("getblockcount", "")); } LOCK(cs_main); return chainActive.Height(); } static UniValue getbestblockhash(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getbestblockhash\n" "\nReturns the hash of the best (tip) block in the " "longest blockchain.\n" "\nResult:\n" "\"hex\" (string) the block hash hex encoded\n" "\nExamples:\n" + HelpExampleCli("getbestblockhash", "") + HelpExampleRpc("getbestblockhash", "")); } LOCK(cs_main); return chainActive.Tip()->GetBlockHash().GetHex(); } UniValue getfinalizedblockhash(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getfinalizedblockhash\n" "\nReturns the hash of the currently finalized block\n" "\nResult:\n" "\"hex\" (string) the block hash hex encoded\n"); } LOCK(cs_main); const CBlockIndex *blockIndexFinalized = GetFinalizedBlock(); if (blockIndexFinalized) { return blockIndexFinalized->GetBlockHash().GetHex(); } return UniValue(UniValue::VSTR); } void RPCNotifyBlockChange(bool ibd, const CBlockIndex *pindex) { if (pindex) { std::lock_guard lock(cs_blockchange); latestblock.hash = pindex->GetBlockHash(); latestblock.height = pindex->nHeight; } cond_blockchange.notify_all(); } static UniValue waitfornewblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "waitfornewblock (timeout)\n" "\nWaits for a specific new block and returns " "useful info about it.\n" "\nReturns the current block on timeout or exit.\n" "\nArguments:\n" "1. timeout (int, optional, default=0) Time in " "milliseconds to wait for a response. 0 indicates " "no timeout.\n" "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" "}\n" "\nExamples:\n" + HelpExampleCli("waitfornewblock", "1000") + HelpExampleRpc("waitfornewblock", "1000")); } int timeout = 0; if (!request.params[0].isNull()) { timeout = request.params[0].get_int(); } CUpdatedBlock block; { WAIT_LOCK(cs_blockchange, lock); block = latestblock; if (timeout) { cond_blockchange.wait_for( lock, std::chrono::milliseconds(timeout), [&block] { return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); }); } else { cond_blockchange.wait(lock, [&block] { return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); }); } block = latestblock; } UniValue ret(UniValue::VOBJ); ret.pushKV("hash", block.hash.GetHex()); ret.pushKV("height", block.height); return ret; } static UniValue waitforblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "waitforblock (timeout)\n" "\nWaits for a specific new block and returns useful info about " "it.\n" "\nReturns the current block on timeout or exit.\n" "\nArguments:\n" "1. \"blockhash\" (required, string) Block hash to wait for.\n" "2. timeout (int, optional, default=0) Time in milliseconds " "to wait for a response. 0 indicates no timeout.\n" "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" "}\n" "\nExamples:\n" + HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4" "570b24c9ed7b4a8c619eb02596f8862\", " "1000") + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4" "570b24c9ed7b4a8c619eb02596f8862\", " "1000")); } int timeout = 0; uint256 hash = uint256S(request.params[0].get_str()); if (!request.params[1].isNull()) { timeout = request.params[1].get_int(); } CUpdatedBlock block; { WAIT_LOCK(cs_blockchange, lock); if (timeout) { cond_blockchange.wait_for( lock, std::chrono::milliseconds(timeout), [&hash] { return latestblock.hash == hash || !IsRPCRunning(); }); } else { cond_blockchange.wait(lock, [&hash] { return latestblock.hash == hash || !IsRPCRunning(); }); } block = latestblock; } UniValue ret(UniValue::VOBJ); ret.pushKV("hash", block.hash.GetHex()); ret.pushKV("height", block.height); return ret; } static UniValue waitforblockheight(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "waitforblockheight (timeout)\n" "\nWaits for (at least) block height and returns the height and " "hash\n" "of the current tip.\n" "\nReturns the current block on timeout or exit.\n" "\nArguments:\n" "1. height (required, int) Block height to wait for (int)\n" "2. timeout (int, optional, default=0) Time in milliseconds to " "wait for a response. 0 indicates no timeout.\n" "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" "}\n" "\nExamples:\n" + HelpExampleCli("waitforblockheight", "\"100\", 1000") + HelpExampleRpc("waitforblockheight", "\"100\", 1000")); } int timeout = 0; int height = request.params[0].get_int(); if (!request.params[1].isNull()) { timeout = request.params[1].get_int(); } CUpdatedBlock block; { WAIT_LOCK(cs_blockchange, lock); if (timeout) { cond_blockchange.wait_for( lock, std::chrono::milliseconds(timeout), [&height] { return latestblock.height >= height || !IsRPCRunning(); }); } else { cond_blockchange.wait(lock, [&height] { return latestblock.height >= height || !IsRPCRunning(); }); } block = latestblock; } UniValue ret(UniValue::VOBJ); ret.pushKV("hash", block.hash.GetHex()); ret.pushKV("height", block.height); return ret; } static UniValue syncwithvalidationinterfacequeue(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 0) { throw std::runtime_error( "syncwithvalidationinterfacequeue\n" "\nWaits for the validation interface queue to catch up on " "everything that was there when we entered this function.\n" "\nExamples:\n" + HelpExampleCli("syncwithvalidationinterfacequeue", "") + HelpExampleRpc("syncwithvalidationinterfacequeue", "")); } SyncWithValidationInterfaceQueue(); return NullUniValue; } static UniValue getdifficulty(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error("getdifficulty\n" "\nReturns the proof-of-work difficulty as a " "multiple of the minimum difficulty.\n" "\nResult:\n" "n.nnn (numeric) the proof-of-work " "difficulty as a multiple of the minimum " "difficulty.\n" "\nExamples:\n" + HelpExampleCli("getdifficulty", "") + HelpExampleRpc("getdifficulty", "")); } LOCK(cs_main); return GetDifficulty(chainActive.Tip()); } static std::string EntryDescriptionString() { return " \"size\" : n, (numeric) transaction size.\n" " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" " \"modifiedfee\" : n, (numeric) transaction fee with fee " "deltas used for mining priority\n" " \"time\" : n, (numeric) local time transaction " "entered pool in seconds since 1 Jan 1970 GMT\n" " \"height\" : n, (numeric) block height when " "transaction entered pool\n" " \"startingpriority\" : n, (numeric) DEPRECATED. Priority when " "transaction entered pool\n" " \"currentpriority\" : n, (numeric) DEPRECATED. Transaction " "priority now\n" " \"descendantcount\" : n, (numeric) number of in-mempool " "descendant transactions (including this one)\n" " \"descendantsize\" : n, (numeric) virtual transaction size " "of in-mempool descendants (including this one)\n" " \"descendantfees\" : n, (numeric) modified fees (see above) " "of in-mempool descendants (including this one)\n" " \"ancestorcount\" : n, (numeric) number of in-mempool " "ancestor transactions (including this one)\n" " \"ancestorsize\" : n, (numeric) virtual transaction size " "of in-mempool ancestors (including this one)\n" " \"ancestorfees\" : n, (numeric) modified fees (see above) " "of in-mempool ancestors (including this one)\n" " \"depends\" : [ (array) unconfirmed transactions " "used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" " ... ]\n" " \"spentby\" : [ (array) unconfirmed transactions " "spending outputs from this transaction\n" " \"transactionid\", (string) child transaction id\n" " ... ]\n"; } static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCKS_REQUIRED(g_mempool.cs) { AssertLockHeld(g_mempool.cs); info.pushKV("size", (int)e.GetTxSize()); info.pushKV("fee", ValueFromAmount(e.GetFee())); info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); info.pushKV("time", e.GetTime()); info.pushKV("height", (int)e.GetHeight()); info.pushKV("startingpriority", e.GetPriority(e.GetHeight())); info.pushKV("currentpriority", e.GetPriority(chainActive.Height())); info.pushKV("descendantcount", e.GetCountWithDescendants()); info.pushKV("descendantsize", e.GetSizeWithDescendants()); info.pushKV("descendantfees", e.GetModFeesWithDescendants() / SATOSHI); info.pushKV("ancestorcount", e.GetCountWithAncestors()); info.pushKV("ancestorsize", e.GetSizeWithAncestors()); info.pushKV("ancestorfees", e.GetModFeesWithAncestors() / SATOSHI); const CTransaction &tx = e.GetTx(); std::set setDepends; for (const CTxIn &txin : tx.vin) { if (g_mempool.exists(txin.prevout.GetTxId())) { setDepends.insert(txin.prevout.GetTxId().ToString()); } } UniValue depends(UniValue::VARR); for (const std::string &dep : setDepends) { depends.push_back(dep); } info.pushKV("depends", depends); UniValue spent(UniValue::VARR); const CTxMemPool::txiter &it = g_mempool.mapTx.find(tx.GetId()); const CTxMemPool::setEntries &setChildren = g_mempool.GetMemPoolChildren(it); for (const CTxMemPool::txiter &childiter : setChildren) { spent.push_back(childiter->GetTx().GetId().ToString()); } info.pushKV("spentby", spent); } UniValue mempoolToJSON(bool fVerbose) { if (fVerbose) { LOCK(g_mempool.cs); UniValue o(UniValue::VOBJ); for (const CTxMemPoolEntry &e : g_mempool.mapTx) { const uint256 &txid = e.GetTx().GetId(); UniValue info(UniValue::VOBJ); entryToJSON(info, e); o.pushKV(txid.ToString(), info); } return o; } else { std::vector vtxids; g_mempool.queryHashes(vtxids); UniValue a(UniValue::VARR); for (const uint256 &txid : vtxids) { a.push_back(txid.ToString()); } return a; } } static UniValue getrawmempool(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "getrawmempool ( verbose )\n" "\nReturns all transaction ids in memory pool as a json array of " "string transaction ids.\n" "\nHint: use getmempoolentry to fetch a specific transaction from " "the mempool.\n" "\nArguments:\n" "1. verbose (boolean, optional, default=false) True for a json " "object, false for array of transaction ids\n" "\nResult: (for verbose = false):\n" "[ (json array of string)\n" " \"transactionid\" (string) The transaction id\n" " ,...\n" "]\n" "\nResult: (for verbose = true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() + " }, ...\n" "}\n" "\nExamples:\n" + HelpExampleCli("getrawmempool", "true") + HelpExampleRpc("getrawmempool", "true")); } bool fVerbose = false; if (!request.params[0].isNull()) { fVerbose = request.params[0].get_bool(); } return mempoolToJSON(fVerbose); } static UniValue getmempoolancestors(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getmempoolancestors txid (verbose)\n" "\nIf txid is in the mempool, returns all in-mempool ancestors.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id " "(must be in mempool)\n" "2. verbose (boolean, optional, default=false) " "True for a json object, false for array of transaction ids\n" "\nResult (for verbose=false):\n" "[ (json array of strings)\n" " \"transactionid\" (string) The transaction id of an " "in-mempool ancestor transaction\n" " ,...\n" "]\n" "\nResult (for verbose=true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() + " }, ...\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmempoolancestors", "\"mytxid\"") + HelpExampleRpc("getmempoolancestors", "\"mytxid\"")); } bool fVerbose = false; if (!request.params[1].isNull()) { fVerbose = request.params[1].get_bool(); } uint256 hash = ParseHashV(request.params[0], "parameter 1"); LOCK(g_mempool.cs); CTxMemPool::txiter it = g_mempool.mapTx.find(hash); if (it == g_mempool.mapTx.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); } CTxMemPool::setEntries setAncestors; uint64_t noLimit = std::numeric_limits::max(); std::string dummy; g_mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false); if (!fVerbose) { UniValue o(UniValue::VARR); for (CTxMemPool::txiter ancestorIt : setAncestors) { o.push_back(ancestorIt->GetTx().GetId().ToString()); } return o; } else { UniValue o(UniValue::VOBJ); for (CTxMemPool::txiter ancestorIt : setAncestors) { const CTxMemPoolEntry &e = *ancestorIt; const uint256 &_hash = e.GetTx().GetId(); UniValue info(UniValue::VOBJ); entryToJSON(info, e); o.pushKV(_hash.ToString(), info); } return o; } } static UniValue getmempooldescendants(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getmempooldescendants txid (verbose)\n" "\nIf txid is in the mempool, returns all in-mempool descendants.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id " "(must be in mempool)\n" "2. verbose (boolean, optional, default=false) " "True for a json object, false for array of transaction ids\n" "\nResult (for verbose=false):\n" "[ (json array of strings)\n" " \"transactionid\" (string) The transaction id of an " "in-mempool descendant transaction\n" " ,...\n" "]\n" "\nResult (for verbose=true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() + " }, ...\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmempooldescendants", "\"mytxid\"") + HelpExampleRpc("getmempooldescendants", "\"mytxid\"")); } bool fVerbose = false; if (!request.params[1].isNull()) { fVerbose = request.params[1].get_bool(); } uint256 hash = ParseHashV(request.params[0], "parameter 1"); LOCK(g_mempool.cs); CTxMemPool::txiter it = g_mempool.mapTx.find(hash); if (it == g_mempool.mapTx.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); } CTxMemPool::setEntries setDescendants; g_mempool.CalculateDescendants(it, setDescendants); // CTxMemPool::CalculateDescendants will include the given tx setDescendants.erase(it); if (!fVerbose) { UniValue o(UniValue::VARR); for (CTxMemPool::txiter descendantIt : setDescendants) { o.push_back(descendantIt->GetTx().GetId().ToString()); } return o; } else { UniValue o(UniValue::VOBJ); for (CTxMemPool::txiter descendantIt : setDescendants) { const CTxMemPoolEntry &e = *descendantIt; const uint256 &_hash = e.GetTx().GetId(); UniValue info(UniValue::VOBJ); entryToJSON(info, e); o.pushKV(_hash.ToString(), info); } return o; } } static UniValue getmempoolentry(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getmempoolentry txid\n" "\nReturns mempool data for given transaction\n" "\nArguments:\n" "1. \"txid\" (string, required) " "The transaction id (must be in mempool)\n" "\nResult:\n" "{ (json object)\n" + EntryDescriptionString() + "}\n" "\nExamples:\n" + HelpExampleCli("getmempoolentry", "\"mytxid\"") + HelpExampleRpc("getmempoolentry", "\"mytxid\"")); } uint256 hash = ParseHashV(request.params[0], "parameter 1"); LOCK(g_mempool.cs); CTxMemPool::txiter it = g_mempool.mapTx.find(hash); if (it == g_mempool.mapTx.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); } const CTxMemPoolEntry &e = *it; UniValue info(UniValue::VOBJ); entryToJSON(info, e); return info; } static UniValue getblockhash(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getblockhash height\n" "\nReturns hash of block in best-block-chain at height provided.\n" "\nArguments:\n" "1. height (numeric, required) The height index\n" "\nResult:\n" "\"hash\" (string) The block hash\n" "\nExamples:\n" + HelpExampleCli("getblockhash", "1000") + HelpExampleRpc("getblockhash", "1000")); } LOCK(cs_main); int nHeight = request.params[0].get_int(); if (nHeight < 0 || nHeight > chainActive.Height()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); } CBlockIndex *pblockindex = chainActive[nHeight]; return pblockindex->GetBlockHash().GetHex(); } static UniValue getblockheader(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getblockheader \"hash\" ( verbose )\n" "\nIf verbose is false, returns a string that is serialized, " "hex-encoded data for blockheader 'hash'.\n" "If verbose is true, returns an Object with information about " "blockheader .\n" "\nArguments:\n" "1. \"hash\" (string, required) The block hash\n" "2. verbose (boolean, optional, default=true) true for a " "json object, false for the hex encoded data\n" "\nResult (for verbose = true):\n" "{\n" " \"hash\" : \"hash\", (string) the block hash (same as " "provided)\n" " \"confirmations\" : n, (numeric) The number of confirmations, " "or -1 if the block is not on the main chain\n" " \"height\" : n, (numeric) The block height or index\n" " \"version\" : n, (numeric) The block version\n" " \"versionHex\" : \"00000000\", (string) The block version " "formatted in hexadecimal\n" " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" " \"time\" : ttt, (numeric) The block time in seconds " "since epoch (Jan 1 1970 GMT)\n" " \"mediantime\" : ttt, (numeric) The median block time in " "seconds since epoch (Jan 1 1970 GMT)\n" " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" " \"chainwork\" : \"0000...1f3\" (string) Expected number of " "hashes required to produce the current chain (in hex)\n" " \"previousblockhash\" : \"hash\", (string) The hash of the " "previous block\n" " \"nextblockhash\" : \"hash\", (string) The hash of the " "next block\n" "}\n" "\nResult (for verbose=false):\n" "\"data\" (string) A string that is serialized, " "hex-encoded data for block 'hash'.\n" "\nExamples:\n" + HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec3" "7b049d214adbda81d7e2a3dd146f6ed09" "\"") + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec3" "7b049d214adbda81d7e2a3dd146f6ed09" "\"")); } LOCK(cs_main); std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); bool fVerbose = true; if (!request.params[1].isNull()) { fVerbose = request.params[1].get_bool(); } const CBlockIndex *pblockindex = LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } if (!fVerbose) { CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); ssBlock << pblockindex->GetBlockHeader(); std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); return strHex; } return blockheaderToJSON(chainActive.Tip(), pblockindex); } static CBlock GetBlockChecked(const Config &config, const CBlockIndex *pblockindex) { CBlock block; if (fHavePruned && !pblockindex->nStatus.hasData() && pblockindex->nTx > 0) { throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); } if (!ReadBlockFromDisk(block, pblockindex, config)) { // Block not found on disk. This could be because we have the block // header in our index but don't have the block (for example if a // non-whitelisted node sends us an unrequested long chain of valid // blocks, we add the headers to our index, but don't accept the block). throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk"); } return block; } static UniValue getblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getblock \"blockhash\" ( verbosity )\n" "\nIf verbosity is 0 or false, returns a string that is " "serialized, hex-encoded data for block 'hash'.\n" "If verbosity is 1 or true, returns an Object with information " "about block .\n" "If verbosity is 2, returns an Object with information about block " " and information about each transaction.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) The block hash\n" "2. verbosity (numeric, optional, default=1) 0 for " "hex-encoded data, 1 for a json object, and 2 for json object with " "transaction data\n" "\nResult (for verbosity = 0):\n" "\"data\" (string) A string that is serialized, " "hex-encoded data for block 'hash'.\n" "\nResult (for verbosity = 1):\n" "{\n" " \"hash\" : \"hash\", (string) The block hash (same as " "provided)\n" " \"confirmations\" : n, (numeric) The number of confirmations, " "or -1 if the block is not on the main chain\n" " \"size\" : n, (numeric) The block size\n" " \"height\" : n, (numeric) The block height or index\n" " \"version\" : n, (numeric) The block version\n" " \"versionHex\" : \"00000000\", (string) The block version " "formatted in hexadecimal\n" " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" " \"tx\" : [ (array of string) The transaction ids\n" " \"transactionid\" (string) The transaction id\n" " ,...\n" " ],\n" " \"time\" : ttt, (numeric) The block time in seconds " "since epoch (Jan 1 1970 GMT)\n" " \"mediantime\" : ttt, (numeric) The median block time in " "seconds since epoch (Jan 1 1970 GMT)\n" " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" " \"chainwork\" : \"xxxx\", (string) Expected number of hashes " "required to produce the chain up to this block (in hex)\n" " \"previousblockhash\" : \"hash\", (string) The hash of the " "previous block\n" " \"nextblockhash\" : \"hash\" (string) The hash of the " "next block\n" "}\n" "\nResult (for verbosity = 2):\n" "{\n" " ..., Same output as verbosity = 1\n" " \"tx\" : [ (array of Objects) The transactions in " "the format of the getrawtransaction RPC; different from verbosity " "= 1 \"tx\" result\n" " ...\n" " ],\n" " ... Same output as verbosity = 1\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d" "214adbda81d7e2a3dd146f6ed09\"") + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d" "214adbda81d7e2a3dd146f6ed09\"")); } LOCK(cs_main); std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); int verbosity = 1; if (!request.params[1].isNull()) { if (request.params[1].isNum()) { verbosity = request.params[1].get_int(); } else { verbosity = request.params[1].get_bool() ? 1 : 0; } } const CBlockIndex *pblockindex = LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } const CBlock block = GetBlockChecked(config, pblockindex); if (verbosity <= 0) { CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags()); ssBlock << block; std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); return strHex; } return blockToJSON(block, chainActive.Tip(), pblockindex, verbosity >= 2); } struct CCoinsStats { int nHeight; uint256 hashBlock; uint64_t nTransactions; uint64_t nTransactionOutputs; uint64_t nBogoSize; uint256 hashSerialized; uint64_t nDiskSize; Amount nTotalAmount; CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), nDiskSize(0), nTotalAmount() {} }; static void ApplyStats(CCoinsStats &stats, CHashWriter &ss, const uint256 &hash, const std::map &outputs) { assert(!outputs.empty()); ss << hash; ss << VARINT(outputs.begin()->second.GetHeight() * 2 + outputs.begin()->second.IsCoinBase()); stats.nTransactions++; for (const auto output : outputs) { ss << VARINT(output.first + 1); ss << output.second.GetTxOut().scriptPubKey; ss << VARINT(output.second.GetTxOut().nValue / SATOSHI); stats.nTransactionOutputs++; stats.nTotalAmount += output.second.GetTxOut().nValue; stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ + 2 /* scriptPubKey len */ + output.second.GetTxOut().scriptPubKey.size() /* scriptPubKey */; } ss << VARINT(0); } //! Calculate statistics about the unspent transaction output set static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) { std::unique_ptr pcursor(view->Cursor()); assert(pcursor); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); stats.hashBlock = pcursor->GetBestBlock(); { LOCK(cs_main); stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight; } ss << stats.hashBlock; uint256 prevkey; std::map outputs; while (pcursor->Valid()) { boost::this_thread::interruption_point(); COutPoint key; Coin coin; if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { if (!outputs.empty() && key.GetTxId() != prevkey) { ApplyStats(stats, ss, prevkey, outputs); outputs.clear(); } prevkey = key.GetTxId(); outputs[key.GetN()] = std::move(coin); } else { return error("%s: unable to read value", __func__); } pcursor->Next(); } if (!outputs.empty()) { ApplyStats(stats, ss, prevkey, outputs); } stats.hashSerialized = ss.GetHash(); stats.nDiskSize = view->EstimateSize(); return true; } static UniValue pruneblockchain(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "pruneblockchain\n" "\nArguments:\n" "1. \"height\" (numeric, required) The block height to prune " "up to. May be set to a discrete height, or a unix timestamp\n" " to prune blocks whose block time is at least 2 " "hours older than the provided timestamp.\n" "\nResult:\n" "n (numeric) Height of the last block pruned.\n" "\nExamples:\n" + HelpExampleCli("pruneblockchain", "1000") + HelpExampleRpc("pruneblockchain", "1000")); } if (!fPruneMode) { throw JSONRPCError( RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode."); } LOCK(cs_main); int heightParam = request.params[0].get_int(); if (heightParam < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height."); } // Height value more than a billion is too high to be a block height, and // too low to be a block time (corresponds to timestamp from Sep 2001). if (heightParam > 1000000000) { // Add a 2 hour buffer to include blocks which might have had old // timestamps CBlockIndex *pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW); if (!pindex) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp."); } heightParam = pindex->nHeight; } unsigned int height = (unsigned int)heightParam; unsigned int chainHeight = (unsigned int)chainActive.Height(); if (chainHeight < config.GetChainParams().PruneAfterHeight()) { throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning."); } else if (height > chainHeight) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height."); } else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) { LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. " "Retaining the minimum number of blocks."); height = chainHeight - MIN_BLOCKS_TO_KEEP; } PruneBlockFilesManual(height); return uint64_t(height); } static UniValue gettxoutsetinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "gettxoutsetinfo\n" "\nReturns statistics about the unspent transaction output set.\n" "Note this call may take some time.\n" "\nResult:\n" "{\n" " \"height\":n, (numeric) The current block height (index)\n" " \"bestblock\": \"hex\", (string) the best block hash hex\n" " \"transactions\": n, (numeric) The number of transactions\n" " \"txouts\": n, (numeric) The number of output " "transactions\n" " \"bogosize\": n, (numeric) A database-independent " "metric for UTXO set size\n" " \"hash_serialized\": \"hash\", (string) The serialized hash\n" " \"disk_size\": n, (numeric) The estimated size of the " "chainstate on disk\n" " \"total_amount\": x.xxx (numeric) The total amount\n" "}\n" "\nExamples:\n" + HelpExampleCli("gettxoutsetinfo", "") + HelpExampleRpc("gettxoutsetinfo", "")); } UniValue ret(UniValue::VOBJ); CCoinsStats stats; FlushStateToDisk(); if (GetUTXOStats(pcoinsdbview.get(), stats)) { ret.pushKV("height", int64_t(stats.nHeight)); ret.pushKV("bestblock", stats.hashBlock.GetHex()); ret.pushKV("transactions", int64_t(stats.nTransactions)); ret.pushKV("txouts", int64_t(stats.nTransactionOutputs)); ret.pushKV("bogosize", int64_t(stats.nBogoSize)); ret.pushKV("hash_serialized", stats.hashSerialized.GetHex()); ret.pushKV("disk_size", stats.nDiskSize); ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount)); } else { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); } return ret; } UniValue gettxout(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) { throw std::runtime_error( "gettxout \"txid\" n ( include_mempool )\n" "\nReturns details about an unspent transaction output.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" "2. \"n\" (numeric, required) vout number\n" "3. \"include_mempool\" (boolean, optional) Whether to include " "the mempool. Default: true." " Note that an unspent output that is spent in the mempool " "won't appear.\n" "\nResult:\n" "{\n" " \"bestblock\" : \"hash\", (string) the block hash\n" " \"confirmations\" : n, (numeric) The number of " "confirmations\n" " \"value\" : x.xxx, (numeric) The transaction value " "in " + CURRENCY_UNIT + "\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"code\", (string) \n" " \"hex\" : \"hex\", (string) \n" " \"reqSigs\" : n, (numeric) Number of required " "signatures\n" " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n" " \"addresses\" : [ (array of string) array of " "bitcoin addresses\n" " \"address\" (string) bitcoin address\n" " ,...\n" " ]\n" " },\n" " \"coinbase\" : true|false (boolean) Coinbase or not\n" "}\n" "\nExamples:\n" "\nGet unspent transactions\n" + HelpExampleCli("listunspent", "") + "\nView the details\n" + HelpExampleCli("gettxout", "\"txid\" 1") + "\nAs a json rpc call\n" + HelpExampleRpc("gettxout", "\"txid\", 1")); } LOCK(cs_main); UniValue ret(UniValue::VOBJ); std::string strTxId = request.params[0].get_str(); TxId txid(uint256S(strTxId)); int n = request.params[1].get_int(); COutPoint out(txid, n); bool fMempool = true; if (!request.params[2].isNull()) { fMempool = request.params[2].get_bool(); } Coin coin; if (fMempool) { LOCK(g_mempool.cs); CCoinsViewMemPool view(pcoinsTip.get(), g_mempool); if (!view.GetCoin(out, coin) || g_mempool.isSpent(out)) { return NullUniValue; } } else { if (!pcoinsTip->GetCoin(out, coin)) { return NullUniValue; } } const CBlockIndex *pindex = LookupBlockIndex(pcoinsTip->GetBestBlock()); ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); if (coin.GetHeight() == MEMPOOL_HEIGHT) { ret.pushKV("confirmations", 0); } else { ret.pushKV("confirmations", int64_t(pindex->nHeight - coin.GetHeight() + 1)); } ret.pushKV("value", ValueFromAmount(coin.GetTxOut().nValue)); UniValue o(UniValue::VOBJ); ScriptPubKeyToUniv(coin.GetTxOut().scriptPubKey, o, true); ret.pushKV("scriptPubKey", o); ret.pushKV("coinbase", coin.IsCoinBase()); return ret; } static UniValue verifychain(const Config &config, const JSONRPCRequest &request) { int nCheckLevel = gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL); int nCheckDepth = gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS); if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( "verifychain ( checklevel nblocks )\n" "\nVerifies blockchain database.\n" "\nArguments:\n" "1. checklevel (numeric, optional, 0-4, default=" + strprintf("%d", nCheckLevel) + ") How thorough the block verification is.\n" "2. nblocks (numeric, optional, default=" + strprintf("%d", nCheckDepth) + ", 0=all) The number of blocks to check.\n" "\nResult:\n" "true|false (boolean) Verified or not\n" "\nExamples:\n" + HelpExampleCli("verifychain", "") + HelpExampleRpc("verifychain", "")); } LOCK(cs_main); if (!request.params[0].isNull()) { nCheckLevel = request.params[0].get_int(); } if (!request.params[1].isNull()) { nCheckDepth = request.params[1].get_int(); } return CVerifyDB().VerifyDB(config, pcoinsTip.get(), nCheckLevel, nCheckDepth); } /** Implementation of IsSuperMajority with better feedback */ static UniValue SoftForkMajorityDesc(int version, const CBlockIndex *pindex, const Consensus::Params &consensusParams) { UniValue rv(UniValue::VOBJ); bool activated = false; switch (version) { case 2: activated = pindex->nHeight >= consensusParams.BIP34Height; break; case 3: activated = pindex->nHeight >= consensusParams.BIP66Height; break; case 4: activated = pindex->nHeight >= consensusParams.BIP65Height; break; case 5: activated = pindex->nHeight >= consensusParams.CSVHeight; break; } rv.pushKV("status", activated); return rv; } static UniValue SoftForkDesc(const std::string &name, int version, const CBlockIndex *pindex, const Consensus::Params &consensusParams) { UniValue rv(UniValue::VOBJ); rv.pushKV("id", name); rv.pushKV("version", version); rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams)); return rv; } UniValue getblockchaininfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getblockchaininfo\n" "Returns an object containing various state info regarding " "blockchain processing.\n" "DEPRECATION WARNING: The 'softforks' output has been deprecated " "and will be\n" "removed v0.20. For the time being it will only be shown here when " "bitcoind\n" "is started with -deprecatedrpc=getblockchaininfo.\n" "\nResult:\n" "{\n" " \"chain\": \"xxxx\", (string) current network name " "as defined in BIP70 (main, test, regtest)\n" " \"blocks\": xxxxxx, (numeric) the current number of " "blocks processed in the server\n" " \"headers\": xxxxxx, (numeric) the current number of " "headers we have validated\n" " \"bestblockhash\": \"...\", (string) the hash of the " "currently best block\n" " \"difficulty\": xxxxxx, (numeric) the current " "difficulty\n" " \"mediantime\": xxxxxx, (numeric) median time for the " "current best block\n" " \"verificationprogress\": xxxx, (numeric) estimate of " "verification progress [0..1]\n" " \"initialblockdownload\": xxxx, (bool) (debug information) " "estimate of whether this node is in Initial Block Download mode.\n" " \"chainwork\": \"xxxx\" (string) total amount of work " "in active chain, in hexadecimal\n" " \"size_on_disk\": xxxxxx, (numeric) the estimated size of " "the block and undo files on disk\n" " \"pruned\": xx, (boolean) if the blocks are " "subject to pruning\n" " \"pruneheight\": xxxxxx, (numeric) lowest-height " "complete block stored (only present if pruning is enabled)\n" " \"automatic_pruning\": xx, (boolean) whether automatic " "pruning is enabled (only present if pruning is enabled)\n" " \"prune_target_size\": xxxxxx, (numeric) the target size " "used by pruning (only present if automatic pruning is enabled)\n" " \"softforks\": [ (array) DEPRECATED: status of " "softforks in progress\n" " {\n" " \"id\": \"xxxx\", (string) name of softfork\n" " \"version\": xx, (numeric) block version\n" " \"reject\": { (object) progress toward " "rejecting pre-softfork blocks\n" " \"status\": xx, (boolean) true if threshold " "reached\n" " },\n" " }, ...\n" " ]\n" " \"warnings\" : \"...\", (string) any network and " "blockchain warnings.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "")); } LOCK(cs_main); const CBlockIndex *tip = chainActive.Tip(); UniValue obj(UniValue::VOBJ); obj.pushKV("chain", config.GetChainParams().NetworkIDString()); obj.pushKV("blocks", int(chainActive.Height())); obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1); obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex()); obj.pushKV("difficulty", double(GetDifficulty(tip))); obj.pushKV("mediantime", int64_t(tip->GetMedianTimePast())); obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip)); obj.pushKV("initialblockdownload", IsInitialBlockDownload()); obj.pushKV("chainwork", tip->nChainWork.GetHex()); obj.pushKV("size_on_disk", CalculateCurrentUsage()); obj.pushKV("pruned", fPruneMode); if (fPruneMode) { const CBlockIndex *block = tip; assert(block); while (block->pprev && (block->pprev->nStatus.hasData())) { block = block->pprev; } obj.pushKV("pruneheight", block->nHeight); // if 0, execution bypasses the whole if block. bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1); obj.pushKV("automatic_pruning", automatic_pruning); if (automatic_pruning) { obj.pushKV("prune_target_size", nPruneTarget); } } if (IsDeprecatedRPCEnabled(gArgs, "getblockchaininfo")) { const Consensus::Params &consensusParams = config.GetChainParams().GetConsensus(); UniValue softforks(UniValue::VARR); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); softforks.push_back(SoftForkDesc("csv", 5, tip, consensusParams)); obj.pushKV("softforks", softforks); } obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } /** Comparison function for sorting the getchaintips heads. */ struct CompareBlocksByHeight { bool operator()(const CBlockIndex *a, const CBlockIndex *b) const { // Make sure that unequal blocks with the same height do not compare // equal. Use the pointers themselves to make a distinction. if (a->nHeight != b->nHeight) { return (a->nHeight > b->nHeight); } return a < b; } }; static UniValue getchaintips(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getchaintips\n" "Return information about all known tips in the block tree," " including the main chain as well as orphaned branches.\n" "\nResult:\n" "[\n" " {\n" " \"height\": xxxx, (numeric) height of the chain tip\n" " \"hash\": \"xxxx\", (string) block hash of the tip\n" " \"branchlen\": 0 (numeric) zero for main chain\n" " \"status\": \"active\" (string) \"active\" for the main " "chain\n" " },\n" " {\n" " \"height\": xxxx,\n" " \"hash\": \"xxxx\",\n" " \"branchlen\": 1 (numeric) length of branch " "connecting the tip to the main chain\n" " \"status\": \"xxxx\" (string) status of the chain " "(active, valid-fork, valid-headers, headers-only, invalid)\n" " }\n" "]\n" "Possible values for status:\n" "1. \"invalid\" This branch contains at least one " "invalid block\n" "2. \"parked\" This branch contains at least one " "parked block\n" "3. \"headers-only\" Not all blocks for this branch are " "available, but the headers are valid\n" "4. \"valid-headers\" All blocks are available for this " "branch, but they were never fully validated\n" "5. \"valid-fork\" This branch is not part of the " "active chain, but is fully validated\n" "6. \"active\" This is the tip of the active main " "chain, which is certainly valid\n" "\nExamples:\n" + HelpExampleCli("getchaintips", "") + HelpExampleRpc("getchaintips", "")); } LOCK(cs_main); /** * Idea: the set of chain tips is chainActive.tip, plus orphan blocks which * do not have another orphan building off of them. * Algorithm: * - Make one pass through mapBlockIndex, picking out the orphan blocks, * and also storing a set of the orphan block's pprev pointers. * - Iterate through the orphan blocks. If the block isn't pointed to by * another orphan, it is a chain tip. * - add chainActive.Tip() */ std::set setTips; std::set setOrphans; std::set setPrevs; for (const std::pair &item : mapBlockIndex) { if (!chainActive.Contains(item.second)) { setOrphans.insert(item.second); setPrevs.insert(item.second->pprev); } } for (std::set::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it) { if (setPrevs.erase(*it) == 0) { setTips.insert(*it); } } // Always report the currently active tip. setTips.insert(chainActive.Tip()); /* Construct the output array. */ UniValue res(UniValue::VARR); for (const CBlockIndex *block : setTips) { UniValue obj(UniValue::VOBJ); obj.pushKV("height", block->nHeight); obj.pushKV("hash", block->phashBlock->GetHex()); const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight; obj.pushKV("branchlen", branchLen); std::string status; if (chainActive.Contains(block)) { // This block is part of the currently active chain. status = "active"; } else if (block->nStatus.isInvalid()) { // This block or one of its ancestors is invalid. status = "invalid"; } else if (block->nStatus.isOnParkedChain()) { // This block or one of its ancestors is parked. status = "parked"; } else if (block->nChainTx == 0) { // This block cannot be connected because full block data for it or // one of its parents is missing. status = "headers-only"; } else if (block->IsValid(BlockValidity::SCRIPTS)) { // This block is fully validated, but no longer part of the active // chain. It was probably the active block once, but was // reorganized. status = "valid-fork"; } else if (block->IsValid(BlockValidity::TREE)) { // The headers for this block are valid, but it has not been // validated. It was probably never part of the most-work chain. status = "valid-headers"; } else { // No clue. status = "unknown"; } obj.pushKV("status", status); res.push_back(obj); } return res; } UniValue mempoolInfoToJSON() { UniValue ret(UniValue::VOBJ); ret.pushKV("size", (int64_t)g_mempool.size()); ret.pushKV("bytes", (int64_t)g_mempool.GetTotalTxSize()); ret.pushKV("usage", (int64_t)g_mempool.DynamicMemoryUsage()); size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; ret.pushKV("maxmempool", (int64_t)maxmempool); ret.pushKV("mempoolminfee", ValueFromAmount( std::max(g_mempool.GetMinFee(maxmempool), ::minRelayTxFee) .GetFeePerK())); ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())); return ret; } static UniValue getmempoolinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getmempoolinfo\n" "\nReturns details on the active state of the TX memory pool.\n" "\nResult:\n" "{\n" " \"size\": xxxxx, (numeric) Current tx count\n" " \"bytes\": xxxxx, (numeric) Transaction size.\n" " \"usage\": xxxxx, (numeric) Total memory usage for " "the mempool\n" " \"maxmempool\": xxxxx, (numeric) Maximum memory usage " "for the mempool\n" " \"mempoolminfee\": xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and " "minimum mempool fee\n" " \"minrelaytxfee\": xxxxx (numeric) Current minimum relay " "fee for transactions\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmempoolinfo", "") + HelpExampleRpc("getmempoolinfo", "")); } return mempoolInfoToJSON(); } static UniValue preciousblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "preciousblock \"blockhash\"\n" "\nTreats a block as if it were received before others with the " "same work.\n" "\nA later preciousblock call can override the effect of an " "earlier one.\n" "\nThe effects of preciousblock are not retained across restarts.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) the hash of the block to " "mark as precious\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("preciousblock", "\"blockhash\"") + HelpExampleRpc("preciousblock", "\"blockhash\"")); } std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); CBlockIndex *pblockindex; { LOCK(cs_main); pblockindex = LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } } CValidationState state; PreciousBlock(config, state, pblockindex); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } return NullUniValue; } UniValue finalizeblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "finalizeblock \"blockhash\"\n" "\nTreats a block as final. It cannot be reorged. Any chain\n" "that does not contain this block is invalid. Used on a less\n" "work chain, it can effectively PUTS YOU OUT OF CONSENSUS.\n" "USE WITH CAUTION!\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("finalizeblock", "\"blockhash\"") + HelpExampleRpc("finalizeblock", "\"blockhash\"")); } std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); CValidationState state; { LOCK(cs_main); CBlockIndex *pblockindex = LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } FinalizeBlockAndInvalidate(config, state, pblockindex); } if (state.IsValid()) { ActivateBestChain(config, state); } if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); } return NullUniValue; } static UniValue invalidateblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "invalidateblock \"blockhash\"\n" "\nPermanently marks a block as invalid, as if it " "violated a consensus rule.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) the hash of " "the block to mark as invalid\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("invalidateblock", "\"blockhash\"") + HelpExampleRpc("invalidateblock", "\"blockhash\"")); } const std::string strHash = request.params[0].get_str(); const uint256 hash(uint256S(strHash)); CValidationState state; { LOCK(cs_main); CBlockIndex *pblockindex = LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } InvalidateBlock(config, state, pblockindex); } if (state.IsValid()) { ActivateBestChain(config, state); } if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); } return NullUniValue; } UniValue parkblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error("parkblock \"blockhash\"\n" "\nMarks a block as parked.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) the " "hash of the block to park\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("parkblock", "\"blockhash\"") + HelpExampleRpc("parkblock", "\"blockhash\"")); } const std::string strHash = request.params[0].get_str(); const uint256 hash(uint256S(strHash)); CValidationState state; { LOCK(cs_main); if (mapBlockIndex.count(hash) == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } CBlockIndex *pblockindex = mapBlockIndex[hash]; ParkBlock(config, state, pblockindex); } if (state.IsValid()) { ActivateBestChain(config, state); } if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } return NullUniValue; } static UniValue reconsiderblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "reconsiderblock \"blockhash\"\n" "\nRemoves invalidity status of a block and its descendants, " "reconsider them for activation.\n" "This can be used to undo the effects of invalidateblock.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) the hash of the block to " "reconsider\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("reconsiderblock", "\"blockhash\"") + HelpExampleRpc("reconsiderblock", "\"blockhash\"")); } const std::string strHash = request.params[0].get_str(); const uint256 hash(uint256S(strHash)); { LOCK(cs_main); CBlockIndex *pblockindex = LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } ResetBlockFailureFlags(pblockindex); } CValidationState state; ActivateBestChain(config, state); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); } return NullUniValue; } UniValue unparkblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "unparkblock \"blockhash\"\n" "\nRemoves parked status of a block and its descendants, " "reconsider them for activation.\n" "This can be used to undo the effects of parkblock.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) the hash of the block to " "unpark\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("unparkblock", "\"blockhash\"") + HelpExampleRpc("unparkblock", "\"blockhash\"")); } const std::string strHash = request.params[0].get_str(); const uint256 hash(uint256S(strHash)); { LOCK(cs_main); if (mapBlockIndex.count(hash) == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } CBlockIndex *pblockindex = mapBlockIndex[hash]; UnparkBlockAndChildren(pblockindex); } CValidationState state; ActivateBestChain(config, state); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } return NullUniValue; } static UniValue getchaintxstats(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( "getchaintxstats ( nblocks blockhash )\n" "\nCompute statistics about the total number and rate of " "transactions in the chain.\n" "\nArguments:\n" "1. nblocks (numeric, optional) Size of the window in number " "of blocks (default: one month).\n" "2. \"blockhash\" (string, optional) The hash of the block that " "ends the window.\n" "\nResult:\n" "{\n" " \"time\": xxxxx, (numeric) The " "timestamp for the final block in the window in UNIX format.\n" " \"txcount\": xxxxx, (numeric) The total " "number of transactions in the chain up to that point.\n" " \"window_final_block_hash\": \"...\", (string) The hash of " "the final block in the window.\n" " \"window_block_count\": xxxxx, (numeric) Size of " "the window in number of blocks.\n" " \"window_tx_count\": xxxxx, (numeric) The number " "of transactions in the window. Only returned if " "\"window_block_count\" is > 0.\n" " \"window_interval\": xxxxx, (numeric) The elapsed " "time in the window in seconds. Only returned if " "\"window_block_count\" is > 0.\n" " \"txrate\": x.xx, (numeric) The average " "rate of transactions per second in the window. Only returned if " "\"window_interval\" is > 0.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getchaintxstats", "") + HelpExampleRpc("getchaintxstats", "2016")); } const CBlockIndex *pindex; // By default: 1 month int blockcount = 30 * 24 * 60 * 60 / config.GetChainParams().GetConsensus().nPowTargetSpacing; if (request.params[1].isNull()) { LOCK(cs_main); pindex = chainActive.Tip(); } else { uint256 hash = uint256S(request.params[1].get_str()); LOCK(cs_main); pindex = LookupBlockIndex(hash); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } if (!chainActive.Contains(pindex)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain"); } } assert(pindex != nullptr); if (request.params[0].isNull()) { blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1)); } else { blockcount = request.params[0].get_int(); if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: " "should be between 0 and " "the block's height - 1"); } } const CBlockIndex *pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount); int nTimeDiff = pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast(); int nTxDiff = pindex->nChainTx - pindexPast->nChainTx; UniValue ret(UniValue::VOBJ); ret.pushKV("time", int64_t(pindex->nTime)); ret.pushKV("txcount", int64_t(pindex->nChainTx)); ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex()); ret.pushKV("window_block_count", blockcount); if (blockcount > 0) { ret.pushKV("window_tx_count", nTxDiff); ret.pushKV("window_interval", nTimeDiff); if (nTimeDiff > 0) { ret.pushKV("txrate", double(nTxDiff) / nTimeDiff); } } return ret; } +template +static T CalculateTruncatedMedian(std::vector &scores) { + size_t size = scores.size(); + if (size == 0) { + return T(); + } + + std::sort(scores.begin(), scores.end()); + if (size % 2 == 0) { + return (scores[size / 2 - 1] + scores[size / 2]) / 2; + } else { + return scores[size / 2]; + } +} + +template static inline bool SetHasKeys(const std::set &set) { + return false; +} +template +static inline bool SetHasKeys(const std::set &set, const Tk &key, + const Args &... args) { + return (set.count(key) != 0) || SetHasKeys(set, args...); +} + +// outpoint (needed for the utxo index) + nHeight + fCoinBase +static constexpr size_t PER_UTXO_OVERHEAD = + sizeof(COutPoint) + sizeof(uint32_t) + sizeof(bool); + +static UniValue getblockstats(const Config &config, + const JSONRPCRequest &request) { + if (request.fHelp || request.params.size() < 1 || + request.params.size() > 4) { + throw std::runtime_error( + "getblockstats hash_or_height ( stats )\n" + "\nCompute per block statistics for a given window. All amounts " + "are in " + + CURRENCY_UNIT + + ".\n" + "It won't work for some heights with pruning.\n" + "It won't work without -txindex for utxo_size_inc, *fee or " + "*feerate stats.\n" + "\nArguments:\n" + "1. \"hash_or_height\" (string or numeric, required) The block " + "hash or height of the target block\n" + "2. \"stats\" (array, optional) Values to plot, by " + "default all values (see result below)\n" + " [\n" + " \"height\", (string, optional) Selected statistic\n" + " \"time\", (string, optional) Selected statistic\n" + " ,...\n" + " ]\n" + "\nResult:\n" + "{ (json object)\n" + " \"avgfee\": x.xxx, (numeric) Average fee in the block\n" + " \"avgfeerate\": x.xxx, (numeric) Average feerate (in " + + CURRENCY_UNIT + + " per byte)\n" + " \"avgtxsize\": xxxxx, (numeric) Average transaction size\n" + " \"blockhash\": xxxxx, (string) The block hash (to check " + "for potential reorgs)\n" + " \"height\": xxxxx, (numeric) The height of the block\n" + " \"ins\": xxxxx, (numeric) The number of inputs " + "(excluding coinbase)\n" + " \"maxfee\": xxxxx, (numeric) Maximum fee in the block\n" + " \"maxfeerate\": xxxxx, (numeric) Maximum feerate (in " + + CURRENCY_UNIT + + " per byte)\n" + " \"maxtxsize\": xxxxx, (numeric) Maximum transaction size\n" + " \"medianfee\": x.xxx, (numeric) Truncated median fee in " + "the block\n" + " \"medianfeerate\": x.xxx, (numeric) Truncated median feerate " + "(in " + + CURRENCY_UNIT + + " per byte)\n" + " \"mediantime\": xxxxx, (numeric) The block median time " + "past\n" + " \"mediantxsize\": xxxxx, (numeric) Truncated median " + "transaction size\n" + " \"minfee\": x.xxx, (numeric) Minimum fee in the block\n" + " \"minfeerate\": xx.xx, (numeric) Minimum feerate (in " + + CURRENCY_UNIT + + " per byte)\n" + " \"mintxsize\": xxxxx, (numeric) Minimum transaction size\n" + " \"outs\": xxxxx, (numeric) The number of outputs\n" + " \"subsidy\": x.xxx, (numeric) The block subsidy\n" + " \"time\": xxxxx, (numeric) The block time\n" + " \"total_out\": x.xxx, (numeric) Total amount in all " + "outputs (excluding coinbase and thus reward [ie subsidy + " + "totalfee])\n" + " \"total_size\": xxxxx, (numeric) Total size of all " + "non-coinbase transactions\n" + " \"totalfee\": x.xxx, (numeric) The fee total\n" + " \"txs\": xxxxx, (numeric) The number of " + "transactions (excluding coinbase)\n" + " \"utxo_increase\": xxxxx, (numeric) The increase/decrease in " + "the number of unspent outputs\n" + " \"utxo_size_inc\": xxxxx, (numeric) The increase/decrease in " + "size for the utxo index (not discounting op_return and similar)\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getblockstats", + "1000 '[\"minfeerate\",\"avgfeerate\"]'") + + HelpExampleRpc("getblockstats", + "1000 '[\"minfeerate\",\"avgfeerate\"]'")); + } + + LOCK(cs_main); + + CBlockIndex *pindex; + if (request.params[0].isNum()) { + const int height = request.params[0].get_int(); + const int current_tip = chainActive.Height(); + if (height < 0) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf("Target block height %d is negative", height)); + } + if (height > current_tip) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf("Target block height %d after current tip %d", height, + current_tip)); + } + + pindex = chainActive[height]; + } else { + const std::string strHash = request.params[0].get_str(); + const uint256 hash(uint256S(strHash)); + pindex = LookupBlockIndex(hash); + if (!pindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + if (!chainActive.Contains(pindex)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + strprintf("Block is not in chain %s", + Params().NetworkIDString())); + } + } + + assert(pindex != nullptr); + + std::set stats; + if (!request.params[1].isNull()) { + const UniValue stats_univalue = request.params[1].get_array(); + for (unsigned int i = 0; i < stats_univalue.size(); i++) { + const std::string stat = stats_univalue[i].get_str(); + stats.insert(stat); + } + } + + const CBlock block = GetBlockChecked(config, pindex); + + // Calculate everything if nothing selected (default) + const bool do_all = stats.size() == 0; + const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0; + const bool do_medianfee = do_all || stats.count("medianfee") != 0; + const bool do_medianfeerate = do_all || stats.count("medianfeerate") != 0; + const bool loop_inputs = + do_all || do_medianfee || do_medianfeerate || + SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", "avgfeerate", + "minfee", "maxfee", "minfeerate", "maxfeerate"); + const bool loop_outputs = do_all || loop_inputs || stats.count("total_out"); + const bool do_calculate_size = + do_mediantxsize || loop_inputs || + SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize"); + + const int64_t blockMaxSize = config.GetMaxBlockSize(); + Amount maxfee = Amount::zero(); + Amount maxfeerate = Amount::zero(); + Amount minfee = MAX_MONEY; + Amount minfeerate = MAX_MONEY; + Amount total_out = Amount::zero(); + Amount totalfee = Amount::zero(); + int64_t inputs = 0; + int64_t maxtxsize = 0; + int64_t mintxsize = blockMaxSize; + int64_t outputs = 0; + int64_t total_size = 0; + int64_t utxo_size_inc = 0; + std::vector fee_array; + std::vector feerate_array; + std::vector txsize_array; + + for (const auto &tx : block.vtx) { + outputs += tx->vout.size(); + Amount tx_total_out = Amount::zero(); + if (loop_outputs) { + for (const CTxOut &out : tx->vout) { + tx_total_out += out.nValue; + utxo_size_inc += + GetSerializeSize(out, SER_NETWORK, PROTOCOL_VERSION) + + PER_UTXO_OVERHEAD; + } + } + + if (tx->IsCoinBase()) { + continue; + } + + // Don't count coinbase's fake input + inputs += tx->vin.size(); + // Don't count coinbase reward + total_out += tx_total_out; + + int64_t tx_size = 0; + if (do_calculate_size) { + + tx_size = tx->GetTotalSize(); + if (do_mediantxsize) { + txsize_array.push_back(tx_size); + } + maxtxsize = std::max(maxtxsize, tx_size); + mintxsize = std::min(mintxsize, tx_size); + total_size += tx_size; + } + + if (loop_inputs) { + + if (!g_txindex) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "One or more of the selected stats requires " + "-txindex enabled"); + } + Amount tx_total_in = Amount::zero(); + for (const CTxIn &in : tx->vin) { + CTransactionRef tx_in; + uint256 hashBlock; + if (!GetTransaction(config, in.prevout.GetTxId(), tx_in, + hashBlock, false)) { + throw JSONRPCError(RPC_INTERNAL_ERROR, + std::string("Unexpected internal error " + "(tx index seems corrupt)")); + } + + CTxOut prevoutput = tx_in->vout[in.prevout.GetN()]; + + tx_total_in += prevoutput.nValue; + utxo_size_inc -= GetSerializeSize(prevoutput, SER_NETWORK, + PROTOCOL_VERSION) + + PER_UTXO_OVERHEAD; + } + + Amount txfee = tx_total_in - tx_total_out; + assert(MoneyRange(txfee)); + if (do_medianfee) { + fee_array.push_back(txfee); + } + maxfee = std::max(maxfee, txfee); + minfee = std::min(minfee, txfee); + totalfee += txfee; + + Amount feerate = txfee / tx_size; + if (do_medianfeerate) { + feerate_array.push_back(feerate); + } + maxfeerate = std::max(maxfeerate, feerate); + minfeerate = std::min(minfeerate, feerate); + } + } + + UniValue ret_all(UniValue::VOBJ); + ret_all.pushKV("avgfee", + ValueFromAmount((block.vtx.size() > 1) + ? totalfee / int((block.vtx.size() - 1)) + : Amount::zero())); + ret_all.pushKV("avgfeerate", + ValueFromAmount((total_size > 0) ? totalfee / total_size + : Amount::zero())); + ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) + ? total_size / (block.vtx.size() - 1) + : 0); + ret_all.pushKV("blockhash", pindex->GetBlockHash().GetHex()); + ret_all.pushKV("height", (int64_t)pindex->nHeight); + ret_all.pushKV("ins", inputs); + ret_all.pushKV("maxfee", ValueFromAmount(maxfee)); + ret_all.pushKV("maxfeerate", ValueFromAmount(maxfeerate)); + ret_all.pushKV("maxtxsize", maxtxsize); + ret_all.pushKV("medianfee", + ValueFromAmount(CalculateTruncatedMedian(fee_array))); + ret_all.pushKV("medianfeerate", + ValueFromAmount(CalculateTruncatedMedian(feerate_array))); + ret_all.pushKV("mediantime", pindex->GetMedianTimePast()); + ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array)); + ret_all.pushKV( + "minfee", + ValueFromAmount((minfee == MAX_MONEY) ? Amount::zero() : minfee)); + ret_all.pushKV("minfeerate", + ValueFromAmount((minfeerate == MAX_MONEY) ? Amount::zero() + : minfeerate)); + ret_all.pushKV("mintxsize", mintxsize == blockMaxSize ? 0 : mintxsize); + ret_all.pushKV("outs", outputs); + ret_all.pushKV("subsidy", ValueFromAmount(GetBlockSubsidy( + pindex->nHeight, Params().GetConsensus()))); + ret_all.pushKV("time", pindex->GetBlockTime()); + ret_all.pushKV("total_out", ValueFromAmount(total_out)); + ret_all.pushKV("total_size", total_size); + ret_all.pushKV("totalfee", ValueFromAmount(totalfee)); + ret_all.pushKV("txs", (int64_t)block.vtx.size()); + ret_all.pushKV("utxo_increase", outputs - inputs); + ret_all.pushKV("utxo_size_inc", utxo_size_inc); + + if (do_all) { + return ret_all; + } + + UniValue ret(UniValue::VOBJ); + for (const std::string &stat : stats) { + const UniValue &value = ret_all[stat]; + if (value.isNull()) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf("Invalid selected statistic %s", stat)); + } + ret.pushKV(stat, value); + } + return ret; +} + static UniValue savemempool(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error("savemempool\n" "\nDumps the mempool to disk. It will fail " "until the previous dump is fully loaded.\n" "\nExamples:\n" + HelpExampleCli("savemempool", "") + HelpExampleRpc("savemempool", "")); } if (!g_is_mempool_loaded) { throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet"); } if (!DumpMempool()) { throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk"); } return NullUniValue; } // clang-format off static const ContextFreeRPCCommand commands[] = { // category name actor (function) argNames // ------------------- ------------------------ ---------------------- ---------- { "blockchain", "getbestblockhash", getbestblockhash, {} }, { "blockchain", "getblock", getblock, {"blockhash","verbosity|verbose"} }, { "blockchain", "getblockchaininfo", getblockchaininfo, {} }, { "blockchain", "getblockcount", getblockcount, {} }, { "blockchain", "getblockhash", getblockhash, {"height"} }, { "blockchain", "getblockheader", getblockheader, {"blockhash","verbose"} }, + { "blockchain", "getblockstats", getblockstats, {"hash_or_height","stats"} }, { "blockchain", "getchaintips", getchaintips, {} }, { "blockchain", "getchaintxstats", getchaintxstats, {"nblocks", "blockhash"} }, { "blockchain", "getdifficulty", getdifficulty, {} }, { "blockchain", "getmempoolancestors", getmempoolancestors, {"txid","verbose"} }, { "blockchain", "getmempooldescendants", getmempooldescendants, {"txid","verbose"} }, { "blockchain", "getmempoolentry", getmempoolentry, {"txid"} }, { "blockchain", "getmempoolinfo", getmempoolinfo, {} }, { "blockchain", "getrawmempool", getrawmempool, {"verbose"} }, { "blockchain", "gettxout", gettxout, {"txid","n","include_mempool"} }, { "blockchain", "gettxoutsetinfo", gettxoutsetinfo, {} }, { "blockchain", "pruneblockchain", pruneblockchain, {"height"} }, { "blockchain", "savemempool", savemempool, {} }, { "blockchain", "verifychain", verifychain, {"checklevel","nblocks"} }, { "blockchain", "preciousblock", preciousblock, {"blockhash"} }, /* Not shown in help */ { "hidden", "getfinalizedblockhash", getfinalizedblockhash, {} }, { "hidden", "finalizeblock", finalizeblock, {"blockhash"} }, { "hidden", "invalidateblock", invalidateblock, {"blockhash"} }, { "hidden", "parkblock", parkblock, {"blockhash"} }, { "hidden", "reconsiderblock", reconsiderblock, {"blockhash"} }, { "hidden", "syncwithvalidationinterfacequeue", syncwithvalidationinterfacequeue, {} }, { "hidden", "unparkblock", unparkblock, {"blockhash"} }, { "hidden", "waitfornewblock", waitfornewblock, {"timeout"} }, { "hidden", "waitforblock", waitforblock, {"blockhash","timeout"} }, { "hidden", "waitforblockheight", waitforblockheight, {"height","timeout"} }, }; // clang-format on void RegisterBlockchainRPCCommands(CRPCTable &t) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { t.appendCommand(commands[vcidx].name, &commands[vcidx]); } } diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index e896a84da..52de30d68 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -1,230 +1,232 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include class CRPCConvertParam { public: std::string methodName; //!< method whose params want conversion int paramIdx; //!< 0-based idx of param to convert std::string paramName; //!< parameter name }; /** * Specify a (method, idx, name) here if the argument is a non-string RPC * argument and needs to be converted from JSON. * * @note Parameter indexes start from 0. */ static const CRPCConvertParam vRPCConvertParams[] = { {"setmocktime", 0, "timestamp"}, {"generate", 0, "nblocks"}, {"generate", 1, "maxtries"}, {"generatetoaddress", 0, "nblocks"}, {"generatetoaddress", 2, "maxtries"}, {"getnetworkhashps", 0, "nblocks"}, {"getnetworkhashps", 1, "height"}, {"sendtoaddress", 1, "amount"}, {"sendtoaddress", 4, "subtractfeefromamount"}, {"settxfee", 0, "amount"}, {"getreceivedbyaddress", 1, "minconf"}, {"getreceivedbyaccount", 1, "minconf"}, {"getreceivedbylabel", 1, "minconf"}, {"listreceivedbyaddress", 0, "minconf"}, {"listreceivedbyaddress", 1, "include_empty"}, {"listreceivedbyaddress", 2, "include_watchonly"}, {"listreceivedbyaddress", 3, "address_filter"}, {"listreceivedbyaccount", 0, "minconf"}, {"listreceivedbyaccount", 1, "include_empty"}, {"listreceivedbyaccount", 2, "include_watchonly"}, {"listreceivedbylabel", 0, "minconf"}, {"listreceivedbylabel", 1, "include_empty"}, {"listreceivedbylabel", 2, "include_watchonly"}, {"getbalance", 1, "minconf"}, {"getbalance", 2, "include_watchonly"}, {"getblockhash", 0, "height"}, {"waitforblockheight", 0, "height"}, {"waitforblockheight", 1, "timeout"}, {"waitforblock", 1, "timeout"}, {"waitfornewblock", 0, "timeout"}, {"move", 2, "amount"}, {"move", 3, "minconf"}, {"sendfrom", 2, "amount"}, {"sendfrom", 3, "minconf"}, {"listtransactions", 1, "count"}, {"listtransactions", 2, "skip"}, {"listtransactions", 3, "include_watchonly"}, {"listaccounts", 0, "minconf"}, {"listaccounts", 1, "include_watchonly"}, {"walletpassphrase", 1, "timeout"}, {"getblocktemplate", 0, "template_request"}, {"listsinceblock", 1, "target_confirmations"}, {"listsinceblock", 2, "include_watchonly"}, {"listsinceblock", 3, "include_removed"}, {"sendmany", 1, "amounts"}, {"sendmany", 2, "minconf"}, {"sendmany", 4, "subtractfeefrom"}, {"addmultisigaddress", 0, "nrequired"}, {"addmultisigaddress", 1, "keys"}, {"createmultisig", 0, "nrequired"}, {"createmultisig", 1, "keys"}, {"listunspent", 0, "minconf"}, {"listunspent", 1, "maxconf"}, {"listunspent", 2, "addresses"}, {"listunspent", 3, "include_unsafe"}, {"listunspent", 4, "query_options"}, {"getblock", 1, "verbosity"}, {"getblock", 1, "verbose"}, {"getblockheader", 1, "verbose"}, {"getchaintxstats", 0, "nblocks"}, {"gettransaction", 1, "include_watchonly"}, {"getrawtransaction", 1, "verbose"}, {"createrawtransaction", 0, "inputs"}, {"createrawtransaction", 1, "outputs"}, {"createrawtransaction", 2, "locktime"}, {"signrawtransaction", 1, "prevtxs"}, {"signrawtransaction", 2, "privkeys"}, {"signrawtransactionwithkey", 1, "privkeys"}, {"signrawtransactionwithkey", 2, "prevtxs"}, {"signrawtransactionwithwallet", 1, "prevtxs"}, {"sendrawtransaction", 1, "allowhighfees"}, {"testmempoolaccept", 0, "rawtxs"}, {"testmempoolaccept", 1, "allowhighfees"}, {"combinerawtransaction", 0, "txs"}, {"fundrawtransaction", 1, "options"}, {"gettxout", 1, "n"}, {"gettxout", 2, "include_mempool"}, {"gettxoutproof", 0, "txids"}, {"lockunspent", 0, "unlock"}, {"lockunspent", 1, "transactions"}, {"importprivkey", 2, "rescan"}, {"importaddress", 2, "rescan"}, {"importaddress", 3, "p2sh"}, {"importpubkey", 2, "rescan"}, {"importmulti", 0, "requests"}, {"importmulti", 1, "options"}, {"verifychain", 0, "checklevel"}, {"verifychain", 1, "nblocks"}, + {"getblockstats", 0, "hash_or_height"}, + {"getblockstats", 1, "stats"}, {"pruneblockchain", 0, "height"}, {"keypoolrefill", 0, "newsize"}, {"getrawmempool", 0, "verbose"}, {"estimatefee", 0, "nblocks"}, {"prioritisetransaction", 1, "priority_delta"}, {"prioritisetransaction", 2, "fee_delta"}, {"setban", 2, "bantime"}, {"setban", 3, "absolute"}, {"setnetworkactive", 0, "state"}, {"getmempoolancestors", 1, "verbose"}, {"getmempooldescendants", 1, "verbose"}, {"disconnectnode", 1, "nodeid"}, // Echo with conversion (For testing only) {"echojson", 0, "arg0"}, {"echojson", 1, "arg1"}, {"echojson", 2, "arg2"}, {"echojson", 3, "arg3"}, {"echojson", 4, "arg4"}, {"echojson", 5, "arg5"}, {"echojson", 6, "arg6"}, {"echojson", 7, "arg7"}, {"echojson", 8, "arg8"}, {"echojson", 9, "arg9"}, {"rescanblockchain", 0, "start_height"}, {"rescanblockchain", 1, "stop_height"}, }; class CRPCConvertTable { private: std::set> members; std::set> membersByName; public: CRPCConvertTable(); bool convert(const std::string &method, int idx) { return (members.count(std::make_pair(method, idx)) > 0); } bool convert(const std::string &method, const std::string &name) { return (membersByName.count(std::make_pair(method, name)) > 0); } }; CRPCConvertTable::CRPCConvertTable() { const unsigned int n_elem = (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); for (unsigned int i = 0; i < n_elem; i++) { members.insert(std::make_pair(vRPCConvertParams[i].methodName, vRPCConvertParams[i].paramIdx)); membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName, vRPCConvertParams[i].paramName)); } } static CRPCConvertTable rpcCvtTable; /** * Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, * false, null) as well as objects and arrays. */ UniValue ParseNonRFCJSONValue(const std::string &strVal) { UniValue jVal; if (!jVal.read(std::string("[") + strVal + std::string("]")) || !jVal.isArray() || jVal.size() != 1) throw std::runtime_error(std::string("Error parsing JSON:") + strVal); return jVal[0]; } UniValue RPCConvertValues(const std::string &strMethod, const std::vector &strParams) { UniValue params(UniValue::VARR); for (unsigned int idx = 0; idx < strParams.size(); idx++) { const std::string &strVal = strParams[idx]; if (!rpcCvtTable.convert(strMethod, idx)) { // insert string value directly params.push_back(strVal); } else { // parse string as JSON, insert bool/number/object/etc. value params.push_back(ParseNonRFCJSONValue(strVal)); } } return params; } UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector &strParams) { UniValue params(UniValue::VOBJ); for (const std::string &s : strParams) { size_t pos = s.find("="); if (pos == std::string::npos) { throw(std::runtime_error("No '=' in named argument '" + s + "', this needs to be present for every " "argument (even if it is empty)")); } std::string name = s.substr(0, pos); std::string value = s.substr(pos + 1); if (!rpcCvtTable.convert(strMethod, name)) { // insert string value directly params.pushKV(name, value); } else { // parse string as JSON, insert bool/number/object/etc. value params.pushKV(name, ParseNonRFCJSONValue(value)); } } return params; } diff --git a/test/functional/data/rpc_getblockstats.json b/test/functional/data/rpc_getblockstats.json new file mode 100644 index 000000000..1e8aa69f7 --- /dev/null +++ b/test/functional/data/rpc_getblockstats.json @@ -0,0 +1,192 @@ +{ + "blocks": [ + "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f20020000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000", + "0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f603cceb3f73a7cb716e4302f895f710a6b2009fef95644e20bb63d48f5036c95fcae3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c510101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020a1b229d0834168737317fe9ff3f0efe41d0c1cf497610fa249f8f75fd3546a43ab6fdeb894ce3c54fb7cfc92eaac87c7dfd6cc9e62924c6c27f90b5b20c8e27cfdae3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c520101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020c0297229e84d3293c968f2d6bda4910b618213d600950beef17e0a31300038279050e9a2ab8e3f8760a0c710f620f3782285ddbccc30d1052d85ded7e91f2e90fdae3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c530101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020d2b56df3df41975d8d86daf76328e439a5742714b03f036107d33ef02fc5910b967da0754ee860c100650b978c4631c2475e08201fb419c168fe75430d9537e0feae3e5dffff7f20070000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c540101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000203b69e1a46ccec282ecee3d3fa7caf445b6af44cb835ee364fefe55e1568c4e03ec4689bb7de3436a6699ee6131891732d24dd50c9d10c4b710092b3c6f42dd94feae3e5dffff7f20040000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c550101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020a0740a8851ecf8dee724c1439ae1e41d36662dfee647e63273bc4b712e547c72d9d1f74c610fc82c269e1f745c76b856929ce97b3c742d9f20684e4db7af80affeae3e5dffff7f20040000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c560101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000207584e34d22e29fdbaf41dd4e008c9e8484333fa388ae435ecbeb5c0ee7055f1328650c6f963f174375b9d58a00590b65e610ffa330b60f297977c8f7d4f7ae75feae3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c570101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020e0ca49a7e455619f042730864472e0fdd56fe50a93a442f8771f34b74b9c23164e2d203ec788a7e63305b013a158534c0256122aa11a365bc82d2cd5d48a96c1ffae3e5dffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c580101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000207cb695ce37dbc0304bcc3de4a48af1592d214c12ebdfeba3a93226a46ef3e4625267144e15c69054189790a40115c5ad8e2bcfbce5ad35c9716cd622d0a22d5dffae3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c590101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020418601965e3425639211ed9bfe548b64032e04b4a09d8678ab364b3c12432b0273f7cfa05ba9dc5d7b2912c468064bad58fa5d1361940019f770ad7d935a612dffae3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c5a0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002090fa44e5eeb49c033ca107b06b5fb4b3f4853d1e8c9eda085220555948387053bb908de4cd1a4dd37cc55673a9c83d7620dbd54e3866e1986df94e5bb0a32422ffae3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c5b0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020651531451f950cb180c42268ae52ff12f5468bc8d253fb62dc787713786fca564a5ffab27e2315cc6a400b374e8859aaa67de3bc880706bf28a022bf90e2720affae3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c5c0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000206826a66b525bd3582b6ecd6d41c3552b5b960469360abae55816e005d4572f2610f7de6913bd1d6754a0ff66c9c25cfc23e66700692a394c4a166901393f594bffae3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c5d0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002031d35234484e6d7be919ffac7f25178166a44de3be0042d6975dd4d921c346171172d6a79b19626f7644210aba0980ecb809217cbd852ae29a004a601783d6ea00af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c5e0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020a994e380dd10ad4f5f9545d70708671dad34fee1adfe3751d7117f47daebba78b039ea59f25f30a2919c6adc330fb4837d506a4739c01f9e200cfb6509f4d77000af3e5dffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c5f0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020f354eda57d1fe7c4121dae09860a8aa1d876407f66b4d8eb6ba9b51abf31486eb77513d1e03f55b26c68bead3b704dde6d81e7b7eac8fa9beb3c9f23741e759e00af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c600101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020855596948e4de375893215035b18ecbdd5046e78180e3a6ccd056e1e12e3a16766b6230d60ead56152fbb95ea4bb7ebeedcf288f316ed4dba6b9feab48b72e9200af3e5dffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01110101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000201470dada492622a488ef6edae8578b2695f1698624fa68b5460cb59614f1f44bc86baaf5c9997b519d43d54e4d174636319f62866367182fb658d4e677f1c6c100af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01120101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020a9d11fde6a53b9a4fdeea3e1b7f7bd0563a65fd4f64ffaba5e3610c8ee29fe34d2ba93b50005955fa9b39460321714e402b49f8777d23039d0fea549ecb8ddf200af3e5dffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01130101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020a86cfbb128c3b3765f9770feb1de972ec41791cc6053fa029d1b4e50daa0bc59e5e2c8e3ba67ddff5e818e6f06422a2c980d9ce319c8960417e3f1c7460d27d001af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01140101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020c93d352c71a40dedcfd961345f979a41bae03d5d576e2448e35679e83e8c286e1af45f68f16fcd03b4afd878000e6e85c87c29fe96b2ed4867faa0067a58a5f901af3e5dffff7f20040000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01150101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020bdfc271ed953f1f97f1905741deaaf71002f6de64dc32556cbb64b4685920a7b36191e3cc96a5f302572e79d0dda9a684ca1df3456dcc878e4773216f2556ecc01af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01160101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020476f64499f6587927bca14bba4aba5a9bd26edc719a4a82581aadf4382028358aac04b5f98111371bd8658dfb169896073887301109189bbcbfbded7ac6bf32101af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01170101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020edf57cdeaedac8d3bd8e30e9dcb160e7af86f4f90c22bcc8628fb72b40711c25d0e8c1dc01bdcb406871341e25adaeb4c0204ad663c3f046ab05a447050dab9c01af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01180101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002074d0959b9657e7e374b459098fbcec42e0688ab9b0378bc94db84700e59d3d111d5ffb2f99d664ecfe4a19def8ca04538a3eba542ce1d9298cb652ac5325946101af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01190101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002032a35987a9168e265faaf16719c2ab0178409c8e988c2c9aed70398dfe6c230e4c8f78b6dbabf44263584964dc6d7ab8b0c081e15c6b66d0639ac621df4203da02af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d011a0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020c54ea8853336e632c7ecb2217a05c0cb592c7a6e0259d576721279b06f66626f69d46667c0ed1f4a61f89b8089c4a3bded1e09c85a9c1024292034df3dda22cc02af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d011b0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020bda7868d270391d11a4a5c9b2893b38e432139a14a91cc17570bd369d1cd884e8617ca924c9fdcbe394013624561643f06263b0f0e3154f19c439b41ef5c3afe02af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d011c0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000207318476fa8aec77230058f12eeaa4173dfe172a6d96a18ea72c26f181ca73714159c3f4eda97aea3b6476376b534924f169ac0616b8e7498a7ebf8ddc2180cda02af3e5dffff7f20050000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d011d0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020f69c2d1d16250b2e2fe15487ccfec5582829dec777ed814d11a982657b73154fcced0eeb3971e928a56409f5bd4599b138e9150c967218c45574cca7314d0c9702af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d011e0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020097d6d6130e29141f998ec0f75e6b9081cca2c58f77a1c3457e7ef388aa4031621cc9d5d3e1785861d1186d6e2b98d0363a7e701015329f1095d4e464b16dda302af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d011f0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020e0e30aa7f9945104a945693ffb06d99a47e873b5d3b89358c6c5b35629c91572afb1dab9d7d56d72929092ca5ffcedf54eca070e4a1ee4beb13d7844b687d00b03af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01200101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020a085d816d1a386a7a14ea593fcc10c80c3002288f34353d3b29886cb0cea5d4756d6bfebbefbc8f231673723c984e8bf3fab45bc4ce2cb97fcfba23d8a5e019303af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01210101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020276bf02315887fcc3239ab4742e369576a2f5647d77df8458dc986fa67ae866c8c3b44989f7cfa98b4bd70140a6dfe62d1e08e6040fd9c7f2101faf0a38182d803af3e5dffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01220101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000207f8d7f3ccdc937fa831d5a07d04d125729a19736f33202490a29a5da070cf875071b4606ca7a80ca328ee8aaf73dde99773064c472c6113d2ecb78ddd9ecaf7e03af3e5dffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01230101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020c0a51fa5f78e617a2b152e99a717c626ef4205ba27d40bea84e1da7354a9b11f1ea1f19ed0b86389be7a3cf84250ae4fea638a1b9b4c5d1f7fc8d02772ef538603af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01240101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000207c6aed7461f11b2e477dec1f71901e31a9b29da50c962b6492432be85866ec6b3a4ca3ac0aec68ada03f4792c99c6101dc7e28f7dd75e92cd34cf61048d0d56003af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01250101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020987b8c1fa17d09b669ae442161f2ead6e331b31f16df30a31c40ed99c0bc2c30e270fe0f6243ea281807d2d86429d99fa3811b83d0327a9cb92ec83e2920254704af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01260101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020ac0c66e32febed120d210b00c7bc4ca8837d7daf6528a95fd2700bceba45af5363433fde0a86bddc7a73b0ad33dc3795d7b13954eac3a7c9ce3a27b5b8b2b63404af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01270101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020b6ab687d84bf442827a095a0249ed885fa8ba80ad07f8b85519ffd246393e6772c126fe79be273718947e4f8d84b23cb00a749e894a47cba2a0770c35138572304af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01280101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020818c86781f0995cbaf913511877b0a56063fc0daf38dfc35df9e7b915c7774777d5cd38b5947ed0d5e692e46511dcce1c090d00d06c57999e8154243774c834204af3e5dffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01290101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020ce09a60de8ea5c4ef0e3a978cc8e2a6f6553f9b0d497f45cace6fcbcb786d50c110ed1bd88bb25662f0d37edad65281cd0290ee453d759ddcfa2abd8281af7d104af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d012a0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020e69da75c4d9de0ca885628346dc881049a621e54e4ba9c7f9b28a8d30267f915aa7e32793f0c10634104ae16cb141d33a8011ccfc1372a78bc12dd559f09453b04af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d012b0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020f04b6a02552122418368fbbe2ff06c53e645d9c23c032a995e5053082b49317bbbc8dfe78d06ec8b45ce5bd73b12b14541bd8ce8aaeb776c211fb60899d10d7f05af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d012c0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002033554aac61ef01e6707e094a98aeeaed6055a24724c6dc8ade13f8e410b5e267017e91213401d2c85b25a3bcf5c4892abd5fdd05e9a1c88c7db506c51b0e797705af3e5dffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d012d0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000203dd7328a391034ead240eb7b9fc9923c0dde4b30b7ab746b90f714d2f9f08541f6a697c4ed25ace6ea1de91d8725c15c7e6aee76aa3dde83036cc5e35bbb2fe505af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d012e0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000205069b1555baa6f2a77e49fd6ae8cc92f2475730dc459e78d83ae91b053882f56ca7be94fe40621eb884d23e6c2cc5b1e47c9c7cdfc639790de1c8d8bdb7efecb05af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d012f0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002045550e1e44685f5427f69a26e756dcc005b989125af1926926dc294167b45f77374ccc6868031ca1a32101281499c42f99bec2201aed1510b3d8b265ba73431405af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01300101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000209d6750f3ee1cb6dc08ee43bfe3e298af1a11b4709ba8060760a5601051af7503ef931bc5001f30d9ed4765cc11f32ccf650b0b0a9d219636db708b5a097b987905af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01310101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002041c64d1b85786976dd5ccb17c6765ca984d835551bbd2004b463af7e7638ff6ea14c87ebfed298d3aead8270010514a2e2f154ee18d9bd51130495f3cd3a2ed006af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01320101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020059d10e3d50555f7d03af6aac0d0dd637267d6645f29be7e829d7c4203402a4da1dbb136439c9317da2a9d22e702cdcfecf9eab2d83c61a18c421028fa07082406af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01330101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020249400878c5c819db559818f8c642bec2f10b05f1da22c4dc24a0aaa848527516782b0d42714f44634ed90b40adb4ce2afbfac8ed14624451edc8274eb9bf0ea06af3e5dffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01340101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020fa6163908cce50019a5dd137c7e36d4e1f46a2022718b9469ac46c0f42fd81191348316f65b142316320ad1a81db800c6a54909a8908d9ec295c37f70beeae0006af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01350101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020f660ccf491edca33073f344294d6c549892023a0ebca3ce100f51b16e896422a7837fed416d6b40fcdd075f92abb299e5dc3027beb2dc7bfc03d24ba1d3131d506af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01360101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020623d3c27c5731c7849ef02c09338ae09ebaca8b01fb82ee5964b74e42a8c8b1263ffe0292f0007bca9935fde929dc33d2cb2343dd6eec1eab0d3c9285370bdd306af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01370101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000208fec0aec0998dff808aa61e894365330eb33e02bd68db039c97347ed10f0cf4844608d0fcd3103dbfcba35e45f102c5b6a165f107530a1dee57ebb61a8127a4607af3e5dffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01380101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020273ea732b1bdc7e5d1a27886d5622998340d7f75207019aedd9f2857e7b2091f66aff85a92fafdfb7701d37e95e61ca850b1ac8d83aa0098ebeaddba4b012f5707af3e5dffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01390101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002095fb442987762ffb75ac53bcae5a482a9b16f29688fc53b142a8c80e9547030cfeea96c15d21fe07642e0bc3fb1f8f7f7e3aba7c564ee5ebef31ad74bace945907af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d013a0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000204ff62d8d1e0f292a018033f62118b6b2080a5cab3e6b495c1cc71ce4af760f532eb882e77b224e4f2981beed7a93deb83e298265de1cf6bea13f690449c64c2f07af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d013b0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000209a8b436302e4a835343f361a176e476f08ffd2be395a1350cc82875dd9f8ee7bdb7d8c159fc0e28c29b3c78d84b014b6972c92e0a5c4c400c2db3cd78779b5db07af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d013c0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020458604d5245623305c1c95f2a6c1481c37e231f55c7feb09fe4af6edc3363a6c26a70fb186c1c53925d320ea5a9f91a5a45c34addba482a1257b9707180787e707af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d013d0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002078a40d9067dc1df5a9ff2c56c7b68a1fb5ccd36db5da6064c8ffd190f0e78a719744c49f321dc7555bdf93a530fcfedf0a2aea297239d9762983d35961eca23608af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d013e0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002081926763bf50d959b4f4446ee6a36fd08a7799909f0965c64ed7fe2140ae6779d75e4e454436c5812b8b50d26ec3999d45f5ba4f5e7443dd6c93e3e598e48e8708af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d013f0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020ee27509923a5b34397e3d08b838cce5da3f8284b864cf0f112b2469f796f8f1801a8f1d361985f3f268f559c99e0569c31c9a749aecb5bf2f7fb32889277650e08af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01400101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020d0393ff8b46d4dfff2cb8cbc52a234621aa2315e72b73fbee3800049c5428f5ce9b50d7487eade308183111a83d777012a76e9ba30edcd6c57b4b5e82809553c08af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01410101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020f8928685487b724f55b9eed6881008278a193e8695dbbde23587d5f2290cbb5ae7326c8f208a5cfbe42b1f17588723b6d87f6dcd8bc814072385b6ee059c57fb08af3e5dffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01420101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002052dfdc3eee69bee8117e6ab324732911a926c4b0f50c0ba1270bfd5ec7eae542b772c98d599e12e0d31f0b6325231a51903d96253f2ffd36d825688beb56427008af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01430101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020a1682451d6e1dc07bc2787adcef3d0b54b01cf6e432795f7fe18c75476086b1af5dfb599b10e717ff51d1e452b7d81b95ee12966e45d121af3db3b7fdce39f2709af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01440101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002082621dd9b93a0527284715890d24d708c74735caa3911e2a0f24e3216ca4b22435d76890d30dc74ca01dddbf7089ce454a450b9464e18cd2ef57ab1547f1e40509af3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01450101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000203979376e3aded23a1c4cf5a29d96032b48b2fe79d93e5e6e1b82ac0876aa441139365c37d902d88b55d101060926205ecf39eec853f66bed9f8960a38175e19a09af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01460101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020fbf88f12c278a9d31a86ac4db23ecba825e4e709797394503c03e28bb2d84f6ccd9a1ecf1fb78f0deae918d7c4cec69ec5a78cbe83bc4bf3a86007fcd49a726109af3e5dffff7f20050000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01470101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002072a64d5fa12d6c182306f20530763fd88f3c78f34e5d4f27548dae339bc62268d91604be34c8ed5184efbec2e557b39129eaf8b3a6af7fed37a079ab1475e7b909af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01480101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020852810fdb8342ea03b951e3c4091a61691a4e688373b4337bf9308d749e12a3b42914188016ba1a8f15929612069b466c18b7be84744139b45b7357f30d9be0c09af3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01490101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020a1865284d852819c7914834b454c6074232bc6149ff171a5e6d8018de77a0427ecbce092c74f607d3faeedc8cdb9d0a5e12486a0fd753298ed8c290c8e5a62e30aaf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d014a0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000203f1e33d80e8ad13503774c520c16e3f4cfa9c7fc8de890f27ab65762948a593a76fc9b9e2c1feb40040fb339343fc7ec75f224ee96cf64d0c3e74a19731df1d80aaf3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d014b0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002060bc0f141abc2c39cbd380c2a873bb19f994b15e9580328a5c973c9164e3cd0b8d1faf9419aef34ebba2aeaeacc251c6f72fc8cd984b043bd34c96cee67dd8740aaf3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d014c0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000200da8bab855e03905beffd3f5f80af62f2195c06904e2b473347a3813bd5a67437293b495ec0672a64e92098bad1fae5756dc66254d39937303c249dbd55bd79a0aaf3e5dffff7f20080000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d014d0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000202020b33af211ab559011d5524645ec6cace7e6d421504240aff408443b380c5c844178adfe02bf625556489ba9fa032f955e48befa8372ac03fc78313f81f5ab0aaf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d014e0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002055de3e128847e9779444c37df254e27ae64848bd313f4387e521a69740a3b01b16b30c88655d6a2b28546234448abfc94b89725ceb3392f90a88bfe4231c9d280aaf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d014f0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020c95ed1f70d46c6f50f077bb215aa64ca8481de181925d3d1f68b938ba7a9fb16a78c01a1be5d15fd56851fdbea5675852ee8b95612fdc0fb6d6404c9de79bbb80baf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01500101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002098cd6d077d5372be2db5b0dea6ae0e2f84f4cd0d58489350d8cfbf0622630113e2e450e87381fff2ff71c9e3f9de544ea5ae54d35813ee8932f1090aa4f656ce0baf3e5dffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01510101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020066518ec278e9720795132250943bc76af640bbd8dd9bd047673df5c91be326b5f25aff8cf477b20e3ebc97b58790312386d1876c3b1cd06ea505d2c401392dd0baf3e5dffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01520101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020180a1786670b80df30a9dff70eac9f98000957f10152dc34968697d066ea38059f5c8d21c629c16479339f18438c79b682718e706b8d3acf3263ad84aced86570baf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01530101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020cbf4860b6f881758f6d71dcfa810e5da38122e878c5b099bfa871ae81fd3e040596ceba5ee82b571f8f64061ca4d9022f7e6d9acb75457765b7013267acd1c340baf3e5dffff7f20040000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01540101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020d60983308cc045c477be39bd0faf65abaf9531efc3264c2f89403e6bda35d17886a727743ef5798dddca45ce0e3b78ec4a6a6ba4bbd4d71e2efebe029e63848f0baf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01550101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020e3da2c18cc8743d6f9b6fb671bec7de0be1f5b3addf524f54954962839f44a5c1481f4e1446f2e82bdad3fc56ba71fd85d90092be9d4b2ca2cb23cbc9278f1df0caf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01560101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000201690b43645707eacf0b90021e78ca06efb56e73cf88605dc16a99d48f95d6c64549d34adb3aa78c5a91f7b353303f9955ea3e707a8b569f31f8581514616c66f0caf3e5dffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01570101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000205ffa114b5d07ec73994e33574cf47a3849399ef0bcef5b3e28183c7809956b36cf98c852c8cb2780c149ee0b939a0a65005d55488127a45103c14694a27e04c00caf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01580101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000201e63eaa9f3f4352299d3365d87a67a2113aa1766f295d2c8aa75d90808cac20b3477ef9069accc89ff24aae58dfc0533c06790815d1ae497375598d2298639210caf3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01590101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020077d1ec43ef98bfe8493d6241258d0c96a47fb57ba32d5300983732294e9dd4c31ae8e66eaf630acc8f92ab418c90dd63511dc6329bca92e455d189a5fa3da4d0caf3e5dffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d015a0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020543d35799aa94a2811cf51f1cebe3c368ea1664591ccb5773d737f3ef455c6553f84fb2dce964710c2a967c969486280615d6a24c23e299cacb89248909936910caf3e5dffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d015b0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002083d4badb13fead2214c5b9e80de4083472ba4a12f376d0efaf5c33d66218215ff50fa117390ec818dd4d01e39cf1243a77930d1d432284d44ad5665fed8ebdf90daf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d015c0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020e40b8b1acae19f7778538c69ee0d6defc851f3737089289df88b90210307de1c0006f17f9e19d453d649f899ce60d805e7969c76a9a3896c9c635398f566eee50daf3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d015d0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000204105546a9150a7fda81f855f789dc7e74c449fd1e90452d8505a098d4d328b113dbd0b909fcb4d0bf698dde11bf868cbed62ba1d73025178abfaac737496cbcc0daf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d015e0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020eafecc64b055e27f27c9970bed106ca394db15a7b37f2d10b686ee3161b5a039a2e190ac4ff4d92e71f3480f0486f8f8d19a5072546cc88b2d3ee0c4ae6e320b0daf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d015f0101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000203594cb0c8d6c44fcb3276245947ba08affaced26df41fe190c15e602d7d8b26b5854adc7c9168972e7948190e88a246b6b16689231337226890076a703b375220daf3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01600101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020b8de96b624d05db102e4d00cb01e167e9562a1bfd38c2e1b4ca9f31e5e8f5f63d132cb86b30801682b7eb051b66156e07c7bc17f59288f7821698d836bb5f79c0daf3e5dffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01610101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000202e6b1b2274f2343d8ba90516444633e802b18242750b8a466351e85ff0b62a67483769f2dca54c9d48ed4056e854d1e3f86e2a5470cbb2f54590f04f2909fe160eaf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01620101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "0000002034a40347b6ac7c059eae4f7fc4cfdf8293170bcf7b9de7cbeb74a5596f23512c3e677d5d36d96494a8e5aa4e51c9ee584931edaeed45bb39f6e7cef8cc73b1440eaf3e5dffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01630101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "000000209b5ebe3f645d941812f524c5f195b5f4c202d5d85d893519bee812f6a45e8c3e9003ebe6f07138bd8b2ea87ab53daf97e59d36dbfd1bca923fc6ec5224e0bad80eaf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01640101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020c97362443ee44a505c2d103ef639f9700e04098c374257fa7fde93d12edabe1fcc64a08492c6297a6de8163441eddfb4f930bb85771ed944370d9377ad16b7750eaf3e5dffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01650101082f454233322e302fffffffff0100f2052a0100000023210295b3f2a34ce06d50236eb6cf6faf28b00e183b9ec8e08d4e86bcbd770780b98fac00000000", + "00000020926cae5c4df5b8bfa22a69deda736348bfac3105ae4de6183dcfc7f8cf2aa67803bda646a6b744182b820a12ff3cdb42c9c1706905d557e312e97ccda499f2970eaf3e5dffff7f20000000000202000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01660101082f454233322e302fffffffff01c0f2052a010000002321039d727b812c66518bc0eea6e3cbee143671320982e680114b7929e22ed0e90d2fac000000000200000001603cceb3f73a7cb716e4302f895f710a6b2009fef95644e20bb63d48f5036c9500000000484730440220775e2fc69304f0dad57d52f68a907743fff75e8b76a02e7c6f175a767d3463f4022061d14b3e24c6923d987b4c17e317dc1b1ec2c6c34e1f20deedf68f97eab9762541feffffff0200286bee000000001976a914f6b0a1ca060a9efb017e24864af78cd7dbdfe3cf88ac40c99a3b000000001976a914db856722ecc6ec4384a829dfc11b6fa13be5d49688ac65000000", + "00000020fecc584e156ced58667116adc01c1734bae5a1b07ae7107bac62c27c08f5e0618d19fd58766ec3e05db1c5d1350dcfd10311f1eba7275cade3749626685a57fd0eaf3e5dffff7f20000000000402000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d01670101082f454233322e302fffffffff017afc062a01000000232102e2a7b752674549a7cd7ab1c223ba1d5625ffbc264ccb78309e0904ef57e6c97dac0000000002000000015104c87bd0b351ac8db6f0751d88f91521951a6886a28ecf8077b043f2269ea2000000006a473044022018fb5b50734a674002f17e0a001ef43a5882b4692612b0e2edcd99ba35613ca0022050dfdf34d6a6a4e6be8641a9b15dd4d189c0b9d482e4a2eae72692d78d24fc1741210317de7ed25740360d259b69f7ee0424141413d0f390f97d43393b8a2b0e47f8bcfeffffff02005ed0b2000000001976a9143eab965a0ad02931cb35f07831a7380657c2d75188ac1ec99a3b000000001976a9148c02f3ed52eca01c36c3a297d654274d4ceff1ae88ac6600000002000000015104c87bd0b351ac8db6f0751d88f91521951a6886a28ecf8077b043f2269ea2010000006a47304402205a683139bb02036f22c586504b3ef1e4e277506a5fb77d7527078cb87ffc269f022079dadf2773ce4c9066ad3ffbeec52ebf80013fc19650b41d2f7fbdc817276d88412103fa5bd147600176539310fff685df6bae8486a78872b4d8e88ba60571894d3a8cfeffffff0240e8a435000000001976a9142457f993fecf4eedbb0f015affa4403793f888d188ac28d8f405000000001976a9145028dd7b86c119d50fa4e022a0c6741a4e3f9b4488ac660000000200000001ab6fdeb894ce3c54fb7cfc92eaac87c7dfd6cc9e62924c6c27f90b5b20c8e27c0000000049483045022100d76b717ffe3c67b580f2971ec0457c19e14b26f25ece8ca09c5fff3d9a0160c5022027cf7e9784dc66ac09b100e3c7cb24386894af829e9955f652d9124898db795041feffffff0200ca9a3b000000001976a914352f9afc79af6613cde35bd314edd884119ca5c588ac40276bee000000001976a914344471651783fc715833b477f89677d69f3a271788ac66000000" + ], + "mocktime": 1564389116, + "stats": [ + { + "avgfee": 0.0, + "avgfeerate": 0.0, + "avgtxsize": 0, + "blockhash": "78a62acff8c7cf3d18e64dae0531acbf486373dade692aa2bfb8f54d5cae6c92", + "height": 101, + "ins": 0, + "maxfee": 0.0, + "maxfeerate": 0.0, + "maxtxsize": 0, + "medianfee": 0.0, + "medianfeerate": 0.0, + "mediantime": 1564389133, + "mediantxsize": 0, + "minfee": 0.0, + "minfeerate": 0.0, + "mintxsize": 0, + "outs": 1, + "subsidy": 50.0, + "time": 1564389134, + "total_out": 0.0, + "total_size": 0, + "totalfee": 0.0, + "txs": 1, + "utxo_increase": 1, + "utxo_size_inc": 85 + }, + { + "avgfee": 1.92e-06, + "avgfeerate": 1e-08, + "avgtxsize": 191, + "blockhash": "61e0f5087cc262ac7b10e77ab0a1e5ba34171cc0ad16716658ed6c154e58ccfe", + "height": 102, + "ins": 1, + "maxfee": 1.92e-06, + "maxfeerate": 1e-08, + "maxtxsize": 191, + "medianfee": 1.92e-06, + "medianfeerate": 1e-08, + "mediantime": 1564389133, + "mediantxsize": 191, + "minfee": 1.92e-06, + "minfeerate": 1e-08, + "mintxsize": 191, + "outs": 3, + "subsidy": 50.0, + "time": 1564389134, + "total_out": 49.99999808, + "total_size": 191, + "totalfee": 1.92e-06, + "txs": 2, + "utxo_increase": 2, + "utxo_size_inc": 150 + }, + { + "avgfee": 0.00022739, + "avgfeerate": 1.06e-06, + "avgtxsize": 214, + "blockhash": "1f5619699145227f1a725669a4a755689a95cedbc0a696f1964187264c596a86", + "height": 103, + "ins": 3, + "maxfee": 0.000678, + "maxfeerate": 3.01e-06, + "maxtxsize": 225, + "medianfee": 2.26e-06, + "medianfeerate": 1e-08, + "mediantime": 1564389134, + "mediantxsize": 225, + "minfee": 1.92e-06, + "minfeerate": 1e-08, + "mintxsize": 192, + "outs": 7, + "subsidy": 50.0, + "time": 1564389134, + "total_out": 99.9993159, + "total_size": 642, + "totalfee": 0.00068218, + "txs": 4, + "utxo_increase": 4, + "utxo_size_inc": 300 + } + ] +} \ No newline at end of file diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py new file mode 100755 index 000000000..ab7757b61 --- /dev/null +++ b/test/functional/rpc_getblockstats.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017-2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test getblockstats rpc call +# +import decimal +import json +import os +import time + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error, +) + +TESTSDIR = os.path.dirname(os.path.realpath(__file__)) + + +def EncodeDecimal(o): + if isinstance(o, decimal.Decimal): + # json.load will read a quoted float as a string and not convert it back + # to decimal, so store the value as unquoted float instead. + return float(o) + raise TypeError(repr(o) + " is not JSON serializable") + + +class GetblockstatsTest(BitcoinTestFramework): + + start_height = 101 + max_stat_pos = 2 + STATS_NEED_TXINDEX = [ + 'avgfee', + 'avgfeerate', + 'maxfee', + 'maxfeerate', + 'medianfee', + 'medianfeerate', + 'minfee', + 'minfeerate', + 'totalfee', + 'utxo_size_inc', + ] + + def add_options(self, parser): + parser.add_argument('--gen-test-data', dest='gen_test_data', + default=False, action='store_true', + help='Generate test data') + parser.add_argument('--test-data', dest='test_data', + default='data/rpc_getblockstats.json', + action='store', metavar='FILE', + help='Test data file') + + def set_test_params(self): + self.num_nodes = 2 + self.extra_args = [['-txindex'], ['-paytxfee=0.003']] + self.setup_clean_chain = True + + def get_stats(self): + return [self.nodes[0].getblockstats(hash_or_height=self.start_height + i) for i in range(self.max_stat_pos + 1)] + + def generate_test_data(self, filename): + mocktime = time.time() + self.nodes[0].generate(101) + + self.nodes[0].sendtoaddress( + address=self.nodes[1].getnewaddress(), amount=10, subtractfeefromamount=True) + self.nodes[0].generate(1) + self.sync_all() + + self.nodes[0].sendtoaddress( + address=self.nodes[0].getnewaddress(), amount=10, subtractfeefromamount=True) + self.nodes[0].sendtoaddress( + address=self.nodes[0].getnewaddress(), amount=10, subtractfeefromamount=False) + self.nodes[1].sendtoaddress( + address=self.nodes[0].getnewaddress(), amount=1, subtractfeefromamount=True) + self.sync_all() + self.nodes[0].generate(1) + + self.expected_stats = self.get_stats() + + blocks = [] + tip = self.nodes[0].getbestblockhash() + blockhash = None + height = 0 + while tip != blockhash: + blockhash = self.nodes[0].getblockhash(height) + blocks.append(self.nodes[0].getblock(blockhash, 0)) + height += 1 + + to_dump = { + 'blocks': blocks, + 'mocktime': int(mocktime), + 'stats': self.expected_stats, + } + with open(filename, 'w') as f: + json.dump(to_dump, f, sort_keys=True, + indent=2, default=EncodeDecimal) + + def load_test_data(self, filename): + with open(filename, 'r') as f: + d = json.load(f, parse_float=decimal.Decimal) + blocks = d['blocks'] + mocktime = d['mocktime'] + self.expected_stats = d['stats'] + self.log.info(self.expected_stats) + + # Set the timestamps from the file so that the nodes can get out of Initial Block Download + self.nodes[0].setmocktime(mocktime) + self.nodes[1].setmocktime(mocktime) + + for i, b in enumerate(blocks): + self.nodes[0].submitblock(b) + + def run_test(self): + test_data = os.path.join(TESTSDIR, self.options.test_data) + if self.options.gen_test_data: + self.generate_test_data(test_data) + else: + self.load_test_data(test_data) + + self.sync_all() + stats = self.get_stats() + expected_stats_noindex = [] + for stat_row in stats: + expected_stats_noindex.append( + {k: v for k, v in stat_row.items() if k not in self.STATS_NEED_TXINDEX}) + + # Make sure all valid statistics are included but nothing else is + expected_keys = self.expected_stats[0].keys() + assert_equal(set(stats[0].keys()), set(expected_keys)) + + assert_equal(stats[0]['height'], self.start_height) + assert_equal(stats[self.max_stat_pos]['height'], + self.start_height + self.max_stat_pos) + + for i in range(self.max_stat_pos + 1): + self.log.info('Checking block {}\n'.format(i)) + assert_equal(stats[i], self.expected_stats[i]) + + # Check selecting block by hash too + blockhash = self.expected_stats[i]['blockhash'] + stats_by_hash = self.nodes[0].getblockstats( + hash_or_height=blockhash) + assert_equal(stats_by_hash, self.expected_stats[i]) + + # Check with the node that has no txindex + stats_no_txindex = self.nodes[1].getblockstats( + hash_or_height=blockhash, stats=list(expected_stats_noindex[i].keys())) + assert_equal(stats_no_txindex, expected_stats_noindex[i]) + + # Make sure each stat can be queried on its own + for stat in expected_keys: + for i in range(self.max_stat_pos + 1): + result = self.nodes[0].getblockstats( + hash_or_height=self.start_height + i, stats=[stat]) + assert_equal(list(result.keys()), [stat]) + if result[stat] != self.expected_stats[i][stat]: + self.log.info('result[{}] ({}) failed, {!r} != {!r}'.format( + stat, i, result[stat], self.expected_stats[i][stat])) + assert_equal(result[stat], self.expected_stats[i][stat]) + + # Make sure only the selected statistics are included (more than one) + some_stats = {'minfee', 'maxfee'} + stats = self.nodes[0].getblockstats( + hash_or_height=1, stats=list(some_stats)) + assert_equal(set(stats.keys()), some_stats) + + # Test invalid parameters raise the proper json exceptions + tip = self.start_height + self.max_stat_pos + assert_raises_rpc_error(-8, 'Target block height {} after current tip {}'.format( + tip + 1, tip), self.nodes[0].getblockstats, hash_or_height=tip + 1) + assert_raises_rpc_error(-8, 'Target block height {} is negative'.format(-1), + self.nodes[0].getblockstats, hash_or_height=-1) + + # Make sure not valid stats aren't allowed + inv_sel_stat = 'asdfghjkl' + inv_stats = [ + [inv_sel_stat], + ['minfee', inv_sel_stat], + [inv_sel_stat, 'minfee'], + ['minfee', inv_sel_stat, 'maxfee'], + ] + for inv_stat in inv_stats: + assert_raises_rpc_error(-8, 'Invalid selected statistic {}'.format( + inv_sel_stat), self.nodes[0].getblockstats, hash_or_height=1, stats=inv_stat) + + # Make sure we aren't always returning inv_sel_stat as the culprit stat + assert_raises_rpc_error(-8, 'Invalid selected statistic aaa{}'.format(inv_sel_stat), + self.nodes[0].getblockstats, hash_or_height=1, stats=['minfee', 'aaa{}'.format(inv_sel_stat)]) + + assert_raises_rpc_error(-8, 'One or more of the selected stats requires -txindex enabled', + self.nodes[1].getblockstats, hash_or_height=self.start_height + self.max_stat_pos) + + # Mainchain's genesis block shouldn't be found on regtest + assert_raises_rpc_error(-5, 'Block not found', self.nodes[0].getblockstats, + hash_or_height='000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f') + + +if __name__ == '__main__': + GetblockstatsTest().main()