From 689ac23f62b8dbfd4de99497085a4b068af70dc2 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Fri, 7 Jun 2019 18:51:51 +0200 Subject: [PATCH] [RPC] add 'getblockindexstats' function --- src/rpc/blockchain.cpp | 281 +++++++++++++++++++++++++++++------------ src/rpc/client.cpp | 3 + src/rpc/server.cpp | 1 + src/rpc/server.h | 2 + 4 files changed, 206 insertions(+), 81 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 7fe601864fcc6..06fc6a9923533 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include using namespace std; @@ -1028,66 +1029,18 @@ UniValue getfeeinfo(const UniValue& params, bool fHelp) "\nExamples:\n" + HelpExampleCli("getfeeinfo", "5") + HelpExampleRpc("getfeeinfo", "5")); - LOCK(cs_main); - int nBlocks = params[0].get_int(); int nBestHeight = chainActive.Height(); int nStartHeight = nBestHeight - nBlocks; if (nBlocks < 0 || nStartHeight <= 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid start height"); - CAmount nFees = 0; - int64_t nBytes = 0; - int64_t nTotal = 0; - for (int i = nStartHeight; i <= nBestHeight; i++) { - CBlockIndex* pindex = chainActive[i]; - CBlock block; - if (!ReadBlockFromDisk(block, pindex)) - throw JSONRPCError(RPC_DATABASE_ERROR, "failed to read block from disk"); - - CAmount nValueIn = 0; - CAmount nValueOut = 0; - for (const CTransaction& tx : block.vtx) { - if (tx.IsCoinBase() || tx.IsCoinStake()) - continue; - - for (unsigned int j = 0; j < tx.vin.size(); j++) { - if (tx.vin[j].IsZerocoinSpend() || tx.vin[j].IsZerocoinPublicSpend()) { - nValueIn += tx.vin[j].nSequence * COIN; - continue; - } - - COutPoint prevout = tx.vin[j].prevout; - CTransaction txPrev; - uint256 hashBlock; - if(!GetTransaction(prevout.hash, txPrev, hashBlock, true)) - throw JSONRPCError(RPC_DATABASE_ERROR, "failed to read tx from disk"); - nValueIn += txPrev.vout[prevout.n].nValue; - } - - for (unsigned int j = 0; j < tx.vout.size(); j++) { - nValueOut += tx.vout[j].nValue; - } - - nFees += nValueIn - nValueOut; - nBytes += tx.GetSerializeSize(SER_NETWORK, CLIENT_VERSION); - nTotal++; - } - - pindex = chainActive.Next(pindex); - if (!pindex) - break; - } - - UniValue ret(UniValue::VOBJ); - CFeeRate nFeeRate = CFeeRate(nFees, nBytes); - ret.push_back(Pair("txcount", (int64_t)nTotal)); - ret.push_back(Pair("txbytes", (int64_t)nBytes)); - ret.push_back(Pair("ttlfee", FormatMoney(nFees))); - ret.push_back(Pair("feeperkb", FormatMoney(nFeeRate.GetFeePerK()))); - ret.push_back(Pair("rec_highpriorityfee_perkb", FormatMoney(nFeeRate.GetFeePerK() + 1000))); + UniValue newParams(UniValue::VARR); + newParams.push_back(UniValue(nStartHeight)); + newParams.push_back(UniValue(nBlocks)); + newParams.push_back(UniValue(true)); // fFeeOnly - return ret; + return getblockindexstats(newParams, false); } UniValue mempoolInfoToJSON() @@ -1316,17 +1269,46 @@ UniValue getaccumulatorwitness(const UniValue& params, bool fHelp) return obj; } +void validaterange(const UniValue& params, int& heightStart, int& heightEnd, int minHeightStart) +{ + if (params.size() < 2) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Not enough parameters in validaterange"); + } + + const int nBestHeight = chainActive.Height(); + + heightStart = params[0].get_int(); + if (heightStart > nBestHeight) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid starting block (%d). Out of range.", heightStart)); + } + + const int range = params[1].get_int(); + if (range < 1) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range. Must be strictly positive."); + } + + heightEnd = heightStart + range - 1; + + if (heightStart < minHeightStart && heightEnd >= minHeightStart) { + heightStart = minHeightStart; + } + + if (heightEnd > nBestHeight) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid ending block (%d). Out of range.", heightEnd)); + } +} + UniValue getmintsinblocks(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 3) throw runtime_error( - "getmintsinblocks \"height\" \"range\" \"coinDenomination\"\n" + "getmintsinblocks [coinDenomination]\n" "\nReturns the number of mints of a certain denomination" "\noccurred in blocks [height, height+1, height+2, ..., height+range-1]\n" "\nArguments:\n" "1. height (numeric, required) block height where the search starts.\n" "2. range (numeric, required) number of blocks to include.\n" - "2. coinDenomination (numeric, required) coin denomination.\n" + "3. coinDenomination (numeric, required) coin denomination.\n" "\nResult:\n" "{\n" @@ -1339,19 +1321,10 @@ UniValue getmintsinblocks(const UniValue& params, bool fHelp) { HelpExampleCli("getmintsinblocks", "1200000 1000 5") + HelpExampleRpc("getmintsinblocks", "1200000, 1000, 5")); - int nBestHeight = chainActive.Height(); - - int heightStart = params[0].get_int(); - if (heightStart < Params().Zerocoin_StartHeight()) - heightStart = Params().Zerocoin_StartHeight(); - - int range = params[1].get_int(); - if (range < 1) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range. Must be strictly positive."); + LOCK(cs_main); - int heightEnd = heightStart + range - 1; - if (heightEnd > nBestHeight) - heightEnd = nBestHeight; + int heightStart, heightEnd; + validaterange(params, heightStart, heightEnd, Params().Zerocoin_StartHeight()); int d = params[2].get_int(); libzerocoin::CoinDenomination denom = libzerocoin::IntToZerocoinDenomination(d); @@ -1381,7 +1354,7 @@ UniValue getmintsinblocks(const UniValue& params, bool fHelp) { UniValue getserials(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 3) throw runtime_error( - "getserials \"hash\"\n" + "getserials [fVerbose]\n" "\nLook the inputs of any tx in a range of blocks and returns the serial numbers for any coinspend.\n" "\nArguments:\n" @@ -1395,19 +1368,8 @@ UniValue getserials(const UniValue& params, bool fHelp) { LOCK(cs_main); - int nBestHeight = chainActive.Height(); - - int heightStart = params[0].get_int(); - if (heightStart < Params().Zerocoin_StartHeight()) - heightStart = Params().Zerocoin_StartHeight(); - - int range = params[1].get_int(); - if (range < 1) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range. Must be strictly positive."); - - int heightEnd = heightStart + range - 1; - if (heightEnd > nBestHeight) - heightEnd = nBestHeight; + int heightStart, heightEnd; + validaterange(params, heightStart, heightEnd, Params().Zerocoin_StartHeight()); bool fVerbose = false; if (params.size() > 2) { @@ -1496,4 +1458,161 @@ UniValue getserials(const UniValue& params, bool fHelp) { } +UniValue getblockindexstats(const UniValue& params, bool fHelp) { + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error( + "getblockindexstats [fFeeOnly]\n" + "\nReturns aggregated BlockIndex data for blocks " + "\n[height, height+1, height+2, ..., height+range-1]\n" + + "\nArguments:\n" + "1. height (numeric, required) block height where the search starts.\n" + "2. range (numeric, required) number of blocks to include.\n" + "3. fFeeOnly (boolean, optional, default=False) return only fee info.\n" + + "\nResult:\n" + "{\n" + " \"first_block\": \"x\" (integer) First counted block\n" + " \"last_block\": \"x\" (integer) Last counted block\n" + " \"txcount\": xxxxx (numeric) tx count (excluding coinbase/coinstake)\n" + " \"txcount_tot\": xxxxx (numeric) tx count (including coinbase/coinstake)\n" + " \"mintcount\": { [if fFeeOnly=False]\n" + " \"denom_1\": xxxx (numeric) number of mints of denom_1 occurred over the block range\n" + " \"denom_5\": xxxx (numeric) number of mints of denom_5 occurred over the block range\n" + " ... ... number of mints of other denominations: ..., 10, 50, 100, 500, 1000, 5000\n" + " }\n" + " \"spendcount\": { [if fFeeOnly=False]\n" + " \"denom_1\": xxxx (numeric) number of spends of denom_1 occurred over the block range\n" + " \"denom_5\": xxxx (numeric) number of spends of denom_5 occurred over the block range\n" + " ... ... number of spends of other denominations: ..., 10, 50, 100, 500, 1000, 5000\n" + " }\n" + " \"txbytes\": xxxxx (numeric) Sum of the size of all txes over block range\n" + " \"ttlfee\": xxxxx (numeric) Sum of the fee amount of all txes over block range\n" + " \"feeperkb\": xxxxx (numeric) Average fee per kb\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getblockindexstats", "1200000 1000") + + HelpExampleRpc("getblockindexstats", "1200000, 1000")); + + LOCK(cs_main); + + int heightStart, heightEnd; + validaterange(params, heightStart, heightEnd); + // return object + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("Starting block", heightStart)); + ret.push_back(Pair("Ending block", heightEnd)); + + bool fFeeOnly = false; + if (params.size() > 2) { + fFeeOnly = params[2].get_bool(); + } + + CAmount nFees = 0; + int64_t nBytes = 0; + int64_t nTxCount = 0; + int64_t nTxCount_tot = 0; + + std::map mapMintCount; + std::map mapSpendCount; + for (auto& denom : libzerocoin::zerocoinDenomList) { + mapMintCount.insert(make_pair(denom, 0)); + mapSpendCount.insert(make_pair(denom, 0)); + } + + CBlockIndex* pindex = chainActive[heightStart]; + + while (true) { + CBlock block; + if (!ReadBlockFromDisk(block, pindex)) { + throw JSONRPCError(RPC_DATABASE_ERROR, "failed to read block from disk"); + } + + CAmount nValueIn = 0; + CAmount nValueOut = 0; + nTxCount_tot += block.vtx.size(); + + // loop through each tx in block + for (const CTransaction& tx : block.vtx) { + if (tx.IsCoinBase()) + continue; + + if (tx.HasZerocoinSpendInputs()) { + for (unsigned int j = 0; j < tx.vin.size(); j++) { + if (tx.vin[j].IsZerocoinSpend() || tx.vin[j].IsZerocoinPublicSpend()) { + mapSpendCount[libzerocoin::IntToZerocoinDenomination(tx.vin[j].nSequence)]++; + } + } + } + + if (tx.IsCoinStake()) { + continue; + } + + nTxCount++; + + // fetch input value from prevouts + for (unsigned int j = 0; j < tx.vin.size(); j++) { + if (tx.vin[j].IsZerocoinSpend() || tx.vin[j].IsZerocoinPublicSpend()) { + nValueIn += tx.vin[j].nSequence * COIN; + continue; + } + + COutPoint prevout = tx.vin[j].prevout; + CTransaction txPrev; + uint256 hashBlock; + if(!GetTransaction(prevout.hash, txPrev, hashBlock, true)) + throw JSONRPCError(RPC_DATABASE_ERROR, "failed to read tx from disk"); + nValueIn += txPrev.vout[prevout.n].nValue; + } + + // sum output values in nValueOut + for (unsigned int j = 0; j < tx.vout.size(); j++) { + nValueOut += tx.vout[j].nValue; + } + + // update sums + nFees += nValueIn - nValueOut; + nBytes += tx.GetSerializeSize(SER_NETWORK, CLIENT_VERSION); + } + + // add mints to map + if (!fFeeOnly) { + for (auto& denom : libzerocoin::zerocoinDenomList) { + mapMintCount[denom] += count(pindex->vMintDenominationsInBlock.begin(), pindex->vMintDenominationsInBlock.end(), denom); + } + } + + if (pindex->nHeight < heightEnd) { + pindex = chainActive.Next(pindex); + } else { + break; + } + } + + // get fee rate + CFeeRate nFeeRate = CFeeRate(nFees, nBytes); + + // return UniValue object + ret.push_back(Pair("txcount", (int64_t)nTxCount)); + ret.push_back(Pair("txcount_tot", (int64_t)nTxCount_tot)); + if (!fFeeOnly) { + UniValue mint_obj(UniValue::VOBJ); + UniValue spend_obj(UniValue::VOBJ); + for (auto& denom : libzerocoin::zerocoinDenomList) { + mint_obj.push_back(Pair(strprintf("denom_%d", ZerocoinDenominationToInt(denom)), mapMintCount[denom])); + spend_obj.push_back(Pair(strprintf("denom_%d", ZerocoinDenominationToInt(denom)), mapSpendCount[denom])); + } + ret.push_back(Pair("mintcount", mint_obj)); + ret.push_back(Pair("spendcount", spend_obj)); + + } + ret.push_back(Pair("txbytes", (int64_t)nBytes)); + ret.push_back(Pair("ttlfee", FormatMoney(nFees))); + ret.push_back(Pair("feeperkb", FormatMoney(nFeeRate.GetFeePerK()))); + + return ret; + +} diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 0552cd19b9934..5c9914dfe581f 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -150,6 +150,9 @@ static const CRPCConvertParam vRPCConvertParams[] = {"getaccumulatorwitness",2}, {"getmintsvalues", 2}, {"enableautomintaddress", 0}, + {"getblockindexstats", 0}, + {"getblockindexstats", 1}, + {"getblockindexstats", 2}, {"getmintsinblocks", 0}, {"getmintsinblocks", 1}, {"getmintsinblocks", 2}, diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index fe2dcac364885..9d253209a6025 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -304,6 +304,7 @@ static const CRPCCommand vRPCCommands[] = {"blockchain", "findserial", &findserial, true, false, false}, {"blockchain", "getaccumulatorvalues", &getaccumulatorvalues, true, false, false}, {"blockchain", "getaccumulatorwitness", &getaccumulatorwitness, true, false, false}, + {"blockchain", "getblockindexstats", &getblockindexstats, true, false, false}, {"blockchain", "getmintsinblocks", &getmintsinblocks, true, false, false}, {"blockchain", "getserials", &getserials, true, false, false}, {"blockchain", "getblockchaininfo", &getblockchaininfo, true, false, false}, diff --git a/src/rpc/server.h b/src/rpc/server.h index de807b9414c59..e8cc0205a5856 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -308,9 +308,11 @@ extern UniValue invalidateblock(const UniValue& params, bool fHelp); extern UniValue reconsiderblock(const UniValue& params, bool fHelp); extern UniValue getaccumulatorvalues(const UniValue& params, bool fHelp); extern UniValue getaccumulatorwitness(const UniValue& params, bool fHelp); +extern UniValue getblockindexstats(const UniValue& params, bool fHelp); extern UniValue getmintsinblocks(const UniValue& params, bool fHelp); extern UniValue getserials(const UniValue& params, bool fHelp); extern UniValue getchecksumblock(const UniValue& params, bool fHelp); +extern void validaterange(const UniValue& params, int& heightStart, int& heightEnd, int minHeightStart=1); extern UniValue getpoolinfo(const UniValue& params, bool fHelp); // in rpc/masternode.cpp extern UniValue listmasternodes(const UniValue& params, bool fHelp);