Skip to content

Commit

Permalink
[RPC] add 'getblockindexstats' function
Browse files Browse the repository at this point in the history
  • Loading branch information
random-zebra committed Jun 7, 2019
1 parent 458b08c commit 689ac23
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 81 deletions.
281 changes: 200 additions & 81 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <iostream>
#include <univalue.h>
#include <mutex>
#include <numeric>
#include <condition_variable>

using namespace std;
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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 <height> <range> [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"
Expand All @@ -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);
Expand Down Expand Up @@ -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 <height> <range> [fVerbose]\n"
"\nLook the inputs of any tx in a range of blocks and returns the serial numbers for any coinspend.\n"

"\nArguments:\n"
Expand All @@ -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) {
Expand Down Expand Up @@ -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 <height> <range> [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<libzerocoin::CoinDenomination, int64_t> mapMintCount;
std::map<libzerocoin::CoinDenomination, int64_t> 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;

}

3 changes: 3 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
1 change: 1 addition & 0 deletions src/rpc/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 689ac23

Please sign in to comment.