bool GenerateAccumulatorWitness( const PublicCoin &coin, Accumulator& accumulator, AccumulatorWitness& witness, int& nMintsAdded, string& strError, CBlockIndex* pindexCheckpoint) { try { // Lock LogPrint("zero", "%s: generating\n", __func__); if (!LockMethod()) return false; LogPrint("zero", "%s: after lock\n", __func__); int nHeightMintAdded = SearchMintHeightOf(coin.getValue()); //get the checkpoint added at the next multiple of 10 int nHeightCheckpoint = nHeightMintAdded + (10 - (nHeightMintAdded % 10)); //the height to start accumulating coins to add to witness int nAccStartHeight = nHeightMintAdded - (nHeightMintAdded % 10); //Get the accumulator that is right before the cluster of blocks containing our mint was added to the accumulator CBigNum bnAccValue = 0; if (GetAccumulatorValue(nHeightCheckpoint, coin.getDenomination(), bnAccValue)) { if(!bnAccValue && Params().NetworkID() == CBaseChainParams::REGTEST){ accumulator.setInitialValue(); witness.resetValue(accumulator, coin); }else { accumulator.setValue(bnAccValue); witness.resetValue(accumulator, coin); } } //add the pubcoins from the blockchain up to the next checksum starting from the block CBlockIndex *pindex = chainActive[nHeightCheckpoint - 10]; int nChainHeight = chainActive.Height(); int nHeightStop = nChainHeight % 10; nHeightStop = nChainHeight - nHeightStop - 20; // at least two checkpoints deep //If looking for a specific checkpoint if (pindexCheckpoint) nHeightStop = pindexCheckpoint->nHeight - 10; //Iterate through the chain and calculate the witness int nCheckpointsAdded = 0; nMintsAdded = 0; libzerocoin::Accumulator witnessAccumulator = accumulator; if(!calculateAccumulatedBlocksFor( nAccStartHeight, nHeightStop, nHeightMintAdded, pindex, nCheckpointsAdded, bnAccValue, accumulator, witnessAccumulator, coin, strError )){ return error("GenerateAccumulatorWitness(): Calculate accumulated coins failed"); } witness.resetValue(witnessAccumulator, coin); if (!witness.VerifyWitness(accumulator, coin)) return error("%s: failed to verify witness", __func__); // A certain amount of accumulated coins are required if (nMintsAdded < Params().Zerocoin_RequiredAccumulation()) { strError = _(strprintf("Less than %d mints added, unable to create spend", Params().Zerocoin_RequiredAccumulation()).c_str()); return error("%s : %s", __func__, strError); } // calculate how many mints of this denomination existed in the accumulator we initialized nMintsAdded += ComputeAccumulatedCoins(nAccStartHeight, coin.getDenomination()); LogPrint("zero", "%s : %d mints added to witness\n", __func__, nMintsAdded); 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); } }
bool GenerateAccumulatorWitness(const PublicCoin &coin, Accumulator& accumulator, AccumulatorWitness& witness, int nSecurityLevel, int& nMintsAdded, string& strError) { uint256 txid; if (!zerocoinDB->ReadCoinMint(coin.getValue(), txid)) { LogPrint("zero","%s failed to read mint from db\n", __func__); return false; } CTransaction txMinted; uint256 hashBlock; if (!GetTransaction(txid, txMinted, hashBlock)) { LogPrint("zero","%s failed to read tx\n", __func__); return false; } int nHeightMintAdded= mapBlockIndex[hashBlock]->nHeight; uint256 nCheckpointBeforeMint = 0; CBlockIndex* pindex = chainActive[nHeightMintAdded]; int nChanges = 0; //find the checksum when this was added to the accumulator officially, which will be two checksum changes later //reminder that checksums are generated when the block height is a multiple of 10 while (pindex->nHeight < chainActive.Tip()->nHeight - 1) { if (pindex->nHeight == nHeightMintAdded) { pindex = chainActive[pindex->nHeight + 1]; continue; } //check if the next checksum was generated if (pindex->nHeight % 10 == 0) { nChanges++; if (nChanges == 1) { nCheckpointBeforeMint = pindex->nAccumulatorCheckpoint; break; } } pindex = chainActive.Next(pindex); } //the height to start accumulating coins to add to witness int nAccStartHeight = nHeightMintAdded - (nHeightMintAdded % 10); //If the checkpoint is from the recalculated checkpoint period, then adjust it int nHeight_LastGoodCheckpoint = Params().Zerocoin_Block_LastGoodCheckpoint(); int nHeight_Recalculate = Params().Zerocoin_Block_RecalculateAccumulators(); if (pindex->nHeight < nHeight_Recalculate - 10 && pindex->nHeight > nHeight_LastGoodCheckpoint) { //The checkpoint before the mint will be the last good checkpoint nCheckpointBeforeMint = chainActive[nHeight_LastGoodCheckpoint]->nAccumulatorCheckpoint; nAccStartHeight = nHeight_LastGoodCheckpoint - 10; } //Get the accumulator that is right before the cluster of blocks containing our mint was added to the accumulator CBigNum bnAccValue = 0; if (GetAccumulatorValueFromDB(nCheckpointBeforeMint, coin.getDenomination(), bnAccValue)) { if (bnAccValue > 0) { accumulator.setValue(bnAccValue); witness.resetValue(accumulator, coin); } } //security level: this is an important prevention of tracing the coins via timing. Security level represents how many checkpoints //of accumulated coins are added *beyond* the checkpoint that the mint being spent was added too. If each spend added the exact same //amounts of checkpoints after the mint was accumulated, then you could know the range of blocks that the mint originated from. if (nSecurityLevel < 100) { //add some randomness to the user's selection so that it is not always the same nSecurityLevel += CBigNum::randBignum(10).getint(); //security level 100 represents adding all available coins that have been accumulated - user did not select this if (nSecurityLevel >= 100) nSecurityLevel = 99; } //add the pubcoins (zerocoinmints that have been published to the chain) up to the next checksum starting from the block pindex = chainActive[nAccStartHeight]; int nChainHeight = chainActive.Height(); int nHeightStop = nChainHeight % 10; nHeightStop = nChainHeight - nHeightStop - 20; // at least two checkpoints deep int nCheckpointsAdded = 0; nMintsAdded = 0; while (pindex->nHeight < nHeightStop + 1) { if (pindex->nHeight != nAccStartHeight && pindex->pprev->nAccumulatorCheckpoint != pindex->nAccumulatorCheckpoint) ++nCheckpointsAdded; //if a new checkpoint was generated on this block, and we have added the specified amount of checkpointed accumulators, //then initialize the accumulator at this point and break if (!InvalidCheckpointRange(pindex->nHeight) && (pindex->nHeight >= nHeightStop || (nSecurityLevel != 100 && nCheckpointsAdded >= nSecurityLevel))) { uint32_t nChecksum = ParseChecksum(chainActive[pindex->nHeight + 10]->nAccumulatorCheckpoint, coin.getDenomination()); CBigNum bnAccValue = 0; if (!zerocoinDB->ReadAccumulatorValue(nChecksum, bnAccValue)) { LogPrintf("%s : failed to find checksum in database for accumulator\n", __func__); return false; } accumulator.setValue(bnAccValue); break; } // if this block contains mints of the denomination that is being spent, then add them to the witness if (pindex->MintedDenomination(coin.getDenomination())) { //grab mints from this block CBlock block; if(!ReadBlockFromDisk(block, pindex)) { LogPrintf("%s: failed to read block from disk while adding pubcoins to witness\n", __func__); return false; } list<PublicCoin> listPubcoins; if(!BlockToPubcoinList(block, listPubcoins, true)) { LogPrintf("%s: failed to get zerocoin mintlist from block %n\n", __func__, pindex->nHeight); return false; } //add the mints to the witness for (const PublicCoin pubcoin : listPubcoins) { if (pubcoin.getDenomination() != coin.getDenomination()) continue; if (pindex->nHeight == nHeightMintAdded && pubcoin.getValue() == coin.getValue()) continue; witness.addRawValue(pubcoin.getValue()); ++nMintsAdded; } } pindex = chainActive[pindex->nHeight + 1]; } if (nMintsAdded < Params().Zerocoin_RequiredAccumulation()) { strError = _(strprintf("Less than %d mints added, unable to create spend", Params().Zerocoin_RequiredAccumulation()).c_str()); LogPrintf("%s : %s\n", __func__, strError); return false; } // calculate how many mints of this denomination existed in the accumulator we initialized int nZerocoinStartHeight = GetZerocoinStartHeight(); pindex = chainActive[nZerocoinStartHeight]; while (pindex->nHeight < nAccStartHeight) { nMintsAdded += count(pindex->vMintDenominationsInBlock.begin(), pindex->vMintDenominationsInBlock.end(), coin.getDenomination()); pindex = chainActive[pindex->nHeight + 1]; } LogPrint("zero","%s : %d mints added to witness\n", __func__, nMintsAdded); return true; }
bool CalculateAccumulatorWitnessFor( const ZerocoinParams* params, int startHeight, int maxCalulationRange, CoinDenomination den, const CBloomFilter& filter, Accumulator& accumulator, AccumulatorWitness& witness, int& nMintsAdded, string& strError, list<CBigNum>& ret, int &heightStop ){ // Lock if (!LockMethod()) return false; try { // Dummy coin init PublicCoin temp(params, 0, den); // Dummy Acc init Accumulator testingAcc(params, den); //get the checkpoint added at the next multiple of 10 int nHeightCheckpoint = startHeight + (10 - (startHeight % 10)); // Get the base accumulator // TODO: This needs to be changed to the partial witness calculation on the next version. CBigNum bnAccValue = 0; if (GetAccumulatorValue(nHeightCheckpoint, den, bnAccValue)) { accumulator.setValue(bnAccValue); witness.resetValue(accumulator, temp); } // Add the pubcoins from the blockchain up to the next checksum starting from the block CBlockIndex *pindex = chainActive[nHeightCheckpoint -10]; int nChainHeight = chainActive.Height(); int nHeightStop = nChainHeight % 10; nHeightStop = nChainHeight - nHeightStop - 20; // at least two checkpoints deep if (nHeightStop - startHeight > maxCalulationRange) { int stop = (startHeight + maxCalulationRange); int nHeightStop = stop % 10; nHeightStop = stop - nHeightStop - 20; } heightStop = nHeightStop; nMintsAdded = 0; // Starts on top of the witness that the node sent libzerocoin::Accumulator witnessAccumulator(params, den, witness.getValue()); if(!calculateAccumulatedBlocksFor( startHeight, nHeightStop, pindex, bnAccValue, accumulator, den, filter, witnessAccumulator, ret, strError )) return error("CalculateAccumulatorWitnessFor(): Calculate accumulated coins failed"); // reset the value witness.resetValue(witnessAccumulator, temp); // calculate how many mints of this denomination existed in the accumulator we initialized nMintsAdded += ComputeAccumulatedCoins(startHeight, den); LogPrint("zero", "%s : %d mints added to witness\n", __func__, nMintsAdded); return true; } catch (ChecksumInDbNotFoundException e) { return error("%s: ChecksumInDbNotFoundException: %s", __func__, e.message); } catch (GetPubcoinException e) { return error("%s: GetPubcoinException: %s", __func__, e.message); } }