Skip to content

Commit

Permalink
Gettotalsupply (#1502)
Browse files Browse the repository at this point in the history
The current gettotalsupply command originally only counts coinbase transactions which does not factor in inflation due to forged amounts from previously disclosed attacks namely the Zerocoin vulnerability and Bitcoin CVE-2018-17144.

This PR correctly calculates and factors in the inflation figures into the gettotalsupply command.
  • Loading branch information
levonpetrosyan93 authored Feb 21, 2025
1 parent 14ed437 commit 0375dde
Showing 1 changed file with 193 additions and 1 deletion.
194 changes: 193 additions & 1 deletion src/rpc/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1917,6 +1917,154 @@ UniValue getspentinfo(const JSONRPCRequest& request)
return obj;
}

CAmount getzerocoinpoolbalance()
{
CAmount nTotalAmount = 0;

// Iterate over all mints
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
if (GetAddressIndex(uint160(), AddressType::zerocoinMint, addressIndex)) {
for (auto& it : addressIndex) {
nTotalAmount += it.second;
}
}
addressIndex.clear();

// Iterate over all spends
if (GetAddressIndex(uint160(), AddressType::zerocoinSpend, addressIndex)) {
for (auto& it : addressIndex) {
nTotalAmount += it.second;
}
}

return nTotalAmount;
}

CAmount getCVE17144amount()
{
// CVE-2018-17144 was a critical bug that allowed double-spending of inputs
// in the same transaction. This function calculates the total amount of coins
// that were created due to this vulnerability at block 293526.
LOCK(cs_main);
if (chainActive.Height() < 293526) {
throw std::runtime_error("Chain height is less than 293,526.");
}

if (!Params().GetConsensus().IsMain()) {
throw std::runtime_error("Attack only occurred on mainnet");
}

CBlockIndex *atackedBlock = chainActive[293526];
CBlock block;
if (!ReadBlockFromDisk(block, atackedBlock, ::Params().GetConsensus())) {
throw std::runtime_error("Failed to read block 293526 from disk");
}
CAmount amount = 0;
for (CTransactionRef tx : block.vtx) {
if (!tx->IsCoinBase() && !tx->HasNoRegularInputs()) {
std::set<COutPoint> vInOutPoints;
for (const auto& txin : tx->vin)
{
if (!vInOutPoints.insert(txin.prevout).second) {
CTransactionRef tx;
uint256 hashBlock;
if (!GetTransaction(txin.prevout.hash, tx, Params().GetConsensus(), hashBlock, true)) {
continue;
}
if (txin.prevout.n >= tx->vout.size()) {
continue; // Skip if output index is out of bounds
}
amount += tx->vout[txin.prevout.n].nValue;
}
}
}
}
return amount;
}

// another way to calculate the forged amount
CAmount getCVE17144amountNew()
{
// CVE-2018-17144 was a critical bug that allowed double-spending of inputs
// in the same transaction. This function calculates the total amount of coins
// that were created due to this vulnerability at block 293526.
LOCK(cs_main);
if (chainActive.Height() < 293526) {
throw std::runtime_error("Chain height is less than 293,526.");
}

if (!Params().GetConsensus().IsMain()) {
throw std::runtime_error("Attack only occurred on mainnet");
}

CBlockIndex *atackedBlock = chainActive[293526];
CBlock block;
if (!ReadBlockFromDisk(block, atackedBlock, ::Params().GetConsensus())) {
throw std::runtime_error("Failed to read block 293526 from disk");
}

std::unordered_map<uint160, AddressType> addresses;
for (CTransactionRef tx : block.vtx) {
if (!tx->IsCoinBase() && !tx->HasNoRegularInputs()) {
for (const auto& txout : tx->vout)
{
CTxDestination addr;
if (!ExtractDestination(txout.scriptPubKey, addr))
continue;
CBitcoinAddress address(addr);
uint160 hashBytes;
AddressType type = AddressType::unknown;
if (!address.GetIndexKey(hashBytes, type)) {
continue;
}
addresses.insert({hashBytes, type});
}

for (const auto& txin : tx->vin)
{
CTransactionRef tx;
uint256 hashBlock;
if (!GetTransaction(txin.prevout.hash, tx, Params().GetConsensus(), hashBlock, true)) {
continue;
}
if (txin.prevout.n >= tx->vout.size()) {
continue; // Skip if output index is out of bounds
}

CTxDestination addr;
if (!ExtractDestination(tx->vout[txin.prevout.n].scriptPubKey, addr))
continue;
CBitcoinAddress address(addr);
uint160 hashBytes;
AddressType type = AddressType::unknown;
if (!address.GetIndexKey(hashBytes, type)) {
continue;
}
addresses.insert({hashBytes, type});
}
}
}

CAmount result = 0;
for (const auto& it : addresses) {
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
if (!GetAddressIndex(it.first, it.second, addressIndex)) {
continue;
}
CAmount amount = 0;
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) {
amount += it->second;
}

if (amount < 0)
result += amount;
}



return result;
}

UniValue gettotalsupply(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
Expand All @@ -1938,12 +2086,55 @@ UniValue gettotalsupply(const JSONRPCRequest& request)
if(!pblocktree->ReadTotalSupply(total))
throw JSONRPCError(RPC_DATABASE_ERROR, "Cannot read the total supply from the database. This functionality requires -addressindex to be enabled. Enabling -addressindex requires reindexing.");

total -= getzerocoinpoolbalance(); //498,397.00000000 The actual amount of coins forged during the Zerocoin attacks (the negative balance after the pool closed),
total += getCVE17144amount(); //320,841.99803185 The cmount of forged coins during CVE-2018-17144 attacks,
total -= 16810168037465;// burnt Coins sent to unrecoverable address https://explorer.firo.org/tx/0b53178c1b22bae4c04ef943ee6d6d30f2483327fe9beb54952951592e8ce368
UniValue result(UniValue::VOBJ);
result.push_back(Pair("total", total));

return result;
}

UniValue getzerocoinpoolbalance(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
"getzerocoinpoolbalance\n"
"\nReturns the total coin amount, which remains after zerocoin pool closed.\n"
"\nArguments: none\n"
"\nResult:\n"
"{\n"
" \"total\" (string) The total balance\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getzerocoinpoolbalance", "")
+ HelpExampleRpc("getzerocoinpoolbalance", "")
);

return getzerocoinpoolbalance();
}

UniValue getCVE17144amount(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
"getCVE17144amount\n"
"\nReturns the total amount of forged coins during CVE-2018-17144 attacks.\n"
"\nArguments: none\n"
"\nResult:\n"
"{\n"
" \"total\" (string) The total balance\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getCVE17144amount", "")
+ HelpExampleRpc("getCVE17144amount", "")
);
UniValue results(UniValue::VOBJ);
results.push_back(Pair("firstWay",getCVE17144amount()));
results.push_back(Pair("secondWay",getCVE17144amountNew()));
return results;
}

UniValue getinfoex(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
Expand Down Expand Up @@ -2069,7 +2260,8 @@ static const CRPCCommand commands[] =
/* Not shown in help */
{ "hidden", "getinfoex", &getinfoex, false },
{ "addressindex", "gettotalsupply", &gettotalsupply, false },

{ "addressindex", "getzerocoinpoolbalance", &getzerocoinpoolbalance, false },
{ "addressindex", "getCVE17144amount", &getCVE17144amount, false },
/* Mobile related */
{ "mobile", "getanonymityset", &getanonymityset, false },
{ "mobile", "getmintmetadata", &getmintmetadata, true },
Expand Down

0 comments on commit 0375dde

Please sign in to comment.