diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 900d863d8c..0d576dbea1 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -15,6 +15,7 @@ #include "consensus/validation.h" #include "validation.h" #include "core_io.h" +// #include "index/txindex.h" #include "policy/feerate.h" #include "policy/policy.h" #include "primitives/transaction.h" @@ -37,6 +38,7 @@ #include +#include #include // boost::thread::interrupt #include @@ -835,6 +837,26 @@ UniValue getblockheaders(const JSONRPCRequest& request) return arrHeaders; } +static CBlock GetBlockChecked(const CBlockIndex* pblockindex) +{ + CBlock block; + if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) { + throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); + } + + if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) { + // 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; +} + + UniValue getmerkleblocks(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) @@ -881,21 +903,8 @@ UniValue getmerkleblocks(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Count is out of range"); } - CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; - - if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) { - throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); - } - - if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) { - // 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"); - } + CBlock block = GetBlockChecked(pblockindex); UniValue arrMerkleBlocks(UniValue::VARR); @@ -993,19 +1002,8 @@ UniValue getblock(const JSONRPCRequest& request) if (mapBlockIndex.count(hash) == 0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; - - if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) - throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); - - if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) - // 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"); + const CBlock block = GetBlockChecked(pblockindex); if (verbosity <= 0) { @@ -1804,6 +1802,259 @@ UniValue getchaintxstats(const JSONRPCRequest& request) return ret; } +template +static T CalculateTruncatedMedian(std::vector& scores) +{ + size_t size = scores.size(); + if (size == 0) { + return 0; + } + + 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 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 duffs.\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\": xxxxx, (numeric) Average fee in the block\n" + " \"avgfeerate\": xxxxx, (numeric) Average feerate (in duffs 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 duffs per byte)\n" + " \"maxtxsize\": xxxxx, (numeric) Maximum transaction size\n" + " \"medianfee\": xxxxx, (numeric) Truncated median fee in the block\n" + " \"medianfeerate\": xxxxx, (numeric) Truncated median feerate (in duffs per byte)\n" + " \"mediantime\": xxxxx, (numeric) The block median time past\n" + " \"mediantxsize\": xxxxx, (numeric) Truncated median transaction size\n" + " \"minfee\": xxxxx, (numeric) Minimum fee in the block\n" + " \"minfeerate\": xxxxx, (numeric) Minimum feerate (in duffs per byte)\n" + " \"mintxsize\": xxxxx, (numeric) Minimum transaction size\n" + " \"outs\": xxxxx, (numeric) The number of outputs\n" + " \"subsidy\": xxxxx, (numeric) The block subsidy\n" + " \"time\": xxxxx, (numeric) The block time\n" + " \"total_out\": xxxxx, (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\": xxxxx, (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 uint256 hash = ParseHashV(request.params[0], "parameter 1"); + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + pindex = mapBlockIndex[hash]; + // 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(pindex); + + const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default) + 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_all || do_mediantxsize || + SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "avgfeerate", "medianfeerate", "minfeerate", "maxfeerate"); + + CAmount maxfee = 0; + CAmount maxfeerate = 0; + CAmount minfee = MAX_MONEY; + CAmount minfeerate = MAX_MONEY; + CAmount total_out = 0; + CAmount totalfee = 0; + int64_t inputs = 0; + int64_t maxtxsize = 0; + int64_t mintxsize = MaxBlockSize(true); + 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(); + + CAmount tx_total_out = 0; + 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; + } + + inputs += tx->vin.size(); // Don't count coinbase's fake input + total_out += tx_total_out; // Don't count coinbase reward + + 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 (!fTxIndex) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled"); + } + CAmount tx_total_in = 0; + for (const CTxIn& in : tx->vin) { + CTransactionRef tx_in; + uint256 hashBlock; + if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock, false)) { + throw JSONRPCError(RPC_INTERNAL_ERROR, std::string("Unexpected internal error (tx index seems corrupt)")); + } + + CTxOut prevoutput = tx_in->vout[in.prevout.n]; + + tx_total_in += prevoutput.nValue; + utxo_size_inc -= GetSerializeSize(prevoutput, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; + } + + CAmount 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; + + CAmount feerate = tx_size ? txfee / tx_size : 0; + 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", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0); + ret_all.pushKV("avgfeerate", total_size ? totalfee / total_size : 0); // Unit: sat/byte + 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", maxfee); + ret_all.pushKV("maxfeerate", maxfeerate); + ret_all.pushKV("maxtxsize", maxtxsize); + ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array)); + ret_all.pushKV("medianfeerate", CalculateTruncatedMedian(feerate_array)); + ret_all.pushKV("mediantime", pindex->GetMedianTimePast()); + ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array)); + ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee); + ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate); + ret_all.pushKV("mintxsize", mintxsize == MaxBlockSize(true) ? 0 : mintxsize); + ret_all.pushKV("outs", outputs); + ret_all.pushKV("subsidy", pindex->pprev ? GetBlockSubsidy(pindex->pprev->nBits, pindex->pprev->nHeight, Params().GetConsensus()) : 50 * COIN); + ret_all.pushKV("time", pindex->GetBlockTime()); + ret_all.pushKV("total_out", total_out); + ret_all.pushKV("total_size", total_size); + ret_all.pushKV("totalfee", 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; +} + UniValue getspecialtxes(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 5) @@ -1871,14 +2122,8 @@ UniValue getspecialtxes(const JSONRPCRequest& request) if (mapBlockIndex.count(hash) == 0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; - - if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); - - if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + const CBlock block = GetBlockChecked(pblockindex); int nTxNum = 0; UniValue result(UniValue::VARR); @@ -1916,6 +2161,7 @@ static const CRPCCommand commands[] = // --------------------- ------------------------ ----------------------- ------ ---------- { "blockchain", "getblockchaininfo", &getblockchaininfo, true, {} }, { "blockchain", "getchaintxstats", &getchaintxstats, true, {"nblocks", "blockhash"} }, + { "blockchain", "getblockstats", &getblockstats, true, {"hash_or_height", "stats"} }, { "blockchain", "getbestblockhash", &getbestblockhash, true, {} }, { "blockchain", "getblockcount", &getblockcount, true, {} }, { "blockchain", "getblock", &getblock, true, {"blockhash","verbosity|verbose"} }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index f5bba96e5d..c8de1d75c0 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -135,6 +135,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "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" }, diff --git a/test/functional/data/rpc_getblockstats.json b/test/functional/data/rpc_getblockstats.json new file mode 100644 index 0000000000..f896190700 --- /dev/null +++ b/test/functional/data/rpc_getblockstats.json @@ -0,0 +1,192 @@ +{ + "blocks": [ + "010000000000000000000000000000000000000000000000000000000000000000000000c762a6567f3cc092f0684bb62b7e00a84890b990f07cc71a6bb58d64b98e02e0b9968054ffff7f20ffba10000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6204ffff001d01044c5957697265642030392f4a616e2f3230313420546865204772616e64204578706572696d656e7420476f6573204c6976653a204f76657273746f636b2e636f6d204973204e6f7720416363657074696e6720426974636f696e73ffffffff0100f2052a010000004341040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9ac00000000", + "000000202e3df23eec5cd6a86edd509539028e2c3a3dc05315eb28f2baa43218ca080000d95bb928a2b4d3efb01ceb96a9d4d11f9ef238c189f43ab970b5819c4e5397feba968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000207fcf56a1a310cc4105b6b815fa8b2d11dc50c1a7896886a6b6836667e0132c7dbaa1d7dc2bc6a35265e613348723653552ad0db810346ed10af24ea90d1b4c39bb968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000208e27356a6e97c5edf4bc661b4713462e238560029346c4966102dd6ad6869e24e578c0ebcda8b67ad71142c25b50802a6a292253ac3584be187c229b2e8f48d3bb968054ffff7f20040000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03530101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020b6a89f04f71289c39585f5f6f9529a472d9e233cea6a66a1cc4992e6506650669cc0cd7aad221ffcf3c17c42c99c1c6b9af43ebf87de78f5b74ad5bc8ad7fcc7bc968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03540101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020067660919c37f6517850647faf69df27477528e543133b05a8545d560e7142048ce7479a046e59174bdda33e31bc5298a26ca02f0797603a3b011f0122b9026fbc968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03550101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000202110f07ef7f230fd8e3f09d8455fc56aa4c789730f501ab06de5467f6d4cf91c9a445c5f674b7c10739957ebeb1d14942656dd1253f2cc67a4d0912cba11cf21bc968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03560101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002019aa592464cecd9e1da8ee4bb7cc45aa1b0b5ced7ad0fb9b56603f096d008365059e39106cc7eaf51838014b4bf60b351f7102b81dcde08d046da1e94bfdebbbbc968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03570101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000200d00164ed9c1e1260fcb9f792b6245de1f005cd7c317209fd80bea526ca50f4f34b08d41fe4b10354ae2ad53ff03b40bf52123c215f7a2128fba218f9c8aedefbd968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03580101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000208555657f1f767d8684901a9cc2c0e7d3a58e2862897ef69269b11d5a35d53f2d8c277833310f206a698042adcdf4546d3e37c2e9bf22759fee027a9f98bb4045bd968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03590101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000201bb32c3e1e30e3fe233a298a4046504b58d4027168feee92782ca933b12229683ff642613e755030d0e05a999cb734dca8c2997fb785e64976db49f2bf20b1f6bd968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff035a0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002093ea7db83c1b00f767c5d9a8444a89e673c76650fcd236641d779273f33b8c6a20b271d972a916aec8287502ac54c3ac5d70bf512ef5a14e5fc9b48a29e9fa8ebd968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff035b0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020ed0a183573cb44513ed1406d5778cb297bee55e23601639215997e9fa2947074fcb51d5ead454819a55dbde18a7dd11deefdd8b398608368491a740cc17d40d4bd968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff035c0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000202d020ee433c6b6b3b57e6860445624fe3261ebe8c1a896ed7ad771fa4fe53828b0eab16e3b711bf20a45f7496afaa97b7cb61f741a2575ff67d41c0cc41061a9bd968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff035d0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020a6618af38c19782cb86f4ae9523e25122c83f7d4ad86c3de6269b03cc277552dee036f8b7ea9aa003b00779651b9b1d15ebb1fa325a2fe7907da3a973e7ece68be968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff035e0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000209f2a2e0e63ebe21dae0e544d869a6681a2ce1a8d32a9bb61efbbfb60bf9d0068bd8c6b5d05955040983240f7320f2f6350c31f3dd6bf269f233c44c85416fa27be968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff035f0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020dae294386f34ebefd5600a489a42468bd77b9d9a94f9f0047bffdb6d85147e5ef923d9baf7b33b22335e78698e1ad814ce326e0c8d58a85ed5d76f3a43661e01be968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03600101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000203f153035956b92074431af6c4ab48215b5dfa73ded1d66e0af608503faf3937a96682d1bbc49b8b2b7820e9c1baac0194739e7107648db2cccdbd73d9fd90d7dbe968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401110101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020eb9b52c5bad46a2cccf1c6b455fa1c71108a247d861ffef731d64ffe9349d77d1bb4965dc2f60f4f10c0a4fcf6d32f8f7967a6a6f9ff4ff0081d35365e8f8ca7be968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401120101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000209198a452354ce3d41c31251984c9bde0774936c008729109eab8f87189ff2649905e51b5a9a28498667a927d54643a42650d7f92bcb9881c04f0afd68a432e4fbe968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401130101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020db8504d154d783caba552cda6517a7f467393c8208a00ae7df9706cfa4728f30d9e8beb25174e5bff0a57c75395a2e8d59b77a255450f4778be4ca45efaa5258bf968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401140101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000208e52e044a1a5b3e55ceea15d4463862bb06c429eea8b698ca4cd62559cfe3f7b7aa8e1c0db4603a9a4fafe4b8557c2b6b4d894a6b2329b652f075607e5df6ad4bf968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401150101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002070d4304efdb679ae5591e14753d318ef6db3d311576b51257f29626915b6860ed8f54db28d10cca91740b47776ca83c1ac7f5bd96fd0941bea3c2543f75f4f42bf968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401160101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020b18da05265704504a19343d8c799074b6a750fbe25671f2598801fb32ba3a22b5abeebb1cdff271f6ab4f1c0a2ca36847c21f489165a3dc7aafcc8901a201511bf968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401170101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020ffa6c8d91068a66b3d0cfe6cddbdb5c18a2d888b5ecd63c5a85203f062c0fe3b255b8aefb34245268d17b5cc88f59adc1a46383fdc3df5d9878e27aee0df9c7ebf968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401180101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000206bcb560d13c12b0f5b7f2eb035248c3020ff6b576d77204fab40d38833b49627e84cd4791a83f46d7571ba998f89d14cce2a588f133d04fc3153dc07b5448265bf968054ffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401190101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020bcea973bde6a30d912996ca008e164c59e840b27d4cc99578ce9ff39e1dc676ff415730295094f95726c8a39a4adb431d4de1d176482c20c6f17166c846fd8b1c0968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04011a0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020e3a41004882bc5ad6a67b59033eab40d52a2f4e7b988ebb6948cfab531b5e92eaba53a44c76230dd29b1a10bee19b6960d30d32f3abbbd490b5489b7bf5495bec0968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04011b0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000207f50cddfa5645e01402425d702643ff4cdfcaf2f78cdb0f04054e9713a524e09602706e93374f1c07e9902e0e97df0107f415ef58478a6b5d4072a7c5a38cdffc0968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04011c0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020d0d1df53efaeed01e846f93085245daaf95b6eceb70fb2d6a5097dedde99b049eac1aad944efb3384318c64ad869f7b5fe68840d6d1d043e6cc43c0b743efacac0968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04011d0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002009c4d650f5b21f072b23a3a51724a1f91ce39396d2882ccca3f8b14b21a7b56d4a668bbb125670902b49ede6cd4e2f5c0da12165627b68be2bf8595748282422c0968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04011e0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020a68f7ec4f61e611a85f1c8bac0758bd8b96382b51b1b89d464ac2707d080192ff9cec0863d5e1241557a580cdd8843bf6c995e8caaec8f8e84b10f1c8f9529d7c0968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04011f0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002018c6071e1e2e04480634a8c1567e9a608c874ac1da2aed1d41ec227908148a3029a5b5cb57fd440de434b181085a6fcd9edb11b3f39faaf64fa938b158ca2678c1968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401200101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020e3e21313f5dff437e8d214028aa4686962bb39b0e07036495c59d15fc6098c4e517de3e01b55d9ffca6ca146796b8f615956ec068fdc9e09e900ae7fb8f93750c1968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401210101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020e45c518bdd7df129efab4ffdcce28b341bc34865815f79805b71c09f48f7345ff27efb73f28f7bde71db1e77423375a3db091d3079cf4f1ad262bae64bb3b876c1968054ffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401220101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000202c3defd4604f5a44d619e2b4a7778c71e245b3b946dc8a0df72eb231f1e34f4dc38fd422d26fa5bdee5ecb507afffc55276f4d4631831548a25056e63b0764f1c1968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401230101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002044d5a4767d099d1ce66db9cea8ad0b0269886aadda67f5cd456810ac4c9be61a4d8eee55635eb72a3063015d1b253fc6736d9171e205faf2dd7849bf431928cbc1968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401240101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020df1c63486eef1b1fcaf66298bc72c4ca3fa18cdfc9ae16fcc44e2fb476a8d374579c409f4a869c944257bcb84425b3314266efb09df739745faddffd6caf88e0c1968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401250101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000204b95c58d49293ee93c829caf0e168c5d4dc351b3f03c5cfd90d783fc23ecb26c256d8d1d5d652172154f66af36a2941e2dd64a5c72e1c164325e6791e970ea7ac2968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401260101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020b98f8c9019c2f15fc20e9feac7be5a34c8aee095c892d856fb03404c957c8e07fefb7e7f058744a84fa66f8a01ad7497a21a214cfaf0273a91fe3e52bc25724fc2968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401270101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020ef4a19c437935069b145e84a07be7e4674e6d67631c51e8d70d57dbd14f0a40fc63d7525b75f0539d56997c22d0991cf123dfbd04793824c2aacc62f77b917d1c2968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401280101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000208b1f0937d79bbbded175ac9ae9a7a0071ce3889175b3d56e50813f3d953cdb394e4c4bffcd52b7b921311c27ee643fde75453ba0d9355a5a29bcf2a8d9a6c865c2968054ffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401290101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002059d9defc2c41ecd3472b27bb624c19fed2e898c95d1f31c961f34383e03ca31bf6d098c6397a08116cfaa80d75088f0e5348b8290b29fd0b4c9b17407ce8fb61c2968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04012a0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020fcf24d49e1cd74612ecce9d2f98769638f68d8cb40191330e2d1e418d7c4a93e3bd0172c8fe1869be148ed31276d50beed3278dc1b579bcf1dd18369f8b045b9c2968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04012b0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002096b5ce0c70ea0c4700b3ec1c3ffe2c9ef8d7dd1b69208657d9142d832785b6674283d2ce1f7c63664d1a76ca64d0149cb7ac04c7445b5b2cd62da985b4820a6fc3968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04012c0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002025713aacb9c435d23ef2c6451f28ac7c450169143f26f097fc9ab82d561f5c58d0fd6d62f513609348ff1bac5426fcd91688b7271dcc82451389e7a9240f1988c3968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04012d0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000201e57b1039b3c12a9b4cae4c4c6c3eb4bd9e8172237f3b66307c628962efab336f4dc0e0f0c7ec239f3699a537b353d93fa0423c15d6a2f6f479bb7490adab0c3c3968054ffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04012e0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020a10993c816215503a3e33ecb4c13252cd7f3b2d779385e3e8fbae114c6479c3ad8cdc73f080baff2ba3e233fbb3f11a3de923bd209da3e58451f4e56c9bdb5a2c3968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04012f0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002080c814a4fe681dfbbb6acdd01dc109d7738bbfa98de412815df7986b2ae22d67585ca5c8c31b252069b7027639be58d96f7cbfe70c0a490b22f925a07ecd8ce8c3968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401300101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000206e4a5ce81b8d7ddf16161d374dc83131904c546dac3b733263768309de76276c22d27317a6e4067d9b0e67b30321acc5d5a6b5ddc3721077e898a6b0c859795ec3968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401310101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020cf3eb1d6e5e4b794b6791ada605f5212c5116fb9a2b5a071f082dedc59c2523b894e3a27194ac986fd643de4118d6192760556b03b5dae801986102ac772c278c4968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401320101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020448ce084a8e488210ed86a85800239f32c148041e164ae01b9833f1ec581d649970486e728fe3d0f0651e6949c6650f761961ce908bb70a6c13968f84cbde132c4968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401330101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000202cc74bf3b3a16026333164df71543790d964f9b6f1bec0ddc9f843edfa10f57dab5af2b5f11840097f80ead19ccfe621046660f0caec2fb907894651c349f439c4968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401340101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020fc0b565f57b248b00a1c72d6ef2d045c772ab6f2ed4b8550a6692a98fef8dc3403b1c2d42d9365dc8dc709c04b3bd389eef6d8050f2fe28a2a59c15d46c35dfec4968054ffff7f20050000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401350101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000206581c8b5f8e3271301eca1f650f4b12bf7a6ef4389be521245c153d3e6c84223a8c6c146ff1f8d0f4bb420c6bff5224087e9d8758d3cb1220757d53dc7db000fc4968054ffff7f20040000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401360101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000209c8b4e77d2ec79b70059a12eea0ed2c3a1892d340229b85073c685b4b0e63927fa7ac8eff9288431cabbddfc66772542833362113e2e9713016df1547254025bc4968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401370101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020c559a9e1086d9d2f2b66b0db9386daf061d8b470fa6251e850a0011d6d968a46843397dd87173632f1c2585ef83023f25f48dbd9b89f6a73a1238e4d5ffce787c5968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401380101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000207a2c3bd37d5e59cfae2df7c985460fbf5df3827078fedcb07505c3e595e7d40389c0538d91d5d275093b8b7e094beb7a6b00ff8d1e413bc9b9c68f062a4ff463c5968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401390101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002094a48b7cec0d18270314893209cb581e50cf6f1eb667a01c9ec7835f3b437066ab7ab7241cdebe6a5a201a464a08a7126f1410fa3a0021800ea4d1a46d7d7db4c5968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04013a0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002001fbc84b7f31fc9b01e81f19e14c22d4f386e210e9b25d0f410c12f1078e7c3d5db275b1f9fd2cffeeaececf5a6d41c5de4e227d3f0e426e6784cd342b935de4c5968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04013b0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002003c254f2b65b6bfdb0f1b66cb9c8f17a0a0833eaffcff66d784761ef7ee8e027f0cce75f6f59dbb3bfc080068b62f622c61878942fc64fc29e76a6cfa97768afc5968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04013c0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000209ac5170773ab5950842d8c05800cd9142009fd2f2e43bad0ac009c172377db790d58126e684e40cd2fe623a424c9ff680e9367d992337a69b1d085d3b74a406bc5968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04013d0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000206a6f17881995afada19a945de7805948f1bde48ae2655a8763b47e0ad7e3c01005024958a8dfc1e0adcedf31eba27ac3ef2a0b8b574f41a881c605337bc4a1bbc6968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04013e0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020dfb95e17c85412abe164ff43d302c5567b42f15c8e0b73a041db2aa12080240e49107bf761371cda528ca44e189ffd07c9de4a64f835112339da62afd22441d1c6968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04013f0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002066a951d878d002478d15fd59ea9961a7212f10cc8ba22c7252af27a9b421a74da875ffeee73ae312d8172be74230d2afdb1c79e3c50d24faf0d177b7e4e614d5c6968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401400101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020413728bfba18a95203077c7c3627349d88ac8a3ad981902e15b908b45c4cde197d834d320ae121a1ccfb0fbd24147625c206eef436351e4eec1b16b8501366cdc6968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401410101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020e20b4be2808d8f3877bbd9bce136dc05e510da48d98524316dca6613f44d437907924ff519cbd7172368c08dd3c61e17339ceb956f77fd97259f09bffa71183cc6968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401420101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000201e15a9985bfd9ced0a9d3fb0f4452e8e7ca86fd565f1a31f737e90f153ba9d224abe9a27b9eafc06ce5cd7d8adf71527839a2e616c0885d85a6dfb8c455efcc5c6968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401430101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020da2914eff086f2780c7e052050d3613ce867dabf271c8f6655a12b2d8830d056ad8bd9f1f53ce32fa39e662339e5cab0d494f023e43297d9a024f927fd494d05c7968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401440101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020a9bebba4f45297a617a24ceaff1a8e52c7e4f5e8c36f7ce2b3ddee1829ce8e3f1ad6d7241335b6164c70f3546721aee27077db1fb7eee5bc4fcf31dd480352e0c7968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401450101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020b99f49545d15c5606479e40bcd827ee78ae30426d77602d340c5cdd23519e267e90fbf303e1dbd36d9866892fe7c324790cb67183213edf4932472ba51be2e9cc7968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401460101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002019f2cfe65a501beb47554ba482b4ecf350fa75da95675af923609239cd27f672ff4c208b9cedec0547042f592e7f0429ecbf6b672c7ccac45ac087ed27857886c7968054ffff7f20050000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401470101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000201a07c5c0b11c184f7688eec5f4765b330d2785fb0a6eed4544e6a3befeb347639374ef3bc633c5ac02654a1944db2c2c068e3da5fa95f2ff0cbb83c8d0d4c45fc7968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401480101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002018809dc9b6de89bcfc75ed7c285153c2f3b25ec4f95869efbb4c0999f97878594038a7a99b60afde6a17ca7f7cbf734e8faf7dd7a341fb02a2af9356a196bde7c7968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401490101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000204d72257f642fb619e59ebf687692cc72f968106b9eead697dd9bb021af65f142d622d78a617b738a5bb9606678eb25b5f0884e6268531ba9296a761c569f788bc8968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04014a0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002094dfad4e8b5e63cd9e39a307dcfcee019e151a972543471f61f805c539252140fefdd16c208e8633a05b864bf60a4a93ee71b98a7b8125e25681442d7c9bf538c8968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04014b0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000204935a3ff051219232abb3b604baa911383ce24ead00ae3a3c6063464860040013f7818eae4a13b712ff945e9fdf3b3eb37bcaeb33df8fd80243b4bc9d287b5c0c8968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04014c0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000202c330267a4ed9436d24ef59cf8d6d1bdc6942046b2451917b1d3145ecadf993bbaa22905da24f918eea8853976ebf2836aa4acacc51d41822b6917f756147f98c8968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04014d0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020dd73782c4f8a93663e23e8be418b4e50268c0de7226a77ae36a573fbc0f16870173d3d33a8ac186e969d9bc1c4b74c5a5f50944c703d185be7ef0c71b1730214c8968054ffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04014e0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000207485444d5feed9bb2e3765e776bf2aa691fc31ac79ac65781768579bf9b60c632225485f95794701b7a8f2399673b4d1003ea87fa80a4a1351573c52db8576c3c8968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04014f0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020480950162d22b925aebc47b43acebb4cf06f79091360159161ae122c6da01654c260b195d5418320d60e3d51496a3e87f7aa321a04a4afb5c0e755b09e993243c9968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401500101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000207de07a24bb6e7d8019cf7f4d31316abe4c18b769c4fbb9b6bb2941ab49612a42225be312165cefb753433a394fb735d8ae21d788738ffdf64cd28780a0abf25dc9968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401510101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020b57350a195f62cbd292965acd1c0cb92be4a429bd4e4bab926c75ce83901ea64dab8796f9c1b80684d27dee14df3ad211d75ceb9afbb5d4a894942b3cc7c3102c9968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401520101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020bb3252615f4249f23caec5f76059ed424334328267ae1d3197cbeb285b115b613390c34632cc17ddd721aceed4c62dd5ba49f186b8324bc26a0ca982a68d6434c9968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401530101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000204bbf8b03fc9ce8993e48484b7db67188b6e9b646a51e16bd644ab00e4dbe24773ad044d19eafefdb6b4c6d65ececa7337a0a0064f3196b1d46fd4af4343275acc9968054ffff7f20040000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401540101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020a0b1eaac5fbf6ab980465a5be1d8f31db4196903067ed5b859030f3dca4dc95981889dd07230b24a75ee5cd2fcb3e45895e80850cbe7fc7ab78e5b08f1357601c9968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401550101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000204162aa81039c26c262accee0bd8371d1d2d6b677f07b1ab529928de10ba8de757ffa4ca475962e0bda93321757c41a6a5f515a7b5c0f7de6a28840de23216bb8ca968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401560101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000207aca991bb589dba9951725740e66e5594515ddae23f2ef2893388c26712087602857bc58f1f998c468a532c8626554fd04b878e3350ddff0caf628e1a806148aca968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401570101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020960bdf6bfc82efb561e5c4b471ea78807fdb8b157660371b16f1e4dac96e5f063596d3c5267468af37d63aa993274092dd3be81f98360867979230c454d10a4dca968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401580101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000203baaed5d8e61fbdc450bab30fe5272b039609905b222592788a1c5a58447957ebf3ac597821a09206b918da40cc25cbade47cdd5225752641974bebace56edc8ca968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401590101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020dd1e1a5d30b17205f9bb909cffde17256e652946cf4b00a7d61e24c1393d36703a0a1f7021f029b359d76e54b669d67ceb2c49f6dbbd3d904f0edc5867144a8dca968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04015a0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020d4df9e6bfb3bdbd4141b3b23e05feccd1d4aa5571921f20714a0b7c7ffca251aaa5455bf04393250685f9c1c0584644fe3f14ee8155229b0e2f81059dd461201ca968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04015b0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020c79f05a1e07d74455841f6ad39b1c3d1ae21082b01f51f1f479fac41e16e8923bc27a86f966b1cdb51e7ccefdda6d065171b63e22a5e0b9a22ee2df893e44e5fcb968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04015c0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020dc1eeeb842e3fd10f5fc87c9ea4a80b469edb444b0c467075ffbc84bf5ec33289697c074dfcafeb6b820702f76af188afcf515e2f1be613378cf005c1e82a9efcb968054ffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04015d0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000203f9c492a4a4685c0e566b550f6fe9916111e7db45bb57b371234e57d60243242e27cfb5e0923a0307ea05506a8f68db3d1df04d7f3f9ab19762bc3a5878642a4cb968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04015e0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020863e87e6652dfe6b59836492bc0f3fb37a82ef2529ff15a1aede36d2063dd331c451fad00de58fbcb3a1b8ffdb6e1e2b7c90a81c0c2fb0a7d55bd16bce1d90a7cb968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff04015f0101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000205385df1f1536c3605f689c63cb703c8dd5172b6397b1ba70d20e7906b57fa93cc27d3af5e7f96c2fc6b3e4d78273ebfb637f7eab33b35d55f290586267e3e398cb968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401600101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020ed05b823ddf4554fd9045193277a0faab20a70e891027f0596e3b249174d264bba1779eccc576afc207a3cc6a8e557c02bd9cbe3a8ce5ae54167749a1002448fcb968054ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401610101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "000000201efffecbd5318020fe163d345818b7d65aaa360455ccb7355cea82b12d55de42587b4d615f41213977e4cd1afa8db8d348faa48db853356b58dc96db003af152cc968054ffff7f20040000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401620101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002010ecbaf4182ceb024d15fc78746de38eeec04534b080062ae68c8479cbb012229dc232c0f7c87d468b95f6828a6f8352adc1f6d943daf591819a9c0197e8888acc968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401630101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020908845e301e51d871ef682c4af4fce6b3c4ed3fcf19d75cde19be1732ed0430a5a5dc14043b54478caee581965be5e03ba18ae204953a5f912eb7e55d1aeecf6cc968054ffff7f20010000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401640101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "00000020970b5854877f7185951a7533c1eab484f9fedb0f1f2777f75ceab6c4a623f462150382883dab55e415bc27930cab432681f6ef4026d261a455ca753159c32548cc968054ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401650101ffffffff0100743ba40b000000232102caa63f5d3c98e3c3288bf2d48781a37310413e5ae8bd1739fcdc5acf2a531df6ac00000000", + "0000002099a4c0d31444c28fd4342ca134b6b48cd631fe035becde97bd25101a564f6501ce6e718907f6b9b8a5c3d1f3da745ae701009300608ef96c525bdc69f0cc2f8bcc968054ffff7f20010000000202000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401660101ffffffff01c0743ba40b0000002321032db0e0841321363240d1d23c080293af0d09d513738ee265c1bf28afe7676352ac000000000200000001d95bb928a2b4d3efb01ceb96a9d4d11f9ef238c189f43ab970b5819c4e5397fe00000000494830450221009a83977e9c09f8689812d8ab9e4abaf8a165f949ed7fd7518210153705dba8b6022007cce45c49b9d1f0b38e1135f02474943f904d5f889b5b40c1ed4b93c4908d0c01feffffff0240c99a3b000000001976a914ee3d2fdcc2a2e196f8cd2926d4f2ab35db622b2188ac00aaa0680b0000001976a914521eb1d6939e11bb510cbce081a0a47db46b92c488ac65000000", + "00000020431436f4c273aefda5852964afb7db65472a6e77479c62b230a96902a8a09c6595e4787454c7977ef362bae2e9337221222ef86ee3a5883ce21f773c81c5c84acc968054ffff7f20010000000402000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401670101ffffffff017a7e3ca40b00000023210287d435ce15fb58e759a59d3521ecfc60c460dcd1ef5626a53302e50c54f904e6ac000000000200000001d1d98a1afcd724adf7d8562332bf354076a34fbe01c7ffd9d9e398a7e556f9ed000000006b483045022100fb7d906e086c85817f0a825db10476fd2d05fe7643262afe3498af7af220dbe102202de82497c0834ab6ef4ec67d06df0a898dbfd6c2f6001e90c13fb8d08674e08701210385d80e72a67e94af5ef2b1b7bb597bee585b60ff75b1dbe1637c1eb2c68c8dc7feffffff0228d8f405000000001976a914cf21eeb09f8459175ef0423ef2aa77a01d279baf88ac40e8a435000000001976a9145ac57136a313e96730372a674a8c989e18d699c288ac660000000200000001d1d98a1afcd724adf7d8562332bf354076a34fbe01c7ffd9d9e398a7e556f9ed010000006a473044022038360e9581bb1029736b9f53dedc6cb2fd6ef4ed9daf40ceb3f070c7c2c49c9e022032fdba975c5f2a390010e04f30dbfc05d8b6bfab8804b8b8fbc03c8cb9a955b401210304de6780a2a0d390c31f9a6955aab2e4bb723e7ce360fa506c7c13f8b61f95ebfeffffff021ec99a3b000000001976a9148054f981b25c1df6e135681cad224509520e16b588ac00e0052d0b0000001976a91443a569f0e4d6d476dcb63906c90017c17b0ac6b988ac0d0000000200000001baa1d7dc2bc6a35265e613348723653552ad0db810346ed10af24ea90d1b4c39000000004948304502210085fc86a66d6f9ef0f792f9ba1bb3bb5d66af88aa3d78a1c7d09bc095556c89a1022043dc7c73643930ac1a78b4f8210a35c35182683f0303c95927a877274c80847b01feffffff0200ca9a3b000000001976a9140be309740066ab801360d8fff294d1d1e3049e5488ac40a9a0680b0000001976a9149ec13209532c8315efd4f4fc92c1b840fb3ec99988ac25000000" + ], + "mocktime": 1417713337, + "stats": [ + { + "avgfee": 0, + "avgfeerate": 0, + "avgtxsize": 0, + "blockhash": "01654f561a1025bd97deec5b03fe31d68cb4b634a12c34d48fc24414d3c0a499", + "height": 101, + "ins": 0, + "maxfee": 0, + "maxfeerate": 0, + "maxtxsize": 0, + "medianfee": 0, + "medianfeerate": 0, + "mediantime": 1417713355, + "mediantxsize": 0, + "minfee": 0, + "minfeerate": 0, + "mintxsize": 0, + "outs": 1, + "subsidy": 50000000000, + "time": 1417713356, + "total_out": 0, + "total_size": 0, + "totalfee": 0, + "txs": 1, + "utxo_increase": 1, + "utxo_size_inc": 85 + }, + { + "avgfee": 192, + "avgfeerate": 1, + "avgtxsize": 192, + "blockhash": "659ca0a80269a930b2629c47776e2a4765dbb7af642985a5fdae73c2f4361443", + "height": 102, + "ins": 1, + "maxfee": 192, + "maxfeerate": 1, + "maxtxsize": 192, + "medianfee": 192, + "medianfeerate": 1, + "mediantime": 1417713355, + "mediantxsize": 192, + "minfee": 192, + "minfeerate": 1, + "mintxsize": 192, + "outs": 3, + "subsidy": 50000000000, + "time": 1417713356, + "total_out": 49999999808, + "total_size": 192, + "totalfee": 192, + "txs": 2, + "utxo_increase": 2, + "utxo_size_inc": 150 + }, + { + "avgfee": 22739, + "avgfeerate": 106, + "avgtxsize": 214, + "blockhash": "1be37db8b04b80d607631fe5d77351b27eabee984140a790bd1b852a715f0e6d", + "height": 103, + "ins": 3, + "maxfee": 67800, + "maxfeerate": 300, + "maxtxsize": 226, + "medianfee": 226, + "medianfeerate": 1, + "mediantime": 1417713356, + "mediantxsize": 225, + "minfee": 192, + "minfeerate": 1, + "mintxsize": 192, + "outs": 7, + "subsidy": 50000000000, + "time": 1417713356, + "total_out": 99999931590, + "total_size": 643, + "totalfee": 68218, + "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 0000000000..3c338cd264 --- /dev/null +++ b/test/functional/rpc_getblockstats.py @@ -0,0 +1,181 @@ +#!/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 +# +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + assert_raises_jsonrpc, +) +import json +import os +import time + +TESTSDIR = os.path.dirname(os.path.realpath(__file__)) + +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_option('--gen-test-data', dest='gen_test_data', + default=False, action='store_true', + help='Generate test data') + parser.add_option('--test-data', dest='test_data', + default='data/rpc_getblockstats.json', + action='store', metavar='FILE', + help='Test data file') + + # def set_test_params(self): + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.extra_args = [['-txindex'], ['-txindex=0', '-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): + 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(self.mocktime), + 'stats': self.expected_stats, + } + with open(filename, 'w') as f: + json.dump(to_dump, f, sort_keys=True, indent=2) + + def load_test_data(self, filename): + with open(filename, 'r') as f: + d = json.load(f) + blocks = d['blocks'] + self.mocktime = d['mocktime'] + self.expected_stats = d['stats'] + + # Set the timestamps from the file so that the nodes can get out of Initial Block Download + self.nodes[0].setmocktime(self.mocktime) + self.nodes[1].setmocktime(self.mocktime) + + for b in 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 %d\n' % (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[%s] (%d) failed, %r != %r' % ( + 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_jsonrpc(-8, 'Target block height %d after current tip %d' % (tip+1, tip), + self.nodes[0].getblockstats, hash_or_height=tip+1) + assert_raises_jsonrpc(-8, 'Target block height %d is negative' % (-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_jsonrpc(-8, 'Invalid selected statistic %s' % 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_jsonrpc(-8, 'Invalid selected statistic aaa%s' % inv_sel_stat, + self.nodes[0].getblockstats, hash_or_height=1, stats=['minfee' , 'aaa%s' % inv_sel_stat]) + + assert_raises_jsonrpc(-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_jsonrpc(-5, 'Block not found', self.nodes[0].getblockstats, + hash_or_height='000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f') + +if __name__ == '__main__': + GetblockstatsTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 9d14f06c74..5f08a9620f 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -126,6 +126,7 @@ BASE_SCRIPTS= [ 'p2p-leaktests.py', 'p2p-compactblocks.py', 'sporks.py', + 'rpc_getblockstats.py', 'p2p-fingerprint.py', 'wallet-encryption.py', 'uptime.py',