Skip to content

Commit 689ac23

Browse files
committed
[RPC] add 'getblockindexstats' function
1 parent 458b08c commit 689ac23

File tree

4 files changed

+206
-81
lines changed

4 files changed

+206
-81
lines changed

src/rpc/blockchain.cpp

Lines changed: 200 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <iostream>
2626
#include <univalue.h>
2727
#include <mutex>
28+
#include <numeric>
2829
#include <condition_variable>
2930

3031
using namespace std;
@@ -1028,66 +1029,18 @@ UniValue getfeeinfo(const UniValue& params, bool fHelp)
10281029
"\nExamples:\n" +
10291030
HelpExampleCli("getfeeinfo", "5") + HelpExampleRpc("getfeeinfo", "5"));
10301031

1031-
LOCK(cs_main);
1032-
10331032
int nBlocks = params[0].get_int();
10341033
int nBestHeight = chainActive.Height();
10351034
int nStartHeight = nBestHeight - nBlocks;
10361035
if (nBlocks < 0 || nStartHeight <= 0)
10371036
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid start height");
10381037

1039-
CAmount nFees = 0;
1040-
int64_t nBytes = 0;
1041-
int64_t nTotal = 0;
1042-
for (int i = nStartHeight; i <= nBestHeight; i++) {
1043-
CBlockIndex* pindex = chainActive[i];
1044-
CBlock block;
1045-
if (!ReadBlockFromDisk(block, pindex))
1046-
throw JSONRPCError(RPC_DATABASE_ERROR, "failed to read block from disk");
1047-
1048-
CAmount nValueIn = 0;
1049-
CAmount nValueOut = 0;
1050-
for (const CTransaction& tx : block.vtx) {
1051-
if (tx.IsCoinBase() || tx.IsCoinStake())
1052-
continue;
1053-
1054-
for (unsigned int j = 0; j < tx.vin.size(); j++) {
1055-
if (tx.vin[j].IsZerocoinSpend() || tx.vin[j].IsZerocoinPublicSpend()) {
1056-
nValueIn += tx.vin[j].nSequence * COIN;
1057-
continue;
1058-
}
1059-
1060-
COutPoint prevout = tx.vin[j].prevout;
1061-
CTransaction txPrev;
1062-
uint256 hashBlock;
1063-
if(!GetTransaction(prevout.hash, txPrev, hashBlock, true))
1064-
throw JSONRPCError(RPC_DATABASE_ERROR, "failed to read tx from disk");
1065-
nValueIn += txPrev.vout[prevout.n].nValue;
1066-
}
1067-
1068-
for (unsigned int j = 0; j < tx.vout.size(); j++) {
1069-
nValueOut += tx.vout[j].nValue;
1070-
}
1071-
1072-
nFees += nValueIn - nValueOut;
1073-
nBytes += tx.GetSerializeSize(SER_NETWORK, CLIENT_VERSION);
1074-
nTotal++;
1075-
}
1076-
1077-
pindex = chainActive.Next(pindex);
1078-
if (!pindex)
1079-
break;
1080-
}
1081-
1082-
UniValue ret(UniValue::VOBJ);
1083-
CFeeRate nFeeRate = CFeeRate(nFees, nBytes);
1084-
ret.push_back(Pair("txcount", (int64_t)nTotal));
1085-
ret.push_back(Pair("txbytes", (int64_t)nBytes));
1086-
ret.push_back(Pair("ttlfee", FormatMoney(nFees)));
1087-
ret.push_back(Pair("feeperkb", FormatMoney(nFeeRate.GetFeePerK())));
1088-
ret.push_back(Pair("rec_highpriorityfee_perkb", FormatMoney(nFeeRate.GetFeePerK() + 1000)));
1038+
UniValue newParams(UniValue::VARR);
1039+
newParams.push_back(UniValue(nStartHeight));
1040+
newParams.push_back(UniValue(nBlocks));
1041+
newParams.push_back(UniValue(true)); // fFeeOnly
10891042

1090-
return ret;
1043+
return getblockindexstats(newParams, false);
10911044
}
10921045

10931046
UniValue mempoolInfoToJSON()
@@ -1316,17 +1269,46 @@ UniValue getaccumulatorwitness(const UniValue& params, bool fHelp)
13161269
return obj;
13171270
}
13181271

1272+
void validaterange(const UniValue& params, int& heightStart, int& heightEnd, int minHeightStart)
1273+
{
1274+
if (params.size() < 2) {
1275+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Not enough parameters in validaterange");
1276+
}
1277+
1278+
const int nBestHeight = chainActive.Height();
1279+
1280+
heightStart = params[0].get_int();
1281+
if (heightStart > nBestHeight) {
1282+
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid starting block (%d). Out of range.", heightStart));
1283+
}
1284+
1285+
const int range = params[1].get_int();
1286+
if (range < 1) {
1287+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range. Must be strictly positive.");
1288+
}
1289+
1290+
heightEnd = heightStart + range - 1;
1291+
1292+
if (heightStart < minHeightStart && heightEnd >= minHeightStart) {
1293+
heightStart = minHeightStart;
1294+
}
1295+
1296+
if (heightEnd > nBestHeight) {
1297+
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid ending block (%d). Out of range.", heightEnd));
1298+
}
1299+
}
1300+
13191301
UniValue getmintsinblocks(const UniValue& params, bool fHelp) {
13201302
if (fHelp || params.size() != 3)
13211303
throw runtime_error(
1322-
"getmintsinblocks \"height\" \"range\" \"coinDenomination\"\n"
1304+
"getmintsinblocks <height> <range> [coinDenomination]\n"
13231305
"\nReturns the number of mints of a certain denomination"
13241306
"\noccurred in blocks [height, height+1, height+2, ..., height+range-1]\n"
13251307

13261308
"\nArguments:\n"
13271309
"1. height (numeric, required) block height where the search starts.\n"
13281310
"2. range (numeric, required) number of blocks to include.\n"
1329-
"2. coinDenomination (numeric, required) coin denomination.\n"
1311+
"3. coinDenomination (numeric, required) coin denomination.\n"
13301312

13311313
"\nResult:\n"
13321314
"{\n"
@@ -1339,19 +1321,10 @@ UniValue getmintsinblocks(const UniValue& params, bool fHelp) {
13391321
HelpExampleCli("getmintsinblocks", "1200000 1000 5") +
13401322
HelpExampleRpc("getmintsinblocks", "1200000, 1000, 5"));
13411323

1342-
int nBestHeight = chainActive.Height();
1343-
1344-
int heightStart = params[0].get_int();
1345-
if (heightStart < Params().Zerocoin_StartHeight())
1346-
heightStart = Params().Zerocoin_StartHeight();
1347-
1348-
int range = params[1].get_int();
1349-
if (range < 1)
1350-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range. Must be strictly positive.");
1324+
LOCK(cs_main);
13511325

1352-
int heightEnd = heightStart + range - 1;
1353-
if (heightEnd > nBestHeight)
1354-
heightEnd = nBestHeight;
1326+
int heightStart, heightEnd;
1327+
validaterange(params, heightStart, heightEnd, Params().Zerocoin_StartHeight());
13551328

13561329
int d = params[2].get_int();
13571330
libzerocoin::CoinDenomination denom = libzerocoin::IntToZerocoinDenomination(d);
@@ -1381,7 +1354,7 @@ UniValue getmintsinblocks(const UniValue& params, bool fHelp) {
13811354
UniValue getserials(const UniValue& params, bool fHelp) {
13821355
if (fHelp || params.size() < 2 || params.size() > 3)
13831356
throw runtime_error(
1384-
"getserials \"hash\"\n"
1357+
"getserials <height> <range> [fVerbose]\n"
13851358
"\nLook the inputs of any tx in a range of blocks and returns the serial numbers for any coinspend.\n"
13861359

13871360
"\nArguments:\n"
@@ -1395,19 +1368,8 @@ UniValue getserials(const UniValue& params, bool fHelp) {
13951368

13961369
LOCK(cs_main);
13971370

1398-
int nBestHeight = chainActive.Height();
1399-
1400-
int heightStart = params[0].get_int();
1401-
if (heightStart < Params().Zerocoin_StartHeight())
1402-
heightStart = Params().Zerocoin_StartHeight();
1403-
1404-
int range = params[1].get_int();
1405-
if (range < 1)
1406-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range. Must be strictly positive.");
1407-
1408-
int heightEnd = heightStart + range - 1;
1409-
if (heightEnd > nBestHeight)
1410-
heightEnd = nBestHeight;
1371+
int heightStart, heightEnd;
1372+
validaterange(params, heightStart, heightEnd, Params().Zerocoin_StartHeight());
14111373

14121374
bool fVerbose = false;
14131375
if (params.size() > 2) {
@@ -1496,4 +1458,161 @@ UniValue getserials(const UniValue& params, bool fHelp) {
14961458

14971459
}
14981460

1461+
UniValue getblockindexstats(const UniValue& params, bool fHelp) {
1462+
if (fHelp || params.size() < 2 || params.size() > 3)
1463+
throw runtime_error(
1464+
"getblockindexstats <height> <range> [fFeeOnly]\n"
1465+
"\nReturns aggregated BlockIndex data for blocks "
1466+
"\n[height, height+1, height+2, ..., height+range-1]\n"
1467+
1468+
"\nArguments:\n"
1469+
"1. height (numeric, required) block height where the search starts.\n"
1470+
"2. range (numeric, required) number of blocks to include.\n"
1471+
"3. fFeeOnly (boolean, optional, default=False) return only fee info.\n"
1472+
1473+
"\nResult:\n"
1474+
"{\n"
1475+
" \"first_block\": \"x\" (integer) First counted block\n"
1476+
" \"last_block\": \"x\" (integer) Last counted block\n"
1477+
" \"txcount\": xxxxx (numeric) tx count (excluding coinbase/coinstake)\n"
1478+
" \"txcount_tot\": xxxxx (numeric) tx count (including coinbase/coinstake)\n"
1479+
" \"mintcount\": { [if fFeeOnly=False]\n"
1480+
" \"denom_1\": xxxx (numeric) number of mints of denom_1 occurred over the block range\n"
1481+
" \"denom_5\": xxxx (numeric) number of mints of denom_5 occurred over the block range\n"
1482+
" ... ... number of mints of other denominations: ..., 10, 50, 100, 500, 1000, 5000\n"
1483+
" }\n"
1484+
" \"spendcount\": { [if fFeeOnly=False]\n"
1485+
" \"denom_1\": xxxx (numeric) number of spends of denom_1 occurred over the block range\n"
1486+
" \"denom_5\": xxxx (numeric) number of spends of denom_5 occurred over the block range\n"
1487+
" ... ... number of spends of other denominations: ..., 10, 50, 100, 500, 1000, 5000\n"
1488+
" }\n"
1489+
" \"txbytes\": xxxxx (numeric) Sum of the size of all txes over block range\n"
1490+
" \"ttlfee\": xxxxx (numeric) Sum of the fee amount of all txes over block range\n"
1491+
" \"feeperkb\": xxxxx (numeric) Average fee per kb\n"
1492+
"}\n"
1493+
1494+
"\nExamples:\n" +
1495+
HelpExampleCli("getblockindexstats", "1200000 1000") +
1496+
HelpExampleRpc("getblockindexstats", "1200000, 1000"));
1497+
1498+
LOCK(cs_main);
1499+
1500+
int heightStart, heightEnd;
1501+
validaterange(params, heightStart, heightEnd);
1502+
// return object
1503+
UniValue ret(UniValue::VOBJ);
1504+
ret.push_back(Pair("Starting block", heightStart));
1505+
ret.push_back(Pair("Ending block", heightEnd));
1506+
1507+
bool fFeeOnly = false;
1508+
if (params.size() > 2) {
1509+
fFeeOnly = params[2].get_bool();
1510+
}
1511+
1512+
CAmount nFees = 0;
1513+
int64_t nBytes = 0;
1514+
int64_t nTxCount = 0;
1515+
int64_t nTxCount_tot = 0;
1516+
1517+
std::map<libzerocoin::CoinDenomination, int64_t> mapMintCount;
1518+
std::map<libzerocoin::CoinDenomination, int64_t> mapSpendCount;
1519+
for (auto& denom : libzerocoin::zerocoinDenomList) {
1520+
mapMintCount.insert(make_pair(denom, 0));
1521+
mapSpendCount.insert(make_pair(denom, 0));
1522+
}
1523+
1524+
CBlockIndex* pindex = chainActive[heightStart];
1525+
1526+
while (true) {
1527+
CBlock block;
1528+
if (!ReadBlockFromDisk(block, pindex)) {
1529+
throw JSONRPCError(RPC_DATABASE_ERROR, "failed to read block from disk");
1530+
}
1531+
1532+
CAmount nValueIn = 0;
1533+
CAmount nValueOut = 0;
1534+
nTxCount_tot += block.vtx.size();
1535+
1536+
// loop through each tx in block
1537+
for (const CTransaction& tx : block.vtx) {
1538+
if (tx.IsCoinBase())
1539+
continue;
1540+
1541+
if (tx.HasZerocoinSpendInputs()) {
1542+
for (unsigned int j = 0; j < tx.vin.size(); j++) {
1543+
if (tx.vin[j].IsZerocoinSpend() || tx.vin[j].IsZerocoinPublicSpend()) {
1544+
mapSpendCount[libzerocoin::IntToZerocoinDenomination(tx.vin[j].nSequence)]++;
1545+
}
1546+
}
1547+
}
1548+
1549+
if (tx.IsCoinStake()) {
1550+
continue;
1551+
}
1552+
1553+
nTxCount++;
1554+
1555+
// fetch input value from prevouts
1556+
for (unsigned int j = 0; j < tx.vin.size(); j++) {
1557+
if (tx.vin[j].IsZerocoinSpend() || tx.vin[j].IsZerocoinPublicSpend()) {
1558+
nValueIn += tx.vin[j].nSequence * COIN;
1559+
continue;
1560+
}
1561+
1562+
COutPoint prevout = tx.vin[j].prevout;
1563+
CTransaction txPrev;
1564+
uint256 hashBlock;
1565+
if(!GetTransaction(prevout.hash, txPrev, hashBlock, true))
1566+
throw JSONRPCError(RPC_DATABASE_ERROR, "failed to read tx from disk");
1567+
nValueIn += txPrev.vout[prevout.n].nValue;
1568+
}
1569+
1570+
// sum output values in nValueOut
1571+
for (unsigned int j = 0; j < tx.vout.size(); j++) {
1572+
nValueOut += tx.vout[j].nValue;
1573+
}
1574+
1575+
// update sums
1576+
nFees += nValueIn - nValueOut;
1577+
nBytes += tx.GetSerializeSize(SER_NETWORK, CLIENT_VERSION);
1578+
}
1579+
1580+
// add mints to map
1581+
if (!fFeeOnly) {
1582+
for (auto& denom : libzerocoin::zerocoinDenomList) {
1583+
mapMintCount[denom] += count(pindex->vMintDenominationsInBlock.begin(), pindex->vMintDenominationsInBlock.end(), denom);
1584+
}
1585+
}
1586+
1587+
if (pindex->nHeight < heightEnd) {
1588+
pindex = chainActive.Next(pindex);
1589+
} else {
1590+
break;
1591+
}
1592+
}
1593+
1594+
// get fee rate
1595+
CFeeRate nFeeRate = CFeeRate(nFees, nBytes);
1596+
1597+
// return UniValue object
1598+
ret.push_back(Pair("txcount", (int64_t)nTxCount));
1599+
ret.push_back(Pair("txcount_tot", (int64_t)nTxCount_tot));
1600+
if (!fFeeOnly) {
1601+
UniValue mint_obj(UniValue::VOBJ);
1602+
UniValue spend_obj(UniValue::VOBJ);
1603+
for (auto& denom : libzerocoin::zerocoinDenomList) {
1604+
mint_obj.push_back(Pair(strprintf("denom_%d", ZerocoinDenominationToInt(denom)), mapMintCount[denom]));
1605+
spend_obj.push_back(Pair(strprintf("denom_%d", ZerocoinDenominationToInt(denom)), mapSpendCount[denom]));
1606+
}
1607+
ret.push_back(Pair("mintcount", mint_obj));
1608+
ret.push_back(Pair("spendcount", spend_obj));
1609+
1610+
}
1611+
ret.push_back(Pair("txbytes", (int64_t)nBytes));
1612+
ret.push_back(Pair("ttlfee", FormatMoney(nFees)));
1613+
ret.push_back(Pair("feeperkb", FormatMoney(nFeeRate.GetFeePerK())));
1614+
1615+
return ret;
1616+
1617+
}
14991618

src/rpc/client.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
150150
{"getaccumulatorwitness",2},
151151
{"getmintsvalues", 2},
152152
{"enableautomintaddress", 0},
153+
{"getblockindexstats", 0},
154+
{"getblockindexstats", 1},
155+
{"getblockindexstats", 2},
153156
{"getmintsinblocks", 0},
154157
{"getmintsinblocks", 1},
155158
{"getmintsinblocks", 2},

src/rpc/server.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ static const CRPCCommand vRPCCommands[] =
304304
{"blockchain", "findserial", &findserial, true, false, false},
305305
{"blockchain", "getaccumulatorvalues", &getaccumulatorvalues, true, false, false},
306306
{"blockchain", "getaccumulatorwitness", &getaccumulatorwitness, true, false, false},
307+
{"blockchain", "getblockindexstats", &getblockindexstats, true, false, false},
307308
{"blockchain", "getmintsinblocks", &getmintsinblocks, true, false, false},
308309
{"blockchain", "getserials", &getserials, true, false, false},
309310
{"blockchain", "getblockchaininfo", &getblockchaininfo, true, false, false},

src/rpc/server.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,11 @@ extern UniValue invalidateblock(const UniValue& params, bool fHelp);
308308
extern UniValue reconsiderblock(const UniValue& params, bool fHelp);
309309
extern UniValue getaccumulatorvalues(const UniValue& params, bool fHelp);
310310
extern UniValue getaccumulatorwitness(const UniValue& params, bool fHelp);
311+
extern UniValue getblockindexstats(const UniValue& params, bool fHelp);
311312
extern UniValue getmintsinblocks(const UniValue& params, bool fHelp);
312313
extern UniValue getserials(const UniValue& params, bool fHelp);
313314
extern UniValue getchecksumblock(const UniValue& params, bool fHelp);
315+
extern void validaterange(const UniValue& params, int& heightStart, int& heightEnd, int minHeightStart=1);
314316

315317
extern UniValue getpoolinfo(const UniValue& params, bool fHelp); // in rpc/masternode.cpp
316318
extern UniValue listmasternodes(const UniValue& params, bool fHelp);

0 commit comments

Comments
 (0)