diff --git a/src/accumulators.cpp b/src/accumulators.cpp index aa757227104cb..2293e09b0d17b 100644 --- a/src/accumulators.cpp +++ b/src/accumulators.cpp @@ -152,7 +152,7 @@ bool EraseCheckpoints(int nStartHeight, int nEndHeight) } //Get checkpoint value for a specific block height -bool CalculateAccumulatorCheckpoint(int nHeight, uint256& nCheckpoint) +bool CalculateAccumulatorCheckpoint(int nHeight, uint256& nCheckpoint, AccumulatorMap& mapAccumulators) { if (nHeight < Params().Zerocoin_StartHeight()) { nCheckpoint = 0; @@ -166,7 +166,7 @@ bool CalculateAccumulatorCheckpoint(int nHeight, uint256& nCheckpoint) } //set the accumulators to last checkpoint value - AccumulatorMap mapAccumulators; + mapAccumulators.Reset(); if (!mapAccumulators.Load(chainActive[nHeight - 1]->nAccumulatorCheckpoint)) { if (chainActive[nHeight - 1]->nAccumulatorCheckpoint == 0) { //Before zerocoin is fully activated so set to init state @@ -216,14 +216,12 @@ bool CalculateAccumulatorCheckpoint(int nHeight, uint256& nCheckpoint) //grab mints from this block CBlock block; if(!ReadBlockFromDisk(block, pindex)) { - LogPrint("zero","%s: failed to read block from disk\n", __func__); - return false; + return error("%s: failed to read block from disk\n", __func__); } std::list listPubcoins; if (!BlockToPubcoinList(block, listPubcoins, fFilterInvalid)) { - LogPrint("zero","%s: failed to get zerocoin mintlist from block %n\n", __func__, pindex->nHeight); - return false; + return error("%s: failed to get zerocoin mintlist from block %d\n", __func__, pindex->nHeight); } nTotalMintsFound += listPubcoins.size(); @@ -232,23 +230,18 @@ bool CalculateAccumulatorCheckpoint(int nHeight, uint256& nCheckpoint) //add the pubcoins to accumulator for (const PublicCoin pubcoin : listPubcoins) { if(!mapAccumulators.Accumulate(pubcoin, true)) { - LogPrintf("%s: failed to add pubcoin to accumulator at height %n\n", __func__, pindex->nHeight); - return false; + return error("%s: failed to add pubcoin to accumulator at height %n\n", __func__, pindex->nHeight); } } pindex = chainActive.Next(pindex); } // if there were no new mints found, the accumulator checkpoint will be the same as the last checkpoint - if (nTotalMintsFound == 0) { + if (nTotalMintsFound == 0) nCheckpoint = chainActive[nHeight - 1]->nAccumulatorCheckpoint; - } else nCheckpoint = mapAccumulators.GetCheckpoint(); - // make sure that these values are databased because reorgs may have deleted the checksums from DB - DatabaseChecksums(mapAccumulators); - LogPrint("zero", "%s checkpoint=%s\n", __func__, nCheckpoint.GetHex()); return true; } @@ -258,6 +251,51 @@ bool InvalidCheckpointRange(int nHeight) return nHeight > Params().Zerocoin_Block_LastGoodCheckpoint() && nHeight < Params().Zerocoin_Block_RecalculateAccumulators(); } +bool ValidateAccumulatorCheckpoint(const CBlock& block, CBlockIndex* pindex, AccumulatorMap& mapAccumulators) +{ + if (!fVerifyingBlocks && pindex->nHeight >= Params().Zerocoin_StartHeight() && pindex->nHeight % 10 == 0) { + uint256 nCheckpointCalculated = 0; + + // if IDB, invalid outpoints must be calculated or else acc checkpoint will be incorrect + if (pindex->nHeight == Params().Zerocoin_Block_RecalculateAccumulators()) + PopulateInvalidOutPointMap(); + + if (!CalculateAccumulatorCheckpoint(pindex->nHeight, nCheckpointCalculated, mapAccumulators)) { + //Calculate list of checkpoints that may be missing due to deletion on block 809000, and rewinding back before 809000 + int nStop = Params().Zerocoin_Block_RecalculateAccumulators() + 20; + if (pindex->nHeight < nStop && pindex->nHeight > Params().Zerocoin_Block_LastGoodCheckpoint()) { + LogPrintf("%s : Checkpoint not found for block %d, recalculating accumulators\n", __func__, pindex->nHeight); + CBlockIndex* pindexCheckpoint = chainActive[Params().Zerocoin_Block_LastGoodCheckpoint()]; + list listCheckpoints; + while (pindexCheckpoint->nHeight <= nStop) { + if (!count(listCheckpoints.begin(), listCheckpoints.end(), pindexCheckpoint->nAccumulatorCheckpoint)) + listCheckpoints.emplace_back(pindexCheckpoint->nAccumulatorCheckpoint); + + pindexCheckpoint = chainActive.Next(pindexCheckpoint); + if (!pindexCheckpoint) + break; + } + + string strError; + if (!ReindexAccumulators(listCheckpoints, strError) || !CalculateAccumulatorCheckpoint(pindex->nHeight, nCheckpointCalculated, mapAccumulators)) + return error("%s : failed to recalculate accumulator checkpoint", __func__); + } else { + return error("%s : failed to calculate accumulator checkpoint", __func__); + } + } + + if (nCheckpointCalculated != block.nAccumulatorCheckpoint) { + LogPrintf("%s: block=%d calculated: %s\n block: %s\n", __func__, pindex->nHeight, nCheckpointCalculated.GetHex(), block.nAccumulatorCheckpoint.GetHex()); + return error("%s : accumulator does not match calculated value", __func__); + } + } else if (!fVerifyingBlocks) { + if (block.nAccumulatorCheckpoint != pindex->pprev->nAccumulatorCheckpoint) + return error("%s : new accumulator checkpoint generated on a block that is not multiple of 10", __func__); + } + + return true; +} + bool GenerateAccumulatorWitness(const PublicCoin &coin, Accumulator& accumulator, AccumulatorWitness& witness, int nSecurityLevel, int& nMintsAdded, string& strError) { uint256 txid; diff --git a/src/accumulators.h b/src/accumulators.h index e5778e5a337f7..c49d81556946e 100644 --- a/src/accumulators.h +++ b/src/accumulators.h @@ -6,20 +6,24 @@ #define PIVX_ACCUMULATORS_H #include "libzerocoin/Accumulator.h" -#include "libzerocoin/Denominations.h" #include "libzerocoin/Coin.h" +#include "libzerocoin/Denominations.h" #include "primitives/zerocoin.h" +#include "accumulatormap.h" +#include "chain.h" #include "uint256.h" bool GenerateAccumulatorWitness(const libzerocoin::PublicCoin &coin, libzerocoin::Accumulator& accumulator, libzerocoin::AccumulatorWitness& witness, int nSecurityLevel, int& nMintsAdded, std::string& strError); bool GetAccumulatorValueFromDB(uint256 nCheckpoint, libzerocoin::CoinDenomination denom, CBigNum& bnAccValue); bool GetAccumulatorValueFromChecksum(uint32_t nChecksum, bool fMemoryOnly, CBigNum& bnAccValue); void AddAccumulatorChecksum(const uint32_t nChecksum, const CBigNum &bnValue, bool fMemoryOnly); -bool CalculateAccumulatorCheckpoint(int nHeight, uint256& nCheckpoint); +bool CalculateAccumulatorCheckpoint(int nHeight, uint256& nCheckpoint, AccumulatorMap& mapAccumulators); +void DatabaseChecksums(AccumulatorMap& mapAccumulators); bool LoadAccumulatorValuesFromDB(const uint256 nCheckpoint); bool EraseAccumulatorValues(const uint256& nCheckpointErase, const uint256& nCheckpointPrevious); uint32_t ParseChecksum(uint256 nChecksum, libzerocoin::CoinDenomination denomination); uint32_t GetChecksum(const CBigNum &bnValue); bool InvalidCheckpointRange(int nHeight); +bool ValidateAccumulatorCheckpoint(const CBlock& block, CBlockIndex* pindex, AccumulatorMap& mapAccumulators); #endif //PIVX_ACCUMULATORS_H diff --git a/src/main.cpp b/src/main.cpp index 5506873105714..48f9f3d0f563e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,6 +34,7 @@ #include "primitives/zerocoin.h" #include "libzerocoin/Denominations.h" +#include "accumulatormap.h" #include @@ -1274,14 +1275,23 @@ CoinSpend TxInToZerocoinSpend(const CTxIn& txin) return CoinSpend(Params().Zerocoin_Params(), serializedCoinSpend); } -bool IsZerocoinSpendUnknown(CoinSpend coinSpend, uint256 hashTx, CValidationState& state) +//Check a zerocoinspend considering external context such as blockchain data, height, etc. +bool ContextualCheckCoinSpend(const CoinSpend& spend, CBlockIndex* pindex, const uint256& txid) { - uint256 hashTxFromDB; - if(zerocoinDB->ReadCoinSpend(coinSpend.getCoinSerialNumber(), hashTxFromDB)) - return hashTx == hashTxFromDB; + // Make sure that the serial number is in valid range + if (pindex->nHeight >= Params().Zerocoin_Block_EnforceSerialRange()) { + if (!spend.HasValidSerial(Params().Zerocoin_Params())) + return error("%s : txid=%s in block %d contains invalid serial %s\n", __func__, txid.GetHex(), + pindex->nHeight, spend.getCoinSerialNumber()); + } - if(!zerocoinDB->WriteCoinSpend(coinSpend.getCoinSerialNumber(), hashTx)) - return state.DoS(100, error("CheckZerocoinSpend(): Failed to write zerocoin mint to database")); + //Is the serial already in the blockchain? + int nHeightTxSpend = 0; + if (IsSerialInBlockchain(spend.getCoinSerialNumber(), nHeightTxSpend)) { + if(!fVerifyingBlocks || (fVerifyingBlocks && pindex->nHeight > nHeightTxSpend)) + return error("%s : zPiv with serial %s is already in the block %d\n", __func__, + spend.getCoinSerialNumber().GetHex(), nHeightTxSpend); + } return true; } @@ -1592,6 +1602,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa CCoinsViewCache view(&dummy); CAmount nValueIn = 0; + uint256 txid = tx.GetHash(); if(tx.IsZerocoinSpend()){ nValueIn = tx.GetZerocoinSpent(); @@ -1606,15 +1617,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa if (!txIn.scriptSig.IsZerocoinSpend()) continue; CoinSpend spend = TxInToZerocoinSpend(txIn); - int nHeightTx = 0; - if (IsSerialInBlockchain(spend.getCoinSerialNumber(), nHeightTx)) - return state.Invalid(error("%s : zPiv spend with serial %s is already in block %d\n", - __func__, spend.getCoinSerialNumber().GetHex(), nHeightTx)); - - //Is serial in the acceptable range - if (!spend.HasValidSerial(Params().Zerocoin_Params())) - return state.Invalid(error("%s : zPiv spend with serial %s from tx %s is not in valid range\n", - __func__, spend.getCoinSerialNumber().GetHex(), tx.GetHash().GetHex())); + if (!ContextualCheckCoinSpend(spend, chainActive.Tip(), txid)) + return state.Invalid(error("%s: zPIV spend in tx %s failed to pass context checks", __func__, txid.GetHex())); } } else { LOCK(pool.cs); @@ -3164,7 +3168,8 @@ bool ReindexAccumulators(list& listMissingCheckpoints, string& strError //uiInterface.ShowProgress(_("Calculating missing accumulators..."), (int) (dPercent * 100)); if (find(listMissingCheckpoints.begin(), listMissingCheckpoints.end(), pindex->nAccumulatorCheckpoint) != listMissingCheckpoints.end()) { uint256 nCheckpointCalculated = 0; - if (!CalculateAccumulatorCheckpoint(pindex->nHeight, nCheckpointCalculated)) { + AccumulatorMap mapAccumulators; + if (!CalculateAccumulatorCheckpoint(pindex->nHeight, nCheckpointCalculated, mapAccumulators)) { // GetCheckpoint could have terminated due to a shutdown request. Check this here. if (ShutdownRequested()) break; @@ -3179,6 +3184,7 @@ bool ReindexAccumulators(list& listMissingCheckpoints, string& strError return false; } + DatabaseChecksums(mapAccumulators); auto it = find(listMissingCheckpoints.begin(), listMissingCheckpoints.end(), pindex->nAccumulatorCheckpoint); listMissingCheckpoints.erase(it); } @@ -3194,6 +3200,46 @@ bool ReindexAccumulators(list& listMissingCheckpoints, string& strError return true; } +bool UpdateZPIVSupply(const CBlock& block, CBlockIndex* pindex) +{ + std::list listMints; + bool fFilterInvalid = pindex->nHeight >= Params().Zerocoin_Block_RecalculateAccumulators(); + BlockToZerocoinMintList(block, listMints, fFilterInvalid); + std::list listSpends = ZerocoinSpendListFromBlock(block, fFilterInvalid); + + // Initialize zerocoin supply to the supply from previous block + if (pindex->pprev && pindex->pprev->GetBlockHeader().nVersion > 3) { + for (auto& denom : zerocoinDenomList) { + pindex->mapZerocoinSupply.at(denom) = pindex->pprev->mapZerocoinSupply.at(denom); + } + } + + // Track zerocoin money supply + CAmount nAmountZerocoinSpent = 0; + pindex->vMintDenominationsInBlock.clear(); + if (pindex->pprev) { + for (auto& m : listMints) { + libzerocoin::CoinDenomination denom = m.GetDenomination(); + pindex->vMintDenominationsInBlock.push_back(m.GetDenomination()); + pindex->mapZerocoinSupply.at(denom)++; + } + + for (auto& denom : listSpends) { + pindex->mapZerocoinSupply.at(denom)--; + nAmountZerocoinSpent += libzerocoin::ZerocoinDenominationToAmount(denom); + + // zerocoin failsafe + if (pindex->mapZerocoinSupply.at(denom) < 0) + return error("Block contains zerocoins that spend more than are in the available supply to spend"); + } + } + + for (auto& denom : zerocoinDenomList) + LogPrint("zero" "%s coins for denomination %d pubcoin %s\n", __func__, pindex->mapZerocoinSupply.at(denom), denom); + + return true; +} + static int64_t nTimeVerify = 0; static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; @@ -3254,19 +3300,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } } - // BIP16 didn't become active until Apr 1 2012 - int64_t nBIP16SwitchTime = 1333238400; - bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime); - - unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; - - // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded: - if (block.nVersion >= 3 && CBlockIndex::IsSuperMajority(3, pindex->pprev, Params().EnforceBlockUpgradeMajority())) { - flags |= SCRIPT_VERIFY_DERSIG; - } - - CBlockUndo blockundo; - CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); int64_t nTimeStart = GetTimeMicros(); @@ -3275,7 +3308,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin unsigned int nSigOps = 0; CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector > vPos; + std::vector > vSpends; vPos.reserve(block.vtx.size()); + CBlockUndo blockundo; blockundo.vtxundo.reserve(block.vtx.size() - 1); CAmount nValueOut = 0; CAmount nValueIn = 0; @@ -3287,13 +3322,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin nInputs += tx.vin.size(); nSigOps += GetLegacySigOpCount(tx); if (nSigOps > nMaxBlockSigOps) - return state.DoS(100, error("ConnectBlock() : too many sigops"), - REJECT_INVALID, "bad-blk-sigops"); + return state.DoS(100, error("ConnectBlock() : too many sigops"), REJECT_INVALID, "bad-blk-sigops"); //Temporarily disable zerocoin transactions for maintenance if (block.nTime > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && !IsInitialBlockDownload() && tx.ContainsZerocoins()) { return state.DoS(100, error("ConnectBlock() : zerocoin transactions are currently in maintenance mode")); } + if (tx.IsZerocoinSpend()) { int nHeightTx = 0; uint256 txid = tx.GetHash(); @@ -3307,35 +3342,19 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } //Check for double spending of serial #'s + set setSerials; for (const CTxIn& txIn : tx.vin) { if (!txIn.scriptSig.IsZerocoinSpend()) continue; CoinSpend spend = TxInToZerocoinSpend(txIn); nValueIn += spend.getDenomination() * COIN; - // Make sure that the serial number is in valid range - if (!spend.HasValidSerial(Params().Zerocoin_Params())) { - string strError = strprintf("%s : txid=%s in block %d contains invalid serial %s\n", __func__, tx.GetHash().GetHex(), pindex->nHeight, spend.getCoinSerialNumber()); - if (pindex->nHeight >= Params().Zerocoin_Block_EnforceSerialRange()) - return state.DoS(100, error(strError.c_str())); - strError = "NOT ENFORCING : " + strError; - LogPrintf(strError.c_str()); - } - - //Is the serial already in the blockchain? - uint256 hashTxFromDB; - int nHeightTxSpend = 0; - if (zerocoinDB->ReadCoinSpend(spend.getCoinSerialNumber(), hashTxFromDB)) { - if(IsSerialInBlockchain(spend.getCoinSerialNumber(), nHeightTxSpend)) { - if(!fVerifyingBlocks || (fVerifyingBlocks && pindex->nHeight > nHeightTxSpend)) - return state.DoS(100, error("%s : zPiv with serial %s is already in the block %d\n", - __func__, spend.getCoinSerialNumber().GetHex(), nHeightTxSpend)); - } - } + //Perform checks on the spend that are based on blockchain context + if (!ContextualCheckCoinSpend(spend, pindex, txid)) + return state.DoS(100, error("%s: Coinspend is not valid in block %s", __func__, block.GetHash().GetHex())); - //record spend to database - if (!zerocoinDB->WriteCoinSpend(spend.getCoinSerialNumber(), tx.GetHash())) - return error("%s : failed to record coin serial to database"); + //queue for db write after the 'justcheck' section has concluded + vSpends.emplace_back(make_pair(spend, tx.GetHash())); } } else if (!tx.IsCoinBase()) { if (!view.HaveInputs(tx)) @@ -3350,21 +3369,19 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } } - if (fStrictPayToScriptHash) { - // Add in sigops done by pay-to-script-hash inputs; - // this is to prevent a "rogue miner" from creating - // an incredibly-expensive-to-validate block. - nSigOps += GetP2SHSigOpCount(tx, view); - if (nSigOps > nMaxBlockSigOps) - return state.DoS(100, error("ConnectBlock() : too many sigops"), - REJECT_INVALID, "bad-blk-sigops"); - } + // Add in sigops done by pay-to-script-hash inputs; + // this is to prevent a "rogue miner" from creating + // an incredibly-expensive-to-validate block. + nSigOps += GetP2SHSigOpCount(tx, view); + if (nSigOps > nMaxBlockSigOps) + return state.DoS(100, error("ConnectBlock() : too many sigops"), REJECT_INVALID, "bad-blk-sigops"); if (!tx.IsCoinStake()) nFees += view.GetValueIn(tx) - tx.GetValueOut(); nValueIn += view.GetValueIn(tx); std::vector vChecks; + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG; if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); @@ -3381,47 +3398,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } - std::list listMints; - bool fFilterInvalid = pindex->nHeight >= Params().Zerocoin_Block_RecalculateAccumulators(); - BlockToZerocoinMintList(block, listMints, fFilterInvalid); - std::list listSpends = ZerocoinSpendListFromBlock(block, fFilterInvalid); - + //A one-time event where money supply counts were off and recalculated on a certain block. if (pindex->nHeight == Params().Zerocoin_Block_RecalculateAccumulators() + 1) { RecalculateZPIVMinted(); RecalculateZPIVSpent(); RecalculatePIVSupply(Params().Zerocoin_StartHeight()); } - // Initialize zerocoin supply to the supply from previous block - if (pindex->pprev && pindex->pprev->GetBlockHeader().nVersion > 3) { - for (auto& denom : zerocoinDenomList) { - pindex->mapZerocoinSupply.at(denom) = pindex->pprev->mapZerocoinSupply.at(denom); - } - } - - // Track zerocoin money supply - CAmount nAmountZerocoinSpent = 0; - pindex->vMintDenominationsInBlock.clear(); - if (pindex->pprev) { - for (auto& m : listMints) { - libzerocoin::CoinDenomination denom = m.GetDenomination(); - pindex->vMintDenominationsInBlock.push_back(m.GetDenomination()); - pindex->mapZerocoinSupply.at(denom)++; - } - - for (auto& denom : listSpends) { - pindex->mapZerocoinSupply.at(denom)--; - nAmountZerocoinSpent += libzerocoin::ZerocoinDenominationToAmount(denom); - - // zerocoin failsafe - if (pindex->mapZerocoinSupply.at(denom) < 0) - return state.DoS(100, error("Block contains zerocoins that spend more than are in the available supply to spend")); - } - } - - for (auto& denom : zerocoinDenomList) { - LogPrint("zero" "%s coins for denomination %d pubcoin %s\n", __func__, pindex->mapZerocoinSupply.at(denom), denom); - } + //Track zPIV money supply in the block index + if (!UpdateZPIVSupply(block, pindex)) + return state.DoS(100, error("%s: Failed to calculate new zPIV supply for block=%s height=%d", __func__, + block.GetHash().GetHex(), pindex->nHeight), REJECT_INVALID); // track money supply and mint amount info CAmount nMoneySupplyPrev = pindex->pprev ? pindex->pprev->nMoneySupply : 0; @@ -3432,9 +3419,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // FormatMoney(nValueOut), FormatMoney(nValueIn), // FormatMoney(nFees), FormatMoney(pindex->nMint), FormatMoney(nAmountZerocoinSpent)); - if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))) - return error("Connect() : WriteBlockIndex for pindex failed"); - int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart; LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs - 1), nTimeConnect * 0.000001); @@ -3444,53 +3428,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (block.IsProofOfWork()) nExpectedMint += nFees; + //Check that the block does not overmint if (!IsBlockValueValid(block, nExpectedMint, pindex->nMint)) { - return state.DoS(100, - error("ConnectBlock() : reward pays too much (actual=%s vs limit=%s)", - FormatMoney(pindex->nMint), FormatMoney(nExpectedMint)), - REJECT_INVALID, "bad-cb-amount"); + return state.DoS(100, error("ConnectBlock() : reward pays too much (actual=%s vs limit=%s)", + FormatMoney(pindex->nMint), FormatMoney(nExpectedMint)), REJECT_INVALID, "bad-cb-amount"); } - // zerocoin accumulator: if a new accumulator checkpoint was generated, check that it is the correct value - if (!fVerifyingBlocks && pindex->nHeight >= Params().Zerocoin_StartHeight() && pindex->nHeight % 10 == 0) { - uint256 nCheckpointCalculated = 0; - - // if IDB, invalid outpoints must be calculated or else acc checkpoint will be incorrect - if (pindex->nHeight == Params().Zerocoin_Block_RecalculateAccumulators()) - PopulateInvalidOutPointMap(); - - if (!CalculateAccumulatorCheckpoint(pindex->nHeight, nCheckpointCalculated)) { - //Calculate list of checkpoints that may be missing due to deletion on block 809000, and rewinding back before 809000 - int nStop = Params().Zerocoin_Block_RecalculateAccumulators() + 20; - if (pindex->nHeight < nStop && pindex->nHeight > Params().Zerocoin_Block_LastGoodCheckpoint()) { - LogPrintf("%s : Checkpoint not found for block %d, recalculating accumulators\n", __func__, pindex->nHeight); - CBlockIndex* pindexCheckpoint = chainActive[Params().Zerocoin_Block_LastGoodCheckpoint()]; - list listCheckpoints; - while (pindexCheckpoint->nHeight <= nStop) { - if (!count(listCheckpoints.begin(), listCheckpoints.end(), pindexCheckpoint->nAccumulatorCheckpoint)) - listCheckpoints.emplace_back(pindexCheckpoint->nAccumulatorCheckpoint); - - pindexCheckpoint = chainActive.Next(pindexCheckpoint); - if (!pindexCheckpoint) - break; - } - - string strError; - if (!ReindexAccumulators(listCheckpoints, strError) || !CalculateAccumulatorCheckpoint(pindex->nHeight, nCheckpointCalculated)) - return state.DoS(100, error("ConnectBlock() : failed to recalculate accumulator checkpoint")); - } else { - return state.DoS(100, error("ConnectBlock() : failed to calculate accumulator checkpoint")); - } - } - - if (nCheckpointCalculated != block.nAccumulatorCheckpoint) { - LogPrintf("%s: block=%d calculated: %s\n block: %s\n", __func__, pindex->nHeight, nCheckpointCalculated.GetHex(), block.nAccumulatorCheckpoint.GetHex()); - return state.DoS(100, error("ConnectBlock() : accumulator does not match calculated value")); - } - } else if (!fVerifyingBlocks) { - if (block.nAccumulatorCheckpoint != pindex->pprev->nAccumulatorCheckpoint) - return state.DoS(100, error("ConnectBlock() : new accumulator checkpoint generated on a block that is not multiple of 10")); - } + // Ensure that accumulator checkpoints are valid and in the same state as this instance of the chain + AccumulatorMap mapAccumulators; + if (!ValidateAccumulatorCheckpoint(block, pindex, mapAccumulators)) + return state.DoS(100, error("%s: Failed to validate accumulator checkpoint for block=%s height=%d", __func__, + block.GetHash().GetHex(), pindex->nHeight), REJECT_INVALID, "bad-acc-checkpoint"); if (!control.Wait()) return state.DoS(100, false); @@ -3498,6 +3446,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin nTimeVerify += nTime2 - nTimeStart; LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs - 1), nTimeVerify * 0.000001); + //IMPORTANT NOTE: Nothing before this point should actually store to disk (or even memory) if (fJustCheck) return true; @@ -3519,10 +3468,22 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin setDirtyBlockIndex.insert(pindex); } + //Record zPIV serials + for (pair pSpend : vSpends) { + //record spend to database + if (!zerocoinDB->WriteCoinSpend(pSpend.first.getCoinSerialNumber(), pSpend.second)) + return state.Abort(("Failed to record coin serial to database")); + } + + //Record accumulator checksums + DatabaseChecksums(mapAccumulators); + if (fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) return state.Abort("Failed to write transaction index"); + + // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); @@ -5114,72 +5075,6 @@ bool static LoadBlockIndexDB(string& strError) pblocktree->ReadFlag("shutdown", fLastShutdownWasPrepared); LogPrintf("%s: Last shutdown was prepared: %s\n", __func__, fLastShutdownWasPrepared); - //Check for inconsistency with block file info and internal state - if (!fLastShutdownWasPrepared && !GetBoolArg("-forcestart", false) && !GetBoolArg("-reindex", false)) { - unsigned int nHeightLastBlockFile = vinfoBlockFile[nLastBlockFile].nHeightLast + 1; - if (vSortedByHeight.size() > nHeightLastBlockFile && pcoinsTip->GetBestBlock() != vSortedByHeight[nHeightLastBlockFile].second->GetBlockHash()) { - //The database is in a state where a block has been accepted and written to disk, but the - //transaction database (pcoinsTip) was not flushed to disk, and is therefore not in sync with - //the block index database. - - if (!mapBlockIndex.count(pcoinsTip->GetBestBlock())) { - strError = "The wallet has been not been closed gracefully, causing the transaction database to be out of sync with the block database"; - return false; - } - LogPrintf("%s : pcoinstip synced to block height %d, block index height %d\n", __func__, - mapBlockIndex[pcoinsTip->GetBestBlock()]->nHeight, vSortedByHeight.size()); - - //get the index associated with the point in the chain that pcoinsTip is synced to - CBlockIndex *pindexLastMeta = vSortedByHeight[vinfoBlockFile[nLastBlockFile].nHeightLast + 1].second; - CBlockIndex *pindex = vSortedByHeight[0].second; - unsigned int nSortedPos = 0; - for (unsigned int i = 0; i < vSortedByHeight.size(); i++) { - nSortedPos = i; - if (vSortedByHeight[i].first == mapBlockIndex[pcoinsTip->GetBestBlock()]->nHeight + 1) { - pindex = vSortedByHeight[i].second; - break; - } - } - - // Start at the last block that was successfully added to the txdb (pcoinsTip) and manually add all transactions that occurred for each block up until - // the best known block from the block index db. - CCoinsViewCache view(pcoinsTip); - while (nSortedPos < vSortedByHeight.size()) { - CBlock block; - if (!ReadBlockFromDisk(block, pindex)) { - strError = "The wallet has been not been closed gracefully and has caused corruption of blocks stored to disk. Data directory is in an unusable state"; - return false; - } - - vector vtxundo; - vtxundo.reserve(block.vtx.size() - 1); - uint256 hashBlock = block.GetHash(); - for (unsigned int i = 0; i < block.vtx.size(); i++) { - CValidationState state; - CTxUndo undoDummy; - if (i > 0) - vtxundo.push_back(CTxUndo()); - UpdateCoins(block.vtx[i], state, view, i == 0 ? undoDummy : vtxundo.back(), pindex->nHeight); - view.SetBestBlock(hashBlock); - } - - if(pindex->nHeight >= pindexLastMeta->nHeight) - break; - - pindex = vSortedByHeight[++nSortedPos].second; - } - - // Save the updates to disk - if (!view.Flush() || !pcoinsTip->Flush()) - LogPrintf("%s : failed to flush view\n", __func__); - - LogPrintf("%s: Last block properly recorded: #%d %s\n", __func__, pindexLastMeta->nHeight, - pindexLastMeta->GetBlockHash().ToString().c_str()); - LogPrintf("%s : pcoinstip=%d %s\n", __func__, mapBlockIndex[pcoinsTip->GetBestBlock()]->nHeight, - pcoinsTip->GetBestBlock().GetHex()); - } - } - // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); diff --git a/src/main.h b/src/main.h index 46d692e304fab..83a4a86d2173a 100644 --- a/src/main.h +++ b/src/main.h @@ -356,6 +356,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState& state, CCoinsViewCach bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, bool fRejectBadUTXO, CValidationState& state); bool CheckZerocoinMint(const uint256& txHash, const CTxOut& txout, CValidationState& state, bool fCheckOnly = false); bool CheckZerocoinSpend(const CTransaction tx, bool fVerifySignature, CValidationState& state); +bool ContextualCheckCoinSpend(const libzerocoin::CoinSpend& spend, CBlockIndex* pindex, const uint256& txid); libzerocoin::CoinSpend TxInToZerocoinSpend(const CTxIn& txin); bool TxOutToPublicCoin(const CTxOut txout, libzerocoin::PublicCoin& pubCoin, CValidationState& state); bool BlockToPubcoinList(const CBlock& block, list& listPubcoins, bool fFilterInvalid); diff --git a/src/miner.cpp b/src/miner.cpp index ed50a2f182c58..0531402f08138 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -457,7 +457,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); pblock->nNonce = 0; uint256 nCheckpoint = 0; - if(fZerocoinActive && !CalculateAccumulatorCheckpoint(nHeight, nCheckpoint)){ + AccumulatorMap mapAccumulators; + if(fZerocoinActive && !CalculateAccumulatorCheckpoint(nHeight, nCheckpoint, mapAccumulators)){ LogPrintf("%s: failed to get accumulator checkpoint\n", __func__); } pblock->nAccumulatorCheckpoint = nCheckpoint;