Skip to content

Commit 7bba394

Browse files
committed
Merge PIVX-Project#1878: [Core][RPC][BUG] Use sum of utxo values as money supply
dcc032c [Cleanup] Remove ZC_WrappedSerialsSupply no longer needed (random-zebra) 0e7921e [DB] Also prune invalid/fraudulent/frozen outputs from the coins view (random-zebra) b8647bd [Tests] Fix expected PIV supply in mining_pos_reorg (random-zebra) 40b7ea3 [DB] Prune zerocoin mints from coins DB at startup (random-zebra) b89bb91 [BUG] Don't try to spend zc mint coins from the cache in DisconnectBlock (random-zebra) cfc06ed [Core] Don't add zerocoin mints to the coins cache (random-zebra) e8a782c [Cleanup] Remove unused function GetInvalidUTXOValue (random-zebra) db7d8fd [Core] Remove nMoneySupply global and RecalculatePIVSupply function (random-zebra) b674fde [DB] Remove money supply databasing (random-zebra) 373ee90 [Cleanup] Remove --reindexmoneysupply startup flag (random-zebra) 02c9aaa [RPC] Return total available utxo value as moneysupply (random-zebra) cdf9bc1 [Core] Implement CCoinsViewCache::GetTotalAmount (random-zebra) Pull request description: Instead of updating and databasing independently `nMoneySupply`, just use the sum of all unspent tx outputs, returned by the coins cache. This has the following advantages: - Cleaner code: we don't need to add special cases for exceptional one-time events, such as wrapped serials, accumulator recalculation, etc. - More robust: with the old code, `nMoneySupply` is databased at every block and does not follow the chainstate flushing strategy, so it's very easy to get an inconsistent state (see PIVX-Project#1873). Even more so, considering the absence of any consistency check between the actual chain height and the height at which the supply was updated. - More accurate number: this returns only the __spendable__ supply, so it excludes all unspendable outputs (OP_RETURN), such as proposals / finalized budgets collaterals, that were originally counted in. Those outputs, in fact, are effectively "burnt" coins and should not be part of the total supply calculation. Disadvantage: - Calculating the supply from the coins DB is a bit slow, but the only place where this is done is in the RPC `getinfo`. This PR removes the global variable `nMoneySupply` and all related code (Closes PIVX-Project#1873) and gets rid of the startup flag `--reindexmoneysupply` no longer needed (PIVX-Project#1864 introduces `reindex-chainstate`). ⚠️ This also fixes the following bug with zerocoin txes: Currently, on master, `gettxoutsetinfo` reports a total amount of about **811 million PIV**! This is due to improper handling of zerocoin-related "utxos". Basically mints are added to the coins cache (`AddCoin` only skips OP_RETURN coins, not mints) and *never* pruned/spent (as they are skipped inside `UpdateCoins`), except in `DisconnectBlock`. Here we completely remove zerocoin mint outputs from the coins DB. This makes sense, as originally they were not meant to be linked when spending, so should have been considered unspendable outputs, and not included in the first place. With the introduction of Public Spends, mint outputs are now referenced when spending, but we don't need to keep them in the cache either, as double spending is checked only via the zerocoin serial (and, as said, the coins aren't even spent in `UpdateCoins`). We also remove all those utxos flagged as invalid. Removing zerocoin mints from the unspent coins DB also gives a nice reduction to the utxoset size on disk (about 37%, from 428 Mb to 270 Mb). ACKs for top commit: Fuzzbawls: ACK dcc032c Tree-SHA512: da50dd81b9e3a56291812cf35158e1858f5a0c6ebdf1e5018d1e1d20b834dbdccdaa4cb0d82830c6f0a405c71428355615fd57f582065b992d42fa9a7313235e
2 parents ea5f85e + dcc032c commit 7bba394

File tree

14 files changed

+104
-223
lines changed

14 files changed

+104
-223
lines changed

src/chainparams.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ class CMainParams : public CChainParams
183183
consensus.ZC_MinMintFee = 1 * CENT;
184184
consensus.ZC_MinStakeDepth = 200;
185185
consensus.ZC_TimeStart = 1508214600; // October 17, 2017 4:30:00 AM
186-
consensus.ZC_WrappedSerialsSupply = 4131563 * COIN; // zerocoin supply at height_last_ZC_WrappedSerials
187186

188187
// Network upgrades
189188
consensus.vUpgrades[Consensus::BASE_NETWORK].nActivationHeight =
@@ -323,7 +322,6 @@ class CTestNetParams : public CMainParams
323322
consensus.ZC_MinMintFee = 1 * CENT;
324323
consensus.ZC_MinStakeDepth = 200;
325324
consensus.ZC_TimeStart = 1501776000;
326-
consensus.ZC_WrappedSerialsSupply = 0; // WrappedSerials only on main net
327325

328326
// Network upgrades
329327
consensus.vUpgrades[Consensus::BASE_NETWORK].nActivationHeight =
@@ -465,7 +463,6 @@ class CRegTestParams : public CTestNetParams
465463
consensus.ZC_MinMintFee = 1 * CENT;
466464
consensus.ZC_MinStakeDepth = 10;
467465
consensus.ZC_TimeStart = 0; // not implemented on regtest
468-
consensus.ZC_WrappedSerialsSupply = 0;
469466

470467
// Network upgrades
471468
consensus.vUpgrades[Consensus::BASE_NETWORK].nActivationHeight =

src/coins.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "coins.h"
77

88
#include "consensus/consensus.h"
9+
#include "invalid.h"
910
#include "memusage.h"
1011
#include "random.h"
1112

@@ -65,6 +66,7 @@ bool CCoinsViewCache::GetCoin(const COutPoint& outpoint, Coin& coin) const
6566
void CCoinsViewCache::AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite) {
6667
assert(!coin.IsSpent());
6768
if (coin.out.scriptPubKey.IsUnspendable()) return;
69+
if (coin.out.IsZerocoinMint()) return;
6870
CCoinsMap::iterator it;
6971
bool inserted;
7072
std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::tuple<>());
@@ -276,6 +278,37 @@ int CCoinsViewCache::GetCoinDepthAtHeight(const COutPoint& output, int nHeight)
276278
return -1;
277279
}
278280

281+
CAmount CCoinsViewCache::GetTotalAmount() const
282+
{
283+
CAmount nTotal = 0;
284+
285+
std::unique_ptr<CCoinsViewCursor> pcursor(Cursor());
286+
while (pcursor->Valid()) {
287+
Coin coin;
288+
if (pcursor->GetValue(coin) && !coin.IsSpent()) {
289+
nTotal += coin.out.nValue;
290+
}
291+
pcursor->Next();
292+
}
293+
294+
return nTotal;
295+
}
296+
297+
void CCoinsViewCache::PruneInvalidEntries()
298+
{
299+
// Prune zerocoin Mints and fraudulent/frozen outputs
300+
std::unique_ptr<CCoinsViewCursor> pcursor(Cursor());
301+
while (pcursor->Valid()) {
302+
COutPoint key;
303+
Coin coin;
304+
if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
305+
if (coin.out.IsZerocoinMint() || invalid_out::ContainsOutPoint(key))
306+
SpendCoin(key);
307+
}
308+
pcursor->Next();
309+
}
310+
}
311+
279312
static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_SIZE_CURRENT / ::GetSerializeSize(CTxOut(), SER_NETWORK, PROTOCOL_VERSION); // TODO: merge with similar definition in undo.h.
280313

281314
const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
@@ -288,3 +321,4 @@ const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
288321
}
289322
return coinEmpty;
290323
}
324+

src/coins.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,16 @@ class CCoinsViewCache : public CCoinsViewBacked
290290
*/
291291
int GetCoinDepthAtHeight(const COutPoint& output, int nHeight) const;
292292

293+
/*
294+
* Return the sum of the value of all transaction outputs
295+
*/
296+
CAmount GetTotalAmount() const;
297+
298+
/*
299+
* Prune zerocoin mints and frozen outputs - do it once, after initialization
300+
*/
301+
void PruneInvalidEntries();
302+
293303

294304
private:
295305
CCoinsMap::iterator FetchCoin(const COutPoint& outpoint) const;

src/consensus/params.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,6 @@ struct Params {
169169
CAmount ZC_MinMintFee;
170170
int ZC_MinStakeDepth;
171171
int ZC_TimeStart;
172-
CAmount ZC_WrappedSerialsSupply;
173172

174173
libzerocoin::ZerocoinParams* Zerocoin_Params(bool useModulusV1) const
175174
{

src/consensus/zerocoin_verify.cpp

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -191,111 +191,3 @@ bool ContextualCheckZerocoinSpendNoSerialCheck(const CTransaction& tx, const lib
191191
return true;
192192
}
193193

194-
bool RecalculatePIVSupply(int nHeightStart, bool fSkipZpiv)
195-
{
196-
AssertLockHeld(cs_main);
197-
198-
const Consensus::Params& consensus = Params().GetConsensus();
199-
const int chainHeight = chainActive.Height();
200-
if (nHeightStart > chainHeight)
201-
return false;
202-
203-
CBlockIndex* pindex = chainActive[nHeightStart];
204-
if (IsActivationHeight(nHeightStart, consensus, Consensus::UPGRADE_ZC))
205-
nMoneySupply = CAmount(5449796547496199);
206-
207-
if (!fSkipZpiv) {
208-
// initialize supply to 0
209-
mapZerocoinSupply.clear();
210-
for (auto& denom : libzerocoin::zerocoinDenomList) mapZerocoinSupply.emplace(denom, 0);
211-
}
212-
213-
uiInterface.ShowProgress(_("Recalculating PIV supply..."), 0);
214-
while (true) {
215-
if (pindex->nHeight % 1000 == 0) {
216-
LogPrintf("%s : block %d...\n", __func__, pindex->nHeight);
217-
int percent = std::max(1, std::min(99, (int)((double)((pindex->nHeight - nHeightStart) * 100) / (chainHeight - nHeightStart))));
218-
uiInterface.ShowProgress(_("Recalculating PIV supply..."), percent);
219-
}
220-
221-
CBlock block;
222-
assert(ReadBlockFromDisk(block, pindex));
223-
224-
CAmount nValueIn = 0;
225-
CAmount nValueOut = 0;
226-
for (const auto& txIn : block.vtx) {
227-
const CTransaction& tx = *txIn;
228-
for (unsigned int i = 0; i < tx.vin.size(); i++) {
229-
if (tx.IsCoinBase())
230-
break;
231-
232-
if (tx.vin[i].IsZerocoinSpend()) {
233-
nValueIn += tx.vin[i].nSequence * COIN;
234-
continue;
235-
}
236-
237-
COutPoint prevout = tx.vin[i].prevout;
238-
CTransaction txPrev;
239-
uint256 hashBlock;
240-
assert(GetTransaction(prevout.hash, txPrev, hashBlock, true));
241-
nValueIn += txPrev.vout[prevout.n].nValue;
242-
}
243-
244-
for (unsigned int i = 0; i < tx.vout.size(); i++) {
245-
if (i == 0 && tx.IsCoinStake())
246-
continue;
247-
248-
nValueOut += tx.vout[i].nValue;
249-
}
250-
}
251-
252-
// Rewrite money supply
253-
nMoneySupply += (nValueOut - nValueIn);
254-
255-
// Rewrite zpiv supply too
256-
if (!fSkipZpiv && consensus.NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_ZC)) {
257-
UpdateZPIVSupplyConnect(block, pindex, true);
258-
}
259-
260-
// Add fraudulent funds to the supply and remove any recovered funds.
261-
if (pindex->nHeight == consensus.height_ZC_RecalcAccumulators) {
262-
const CAmount nInvalidAmountFiltered = 268200*COIN; //Amount of invalid coins filtered through exchanges, that should be considered valid
263-
LogPrintf("%s : Original money supply=%s\n", __func__, FormatMoney(nMoneySupply));
264-
265-
nMoneySupply += nInvalidAmountFiltered;
266-
LogPrintf("%s : Adding filtered funds to supply + %s : supply=%s\n", __func__, FormatMoney(nInvalidAmountFiltered), FormatMoney(nMoneySupply));
267-
268-
CAmount nLocked = GetInvalidUTXOValue();
269-
nMoneySupply -= nLocked;
270-
LogPrintf("%s : Removing locked from supply - %s : supply=%s\n", __func__, FormatMoney(nLocked), FormatMoney(nMoneySupply));
271-
}
272-
273-
assert(pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)));
274-
275-
// Stop if shutdown was requested
276-
if (ShutdownRequested()) return false;
277-
278-
if (pindex->nHeight < chainHeight)
279-
pindex = chainActive.Next(pindex);
280-
else
281-
break;
282-
}
283-
uiInterface.ShowProgress("", 100);
284-
return true;
285-
}
286-
287-
CAmount GetInvalidUTXOValue()
288-
{
289-
CAmount nValue = 0;
290-
for (auto out : invalid_out::setInvalidOutPoints) {
291-
bool fSpent = false;
292-
CCoinsViewCache cache(pcoinsTip);
293-
const Coin& coin = cache.AccessCoin(out);
294-
if(coin.IsSpent())
295-
fSpent = true;
296-
if (!fSpent)
297-
nValue += coin.out.nValue;
298-
}
299-
300-
return nValue;
301-
}

src/consensus/zerocoin_verify.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,5 @@ int CurrentPublicCoinSpendVersion();
1919
bool CheckPublicCoinSpendVersion(int version);
2020
bool ContextualCheckZerocoinSpend(const CTransaction& tx, const libzerocoin::CoinSpend* spend, int nHeight, const uint256& hashBlock);
2121
bool ContextualCheckZerocoinSpendNoSerialCheck(const CTransaction& tx, const libzerocoin::CoinSpend* spend, int nHeight, const uint256& hashBlock);
22-
bool RecalculatePIVSupply(int nHeightStart, bool fSkipZpiv = true);
23-
CAmount GetInvalidUTXOValue();
2422

2523
#endif //PIVX_CONSENSUS_ZEROCOIN_VERIFY_H

src/init.cpp

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,6 @@ std::string HelpMessage(HelpMessageMode mode)
418418
strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), PIVX_PID_FILENAME));
419419
#endif
420420
strUsage += HelpMessageOpt("-reindex", _("Rebuild block chain index from current blk000??.dat files") + " " + _("on startup"));
421-
strUsage += HelpMessageOpt("-reindexmoneysupply", strprintf(_("Reindex the %s and z%s money supply statistics"), CURRENCY_UNIT, CURRENCY_UNIT) + " " + _("on startup"));
422421
strUsage += HelpMessageOpt("-resync", _("Delete blockchain folders and resync from scratch") + " " + _("on startup"));
423422
#if !defined(WIN32)
424423
strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)"));
@@ -1554,26 +1553,34 @@ bool AppInit2()
15541553
invalid_out::LoadSerials();
15551554

15561555
bool fReindexZerocoin = GetBoolArg("-reindexzerocoin", false);
1557-
bool fReindexMoneySupply = GetBoolArg("-reindexmoneysupply", false);
15581556

15591557
int chainHeight;
1558+
bool fZerocoinActive;
15601559
{
15611560
LOCK(cs_main);
15621561
chainHeight = chainActive.Height();
1562+
fZerocoinActive = consensus.NetworkUpgradeActive(chainHeight, Consensus::UPGRADE_ZC);
1563+
1564+
// Prune zerocoin mints that were improperly stored in the coins database
1565+
// Do it only once, when removing money supply (key 'M') from the DB. Can be skipped in future versions.
1566+
int64_t nDummySupply;
1567+
if (fZerocoinActive && pblocktree->Read('M', nDummySupply)) {
1568+
LogPrintf("Pruning zerocoin mints / invalid outs, at height %d\n", chainHeight);
1569+
pcoinsTip->PruneInvalidEntries();
1570+
pblocktree->Erase('M');
1571+
}
15631572

1564-
// initialize PIV and zPIV supply to 0
1573+
// initialize zPIV supply to 0
15651574
mapZerocoinSupply.clear();
15661575
for (auto& denom : libzerocoin::zerocoinDenomList) mapZerocoinSupply.emplace(denom, 0);
1567-
nMoneySupply = 0;
15681576

1569-
// Load PIV and zPIV supply from DB
1577+
// Load zPIV supply from DB
15701578
if (chainHeight >= 0) {
15711579
const uint256& tipHash = chainActive[chainHeight]->GetBlockHash();
15721580
CLegacyBlockIndex bi;
15731581

15741582
// Load zPIV supply map
1575-
if (!fReindexZerocoin && consensus.NetworkUpgradeActive(chainHeight, Consensus::UPGRADE_ZC) &&
1576-
!zerocoinDB->ReadZCSupply(mapZerocoinSupply)) {
1583+
if (!fReindexZerocoin && fZerocoinActive && !zerocoinDB->ReadZCSupply(mapZerocoinSupply)) {
15771584
// try first reading legacy block index from DB
15781585
if (pblocktree->ReadLegacyBlockIndex(tipHash, bi) && !bi.mapZerocoinSupply.empty()) {
15791586
mapZerocoinSupply = bi.mapZerocoinSupply;
@@ -1582,22 +1589,11 @@ bool AppInit2()
15821589
fReindexZerocoin = true;
15831590
}
15841591
}
1585-
1586-
// Load PIV supply amount
1587-
if (!fReindexMoneySupply && !pblocktree->ReadMoneySupply(nMoneySupply)) {
1588-
// try first reading legacy block index from DB
1589-
if (pblocktree->ReadLegacyBlockIndex(tipHash, bi)) {
1590-
nMoneySupply = bi.nMoneySupply;
1591-
} else {
1592-
// reindex from disk
1593-
fReindexMoneySupply = true;
1594-
}
1595-
}
15961592
}
15971593
}
15981594

15991595
// Drop all information from the zerocoinDB and repopulate
1600-
if (fReindexZerocoin && consensus.NetworkUpgradeActive(chainHeight, Consensus::UPGRADE_ZC)) {
1596+
if (fReindexZerocoin && fZerocoinActive) {
16011597
LOCK(cs_main);
16021598
uiInterface.InitMessage(_("Reindexing zerocoin database..."));
16031599
std::string strError = ReindexZerocoinDB();
@@ -1607,13 +1603,6 @@ bool AppInit2()
16071603
}
16081604
}
16091605

1610-
// Recalculate money supply
1611-
if (fReindexMoneySupply) {
1612-
LOCK(cs_main);
1613-
// Skip zpiv if already reindexed
1614-
RecalculatePIVSupply(1, fReindexZerocoin);
1615-
}
1616-
16171606
if (!fReindex) {
16181607
uiInterface.InitMessage(_("Verifying blocks..."));
16191608

src/miner.cpp

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -271,47 +271,45 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet,
271271
double dPriority = 0;
272272
CAmount nTotalIn = 0;
273273
bool fMissingInputs = false;
274-
bool hasZerocoinSpends = tx.HasZerocoinSpendInputs();
275-
if (hasZerocoinSpends)
274+
275+
if (tx.HasZerocoinSpendInputs()) {
276276
nTotalIn = tx.GetZerocoinSpent();
277+
} else {
278+
for (const CTxIn& txin : tx.vin) {
279+
// Read prev transaction
280+
if (!view.HaveCoin(txin.prevout)) {
281+
// This should never happen; all transactions in the memory
282+
// pool should connect to either transactions in the chain
283+
// or other transactions in the memory pool.
284+
if (!mempool.mapTx.count(txin.prevout.hash)) {
285+
LogPrintf("ERROR: mempool transaction missing input\n");
286+
fMissingInputs = true;
287+
if (porphan)
288+
vOrphan.pop_back();
289+
break;
290+
}
277291

278-
for (const CTxIn& txin : tx.vin) {
279-
// Read prev transaction
280-
if (!view.HaveCoin(txin.prevout)) {
281-
// This should never happen; all transactions in the memory
282-
// pool should connect to either transactions in the chain
283-
// or other transactions in the memory pool.
284-
if (!mempool.mapTx.count(txin.prevout.hash)) {
285-
LogPrintf("ERROR: mempool transaction missing input\n");
286-
fMissingInputs = true;
287-
if (porphan)
288-
vOrphan.pop_back();
289-
break;
292+
// Has to wait for dependencies
293+
if (!porphan) {
294+
// Use list for automatic deletion
295+
vOrphan.push_back(COrphan(&tx));
296+
porphan = &vOrphan.back();
297+
}
298+
mapDependers[txin.prevout.hash].push_back(porphan);
299+
porphan->setDependsOn.insert(txin.prevout.hash);
300+
nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
301+
continue;
290302
}
291303

292-
// Has to wait for dependencies
293-
if (!porphan) {
294-
// Use list for automatic deletion
295-
vOrphan.push_back(COrphan(&tx));
296-
porphan = &vOrphan.back();
297-
}
298-
mapDependers[txin.prevout.hash].push_back(porphan);
299-
porphan->setDependsOn.insert(txin.prevout.hash);
300-
nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
301-
continue;
302-
}
304+
const Coin& coin = view.AccessCoin(txin.prevout);
305+
assert(!coin.IsSpent());
303306

304-
const Coin& coin = view.AccessCoin(txin.prevout);
305-
assert(hasZerocoinSpends || !coin.IsSpent());
306-
307-
CAmount nValueIn = coin.out.nValue;
308-
nTotalIn += nValueIn;
309-
310-
int nConf = nHeight - coin.nHeight;
311-
312-
// zPIV spends can have very large priority, use non-overflowing safe functions
313-
dPriority = double_safe_addition(dPriority, ((double)nValueIn * nConf));
307+
CAmount nValueIn = coin.out.nValue;
308+
nTotalIn += nValueIn;
314309

310+
int nConf = nHeight - coin.nHeight;
311+
dPriority = double_safe_addition(dPriority, ((double)nValueIn * nConf));
312+
}
315313
}
316314
if (fMissingInputs) continue;
317315

0 commit comments

Comments
 (0)