bool InitializeAccumulators(const int nHeight, int& nHeightCheckpoint, AccumulatorMap& mapAccumulators) { if (nHeight < Params().Zerocoin_StartHeight()) return error("%s: height is below zerocoin activated", __func__); //On a specific block, a recalculation of the accumulators will be forced if (nHeight == Params().Zerocoin_Block_RecalculateAccumulators() && Params().NetworkID() != CBaseChainParams::REGTEST) { mapAccumulators.Reset(); if (!mapAccumulators.Load(chainActive[Params().Zerocoin_Block_LastGoodCheckpoint()]->nAccumulatorCheckpoint)) return error("%s: failed to reset to previous checkpoint when recalculating accumulators", __func__); // Erase the checkpoints from the period of time that bad mints were being made if (!EraseCheckpoints(Params().Zerocoin_Block_LastGoodCheckpoint() + 1, nHeight)) return error("%s : failed to erase Checkpoints while recalculating checkpoints", __func__); nHeightCheckpoint = Params().Zerocoin_Block_LastGoodCheckpoint(); return true; } if (nHeight >= Params().Zerocoin_Block_V2_Start()) { //after v2_start, accumulators need to use v2 params mapAccumulators.Reset(Params().Zerocoin_Params(false)); // 20 after v2 start is when the new checkpoints will be in the block, so don't need to load hard checkpoints if (nHeight <= Params().Zerocoin_Block_V2_Start() + 20 && Params().NetworkID() != CBaseChainParams::REGTEST) { //Load hard coded checkpointed value AccumulatorCheckpoints::Checkpoint checkpoint = AccumulatorCheckpoints::GetClosestCheckpoint(nHeight, nHeightCheckpoint); if (nHeightCheckpoint < 0) return error("%s: failed to load hard-checkpoint for block %s", __func__, nHeight); mapAccumulators.Load(checkpoint); return true; } } //Use the previous block's checkpoint to initialize the accumulator's state uint256 nCheckpointPrev = chainActive[nHeight - 1]->nAccumulatorCheckpoint; if (nCheckpointPrev == 0) mapAccumulators.Reset(); else if (!mapAccumulators.Load(nCheckpointPrev)) return error("%s: failed to reset to previous checkpoint", __func__); nHeightCheckpoint = nHeight; return true; }
//Get checkpoint value for a specific block height bool CalculateAccumulatorCheckpoint(int nHeight, uint256& nCheckpoint) { if (nHeight < Params().Zerocoin_StartHeight()) { nCheckpoint = 0; return true; } //the checkpoint is updated every ten blocks, return current active checkpoint if not update block if (nHeight % 10 != 0) { nCheckpoint = chainActive[nHeight - 1]->nAccumulatorCheckpoint; return true; } //set the accumulators to last checkpoint value AccumulatorMap mapAccumulators; if (!mapAccumulators.Load(chainActive[nHeight - 1]->nAccumulatorCheckpoint)) { if (chainActive[nHeight - 1]->nAccumulatorCheckpoint == 0) { //Before zerocoin is fully activated so set to init state mapAccumulators.Reset(); } else { LogPrintf("%s: failed to reset to previous checkpoint\n", __func__); return false; } } //Whether this should filter out invalid/fraudulent outpoints bool fFilterInvalid = nHeight >= Params().Zerocoin_Block_RecalculateAccumulators(); //Accumulate all coins over the last ten blocks that havent been accumulated (height - 20 through height - 11) int nTotalMintsFound = 0; CBlockIndex *pindex = chainActive[nHeight - 20]; //On a specific block, a recalculation of the accumulators will be forced if (nHeight == Params().Zerocoin_Block_RecalculateAccumulators()) { pindex = chainActive[Params().Zerocoin_Block_LastGoodCheckpoint() - 10]; mapAccumulators.Reset(); if (!mapAccumulators.Load(chainActive[Params().Zerocoin_Block_LastGoodCheckpoint()]->nAccumulatorCheckpoint)) { LogPrintf("%s: failed to reset to previous checkpoint when recalculating accumulators\n", __func__); return false; } LogPrintf("*** %s recalculating checkpoint\n", __func__); // Erase the checkpoints from the period of time that bad mints were being made if (!EraseCheckpoints(Params().Zerocoin_Block_LastGoodCheckpoint() + 1, nHeight)) { LogPrintf("%s : failed to erase Checkpoints while recalculating checkpoints\n", __func__); return false; } } while (pindex->nHeight < nHeight - 10) { // checking whether we should stop this process due to a shutdown request if (ShutdownRequested()) { return false; } //make sure this block is eligible for accumulation if (pindex->nHeight < Params().Zerocoin_StartHeight()) { pindex = chainActive[pindex->nHeight + 1]; continue; } //grab mints from this block CBlock block; if(!ReadBlockFromDisk(block, pindex)) { LogPrint("zero","%s: failed to read block from disk\n", __func__); return false; } std::list<PublicCoin> listPubcoins; if (!BlockToPubcoinList(block, listPubcoins, fFilterInvalid)) { LogPrint("zero","%s: failed to get zerocoin mintlist from block %n\n", __func__, pindex->nHeight); return false; } nTotalMintsFound += listPubcoins.size(); LogPrint("zero", "%s found %d mints\n", __func__, listPubcoins.size()); //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; } } 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) { 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; }
bool GenerateAccumulatorWitness(CoinWitnessData* coinWitness, AccumulatorMap& mapAccumulators, CBlockIndex* pindexCheckpoint) { try { // Lock LogPrint("zero", "%s: generating\n", __func__); if (!LockMethod()) return false; LogPrint("zero", "%s: after lock\n", __func__); int64_t nTimeStart = GetTimeMicros(); //If there is a Acc End height filled in, then this has already been partially accumulated. if (!coinWitness->nHeightAccEnd) { LogPrintf("RESET ACC\n"); coinWitness->pAccumulator = std::unique_ptr<Accumulator>(new Accumulator(Params().Zerocoin_Params(false), coinWitness->denom)); coinWitness->pWitness = std::unique_ptr<AccumulatorWitness>(new AccumulatorWitness(Params().Zerocoin_Params(false), *coinWitness->pAccumulator, *coinWitness->coin)); } // Mint added height coinWitness->SetHeightMintAdded(SearchMintHeightOf(coinWitness->coin->getValue())); // Set the initial state of the witness accumulator for this coin. CBigNum bnAccValue = 0; if (!coinWitness->nHeightAccEnd && GetAccumulatorValue(coinWitness->nHeightCheckpoint, coinWitness->coin->getDenomination(), bnAccValue)) { libzerocoin::Accumulator witnessAccumulator(Params().Zerocoin_Params(false), coinWitness->denom, bnAccValue); coinWitness->pAccumulator->setValue(witnessAccumulator.getValue()); } //add the pubcoins from the blockchain up to the next checksum starting from the block int nChainHeight = chainActive.Height(); int nHeightMax = nChainHeight % 10; nHeightMax = nChainHeight - nHeightMax - 20; // at least two checkpoints deep // Determine the height to stop at int nHeightStop; if (pindexCheckpoint) { nHeightStop = pindexCheckpoint->nHeight - 10; nHeightStop -= nHeightStop % 10; LogPrint("zero", "%s: using checkpoint height %d\n", __func__, pindexCheckpoint->nHeight); } else { nHeightStop = nHeightMax; } if (nHeightStop > coinWitness->nHeightAccEnd) AccumulateRange(coinWitness, nHeightStop - 1); mapAccumulators.Load(chainActive[nHeightStop + 10]->nAccumulatorCheckpoint); coinWitness->pWitness->resetValue(*coinWitness->pAccumulator, *coinWitness->coin); if(!coinWitness->pWitness->VerifyWitness(mapAccumulators.GetAccumulator(coinWitness->denom), *coinWitness->coin)) return error("%s: failed to verify witness", __func__); // A certain amount of accumulated coins are required if (coinWitness->nMintsAdded < Params().Zerocoin_RequiredAccumulation()) return error("%s : Less than %d mints added, unable to create spend. %s", __func__, Params().Zerocoin_RequiredAccumulation(), coinWitness->ToString()); // calculate how many mints of this denomination existed in the accumulator we initialized coinWitness->nMintsAdded += ComputeAccumulatedCoins(coinWitness->nHeightAccStart, coinWitness->denom); LogPrint("zero", "%s : %d mints added to witness\n", __func__, coinWitness->nMintsAdded); int64_t nTime1 = GetTimeMicros(); LogPrint("bench", " - Witness generated in %.2fms\n", 0.001 * (nTime1 - nTimeStart)); return true; // TODO: I know that could merge all of this exception but maybe it's not really good.. think if we should have a different treatment for each one } catch (searchMintHeightException e) { return error("%s: searchMintHeightException: %s", __func__, e.message); } catch (ChecksumInDbNotFoundException e) { return error("%s: ChecksumInDbNotFoundException: %s", __func__, e.message); } catch (GetPubcoinException e) { return error("%s: GetPubcoinException: %s", __func__, e.message); } }