This commit is contained in:
2022-02-03 23:45:47 -08:00
parent 42c2062cc4
commit 184ece190c
1438 changed files with 404064 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
// Copyright (c) 2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "accumulatorcheckpoints.h"
#include "accumulatorcheckpoints.json.h"
namespace AccumulatorCheckpoints
{
std::map<int, Checkpoint> mapCheckpoints;
UniValue read_json(const std::string& jsondata)
{
UniValue v;
if (!v.read(jsondata) || !v.isArray())
{
return UniValue(UniValue::VARR);
}
return v.get_array();
}
bool LoadCheckpoints(const std::string& strNetwork)
{
UniValue v;
if (strNetwork == "main")
v = read_json(GetMainCheckpoints());
else if (strNetwork == "test")
v = read_json(GetTestCheckpoints());
else if (strNetwork == "regtest")
v = read_json(GetRegTestCheckpoints());
else
return false;
if (v.empty())
return false;
for (unsigned int idx = 0; idx < v.size(); idx++) {
const UniValue &val = v[idx];
const UniValue &o = val.get_obj();
const UniValue &vHeight = find_value(o, "height");
if (!vHeight.isNum())
return false;
int nHeight = vHeight.get_int();
if (nHeight < 0)
return false;
Checkpoint checkpoint;
for (auto denom : libzerocoin::zerocoinDenomList) {
const UniValue& vDenomValue = find_value(o, std::to_string(denom));
if (!vDenomValue.isStr()) {
return false;
}
CBigNum bn = 0;
bn.SetHex(vDenomValue.get_str());
checkpoint.insert(std::make_pair(denom, bn));
}
mapCheckpoints.insert(make_pair(nHeight, checkpoint));
}
return true;
}
Checkpoint GetClosestCheckpoint(const int& nHeight, int& nHeightCheckpoint)
{
nHeightCheckpoint = -1;
for (auto it : mapCheckpoints) {
//only checkpoints that are less than the height requested (unless height is less than the first checkpoint)
if (it.first < nHeight) {
if (nHeightCheckpoint == -1)
nHeightCheckpoint = it.first;
if (nHeight - it.first < nHeightCheckpoint)
nHeightCheckpoint = it.first;
}
}
if (nHeightCheckpoint != -1)
return mapCheckpoints.at(nHeightCheckpoint);
return Checkpoint();
}
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) 2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef Agrarian_ACCUMULATORCHECKPOINTS_H
#define Agrarian_ACCUMULATORCHECKPOINTS_H
#include <libzerocoin/bignum.h>
#include <univalue/include/univalue.h>
namespace AccumulatorCheckpoints
{
typedef std::map<libzerocoin::CoinDenomination, CBigNum> Checkpoint;
extern std::map<int, Checkpoint> mapCheckpoints;
UniValue read_json(const std::string& jsondata);
bool LoadCheckpoints(const std::string& strNetwork);
Checkpoint GetClosestCheckpoint(const int& nHeight, int& nHeightCheckpoint);
}
#endif //Agrarian_ACCUMULATORCHECKPOINTS_H
+105
View File
@@ -0,0 +1,105 @@
// Copyright (c) 2017-2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "accumulatormap.h"
#include "accumulators.h"
#include "main.h"
#include "txdb.h"
#include "libzerocoin/Denominations.h"
using namespace libzerocoin;
using namespace std;
//Construct accumulators for all denominations
AccumulatorMap::AccumulatorMap(libzerocoin::ZerocoinParams* params)
{
this->params = params;
for (auto& denom : zerocoinDenomList) {
unique_ptr<Accumulator> uptr(new Accumulator(params, denom));
mapAccumulators.insert(make_pair(denom, std::move(uptr)));
}
}
//Reset each accumulator to its default state
void AccumulatorMap::Reset()
{
Reset(params);
}
void AccumulatorMap::Reset(libzerocoin::ZerocoinParams* params2)
{
this->params = params2;
mapAccumulators.clear();
for (auto& denom : zerocoinDenomList) {
unique_ptr<Accumulator> uptr(new Accumulator(params2, denom));
mapAccumulators.insert(make_pair(denom, std::move(uptr)));
}
}
//Load a checkpoint containing 8 32bit checksums of accumulator values.
bool AccumulatorMap::Load(uint256 nCheckpoint)
{
for (auto& denom : zerocoinDenomList) {
uint32_t nChecksum = ParseChecksum(nCheckpoint, denom);
CBigNum bnValue;
if (!zerocoinDB->ReadAccumulatorValue(nChecksum, bnValue))
return error("%s : cannot find checksum %d", __func__, nChecksum);
mapAccumulators.at(denom)->setValue(bnValue);
}
return true;
}
//Load accumulator map from a hard-checkpoint
void AccumulatorMap::Load(const AccumulatorCheckpoints::Checkpoint& checkpoint)
{
for (auto it : checkpoint)
mapAccumulators.at(it.first)->setValue(it.second);
}
//Add a zerocoin to the accumulator of its denomination.
bool AccumulatorMap::Accumulate(const PublicCoin& pubCoin, bool fSkipValidation)
{
CoinDenomination denom = pubCoin.getDenomination();
if (denom == CoinDenomination::ZQ_ERROR)
return false;
if (fSkipValidation)
mapAccumulators.at(denom)->increment(pubCoin.getValue());
else
mapAccumulators.at(denom)->accumulate(pubCoin);
return true;
}
libzerocoin::Accumulator AccumulatorMap::GetAccumulator(libzerocoin::CoinDenomination denom)
{
return libzerocoin::Accumulator(params, denom, GetValue(denom));
}
//Get the value of a specific accumulator
CBigNum AccumulatorMap::GetValue(CoinDenomination denom)
{
if (denom == CoinDenomination::ZQ_ERROR)
return CBigNum(0);
return mapAccumulators.at(denom)->getValue();
}
//Calculate a 32bit checksum of each accumulator value. Concatenate checksums into uint256
uint256 AccumulatorMap::GetCheckpoint()
{
uint256 nCheckpoint;
//Prevent possible overflows from future changes to the list and forgetting to update this code
assert(zerocoinDenomList.size() == 8);
for (auto& denom : zerocoinDenomList) {
CBigNum bnValue = mapAccumulators.at(denom)->getValue();
uint32_t nCheckSum = GetChecksum(bnValue);
nCheckpoint = nCheckpoint << 32 | nCheckSum;
}
return nCheckpoint;
}
+29
View File
@@ -0,0 +1,29 @@
// Copyright (c) 2017-2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef Agrarian_ACCUMULATORMAP_H
#define Agrarian_ACCUMULATORMAP_H
#include "libzerocoin/Accumulator.h"
#include "libzerocoin/Coin.h"
#include "accumulatorcheckpoints.h"
//A map with an accumulator for each denomination
class AccumulatorMap
{
private:
libzerocoin::ZerocoinParams* params;
std::map<libzerocoin::CoinDenomination, std::unique_ptr<libzerocoin::Accumulator> > mapAccumulators;
public:
explicit AccumulatorMap(libzerocoin::ZerocoinParams* params);
bool Load(uint256 nCheckpoint);
void Load(const AccumulatorCheckpoints::Checkpoint& checkpoint);
bool Accumulate(const libzerocoin::PublicCoin& pubCoin, bool fSkipValidation = false);
libzerocoin::Accumulator GetAccumulator(libzerocoin::CoinDenomination denom);
CBigNum GetValue(libzerocoin::CoinDenomination denom);
uint256 GetCheckpoint();
void Reset();
void Reset(libzerocoin::ZerocoinParams* params2);
};
#endif //Agrarian_ACCUMULATORMAP_H
+931
View File
@@ -0,0 +1,931 @@
// Copyright (c) 2017-2019 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "accumulators.h"
#include "accumulatormap.h"
#include "chainparams.h"
#include "main.h"
#include "txdb.h"
#include "init.h"
#include "spork.h"
#include "accumulatorcheckpoints.h"
#include "zagrchain.h"
#include "tinyformat.h"
using namespace libzerocoin;
std::map<uint32_t, CBigNum> mapAccumulatorValues;
std::list<uint256> listAccCheckpointsNoDB;
uint32_t ParseChecksum(uint256 nChecksum, CoinDenomination denomination)
{
//shift to the beginning bit of this denomination and trim any remaining bits by returning 32 bits only
int pos = distance(zerocoinDenomList.begin(), find(zerocoinDenomList.begin(), zerocoinDenomList.end(), denomination));
nChecksum = nChecksum >> (32*((zerocoinDenomList.size() - 1) - pos));
return nChecksum.Get32();
}
uint32_t GetChecksum(const CBigNum &bnValue)
{
CDataStream ss(SER_GETHASH, 0);
ss << bnValue;
uint256 hash = Hash(ss.begin(), ss.end());
return hash.Get32();
}
// Find the first occurance of a certain accumulator checksum. Return 0 if not found.
int GetChecksumHeight(uint32_t nChecksum, CoinDenomination denomination)
{
CBlockIndex* pindex = chainActive[Params().Zerocoin_StartHeight()];
if (!pindex)
return 0;
//Search through blocks to find the checksum
while (pindex) {
if (ParseChecksum(pindex->nAccumulatorCheckpoint, denomination) == nChecksum)
return pindex->nHeight;
//Skip forward in groups of 10 blocks since checkpoints only change every 10 blocks
if (pindex->nHeight % 10 == 0) {
if (pindex->nHeight + 10 > chainActive.Height())
return 0;
pindex = chainActive[pindex->nHeight + 10];
continue;
}
pindex = chainActive.Next(pindex);
}
return 0;
}
bool GetAccumulatorValueFromChecksum(uint32_t nChecksum, bool fMemoryOnly, CBigNum& bnAccValue)
{
if (mapAccumulatorValues.count(nChecksum)) {
bnAccValue = mapAccumulatorValues.at(nChecksum);
return true;
}
if (fMemoryOnly)
return false;
if (!zerocoinDB->ReadAccumulatorValue(nChecksum, bnAccValue)) {
bnAccValue = 0;
}
return true;
}
bool GetAccumulatorValueFromDB(uint256 nCheckpoint, CoinDenomination denom, CBigNum& bnAccValue)
{
uint32_t nChecksum = ParseChecksum(nCheckpoint, denom);
return GetAccumulatorValueFromChecksum(nChecksum, false, bnAccValue);
}
void AddAccumulatorChecksum(const uint32_t nChecksum, const CBigNum &bnValue)
{
//Since accumulators are switching at v2, stop databasing v1 because its useless. Only focus on v2.
if (chainActive.Height() >= Params().Zerocoin_Block_V2_Start()) {
zerocoinDB->WriteAccumulatorValue(nChecksum, bnValue);
mapAccumulatorValues.insert(make_pair(nChecksum, bnValue));
}
}
void DatabaseChecksums(AccumulatorMap& mapAccumulators)
{
uint256 nCheckpoint = 0;
for (auto& denom : zerocoinDenomList) {
CBigNum bnValue = mapAccumulators.GetValue(denom);
uint32_t nCheckSum = GetChecksum(bnValue);
AddAccumulatorChecksum(nCheckSum, bnValue);
nCheckpoint = nCheckpoint << 32 | nCheckSum;
}
}
bool EraseChecksum(uint32_t nChecksum)
{
//erase from both memory and database
mapAccumulatorValues.erase(nChecksum);
return zerocoinDB->EraseAccumulatorValue(nChecksum);
}
bool EraseAccumulatorValues(const uint256& nCheckpointErase, const uint256& nCheckpointPrevious)
{
for (auto& denomination : zerocoinDenomList) {
uint32_t nChecksumErase = ParseChecksum(nCheckpointErase, denomination);
uint32_t nChecksumPrevious = ParseChecksum(nCheckpointPrevious, denomination);
//if the previous checksum is the same, then it should remain in the database and map
if(nChecksumErase == nChecksumPrevious)
continue;
if (!EraseChecksum(nChecksumErase))
return false;
}
return true;
}
bool LoadAccumulatorValuesFromDB(const uint256 nCheckpoint)
{
for (auto& denomination : zerocoinDenomList) {
uint32_t nChecksum = ParseChecksum(nCheckpoint, denomination);
//if read is not successful then we are not in a state to verify zerocoin transactions
CBigNum bnValue;
if (!zerocoinDB->ReadAccumulatorValue(nChecksum, bnValue)) {
if (!count(listAccCheckpointsNoDB.begin(), listAccCheckpointsNoDB.end(), nCheckpoint))
listAccCheckpointsNoDB.push_back(nCheckpoint);
LogPrint("zero", "%s : Missing databased value for checksum %d\n", __func__, nChecksum);
return false;
}
mapAccumulatorValues.insert(make_pair(nChecksum, bnValue));
}
return true;
}
//Erase accumulator checkpoints for a certain block range
bool EraseCheckpoints(int nStartHeight, int nEndHeight)
{
if (chainActive.Height() < nStartHeight)
return false;
nEndHeight = min(chainActive.Height(), nEndHeight);
CBlockIndex* pindex = chainActive[nStartHeight];
uint256 nCheckpointPrev = pindex->pprev->nAccumulatorCheckpoint;
//Keep a list of checkpoints from the previous block so that we don't delete them
list<uint32_t> listCheckpointsPrev;
for (auto denom : zerocoinDenomList)
listCheckpointsPrev.emplace_back(ParseChecksum(nCheckpointPrev, denom));
while (true) {
uint256 nCheckpointDelete = pindex->nAccumulatorCheckpoint;
for (auto denom : zerocoinDenomList) {
uint32_t nChecksumDelete = ParseChecksum(nCheckpointDelete, denom);
if (count(listCheckpointsPrev.begin(), listCheckpointsPrev.end(), nCheckpointDelete))
continue;
EraseChecksum(nChecksumDelete);
}
LogPrintf("%s : erasing checksums for block %d\n", __func__, pindex->nHeight);
if (pindex->nHeight + 1 <= nEndHeight)
pindex = chainActive.Next(pindex);
else
break;
}
return true;
}
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, AccumulatorMap& mapAccumulators)
{
if (nHeight < Params().Zerocoin_Block_V2_Start()) {
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
int nHeightCheckpoint;
mapAccumulators.Reset();
if (!InitializeAccumulators(nHeight, nHeightCheckpoint, mapAccumulators))
return error("%s: failed to initialize accumulators", __func__);
//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[nHeightCheckpoint - 20];
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))
return error("%s: failed to read block from disk", __func__);
std::list<PublicCoin> listPubcoins;
if (!BlockToPubcoinList(block, listPubcoins, fFilterInvalid))
return error("%s: failed to get zerocoin mintlist from block %d", __func__, pindex->nHeight);
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))
return error("%s: failed to add pubcoin to accumulator at height %d", __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)
nCheckpoint = chainActive[nHeight - 1]->nAccumulatorCheckpoint;
else
nCheckpoint = mapAccumulators.GetCheckpoint();
LogPrint("zero", "%s checkpoint=%s\n", __func__, nCheckpoint.GetHex());
return true;
}
bool InvalidCheckpointRange(int nHeight)
{
return nHeight > Params().Zerocoin_Block_LastGoodCheckpoint() && nHeight < Params().Zerocoin_Block_RecalculateAccumulators();
}
bool ValidateAccumulatorCheckpoint(const CBlock& block, CBlockIndex* pindex, AccumulatorMap& mapAccumulators)
{
//V1 accumulators are completely phased out by the time this code hits the public and begins generating new checkpoints
//It is VERY IMPORTANT that when this is being run and height < v2_start, then zAGR need to be disabled at the same time!!
if (pindex->nHeight < Params().Zerocoin_Block_V2_Start() || fVerifyingBlocks)
return true;
if (pindex->nHeight % 10 == 0) {
uint256 nCheckpointCalculated = 0;
if (!CalculateAccumulatorCheckpoint(pindex->nHeight, nCheckpointCalculated, mapAccumulators))
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__);
}
return true;
}
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;
}
//########################## Witness
//Compute how many coins were added to an accumulator up to the end height
int ComputeAccumulatedCoins(int nHeightEnd, libzerocoin::CoinDenomination denom)
{
CBlockIndex* pindex = chainActive[GetZerocoinStartHeight()];
int n = 0;
while (pindex->nHeight < nHeightEnd) {
n += count(pindex->vMintDenominationsInBlock.begin(), pindex->vMintDenominationsInBlock.end(), denom);
pindex = chainActive.Next(pindex);
}
return n;
}
list<PublicCoin> GetPubcoinFromBlock(const CBlockIndex* pindex){
//grab mints from this block
CBlock block;
if(!ReadBlockFromDisk(block, pindex))
throw GetPubcoinException("GetPubcoinFromBlock: failed to read block from disk while adding pubcoins to witness");
list<libzerocoin::PublicCoin> listPubcoins;
if(!BlockToPubcoinList(block, listPubcoins, true))
throw GetPubcoinException("GetPubcoinFromBlock: failed to get zerocoin mintlist from block "+std::to_string(pindex->nHeight)+"\n");
return listPubcoins;
}
int AddBlockMintsToAccumulator(const CoinDenomination den, const CBloomFilter filter, const CBlockIndex* pindex,
libzerocoin::Accumulator* accumulator, bool isWitness, list<CBigNum>& notAddedCoins)
{
// if this block contains mints of the denomination that is being spent, then add them to the witness
int nMintsAdded = 0;
if (pindex->MintedDenomination(den)) {
//add the mints to the witness
for (const PublicCoin& pubcoin : GetPubcoinFromBlock(pindex)) {
if (pubcoin.getDenomination() != den) {
continue;
}
if (isWitness && filter.contains(pubcoin.getValue().getvch())) {
notAddedCoins.emplace_back(pubcoin.getValue());
continue;
}
accumulator->increment(pubcoin.getValue());
++nMintsAdded;
}
}
return nMintsAdded;
}
int AddBlockMintsToAccumulator(const libzerocoin::PublicCoin& coin, const int nHeightMintAdded, const CBlockIndex* pindex,
libzerocoin::Accumulator* accumulator, bool isWitness)
{
// if this block contains mints of the denomination that is being spent, then add them to the witness
int nMintsAdded = 0;
if (pindex->MintedDenomination(coin.getDenomination())) {
//add the mints to the witness
for (const PublicCoin& pubcoin : GetPubcoinFromBlock(pindex)) {
if (pubcoin.getDenomination() != coin.getDenomination())
continue;
if (isWitness && pindex->nHeight == nHeightMintAdded && pubcoin.getValue() == coin.getValue())
continue;
accumulator->increment(pubcoin.getValue());
++nMintsAdded;
}
}
return nMintsAdded;
}
int AddBlockMintsToAccumulator(CoinWitnessData* coinWitness, const CBlockIndex* pindex, bool isWitness)
{
// TODO: This should be the witness..
return AddBlockMintsToAccumulator(
*coinWitness->coin.get(),
coinWitness->nHeightMintAdded,
pindex,
coinWitness->pAccumulator.get(),
isWitness
);
}
bool GetAccumulatorValue(int& nHeight, const libzerocoin::CoinDenomination denom, CBigNum& bnAccValue)
{
if (nHeight > chainActive.Height())
return error("%s: height %d is more than active chain height", __func__, nHeight);
//Every situation except for about 20 blocks should use this method
uint256 nCheckpointBeforeMint = chainActive[nHeight]->nAccumulatorCheckpoint;
if (nHeight > Params().Zerocoin_Block_V2_Start() + 20) {
return GetAccumulatorValueFromDB(nCheckpointBeforeMint, denom, bnAccValue);
}
int nHeightCheckpoint = 0;
AccumulatorCheckpoints::Checkpoint checkpoint = AccumulatorCheckpoints::GetClosestCheckpoint(nHeight, nHeightCheckpoint);
if (nHeightCheckpoint < 0) {
//Start at the first zerocoin
libzerocoin::Accumulator accumulator(Params().Zerocoin_Params(false), denom);
bnAccValue = accumulator.getValue();
nHeight = Params().Zerocoin_StartHeight() + 10;
return true;
}
nHeight = nHeightCheckpoint;
bnAccValue = checkpoint.at(denom);
return true;
}
//############ Witness Generation
/**
* TODO: Why we are locking the wallet in this way?
* @return
*/
bool LockMethod(){
int nLockAttempts = 0;
while (nLockAttempts < 100) {
TRY_LOCK(cs_main, lockMain);
if(!lockMain) {
MilliSleep(50);
nLockAttempts++;
continue;
}
break;
}
if (nLockAttempts == 100)
return error("%s: could not get lock on cs_main", __func__);
// locked
return true;
}
int SearchMintHeightOf(CBigNum value){
uint256 txid;
if (!zerocoinDB->ReadCoinMint(value, txid))
throw searchMintHeightException("searchForMintHeightOf:: failed to read mint from db");
CTransaction txMinted;
uint256 hashBlock;
if (!GetTransaction(txid, txMinted, hashBlock))
throw searchMintHeightException("searchForMintHeightOf:: failed to read tx");
int nHeightTest;
if (!IsTransactionInChain(txid, nHeightTest))
throw searchMintHeightException("searchForMintHeightOf:: mint tx "+ txid.GetHex() +" is not in chain");
return mapBlockIndex[hashBlock]->nHeight;
}
void AccumulateRange(CoinWitnessData* coinWitness, int nHeightEnd)
{
bool fDoubleCounted = false;
int64_t nTimeStart = GetTimeMicros();
int nHeightStart = std::max(coinWitness->nHeightAccStart, coinWitness->nHeightAccEnd + 1);
CBlockIndex* pindex = chainActive[nHeightStart];
LogPrint("zero", "%s: start=%d end=%d\n", __func__, nHeightStart, nHeightEnd);
while (pindex && pindex->nHeight <= nHeightEnd) {
coinWitness->nMintsAdded += AddBlockMintsToAccumulator(coinWitness, pindex, true);
coinWitness->nHeightAccEnd = pindex->nHeight;
// 10 blocks were accumulated twice when zAGR v2 was activated
if (pindex->nHeight == Params().Zerocoin_Block_Double_Accumulated() + 10 && !fDoubleCounted) {
pindex = chainActive[Params().Zerocoin_Block_Double_Accumulated()];
fDoubleCounted = true;
continue;
}
pindex = chainActive.Next(pindex);
}
int64_t nTimeEnd = GetTimeMicros();
LogPrint("bench", " - Range accumulation completed in %.2fms\n", 0.001 * (nTimeEnd - nTimeStart));
}
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);
}
}
bool calculateAccumulatedBlocksFor(
int startHeight,
int nHeightStop,
CBlockIndex *pindex,
CBigNum &bnAccValue,
libzerocoin::Accumulator &accumulator,
libzerocoin::CoinDenomination den,
CBloomFilter filter,
libzerocoin::Accumulator &witnessAccumulator,
list<CBigNum>& ret,
string& strError
){
bool fDoubleCounted = false;
int nMintsAdded = 0;
while (pindex) {
if (pindex->nHeight >= nHeightStop) {
//If this height is within the invalid range (when fraudulent coins were being minted), then continue past this range
if(InvalidCheckpointRange(pindex->nHeight))
continue;
bnAccValue = 0;
uint256 nCheckpointSpend = chainActive[pindex->nHeight + 10]->nAccumulatorCheckpoint;
if (!GetAccumulatorValueFromDB(nCheckpointSpend, den, bnAccValue) || bnAccValue == 0) {
throw new ChecksumInDbNotFoundException(
"calculateAccumulatedBlocksFor : failed to find checksum in database for accumulator");
}
accumulator.setValue(bnAccValue);
break;
}
nMintsAdded += AddBlockMintsToAccumulator(den, filter, pindex, &witnessAccumulator, true, ret);
// 10 blocks were accumulated twice when zAGR v2 was activated
if (pindex->nHeight == 1050010 && !fDoubleCounted) {
pindex = chainActive[1050000];
fDoubleCounted = true;
continue;
}
pindex = chainActive.Next(pindex);
}
// 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());
throw NotEnoughMintsException(strError);
}
LogPrintf("calculateAccumulatedBlocksFor() : nMintsAdded %d",nMintsAdded);
return true;
}
bool calculateAccumulatedBlocksFor(
int startHeight,
int nHeightStop,
int nHeightMintAdded,
CBlockIndex *pindex,
int &nCheckpointsAdded,
CBigNum &bnAccValue,
libzerocoin::Accumulator &accumulator,
libzerocoin::Accumulator &witnessAccumulator,
libzerocoin::PublicCoin coin,
string& strError
){
int amountOfScannedBlocks = 0;
bool fDoubleCounted = false;
int nMintsAdded = 0;
while (pindex) {
if (pindex->nHeight >= nHeightStop) {
//If this height is within the invalid range (when fraudulent coins were being minted), then continue past this range
if(InvalidCheckpointRange(pindex->nHeight))
continue;
bnAccValue = 0;
uint256 nCheckpointSpend = chainActive[pindex->nHeight + 10]->nAccumulatorCheckpoint;
if (!GetAccumulatorValueFromDB(nCheckpointSpend, coin.getDenomination(), bnAccValue) || bnAccValue == 0) {
throw new ChecksumInDbNotFoundException(
"calculateAccumulatedBlocksFor : failed to find checksum in database for accumulator");
}
accumulator.setValue(bnAccValue);
break;
}
// Add it
nMintsAdded += AddBlockMintsToAccumulator(coin, nHeightMintAdded, pindex, &witnessAccumulator, true);
// 10 blocks were accumulated twice when zAGR v2 was activated
if (pindex->nHeight == 1050010 && !fDoubleCounted) {
pindex = chainActive[1050000];
fDoubleCounted = true;
continue;
}
amountOfScannedBlocks++;
pindex = chainActive.Next(pindex);
}
// 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());
throw NotEnoughMintsException(strError);
}
LogPrintf("calculateAccumulatedBlocksFor() : nMintsAdded %d",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);
}
}
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);
}
}
map<CoinDenomination, int> GetMintMaturityHeight()
{
map<CoinDenomination, pair<int, int > > mapDenomMaturity;
for (auto denom : libzerocoin::zerocoinDenomList)
mapDenomMaturity.insert(make_pair(denom, make_pair(0, 0)));
int nConfirmedHeight = chainActive.Height() - Params().Zerocoin_MintRequiredConfirmations();
// A mint need to get to at least the min maturity height before it will spend.
int nMinimumMaturityHeight = nConfirmedHeight - (nConfirmedHeight % 10);
CBlockIndex* pindex = chainActive[nConfirmedHeight];
while (pindex && pindex->nHeight > Params().Zerocoin_StartHeight()) {
bool isFinished = true;
for (auto denom : libzerocoin::zerocoinDenomList) {
//If the denom has not already had a mint added to it, then see if it has a mint added on this block
if (mapDenomMaturity.at(denom).first < Params().Zerocoin_RequiredAccumulation()) {
mapDenomMaturity.at(denom).first += count(pindex->vMintDenominationsInBlock.begin(),
pindex->vMintDenominationsInBlock.end(), denom);
//if mint was found then record this block as the first block that maturity occurs.
if (mapDenomMaturity.at(denom).first >= Params().Zerocoin_RequiredAccumulation())
mapDenomMaturity.at(denom).second = std::min(pindex->nHeight, nMinimumMaturityHeight);
//Signal that we are finished
isFinished = false;
}
}
if (isFinished)
break;
pindex = chainActive[pindex->nHeight - 1];
}
//Generate final map
map<CoinDenomination, int> mapRet;
for (auto denom : libzerocoin::zerocoinDenomList)
mapRet.insert(make_pair(denom, mapDenomMaturity.at(denom).second));
return mapRet;
}
+94
View File
@@ -0,0 +1,94 @@
// Copyright (c) 2017-2019 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef Agrarian_ACCUMULATORS_H
#define Agrarian_ACCUMULATORS_H
#include "libzerocoin/Accumulator.h"
#include "libzerocoin/Coin.h"
#include "libzerocoin/Denominations.h"
#include "zagr/zerocoin.h"
#include "accumulatormap.h"
#include "chain.h"
#include "uint256.h"
#include "bloom.h"
#include "witness.h"
class CBlockIndex;
std::map<libzerocoin::CoinDenomination, int> GetMintMaturityHeight();
/**
* Calculate the acc witness for a single coin.
* @return true if the witness was calculated well
*/
bool CalculateAccumulatorWitnessFor(
const libzerocoin::ZerocoinParams* params,
int startingHeight,
int maxCalculationRange,
libzerocoin::CoinDenomination den,
const CBloomFilter& filter,
libzerocoin::Accumulator& accumulator,
libzerocoin::AccumulatorWitness& witness,
int& nMintsAdded,
string& strError,
list<CBigNum>& ret,
int &heightStop
);
bool GenerateAccumulatorWitness(
const libzerocoin::PublicCoin &coin,
libzerocoin::Accumulator& accumulator,
libzerocoin::AccumulatorWitness& witness,
int& nMintsAdded,
string& strError,
CBlockIndex* pindexCheckpoint = nullptr);
bool GenerateAccumulatorWitness(CoinWitnessData* coinWitness, AccumulatorMap& mapAccumulators, CBlockIndex* pindexCheckpoint);
list<libzerocoin::PublicCoin> GetPubcoinFromBlock(const CBlockIndex* pindex);
bool GetAccumulatorValueFromDB(uint256 nCheckpoint, libzerocoin::CoinDenomination denom, CBigNum& bnAccValue);
bool GetAccumulatorValue(int& nHeight, const 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, 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);
int GetChecksumHeight(uint32_t nChecksum, libzerocoin::CoinDenomination denomination);
bool InvalidCheckpointRange(int nHeight);
bool ValidateAccumulatorCheckpoint(const CBlock& block, CBlockIndex* pindex, AccumulatorMap& mapAccumulators);
// Exceptions
class NotEnoughMintsException : public std::exception {
public:
std::string message;
NotEnoughMintsException(const string &message) : message(message) {}
};
class GetPubcoinException : public std::exception {
public:
std::string message;
GetPubcoinException(const string &message) : message(message) {}
};
class ChecksumInDbNotFoundException : public std::exception {
public:
std::string message;
ChecksumInDbNotFoundException(const string &message) : message(message) {}
};
class searchMintHeightException : public std::exception {
public:
std::string message;
searchMintHeightException(const string &message) : message(message) {}
};
#endif //Agrarian_ACCUMULATORS_H
+45
View File
@@ -0,0 +1,45 @@
// Copyright (c) 2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <libzerocoin/Coin.h>
#include <tinyformat.h>
#include "deterministicmint.h"
using namespace libzerocoin;
CDeterministicMint::CDeterministicMint()
{
SetNull();
}
CDeterministicMint::CDeterministicMint(uint8_t nVersion, const uint32_t& nCount, const uint256& hashSeed, const uint256& hashSerial, const uint256& hashPubcoin, const uint256& hashStake)
{
SetNull();
this->nVersion = nVersion;
this->nCount = nCount;
this->hashSeed = hashSeed;
this->hashSerial = hashSerial;
this->hashPubcoin = hashPubcoin;
this->hashStake = hashStake;
}
void CDeterministicMint::SetNull()
{
nVersion = PrivateCoin::CURRENT_VERSION;
nCount = 0;
hashSeed = 0;
hashSerial = 0;
hashStake = 0;
hashPubcoin = 0;
txid = 0;
nHeight = 0;
denom = CoinDenomination::ZQ_ERROR;
isUsed = false;
}
std::string CDeterministicMint::ToString() const
{
return strprintf(" DeterministicMint:\n version=%d\n count=%d\n hashseed=%s\n hashSerial=%s\n hashStake=%s\n hashPubcoin=%s\n txid=%s\n height=%d\n denom=%d\n isUsed=%d\n",
nVersion, nCount, hashSeed.GetHex(), hashSerial.GetHex(), hashStake.GetHex(), hashPubcoin.GetHex(), txid.GetHex(), nHeight, denom, isUsed);
}
+67
View File
@@ -0,0 +1,67 @@
// Copyright (c) 2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef Agrarian_DETERMINISTICMINT_H
#define Agrarian_DETERMINISTICMINT_H
#include <libzerocoin/Denominations.h>
#include <uint256.h>
#include <serialize.h>
//struct that is safe to store essential mint data, without holding any information that allows for actual spending (serial, randomness, private key)
class CDeterministicMint
{
private:
uint8_t nVersion;
uint32_t nCount;
uint256 hashSeed;
uint256 hashSerial;
uint256 hashStake;
uint256 hashPubcoin;
uint256 txid;
int nHeight;
libzerocoin::CoinDenomination denom;
bool isUsed;
public:
CDeterministicMint();
CDeterministicMint(uint8_t nVersion, const uint32_t& nCount, const uint256& hashSeed, const uint256& hashSerial, const uint256& hashPubcoin, const uint256& hashStake);
libzerocoin::CoinDenomination GetDenomination() const { return denom; }
uint32_t GetCount() const { return nCount; }
int GetHeight() const { return nHeight; }
uint256 GetSeedHash() const { return hashSeed; }
uint256 GetSerialHash() const { return hashSerial; }
uint256 GetStakeHash() const { return hashStake; }
uint256 GetPubcoinHash() const { return hashPubcoin; }
uint256 GetTxHash() const { return txid; }
uint8_t GetVersion() const { return nVersion; }
bool IsUsed() const { return isUsed; }
void SetDenomination(const libzerocoin::CoinDenomination denom) { this->denom = denom; }
void SetHeight(const int& nHeight) { this->nHeight = nHeight; }
void SetNull();
void SetStakeHash(const uint256& hashStake) { this->hashStake = hashStake; }
void SetTxHash(const uint256& txid) { this->txid = txid; }
void SetUsed(const bool isUsed) { this->isUsed = isUsed; }
std::string ToString() const;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(this->nVersion);
READWRITE(nCount);
READWRITE(hashSeed);
READWRITE(hashSerial);
READWRITE(hashStake);
READWRITE(hashPubcoin);
READWRITE(txid);
READWRITE(nHeight);
READWRITE(denom);
READWRITE(isUsed);
};
};
#endif //Agrarian_DETERMINISTICMINT_H
+109
View File
@@ -0,0 +1,109 @@
// Copyright (c) 2017-2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "mintpool.h"
#include "util.h"
using namespace std;
CMintPool::CMintPool()
{
this->nCountLastGenerated = 0;
this->nCountLastRemoved = 0;
}
CMintPool::CMintPool(uint32_t nCount)
{
this->nCountLastRemoved = nCount;
this->nCountLastGenerated = nCount;
}
void CMintPool::Add(const CBigNum& bnValue, const uint32_t& nCount)
{
uint256 hash = GetPubCoinHash(bnValue);
Add(make_pair(hash, nCount));
LogPrintf("%s : add %s to mint pool, nCountLastGenerated=%d\n", __func__, bnValue.GetHex().substr(0, 6), nCountLastGenerated);
}
void CMintPool::Add(const pair<uint256, uint32_t>& pMint, bool fVerbose)
{
insert(pMint);
if (pMint.second > nCountLastGenerated)
nCountLastGenerated = pMint.second;
if (fVerbose)
LogPrintf("%s : add %s count %d to mint pool\n", __func__, pMint.first.GetHex().substr(0, 6), pMint.second);
}
bool CMintPool::Has(const CBigNum& bnValue)
{
return static_cast<bool>(count(GetPubCoinHash(bnValue)));
}
std::pair<uint256, uint32_t> CMintPool::Get(const CBigNum& bnValue)
{
auto it = find(GetPubCoinHash(bnValue));
return *it;
}
bool SortSmallest(const pair<uint256, uint32_t>& a, const pair<uint256, uint32_t>& b)
{
return a.second < b.second;
}
std::list<pair<uint256, uint32_t> > CMintPool::List()
{
list<pair<uint256, uint32_t> > listMints;
for (auto pMint : *(this)) {
listMints.emplace_back(pMint);
}
listMints.sort(SortSmallest);
return listMints;
}
void CMintPool::Reset()
{
clear();
nCountLastGenerated = 0;
nCountLastRemoved = 0;
}
bool CMintPool::Front(std::pair<uint256, uint32_t>& pMint)
{
if (empty())
return false;
pMint = *begin();
return true;
}
bool CMintPool::Next(pair<uint256, uint32_t>& pMint)
{
auto it = find(pMint.first);
if (it == end() || ++it == end())
return false;
pMint = *it;
return true;
}
void CMintPool::Remove(const CBigNum& bnValue)
{
Remove(GetPubCoinHash(bnValue));
LogPrintf("%s : remove %s from mint pool\n", __func__, bnValue.GetHex().substr(0, 6));
}
void CMintPool::Remove(const uint256& hashPubcoin)
{
auto it = find(hashPubcoin);
if (it == end())
return;
nCountLastRemoved = it->second;
erase(it);
}
+53
View File
@@ -0,0 +1,53 @@
// Copyright (c) 2017-2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef Agrarian_MINTPOOL_H
#define Agrarian_MINTPOOL_H
#include <map>
#include <list>
#include "zagr/zerocoin.h"
#include "libzerocoin/bignum.h"
#include "uint256.h"
/**
* The MintPool only contains mint values that have not been added to the blockchain yet.
* When a mint is generated by the wallet, or found while syncing, the mint will be removed
* from the MintPool.
*
* The MintPool provides a convenient way to check whether mints in the blockchain belong to a
* wallet's deterministic seed.
*/
class CMintPool : public std::map<uint256, uint32_t> //pubcoin hash, count
{
private:
uint32_t nCountLastGenerated;
uint32_t nCountLastRemoved;
public:
CMintPool();
explicit CMintPool(uint32_t nCount);
void Add(const CBigNum& bnValue, const uint32_t& nCount);
void Add(const std::pair<uint256, uint32_t>& pMint, bool fVerbose = false);
bool Has(const CBigNum& bnValue);
void Remove(const CBigNum& bnValue);
void Remove(const uint256& hashPubcoin);
std::pair<uint256, uint32_t> Get(const CBigNum& bnValue);
std::list<std::pair<uint256, uint32_t> > List();
void Reset();
bool Front(std::pair<uint256, uint32_t>& pMint);
bool Next(std::pair<uint256, uint32_t>& pMint);
//The count of the next mint to generate will have be a mint that is already in the pool
//therefore need to return the next value that has not been removed from the pool yet
uint32_t CountOfLastRemoved() { return nCountLastRemoved; }
//The next pool count returns the next count that will be added to the pool
uint32_t CountOfLastGenerated() { return nCountLastGenerated; }
};
#endif //Agrarian_MINTPOOL_H
+110
View File
@@ -0,0 +1,110 @@
// Copyright (c) 2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
#include <tinyformat.h>
#include "witness.h"
void CoinWitnessData::SetNull()
{
coin = nullptr;
pAccumulator = nullptr;
pWitness = nullptr;
nMintsAdded = 0;
nHeightMintAdded = 0;
nHeightCheckpoint = 0;
nHeightAccStart = 0;
nHeightAccEnd = 0;
}
CoinWitnessData::CoinWitnessData()
{
SetNull();
}
std::string CoinWitnessData::ToString()
{
return strprintf("Mints Added: %d\n"
"Height Mint added: %d\n"
"Height Checkpoint: %d\n"
"Height Acc Start: %d\n"
"Height Acc End: %d\n"
"Amount: %s\n"
"Demon: %d\n", nMintsAdded, nHeightMintAdded, nHeightCheckpoint, nHeightAccStart, nHeightAccEnd, coin->getValue().GetHex(), coin->getDenomination());
}
CoinWitnessData::CoinWitnessData(CZerocoinMint& mint)
{
SetNull();
denom = mint.GetDenomination();
isV1 = libzerocoin::ExtractVersionFromSerial(mint.GetSerialNumber()) < libzerocoin::PrivateCoin::PUBKEY_VERSION;
libzerocoin::ZerocoinParams* paramsCoin = Params().Zerocoin_Params(isV1);
coin = std::unique_ptr<libzerocoin::PublicCoin>(new libzerocoin::PublicCoin(paramsCoin, mint.GetValue(), denom));
libzerocoin::Accumulator accumulator1(Params().Zerocoin_Params(false), denom);
pWitness = std::unique_ptr<libzerocoin::AccumulatorWitness>(new libzerocoin::AccumulatorWitness(Params().Zerocoin_Params(false), accumulator1, *coin));
nHeightAccStart = mint.GetHeight();
}
CoinWitnessData::CoinWitnessData(CoinWitnessCacheData& data)
{
SetNull();
denom = data.denom;
isV1 = data.isV1;
libzerocoin::ZerocoinParams* paramsCoin = Params().Zerocoin_Params(isV1);
coin = std::unique_ptr<libzerocoin::PublicCoin>(new libzerocoin::PublicCoin(paramsCoin, data.coinAmount, data.coinDenom));
pAccumulator = std::unique_ptr<libzerocoin::Accumulator>(new libzerocoin::Accumulator(Params().Zerocoin_Params(false), denom, data.accumulatorAmount));
pWitness = std::unique_ptr<libzerocoin::AccumulatorWitness>(new libzerocoin::AccumulatorWitness(Params().Zerocoin_Params(false), *pAccumulator, *coin));
nMintsAdded = data.nMintsAdded;
nHeightMintAdded = data.nHeightMintAdded;
nHeightCheckpoint = data.nHeightCheckpoint;
nHeightAccStart = data.nHeightAccStart;
nHeightAccEnd = data.nHeightAccEnd;
txid = data.txid;
}
void CoinWitnessData::SetHeightMintAdded(int nHeight)
{
nHeightMintAdded = nHeight;
nHeightCheckpoint = nHeight + (10 - (nHeight % 10));
nHeightAccStart = nHeight - (nHeight % 10);
}
void CoinWitnessCacheData::SetNull()
{
nMintsAdded = 0;
nHeightMintAdded = 0;
nHeightCheckpoint = 0;
nHeightAccStart = 0;
nHeightAccEnd = 0;
coinAmount = CBigNum(0);
coinDenom = libzerocoin::CoinDenomination::ZQ_ERROR;
accumulatorAmount = CBigNum(0);
accumulatorDenom = libzerocoin::CoinDenomination::ZQ_ERROR;
}
CoinWitnessCacheData::CoinWitnessCacheData()
{
SetNull();
}
CoinWitnessCacheData::CoinWitnessCacheData(CoinWitnessData* coinWitnessData)
{
SetNull();
denom = coinWitnessData->denom;
isV1 = coinWitnessData->isV1;
txid = coinWitnessData->txid;
nMintsAdded = coinWitnessData->nMintsAdded;
nHeightMintAdded = coinWitnessData->nHeightMintAdded;
nHeightCheckpoint = coinWitnessData->nHeightCheckpoint;
nHeightAccStart = coinWitnessData->nHeightAccStart;
nHeightAccEnd = coinWitnessData->nHeightAccEnd;
coinAmount = coinWitnessData->coin->getValue();
coinDenom = coinWitnessData->coin->getDenomination();
accumulatorAmount = coinWitnessData->pAccumulator->getValue();
accumulatorDenom = coinWitnessData->pAccumulator->getDenomination();
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright (c) 2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef Agrarian_WITNESS_H
#define Agrarian_WITNESS_H
#include <libzerocoin/Accumulator.h>
#include <libzerocoin/Coin.h>
#include "zerocoin.h"
#include "serialize.h"
#define PRECOMPUTE_LRU_CACHE_SIZE 1000
#define PRECOMPUTE_MAX_DIRTY_CACHE_SIZE 100
#define PRECOMPUTE_FLUSH_TIME 300 // 5 minutes
class CoinWitnessCacheData;
class CoinWitnessData
{
public:
std::unique_ptr<libzerocoin::PublicCoin> coin;
std::unique_ptr<libzerocoin::Accumulator> pAccumulator;
std::unique_ptr<libzerocoin::AccumulatorWitness> pWitness;
libzerocoin::CoinDenomination denom;
int nHeightCheckpoint;
int nHeightMintAdded;
int nHeightAccStart;
int nHeightAccEnd;
int nMintsAdded;
uint256 txid;
bool isV1;
CoinWitnessData();
CoinWitnessData(CZerocoinMint& mint);
CoinWitnessData(CoinWitnessCacheData& data);
void SetHeightMintAdded(int nHeight);
void SetNull();
std::string ToString();
};
class CoinWitnessCacheData
{
public:
libzerocoin::CoinDenomination denom;
int nHeightCheckpoint;
int nHeightMintAdded;
int nHeightAccStart;
int nHeightAccEnd;
int nMintsAdded;
uint256 txid;
bool isV1;
CBigNum coinAmount;
libzerocoin::CoinDenomination coinDenom;
CBigNum accumulatorAmount;
libzerocoin::CoinDenomination accumulatorDenom;
CoinWitnessCacheData();
CoinWitnessCacheData(CoinWitnessData* coinWitnessData);
void SetNull();
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(denom);
READWRITE(nHeightCheckpoint);
READWRITE(nHeightMintAdded);
READWRITE(nHeightAccStart);
READWRITE(nHeightAccEnd);
READWRITE(nMintsAdded);
READWRITE(txid);
READWRITE(isV1);
READWRITE(coinAmount); // used to create the PublicCoin
READWRITE(coinDenom);
READWRITE(accumulatorAmount); // used to create the pAccumulator
READWRITE(accumulatorDenom);
};
};
#endif //Agrarian_WITNESS_H
+129
View File
@@ -0,0 +1,129 @@
// Copyright (c) 2019 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "zagr/zagrmodule.h"
#include "zagrchain.h"
#include "libzerocoin/Commitment.h"
#include "libzerocoin/Coin.h"
#include "hash.h"
#include "main.h"
#include "iostream"
bool PublicCoinSpend::Verify(const libzerocoin::Accumulator& a, bool verifyParams) const {
return validate();
}
bool PublicCoinSpend::validate() const {
libzerocoin::ZerocoinParams* params = Params().Zerocoin_Params(false);
// Check that it opens to the input values
libzerocoin::Commitment commitment(
&params->coinCommitmentGroup, getCoinSerialNumber(), randomness);
if (commitment.getCommitmentValue() != pubCoin.getValue()){
return error("%s: commitments values are not equal\n", __func__);
}
// Now check that the signature validates with the serial
if (!HasValidSignature()) {
return error("%s: signature invalid\n", __func__);;
}
return true;
}
const uint256 PublicCoinSpend::signatureHash() const
{
CHashWriter h(0, 0);
h << ptxHash << denomination << getCoinSerialNumber() << randomness << txHash << outputIndex << getSpendType();
return h.GetHash();
}
namespace ZAGRModule {
bool createInput(CTxIn &in, CZerocoinMint &mint, uint256 hashTxOut) {
libzerocoin::ZerocoinParams *params = Params().Zerocoin_Params(false);
uint8_t nVersion = mint.GetVersion();
if (nVersion < libzerocoin::PrivateCoin::PUBKEY_VERSION) {
// No v1 serials accepted anymore.
return error("%s: failed to set zAGR privkey mint version=%d\n", __func__, nVersion);
}
CKey key;
if (!mint.GetKeyPair(key))
return error("%s: failed to set zAGR privkey mint version=%d\n", __func__, nVersion);
PublicCoinSpend spend(params, mint.GetSerialNumber(), mint.GetRandomness(), key.GetPubKey());
spend.setTxOutHash(hashTxOut);
spend.outputIndex = mint.GetOutputIndex();
spend.txHash = mint.GetTxHash();
spend.setDenom(mint.GetDenomination());
std::vector<unsigned char> vchSig;
if (!key.Sign(spend.signatureHash(), vchSig))
throw std::runtime_error("ZAGRModule failed to sign signatureHash\n");
spend.setVchSig(vchSig);
CDataStream ser(SER_NETWORK, PROTOCOL_VERSION);
ser << spend;
std::vector<unsigned char> data(ser.begin(), ser.end());
CScript scriptSigIn = CScript() << OP_ZEROCOINPUBLICSPEND << data.size();
scriptSigIn.insert(scriptSigIn.end(), data.begin(), data.end());
in = CTxIn(mint.GetTxHash(), mint.GetOutputIndex(), scriptSigIn, mint.GetDenomination());
in.nSequence = mint.GetDenomination();
return true;
}
bool parseCoinSpend(const CTxIn &in, const CTransaction &tx, const CTxOut &prevOut, PublicCoinSpend &publicCoinSpend) {
if (!in.IsZerocoinPublicSpend() || !prevOut.IsZerocoinMint())
return error("%s: invalid argument/s\n", __func__);
std::vector<char, zero_after_free_allocator<char> > data;
data.insert(data.end(), in.scriptSig.begin() + 4, in.scriptSig.end());
CDataStream serializedCoinSpend(data, SER_NETWORK, PROTOCOL_VERSION);
libzerocoin::ZerocoinParams *params = Params().Zerocoin_Params(false);
PublicCoinSpend spend(params, serializedCoinSpend);
spend.outputIndex = in.prevout.n;
spend.txHash = in.prevout.hash;
CMutableTransaction txNew(tx);
txNew.vin.clear();
spend.setTxOutHash(txNew.GetHash());
// Check prev out now
CValidationState state;
if (!TxOutToPublicCoin(prevOut, spend.pubCoin, state))
return error("%s: cannot get mint from output\n", __func__);
spend.setDenom(spend.pubCoin.getDenomination());
publicCoinSpend = spend;
return true;
}
bool validateInput(const CTxIn &in, const CTxOut &prevOut, const CTransaction &tx, PublicCoinSpend &publicSpend) {
// Now prove that the commitment value opens to the input
if (!parseCoinSpend(in, tx, prevOut, publicSpend)) {
return false;
}
if (libzerocoin::ZerocoinDenominationToAmount(
libzerocoin::IntToZerocoinDenomination(in.nSequence)) != prevOut.nValue) {
return error("PublicCoinSpend validateInput :: input nSequence different to prevout value\n");
}
return publicSpend.validate();
}
bool ParseZerocoinPublicSpend(const CTxIn &txIn, const CTransaction& tx, CValidationState& state, PublicCoinSpend& publicSpend)
{
CTxOut prevOut;
if(!GetOutput(txIn.prevout.hash, txIn.prevout.n ,state, prevOut)){
return state.DoS(100, error("%s: public zerocoin spend prev output not found, prevTx %s, index %d\n",
__func__, txIn.prevout.hash.GetHex(), txIn.prevout.n));
}
if (!ZAGRModule::parseCoinSpend(txIn, tx, prevOut, publicSpend)) {
return state.Invalid(error("%s: invalid public coin spend parse %s\n", __func__,
tx.GetHash().GetHex()), REJECT_INVALID, "bad-txns-invalid-zagr");
}
return true;
}
}
+92
View File
@@ -0,0 +1,92 @@
// Copyright (c) 2019 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#ifndef Agrarian_ZAGRMODULE_H
#define Agrarian_ZAGRMODULE_H
#include "libzerocoin/bignum.h"
#include "libzerocoin/Denominations.h"
#include "libzerocoin/CoinSpend.h"
#include "libzerocoin/Coin.h"
#include "libzerocoin/SpendType.h"
#include "primitives/transaction.h"
#include "script/script.h"
#include "serialize.h"
#include "uint256.h"
#include <streams.h>
#include <utilstrencodings.h>
#include "zagr/zerocoin.h"
#include "chainparams.h"
static int const COIN_SPEND_PUBLIC_SPEND_VERSION = 3;
class PublicCoinSpend : public libzerocoin::CoinSpend{
public:
PublicCoinSpend(libzerocoin::ZerocoinParams* params):pubCoin(params){};
PublicCoinSpend(libzerocoin::ZerocoinParams* params,
CBigNum serial, CBigNum randomness, CPubKey pubkey):pubCoin(params){
this->coinSerialNumber = serial;
this->randomness = randomness;
this->pubkey = pubkey;
this->spendType = libzerocoin::SpendType::SPEND;
this->version = COIN_SPEND_PUBLIC_SPEND_VERSION;
};
~PublicCoinSpend(){};
template <typename Stream>
PublicCoinSpend(
libzerocoin::ZerocoinParams* params,
Stream& strm):pubCoin(params){
strm >> *this;
this->spendType = libzerocoin::SpendType::SPEND;
}
const uint256 signatureHash() const override;
void setVchSig(std::vector<unsigned char> vchSig) { this->vchSig = vchSig; };
bool Verify(const libzerocoin::Accumulator& a, bool verifyParams = true) const override;
bool validate() const;
// Members
CBigNum randomness;
// prev out values
uint256 txHash = 0;
unsigned int outputIndex = -1;
libzerocoin::PublicCoin pubCoin;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(version);
READWRITE(coinSerialNumber);
READWRITE(randomness);
READWRITE(pubkey);
READWRITE(vchSig);
}
};
class CValidationState;
namespace ZAGRModule {
bool createInput(CTxIn &in, CZerocoinMint& mint, uint256 hashTxOut);
bool parseCoinSpend(const CTxIn &in, const CTransaction& tx, const CTxOut &prevOut, PublicCoinSpend& publicCoinSpend);
bool validateInput(const CTxIn &in, const CTxOut &prevOut, const CTransaction& tx, PublicCoinSpend& ret);
// Public zc spend parse
/**
*
* @param in --> public zc spend input
* @param tx --> input parent
* @param publicCoinSpend ---> return the publicCoinSpend parsed
* @return true if everything went ok
*/
bool ParseZerocoinPublicSpend(const CTxIn &in, const CTransaction& tx, CValidationState& state, PublicCoinSpend& publicCoinSpend);
};
#endif //Agrarian_ZAGRMODULE_H
+536
View File
@@ -0,0 +1,536 @@
// Copyright (c) 2018-2019 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <zagr/deterministicmint.h>
#include "zagrtracker.h"
#include "util.h"
#include "sync.h"
#include "main.h"
#include "txdb.h"
#include "wallet/walletdb.h"
#include "zagr/accumulators.h"
#include "zagr/zagrwallet.h"
#include "witness.h"
using namespace std;
CzAGRTracker::CzAGRTracker(std::string strWalletFile)
{
this->strWalletFile = strWalletFile;
mapSerialHashes.clear();
mapPendingSpends.clear();
fInitialized = false;
}
CzAGRTracker::~CzAGRTracker()
{
mapSerialHashes.clear();
mapPendingSpends.clear();
}
void CzAGRTracker::Init()
{
//Load all CZerocoinMints and CDeterministicMints from the database
if (!fInitialized) {
ListMints(false, false, true);
fInitialized = true;
}
}
bool CzAGRTracker::Archive(CMintMeta& meta)
{
if (mapSerialHashes.count(meta.hashSerial))
mapSerialHashes.at(meta.hashSerial).isArchived = true;
CWalletDB walletdb(strWalletFile);
CZerocoinMint mint;
if (walletdb.ReadZerocoinMint(meta.hashPubcoin, mint)) {
if (!CWalletDB(strWalletFile).ArchiveMintOrphan(mint))
return error("%s: failed to archive zerocoinmint", __func__);
} else {
//failed to read mint from DB, try reading deterministic
CDeterministicMint dMint;
if (!walletdb.ReadDeterministicMint(meta.hashPubcoin, dMint))
return error("%s: could not find pubcoinhash %s in db", __func__, meta.hashPubcoin.GetHex());
if (!walletdb.ArchiveDeterministicOrphan(dMint))
return error("%s: failed to archive deterministic ophaned mint", __func__);
}
LogPrintf("%s: archived pubcoinhash %s\n", __func__, meta.hashPubcoin.GetHex());
return true;
}
bool CzAGRTracker::UnArchive(const uint256& hashPubcoin, bool isDeterministic)
{
CWalletDB walletdb(strWalletFile);
if (isDeterministic) {
CDeterministicMint dMint;
if (!walletdb.UnarchiveDeterministicMint(hashPubcoin, dMint))
return error("%s: failed to unarchive deterministic mint", __func__);
Add(dMint, false);
} else {
CZerocoinMint mint;
if (!walletdb.UnarchiveZerocoinMint(hashPubcoin, mint))
return error("%s: failed to unarchivezerocoin mint", __func__);
Add(mint, false);
}
LogPrintf("%s: unarchived %s\n", __func__, hashPubcoin.GetHex());
return true;
}
CMintMeta CzAGRTracker::Get(const uint256 &hashSerial)
{
if (!mapSerialHashes.count(hashSerial))
return CMintMeta();
return mapSerialHashes.at(hashSerial);
}
CMintMeta CzAGRTracker::GetMetaFromPubcoin(const uint256& hashPubcoin)
{
for (auto it : mapSerialHashes) {
CMintMeta meta = it.second;
if (meta.hashPubcoin == hashPubcoin)
return meta;
}
return CMintMeta();
}
bool CzAGRTracker::GetMetaFromStakeHash(const uint256& hashStake, CMintMeta& meta) const
{
for (auto& it : mapSerialHashes) {
if (it.second.hashStake == hashStake) {
meta = it.second;
return true;
}
}
return false;
}
CoinWitnessData* CzAGRTracker::GetSpendCache(const uint256& hashStake)
{
AssertLockHeld(cs_spendcache);
if (!mapStakeCache.count(hashStake)) {
std::unique_ptr<CoinWitnessData> uptr(new CoinWitnessData());
mapStakeCache.insert(std::make_pair(hashStake, std::move(uptr)));
return mapStakeCache.at(hashStake).get();
}
return mapStakeCache.at(hashStake).get();
}
bool CzAGRTracker::ClearSpendCache()
{
AssertLockHeld(cs_spendcache);
if (!mapStakeCache.empty()) {
mapStakeCache.clear();
return true;
}
return false;
}
std::vector<uint256> CzAGRTracker::GetSerialHashes()
{
vector<uint256> vHashes;
for (auto it : mapSerialHashes) {
if (it.second.isArchived)
continue;
vHashes.emplace_back(it.first);
}
return vHashes;
}
CAmount CzAGRTracker::GetBalance(bool fConfirmedOnly, bool fUnconfirmedOnly) const
{
CAmount nTotal = 0;
//! zerocoin specific fields
std::map<libzerocoin::CoinDenomination, unsigned int> myZerocoinSupply;
for (auto& denom : libzerocoin::zerocoinDenomList) {
myZerocoinSupply.insert(make_pair(denom, 0));
}
{
//LOCK(cs_agrtracker);
// Get Unused coins
for (auto& it : mapSerialHashes) {
CMintMeta meta = it.second;
if (meta.isUsed || meta.isArchived)
continue;
bool fConfirmed = ((meta.nHeight < chainActive.Height() - Params().Zerocoin_MintRequiredConfirmations()) && !(meta.nHeight == 0));
if (fConfirmedOnly && !fConfirmed)
continue;
if (fUnconfirmedOnly && fConfirmed)
continue;
nTotal += libzerocoin::ZerocoinDenominationToAmount(meta.denom);
myZerocoinSupply.at(meta.denom)++;
}
}
if (nTotal < 0 ) nTotal = 0; // Sanity never hurts
return nTotal;
}
CAmount CzAGRTracker::GetUnconfirmedBalance() const
{
return GetBalance(false, true);
}
std::vector<CMintMeta> CzAGRTracker::GetMints(bool fConfirmedOnly) const
{
vector<CMintMeta> vMints;
for (auto& it : mapSerialHashes) {
CMintMeta mint = it.second;
if (mint.isArchived || mint.isUsed)
continue;
bool fConfirmed = (mint.nHeight < chainActive.Height() - Params().Zerocoin_MintRequiredConfirmations());
if (fConfirmedOnly && !fConfirmed)
continue;
vMints.emplace_back(mint);
}
return vMints;
}
//Does a mint in the tracker have this txid
bool CzAGRTracker::HasMintTx(const uint256& txid)
{
for (auto it : mapSerialHashes) {
if (it.second.txid == txid)
return true;
}
return false;
}
bool CzAGRTracker::HasPubcoin(const CBigNum &bnValue) const
{
// Check if this mint's pubcoin value belongs to our mapSerialHashes (which includes hashpubcoin values)
uint256 hash = GetPubCoinHash(bnValue);
return HasPubcoinHash(hash);
}
bool CzAGRTracker::HasPubcoinHash(const uint256& hashPubcoin) const
{
for (auto it : mapSerialHashes) {
CMintMeta meta = it.second;
if (meta.hashPubcoin == hashPubcoin)
return true;
}
return false;
}
bool CzAGRTracker::HasSerial(const CBigNum& bnSerial) const
{
uint256 hash = GetSerialHash(bnSerial);
return HasSerialHash(hash);
}
bool CzAGRTracker::HasSerialHash(const uint256& hashSerial) const
{
auto it = mapSerialHashes.find(hashSerial);
return it != mapSerialHashes.end();
}
bool CzAGRTracker::UpdateZerocoinMint(const CZerocoinMint& mint)
{
if (!HasSerial(mint.GetSerialNumber()))
return error("%s: mint %s is not known", __func__, mint.GetValue().GetHex());
uint256 hashSerial = GetSerialHash(mint.GetSerialNumber());
//Update the meta object
CMintMeta meta = Get(hashSerial);
meta.isUsed = mint.IsUsed();
meta.denom = mint.GetDenomination();
meta.nHeight = mint.GetHeight();
mapSerialHashes.at(hashSerial) = meta;
//Write to db
return CWalletDB(strWalletFile).WriteZerocoinMint(mint);
}
bool CzAGRTracker::UpdateState(const CMintMeta& meta)
{
CWalletDB walletdb(strWalletFile);
if (meta.isDeterministic) {
CDeterministicMint dMint;
if (!walletdb.ReadDeterministicMint(meta.hashPubcoin, dMint)) {
// Check archive just in case
if (!meta.isArchived)
return error("%s: failed to read deterministic mint from database", __func__);
// Unarchive this mint since it is being requested and updated
if (!walletdb.UnarchiveDeterministicMint(meta.hashPubcoin, dMint))
return error("%s: failed to unarchive deterministic mint from database", __func__);
}
dMint.SetTxHash(meta.txid);
dMint.SetHeight(meta.nHeight);
dMint.SetUsed(meta.isUsed);
dMint.SetDenomination(meta.denom);
dMint.SetStakeHash(meta.hashStake);
if (!walletdb.WriteDeterministicMint(dMint))
return error("%s: failed to update deterministic mint when writing to db", __func__);
} else {
CZerocoinMint mint;
if (!walletdb.ReadZerocoinMint(meta.hashPubcoin, mint))
return error("%s: failed to read mint from database", __func__);
mint.SetTxHash(meta.txid);
mint.SetHeight(meta.nHeight);
mint.SetUsed(meta.isUsed);
mint.SetDenomination(meta.denom);
if (!walletdb.WriteZerocoinMint(mint))
return error("%s: failed to write mint to database", __func__);
}
mapSerialHashes[meta.hashSerial] = meta;
return true;
}
void CzAGRTracker::Add(const CDeterministicMint& dMint, bool isNew, bool isArchived, CzAGRWallet* zAGRWallet)
{
bool iszAGRWalletInitialized = (NULL != zAGRWallet);
CMintMeta meta;
meta.hashPubcoin = dMint.GetPubcoinHash();
meta.nHeight = dMint.GetHeight();
meta.nVersion = dMint.GetVersion();
meta.txid = dMint.GetTxHash();
meta.isUsed = dMint.IsUsed();
meta.hashSerial = dMint.GetSerialHash();
meta.hashStake = dMint.GetStakeHash();
meta.denom = dMint.GetDenomination();
meta.isArchived = isArchived;
meta.isDeterministic = true;
if (! iszAGRWalletInitialized)
zAGRWallet = new CzAGRWallet(strWalletFile);
meta.isSeedCorrect = zAGRWallet->CheckSeed(dMint);
if (! iszAGRWalletInitialized)
delete zAGRWallet;
mapSerialHashes[meta.hashSerial] = meta;
if (isNew)
CWalletDB(strWalletFile).WriteDeterministicMint(dMint);
}
void CzAGRTracker::Add(const CZerocoinMint& mint, bool isNew, bool isArchived)
{
CMintMeta meta;
meta.hashPubcoin = GetPubCoinHash(mint.GetValue());
meta.nHeight = mint.GetHeight();
meta.nVersion = libzerocoin::ExtractVersionFromSerial(mint.GetSerialNumber());
meta.txid = mint.GetTxHash();
meta.isUsed = mint.IsUsed();
meta.hashSerial = GetSerialHash(mint.GetSerialNumber());
uint256 nSerial = mint.GetSerialNumber().getuint256();
meta.hashStake = Hash(nSerial.begin(), nSerial.end());
meta.denom = mint.GetDenomination();
meta.isArchived = isArchived;
meta.isDeterministic = false;
meta.isSeedCorrect = true;
mapSerialHashes[meta.hashSerial] = meta;
if (isNew)
CWalletDB(strWalletFile).WriteZerocoinMint(mint);
}
void CzAGRTracker::SetPubcoinUsed(const uint256& hashPubcoin, const uint256& txid)
{
if (!HasPubcoinHash(hashPubcoin))
return;
CMintMeta meta = GetMetaFromPubcoin(hashPubcoin);
meta.isUsed = true;
mapPendingSpends.insert(make_pair(meta.hashSerial, txid));
UpdateState(meta);
}
void CzAGRTracker::SetPubcoinNotUsed(const uint256& hashPubcoin)
{
if (!HasPubcoinHash(hashPubcoin))
return;
CMintMeta meta = GetMetaFromPubcoin(hashPubcoin);
meta.isUsed = false;
if (mapPendingSpends.count(meta.hashSerial))
mapPendingSpends.erase(meta.hashSerial);
UpdateState(meta);
}
void CzAGRTracker::RemovePending(const uint256& txid)
{
uint256 hashSerial;
for (auto it : mapPendingSpends) {
if (it.second == txid) {
hashSerial = it.first;
break;
}
}
if (hashSerial > 0)
mapPendingSpends.erase(hashSerial);
}
bool CzAGRTracker::UpdateStatusInternal(const std::set<uint256>& setMempool, CMintMeta& mint)
{
//! Check whether this mint has been spent and is considered 'pending' or 'confirmed'
// If there is not a record of the block height, then look it up and assign it
uint256 txidMint;
bool isMintInChain = zerocoinDB->ReadCoinMint(mint.hashPubcoin, txidMint);
//See if there is internal record of spending this mint (note this is memory only, would reset on restart)
bool isPendingSpend = static_cast<bool>(mapPendingSpends.count(mint.hashSerial));
// See if there is a blockchain record of spending this mint
uint256 txidSpend;
bool isConfirmedSpend = zerocoinDB->ReadCoinSpend(mint.hashSerial, txidSpend);
// Double check the mempool for pending spend
if (isPendingSpend) {
uint256 txidPendingSpend = mapPendingSpends.at(mint.hashSerial);
if (!setMempool.count(txidPendingSpend) || isConfirmedSpend) {
RemovePending(txidPendingSpend);
isPendingSpend = false;
LogPrintf("%s : Pending txid %s removed because not in mempool\n", __func__, txidPendingSpend.GetHex());
}
}
bool isUsed = isPendingSpend || isConfirmedSpend;
if (!mint.nHeight || !isMintInChain || isUsed != mint.isUsed) {
CTransaction tx;
uint256 hashBlock;
// Txid will be marked 0 if there is no knowledge of the final tx hash yet
if (mint.txid == 0) {
if (!isMintInChain) {
LogPrintf("%s : Failed to find mint in zerocoinDB %s\n", __func__, mint.hashPubcoin.GetHex().substr(0, 6));
mint.isArchived = true;
Archive(mint);
return true;
}
mint.txid = txidMint;
}
if (setMempool.count(mint.txid))
return true;
// Check the transaction associated with this mint
if (!IsInitialBlockDownload() && !GetTransaction(mint.txid, tx, hashBlock, true)) {
LogPrintf("%s : Failed to find tx for mint txid=%s\n", __func__, mint.txid.GetHex());
mint.isArchived = true;
Archive(mint);
return true;
}
// An orphan tx if hashblock is in mapBlockIndex but not in chain active
if (mapBlockIndex.count(hashBlock) && !chainActive.Contains(mapBlockIndex.at(hashBlock))) {
LogPrintf("%s : Found orphaned mint txid=%s\n", __func__, mint.txid.GetHex());
mint.isUsed = false;
mint.nHeight = 0;
if (tx.IsCoinStake()) {
mint.isArchived = true;
Archive(mint);
}
return true;
}
// Check that the mint has correct used status
if (mint.isUsed != isUsed) {
LogPrintf("%s : Set mint %s isUsed to %d\n", __func__, mint.hashPubcoin.GetHex(), isUsed);
mint.isUsed = isUsed;
return true;
}
}
return false;
}
std::set<CMintMeta> CzAGRTracker::ListMints(bool fUnusedOnly, bool fMatureOnly, bool fUpdateStatus, bool fWrongSeed, bool fExcludeV1)
{
CWalletDB walletdb(strWalletFile);
if (fUpdateStatus) {
std::list<CZerocoinMint> listMintsDB = walletdb.ListMintedCoins();
for (auto& mint : listMintsDB)
Add(mint);
LogPrint("zero", "%s: added %d zerocoinmints from DB\n", __func__, listMintsDB.size());
std::list<CDeterministicMint> listDeterministicDB = walletdb.ListDeterministicMints();
CzAGRWallet* zAGRWallet = new CzAGRWallet(strWalletFile);
for (auto& dMint : listDeterministicDB) {
if (fExcludeV1 && dMint.GetVersion() < 2)
continue;
Add(dMint, false, false, zAGRWallet);
}
delete zAGRWallet;
LogPrint("zero", "%s: added %d dzagr from DB\n", __func__, listDeterministicDB.size());
}
std::vector<CMintMeta> vOverWrite;
std::set<CMintMeta> setMints;
std::set<uint256> setMempool;
{
LOCK(mempool.cs);
mempool.getTransactions(setMempool);
}
std::map<libzerocoin::CoinDenomination, int> mapMaturity = GetMintMaturityHeight();
for (auto& it : mapSerialHashes) {
CMintMeta mint = it.second;
//This is only intended for unarchived coins
if (mint.isArchived)
continue;
// Update the metadata of the mints if requested
if (fUpdateStatus && UpdateStatusInternal(setMempool, mint)) {
if (mint.isArchived)
continue;
// Mint was updated, queue for overwrite
vOverWrite.emplace_back(mint);
}
if (fUnusedOnly && mint.isUsed)
continue;
if (fMatureOnly) {
// Not confirmed
if (!mint.nHeight || mint.nHeight > chainActive.Height() - Params().Zerocoin_MintRequiredConfirmations())
continue;
if (mint.nHeight >= mapMaturity.at(mint.denom))
continue;
}
if (!fWrongSeed && !mint.isSeedCorrect)
continue;
setMints.insert(mint);
}
//overwrite any updates
for (CMintMeta& meta : vOverWrite)
UpdateState(meta);
return setMints;
}
void CzAGRTracker::Clear()
{
mapSerialHashes.clear();
}
+58
View File
@@ -0,0 +1,58 @@
// Copyright (c) 2018-2019 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef Agrarian_ZAGRTRACKER_H
#define Agrarian_ZAGRTRACKER_H
#include "zerocoin.h"
#include "witness.h"
#include "sync.h"
#include <list>
class CDeterministicMint;
class CzAGRWallet;
class CzAGRTracker
{
private:
bool fInitialized;
std::string strWalletFile;
std::map<uint256, CMintMeta> mapSerialHashes;
std::map<uint256, uint256> mapPendingSpends; //serialhash, txid of spend
std::map<uint256, std::unique_ptr<CoinWitnessData> > mapStakeCache; //serialhash, witness value, height
bool UpdateStatusInternal(const std::set<uint256>& setMempool, CMintMeta& mint);
public:
CzAGRTracker(std::string strWalletFile);
~CzAGRTracker();
void Add(const CDeterministicMint& dMint, bool isNew = false, bool isArchived = false, CzAGRWallet* zAGRWallet = NULL);
void Add(const CZerocoinMint& mint, bool isNew = false, bool isArchived = false);
bool Archive(CMintMeta& meta);
bool HasPubcoin(const CBigNum& bnValue) const;
bool HasPubcoinHash(const uint256& hashPubcoin) const;
bool HasSerial(const CBigNum& bnSerial) const;
bool HasSerialHash(const uint256& hashSerial) const;
bool HasMintTx(const uint256& txid);
bool IsEmpty() const { return mapSerialHashes.empty(); }
void Init();
CMintMeta Get(const uint256& hashSerial);
CMintMeta GetMetaFromPubcoin(const uint256& hashPubcoin);
bool GetMetaFromStakeHash(const uint256& hashStake, CMintMeta& meta) const;
CAmount GetBalance(bool fConfirmedOnly, bool fUnconfirmedOnly) const;
std::vector<uint256> GetSerialHashes();
mutable CCriticalSection cs_spendcache;
CoinWitnessData* GetSpendCache(const uint256& hashStake) EXCLUSIVE_LOCKS_REQUIRED(cs_spendcache);
bool ClearSpendCache() EXCLUSIVE_LOCKS_REQUIRED(cs_spendcache);
std::vector<CMintMeta> GetMints(bool fConfirmedOnly) const;
CAmount GetUnconfirmedBalance() const;
std::set<CMintMeta> ListMints(bool fUnusedOnly, bool fMatureOnly, bool fUpdateStatus, bool fWrongSeed = false, bool fExcludeV1 = false);
void RemovePending(const uint256& txid);
void SetPubcoinUsed(const uint256& hashPubcoin, const uint256& txid);
void SetPubcoinNotUsed(const uint256& hashPubcoin);
bool UnArchive(const uint256& hashPubcoin, bool isDeterministic);
bool UpdateZerocoinMint(const CZerocoinMint& mint);
bool UpdateState(const CMintMeta& meta);
void Clear();
};
#endif //Agrarian_ZAGRTRACKER_H
+487
View File
@@ -0,0 +1,487 @@
// Copyright (c) 2017-2019 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "zagrwallet.h"
#include "main.h"
#include "txdb.h"
#include "wallet/walletdb.h"
#include "init.h"
#include "wallet/wallet.h"
#include "deterministicmint.h"
#include "zagrchain.h"
using namespace libzerocoin;
CzAGRWallet::CzAGRWallet(std::string strWalletFile)
{
this->strWalletFile = strWalletFile;
CWalletDB walletdb(strWalletFile);
uint256 hashSeed;
bool fFirstRun = !walletdb.ReadCurrentSeedHash(hashSeed);
//Check for old db version of storing zagr seed
if (fFirstRun) {
uint256 seed;
if (walletdb.ReadZAGRSeed_deprecated(seed)) {
//Update to new format, erase old
seedMaster = seed;
hashSeed = Hash(seed.begin(), seed.end());
if (pwalletMain->AddDeterministicSeed(seed)) {
if (walletdb.EraseZAGRSeed_deprecated()) {
LogPrintf("%s: Updated zAGR seed databasing\n", __func__);
fFirstRun = false;
} else {
LogPrintf("%s: failed to remove old zagr seed\n", __func__);
}
}
}
}
//Don't try to do anything if the wallet is locked.
if (pwalletMain->IsLocked()) {
seedMaster = 0;
nCountLastUsed = 0;
this->mintPool = CMintPool();
return;
}
//First time running, generate master seed
uint256 seed;
if (fFirstRun) {
// Borrow random generator from the key class so that we don't have to worry about randomness
CKey key;
key.MakeNewKey(true);
seed = key.GetPrivKey_256();
seedMaster = seed;
LogPrintf("%s: first run of zagr wallet detected, new seed generated. Seedhash=%s\n", __func__, Hash(seed.begin(), seed.end()).GetHex());
} else if (!pwalletMain->GetDeterministicSeed(hashSeed, seed)) {
LogPrintf("%s: failed to get deterministic seed for hashseed %s\n", __func__, hashSeed.GetHex());
return;
}
if (!SetMasterSeed(seed)) {
LogPrintf("%s: failed to save deterministic seed for hashseed %s\n", __func__, hashSeed.GetHex());
return;
}
this->mintPool = CMintPool(nCountLastUsed);
}
bool CzAGRWallet::SetMasterSeed(const uint256& seedMaster, bool fResetCount)
{
CWalletDB walletdb(strWalletFile);
if (pwalletMain->IsLocked())
return false;
if (seedMaster != 0 && !pwalletMain->AddDeterministicSeed(seedMaster)) {
return error("%s: failed to set master seed.", __func__);
}
this->seedMaster = seedMaster;
nCountLastUsed = 0;
if (fResetCount)
walletdb.WriteZAGRCount(nCountLastUsed);
else if (!walletdb.ReadZAGRCount(nCountLastUsed))
nCountLastUsed = 0;
mintPool.Reset();
return true;
}
void CzAGRWallet::Lock()
{
seedMaster = 0;
}
void CzAGRWallet::AddToMintPool(const std::pair<uint256, uint32_t>& pMint, bool fVerbose)
{
mintPool.Add(pMint, fVerbose);
}
//Add the next 20 mints to the mint pool
void CzAGRWallet::GenerateMintPool(uint32_t nCountStart, uint32_t nCountEnd)
{
//Is locked
if (seedMaster == 0)
return;
uint32_t n = nCountLastUsed + 1;
if (nCountStart > 0)
n = nCountStart;
uint32_t nStop = n + 20;
if (nCountEnd > 0)
nStop = std::max(n, n + nCountEnd);
bool fFound;
uint256 hashSeed = Hash(seedMaster.begin(), seedMaster.end());
LogPrintf("%s : n=%d nStop=%d\n", __func__, n, nStop - 1);
for (uint32_t i = n; i < nStop; ++i) {
if (ShutdownRequested())
return;
fFound = false;
// Prevent unnecessary repeated minted
for (auto& pair : mintPool) {
if(pair.second == i) {
fFound = true;
break;
}
}
if(fFound)
continue;
uint512 seedZerocoin = GetZerocoinSeed(i);
CBigNum bnValue;
CBigNum bnSerial;
CBigNum bnRandomness;
CKey key;
SeedToZAGR(seedZerocoin, bnValue, bnSerial, bnRandomness, key);
mintPool.Add(bnValue, i);
CWalletDB(strWalletFile).WriteMintPoolPair(hashSeed, GetPubCoinHash(bnValue), i);
LogPrintf("%s : %s count=%d\n", __func__, bnValue.GetHex().substr(0, 6), i);
}
}
// pubcoin hashes are stored to db so that a full accounting of mints belonging to the seed can be tracked without regenerating
bool CzAGRWallet::LoadMintPoolFromDB()
{
map<uint256, vector<pair<uint256, uint32_t> > > mapMintPool = CWalletDB(strWalletFile).MapMintPool();
uint256 hashSeed = Hash(seedMaster.begin(), seedMaster.end());
for (auto& pair : mapMintPool[hashSeed])
mintPool.Add(pair);
return true;
}
void CzAGRWallet::RemoveMintsFromPool(const std::vector<uint256>& vPubcoinHashes)
{
for (const uint256& hash : vPubcoinHashes)
mintPool.Remove(hash);
}
void CzAGRWallet::GetState(int& nCount, int& nLastGenerated)
{
nCount = this->nCountLastUsed + 1;
nLastGenerated = mintPool.CountOfLastGenerated();
}
//Catch the counter up with the chain
void CzAGRWallet::SyncWithChain(bool fGenerateMintPool)
{
uint32_t nLastCountUsed = 0;
bool found = true;
CWalletDB walletdb(strWalletFile);
set<uint256> setAddedTx;
while (found) {
found = false;
if (fGenerateMintPool)
GenerateMintPool();
LogPrintf("%s: Mintpool size=%d\n", __func__, mintPool.size());
std::set<uint256> setChecked;
list<pair<uint256,uint32_t> > listMints = mintPool.List();
for (pair<uint256, uint32_t> pMint : listMints) {
LOCK(cs_main);
if (setChecked.count(pMint.first))
return;
setChecked.insert(pMint.first);
if (ShutdownRequested())
return;
if (pwalletMain->zagrTracker->HasPubcoinHash(pMint.first)) {
mintPool.Remove(pMint.first);
continue;
}
uint256 txHash;
CZerocoinMint mint;
if (zerocoinDB->ReadCoinMint(pMint.first, txHash)) {
//this mint has already occurred on the chain, increment counter's state to reflect this
LogPrintf("%s : Found wallet coin mint=%s count=%d tx=%s\n", __func__, pMint.first.GetHex(), pMint.second, txHash.GetHex());
found = true;
uint256 hashBlock;
CTransaction tx;
if (!GetTransaction(txHash, tx, hashBlock, true)) {
LogPrintf("%s : failed to get transaction for mint %s!\n", __func__, pMint.first.GetHex());
found = false;
nLastCountUsed = std::max(pMint.second, nLastCountUsed);
continue;
}
//Find the denomination
CoinDenomination denomination = CoinDenomination::ZQ_ERROR;
bool fFoundMint = false;
CBigNum bnValue = 0;
for (const CTxOut& out : tx.vout) {
if (!out.IsZerocoinMint())
continue;
PublicCoin pubcoin(Params().Zerocoin_Params(false));
CValidationState state;
if (!TxOutToPublicCoin(out, pubcoin, state)) {
LogPrintf("%s : failed to get mint from txout for %s!\n", __func__, pMint.first.GetHex());
continue;
}
// See if this is the mint that we are looking for
uint256 hashPubcoin = GetPubCoinHash(pubcoin.getValue());
if (pMint.first == hashPubcoin) {
denomination = pubcoin.getDenomination();
bnValue = pubcoin.getValue();
fFoundMint = true;
break;
}
}
if (!fFoundMint || denomination == ZQ_ERROR) {
LogPrintf("%s : failed to get mint %s from tx %s!\n", __func__, pMint.first.GetHex(), tx.GetHash().GetHex());
found = false;
break;
}
CBlockIndex* pindex = nullptr;
if (mapBlockIndex.count(hashBlock))
pindex = mapBlockIndex.at(hashBlock);
if (!setAddedTx.count(txHash)) {
CBlock block;
CWalletTx wtx(pwalletMain, tx);
if (pindex && ReadBlockFromDisk(block, pindex))
wtx.SetMerkleBranch(block);
//Fill out wtx so that a transaction record can be created
wtx.nTimeReceived = pindex->GetBlockTime();
pwalletMain->AddToWallet(wtx);
setAddedTx.insert(txHash);
}
SetMintSeen(bnValue, pindex->nHeight, txHash, denomination);
nLastCountUsed = std::max(pMint.second, nLastCountUsed);
nCountLastUsed = std::max(nLastCountUsed, nCountLastUsed);
LogPrint("zero", "%s: updated count to %d\n", __func__, nCountLastUsed);
}
}
}
}
bool CzAGRWallet::SetMintSeen(const CBigNum& bnValue, const int& nHeight, const uint256& txid, const CoinDenomination& denom)
{
if (!mintPool.Has(bnValue))
return error("%s: value not in pool", __func__);
pair<uint256, uint32_t> pMint = mintPool.Get(bnValue);
// Regenerate the mint
uint512 seedZerocoin = GetZerocoinSeed(pMint.second);
CBigNum bnValueGen;
CBigNum bnSerial;
CBigNum bnRandomness;
CKey key;
SeedToZAGR(seedZerocoin, bnValueGen, bnSerial, bnRandomness, key);
//Sanity check
if (bnValueGen != bnValue)
return error("%s: generated pubcoin and expected value do not match!", __func__);
// Create mint object and database it
uint256 hashSeed = Hash(seedMaster.begin(), seedMaster.end());
uint256 hashSerial = GetSerialHash(bnSerial);
uint256 hashPubcoin = GetPubCoinHash(bnValue);
uint256 nSerial = bnSerial.getuint256();
uint256 hashStake = Hash(nSerial.begin(), nSerial.end());
CDeterministicMint dMint(PrivateCoin::CURRENT_VERSION, pMint.second, hashSeed, hashSerial, hashPubcoin, hashStake);
dMint.SetDenomination(denom);
dMint.SetHeight(nHeight);
dMint.SetTxHash(txid);
// Check if this is also already spent
int nHeightTx;
uint256 txidSpend;
CTransaction txSpend;
if (IsSerialInBlockchain(hashSerial, nHeightTx, txidSpend, txSpend)) {
//Find transaction details and make a wallettx and add to wallet
dMint.SetUsed(true);
CWalletTx wtx(pwalletMain, txSpend);
CBlockIndex* pindex = chainActive[nHeightTx];
CBlock block;
if (ReadBlockFromDisk(block, pindex))
wtx.SetMerkleBranch(block);
wtx.nTimeReceived = pindex->nTime;
pwalletMain->AddToWallet(wtx);
}
// Add to zagrTracker which also adds to database
pwalletMain->zagrTracker->Add(dMint, true);
//Update the count if it is less than the mint's count
if (nCountLastUsed < pMint.second) {
CWalletDB walletdb(strWalletFile);
nCountLastUsed = pMint.second;
walletdb.WriteZAGRCount(nCountLastUsed);
}
//remove from the pool
mintPool.Remove(dMint.GetPubcoinHash());
return true;
}
// Check if the value of the commitment meets requirements
bool IsValidCoinValue(const CBigNum& bnValue)
{
return bnValue >= Params().Zerocoin_Params(false)->accumulatorParams.minCoinValue &&
bnValue <= Params().Zerocoin_Params(false)->accumulatorParams.maxCoinValue &&
bnValue.isPrime();
}
void CzAGRWallet::SeedToZAGR(const uint512& seedZerocoin, CBigNum& bnValue, CBigNum& bnSerial, CBigNum& bnRandomness, CKey& key)
{
ZerocoinParams* params = Params().Zerocoin_Params(false);
//convert state seed into a seed for the private key
uint256 nSeedPrivKey = seedZerocoin.trim256();
bool isValidKey = false;
key = CKey();
while (!isValidKey) {
nSeedPrivKey = Hash(nSeedPrivKey.begin(), nSeedPrivKey.end());
isValidKey = libzerocoin::GenerateKeyPair(params->coinCommitmentGroup.groupOrder, nSeedPrivKey, key, bnSerial);
}
//hash randomness seed with Bottom 256 bits of seedZerocoin & attempts256 which is initially 0
uint256 randomnessSeed = uint512(seedZerocoin >> 256).trim256();
uint256 hashRandomness = Hash(randomnessSeed.begin(), randomnessSeed.end());
bnRandomness.setuint256(hashRandomness);
bnRandomness = bnRandomness % params->coinCommitmentGroup.groupOrder;
//See if serial and randomness make a valid commitment
// Generate a Pedersen commitment to the serial number
CBigNum commitmentValue = params->coinCommitmentGroup.g.pow_mod(bnSerial, params->coinCommitmentGroup.modulus).mul_mod(
params->coinCommitmentGroup.h.pow_mod(bnRandomness, params->coinCommitmentGroup.modulus),
params->coinCommitmentGroup.modulus);
CBigNum random;
uint256 attempts256 = 0;
// Iterate on Randomness until a valid commitmentValue is found
while (true) {
// Now verify that the commitment is a prime number
// in the appropriate range. If not, we'll throw this coin
// away and generate a new one.
if (IsValidCoinValue(commitmentValue)) {
bnValue = commitmentValue;
return;
}
//Did not create a valid commitment value.
//Change randomness to something new and random and try again
attempts256++;
hashRandomness = Hash(randomnessSeed.begin(), randomnessSeed.end(),
attempts256.begin(), attempts256.end());
random.setuint256(hashRandomness);
bnRandomness = (bnRandomness + random) % params->coinCommitmentGroup.groupOrder;
commitmentValue = commitmentValue.mul_mod(params->coinCommitmentGroup.h.pow_mod(random, params->coinCommitmentGroup.modulus), params->coinCommitmentGroup.modulus);
}
}
uint512 CzAGRWallet::GetZerocoinSeed(uint32_t n)
{
CDataStream ss(SER_GETHASH, 0);
ss << seedMaster << n;
uint512 zerocoinSeed = Hash512(ss.begin(), ss.end());
return zerocoinSeed;
}
void CzAGRWallet::UpdateCount()
{
nCountLastUsed++;
CWalletDB walletdb(strWalletFile);
walletdb.WriteZAGRCount(nCountLastUsed);
}
void CzAGRWallet::GenerateDeterministicZAGR(CoinDenomination denom, PrivateCoin& coin, CDeterministicMint& dMint, bool fGenerateOnly)
{
GenerateMint(nCountLastUsed + 1, denom, coin, dMint);
if (fGenerateOnly)
return;
//TODO remove this leak of seed from logs before merge to master
//LogPrintf("%s : Generated new deterministic mint. Count=%d pubcoin=%s seed=%s\n", __func__, nCount, coin.getPublicCoin().getValue().GetHex().substr(0,6), seedZerocoin.GetHex().substr(0, 4));
}
void CzAGRWallet::GenerateMint(const uint32_t& nCount, const CoinDenomination denom, PrivateCoin& coin, CDeterministicMint& dMint)
{
uint512 seedZerocoin = GetZerocoinSeed(nCount);
CBigNum bnValue;
CBigNum bnSerial;
CBigNum bnRandomness;
CKey key;
SeedToZAGR(seedZerocoin, bnValue, bnSerial, bnRandomness, key);
coin = PrivateCoin(Params().Zerocoin_Params(false), denom, bnSerial, bnRandomness);
coin.setPrivKey(key.GetPrivKey());
coin.setVersion(PrivateCoin::CURRENT_VERSION);
uint256 hashSeed = Hash(seedMaster.begin(), seedMaster.end());
uint256 hashSerial = GetSerialHash(bnSerial);
uint256 nSerial = bnSerial.getuint256();
uint256 hashStake = Hash(nSerial.begin(), nSerial.end());
uint256 hashPubcoin = GetPubCoinHash(bnValue);
dMint = CDeterministicMint(coin.getVersion(), nCount, hashSeed, hashSerial, hashPubcoin, hashStake);
dMint.SetDenomination(denom);
}
bool CzAGRWallet::CheckSeed(const CDeterministicMint& dMint)
{
//Check that the seed is correct todo:handling of incorrect, or multiple seeds
uint256 hashSeed = Hash(seedMaster.begin(), seedMaster.end());
return hashSeed == dMint.GetSeedHash();
}
bool CzAGRWallet::RegenerateMint(const CDeterministicMint& dMint, CZerocoinMint& mint)
{
if (!CheckSeed(dMint)) {
uint256 hashSeed = Hash(seedMaster.begin(), seedMaster.end());
return error("%s: master seed does not match!\ndmint:\n %s \nhashSeed: %s\nseed: %s", __func__, dMint.ToString(), hashSeed.GetHex(), seedMaster.GetHex());
}
//Generate the coin
PrivateCoin coin(Params().Zerocoin_Params(false), dMint.GetDenomination(), false);
CDeterministicMint dMintDummy;
GenerateMint(dMint.GetCount(), dMint.GetDenomination(), coin, dMintDummy);
//Fill in the zerocoinmint object's details
CBigNum bnValue = coin.getPublicCoin().getValue();
if (GetPubCoinHash(bnValue) != dMint.GetPubcoinHash())
return error("%s: failed to correctly generate mint, pubcoin hash mismatch", __func__);
mint.SetValue(bnValue);
CBigNum bnSerial = coin.getSerialNumber();
if (GetSerialHash(bnSerial) != dMint.GetSerialHash())
return error("%s: failed to correctly generate mint, serial hash mismatch", __func__);
mint.SetSerialNumber(bnSerial);
mint.SetRandomness(coin.getRandomness());
mint.SetPrivKey(coin.getPrivKey());
mint.SetVersion(coin.getVersion());
mint.SetDenomination(dMint.GetDenomination());
mint.SetUsed(dMint.IsUsed());
mint.SetTxHash(dMint.GetTxHash());
mint.SetHeight(dMint.GetHeight());
return true;
}
+49
View File
@@ -0,0 +1,49 @@
// Copyright (c) 2017-2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef Agrarian_ZAGRWALLET_H
#define Agrarian_ZAGRWALLET_H
#include <map>
#include "libzerocoin/Coin.h"
#include "mintpool.h"
#include "uint256.h"
#include "zerocoin.h"
class CDeterministicMint;
class CzAGRWallet
{
private:
uint256 seedMaster;
uint32_t nCountLastUsed;
std::string strWalletFile;
CMintPool mintPool;
public:
CzAGRWallet(std::string strWalletFile);
void AddToMintPool(const std::pair<uint256, uint32_t>& pMint, bool fVerbose);
bool SetMasterSeed(const uint256& seedMaster, bool fResetCount = false);
uint256 GetMasterSeed() { return seedMaster; }
void SyncWithChain(bool fGenerateMintPool = true);
void GenerateDeterministicZAGR(libzerocoin::CoinDenomination denom, libzerocoin::PrivateCoin& coin, CDeterministicMint& dMint, bool fGenerateOnly = false);
void GenerateMint(const uint32_t& nCount, const libzerocoin::CoinDenomination denom, libzerocoin::PrivateCoin& coin, CDeterministicMint& dMint);
void GetState(int& nCount, int& nLastGenerated);
bool RegenerateMint(const CDeterministicMint& dMint, CZerocoinMint& mint);
void GenerateMintPool(uint32_t nCountStart = 0, uint32_t nCountEnd = 0);
bool LoadMintPoolFromDB();
void RemoveMintsFromPool(const std::vector<uint256>& vPubcoinHashes);
bool SetMintSeen(const CBigNum& bnValue, const int& nHeight, const uint256& txid, const libzerocoin::CoinDenomination& denom);
bool IsInMintPool(const CBigNum& bnValue) { return mintPool.Has(bnValue); }
void UpdateCount();
void Lock();
void SeedToZAGR(const uint512& seed, CBigNum& bnValue, CBigNum& bnSerial, CBigNum& bnRandomness, CKey& key);
bool CheckSeed(const CDeterministicMint& dMint);
private:
uint512 GetZerocoinSeed(uint32_t n);
};
#endif //Agrarian_ZAGRWALLET_H
+116
View File
@@ -0,0 +1,116 @@
// Copyright (c) 2017-2018 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <streams.h>
#include "zerocoin.h"
#include "hash.h"
#include "util.h"
#include "utilstrencodings.h"
bool CMintMeta::operator <(const CMintMeta& a) const
{
return this->hashPubcoin < a.hashPubcoin;
}
uint256 GetSerialHash(const CBigNum& bnSerial)
{
CDataStream ss(SER_GETHASH, 0);
ss << bnSerial;
return Hash(ss.begin(), ss.end());
}
uint256 GetPubCoinHash(const CBigNum& bnValue)
{
CDataStream ss(SER_GETHASH, 0);
ss << bnValue;
return Hash(ss.begin(), ss.end());
}
bool CZerocoinMint::GetKeyPair(CKey &key) const
{
if (version < STAKABLE_VERSION)
return error("%s: version is %d", __func__, version);
if (privkey.empty())
return error("%s: empty privkey %s", __func__, privkey.data());
return key.SetPrivKey(privkey, true);
}
std::string CZerocoinMint::ToString() const
{
std::string str = strprintf("\n ZerocoinMint:\n version=%d \ntxfrom=%s \nheight=%d \n randomness: %s \n serial %s \n privkey %s\n",
version, txid.GetHex(), nHeight, randomness.GetHex(), serialNumber.GetHex(), HexStr(privkey));
return str;
}
void CZerocoinSpendReceipt::AddSpend(const CZerocoinSpend& spend)
{
vSpends.emplace_back(spend);
}
std::vector<CZerocoinSpend> CZerocoinSpendReceipt::GetSpends()
{
return vSpends;
}
void CZerocoinSpendReceipt::SetStatus(std::string strStatus, int nStatus, int nNeededSpends)
{
strStatusMessage = strStatus;
this->nStatus = nStatus;
this->nNeededSpends = nNeededSpends;
}
std::string CZerocoinSpendReceipt::GetStatusMessage()
{
return strStatusMessage;
}
int CZerocoinSpendReceipt::GetStatus()
{
return nStatus;
}
int CZerocoinSpendReceipt::GetNeededSpends()
{
return nNeededSpends;
}
int GetWrapppedSerialInflation(libzerocoin::CoinDenomination denom){
if(Params().NetworkID() == CBaseChainParams::MAIN) {
switch (denom) {
case libzerocoin::CoinDenomination::ZQ_ONE:
return 7;
case libzerocoin::CoinDenomination::ZQ_FIVE:
return 6;
case libzerocoin::CoinDenomination::ZQ_TEN:
return 36;
case libzerocoin::CoinDenomination::ZQ_FIFTY:
return 22;
case libzerocoin::CoinDenomination::ZQ_ONE_HUNDRED:
return 244;
case libzerocoin::CoinDenomination::ZQ_FIVE_HUNDRED:
return 22;
case libzerocoin::CoinDenomination::ZQ_ONE_THOUSAND:
return 42;
case libzerocoin::CoinDenomination::ZQ_FIVE_THOUSAND:
return 98;
default:
throw std::runtime_error("GetWrapSerialInflation :: Invalid denom");
}
}else{
// Testnet/Regtest is ok.
return 0;
}
}
int64_t GetWrapppedSerialInflationAmount(){
int64_t amount = 0;
for (auto& denom : libzerocoin::zerocoinDenomList){
amount += GetWrapppedSerialInflation(denom) * libzerocoin::ZerocoinDenominationToAmount(denom);
}
return amount;
}
+266
View File
@@ -0,0 +1,266 @@
// Copyright (c) 2017-2019 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef Agrarian_ZEROCOIN_H
#define Agrarian_ZEROCOIN_H
#include <amount.h>
#include <limits.h>
#include <chainparams.h>
#include "libzerocoin/bignum.h"
#include "libzerocoin/Denominations.h"
#include "key.h"
#include "serialize.h"
//struct that is safe to store essential mint data, without holding any information that allows for actual spending (serial, randomness, private key)
struct CMintMeta
{
int nHeight;
uint256 hashSerial;
uint256 hashPubcoin;
uint256 hashStake; //requires different hashing method than hashSerial above
uint8_t nVersion;
libzerocoin::CoinDenomination denom;
uint256 txid;
bool isUsed;
bool isArchived;
bool isDeterministic;
bool isSeedCorrect;
bool operator <(const CMintMeta& a) const;
};
uint256 GetSerialHash(const CBigNum& bnSerial);
uint256 GetPubCoinHash(const CBigNum& bnValue);
class CZerocoinMint
{
private:
libzerocoin::CoinDenomination denomination;
int nHeight;
CBigNum value;
CBigNum randomness;
CBigNum serialNumber;
uint256 txid;
int outputIndex = -1;
CPrivKey privkey;
uint8_t version;
bool isUsed;
public:
static const int STAKABLE_VERSION = 2;
static const int CURRENT_VERSION = 2;
CZerocoinMint()
{
SetNull();
}
CZerocoinMint(libzerocoin::CoinDenomination denom, const CBigNum& value, const CBigNum& randomness, const CBigNum& serialNumber, bool isUsed, const uint8_t& nVersion, CPrivKey* privkey = nullptr)
{
SetNull();
this->denomination = denom;
this->value = value;
this->randomness = randomness;
this->serialNumber = serialNumber;
this->isUsed = isUsed;
this->version = nVersion;
if (nVersion >= 2 && privkey)
this->privkey = *privkey;
}
void SetNull()
{
isUsed = false;
randomness = 0;
value = 0;
denomination = libzerocoin::ZQ_ERROR;
nHeight = 0;
txid = 0;
version = 1;
privkey.clear();
}
uint256 GetHash() const;
CBigNum GetValue() const { return value; }
void SetValue(CBigNum value){ this->value = value; }
libzerocoin::CoinDenomination GetDenomination() const { return denomination; }
int64_t GetDenominationAsAmount() const { return denomination * COIN; }
void SetDenomination(libzerocoin::CoinDenomination denom){ this->denomination = denom; }
int GetHeight() const { return nHeight; }
void SetHeight(int nHeight){ this->nHeight = nHeight; }
bool IsUsed() const { return this->isUsed; }
void SetUsed(bool isUsed){ this->isUsed = isUsed; }
CBigNum GetRandomness() const{ return randomness; }
void SetRandomness(CBigNum rand){ this->randomness = rand; }
CBigNum GetSerialNumber() const { return serialNumber; }
void SetSerialNumber(CBigNum serial){ this->serialNumber = serial; }
uint256 GetTxHash() const { return this->txid; }
void SetTxHash(uint256 txid) { this->txid = txid; }
uint8_t GetVersion() const { return this->version; }
void SetVersion(const uint8_t nVersion) { this->version = nVersion; }
CPrivKey GetPrivKey() const { return this->privkey; }
void SetPrivKey(const CPrivKey& privkey) { this->privkey = privkey; }
bool GetKeyPair(CKey& key) const;
int GetOutputIndex() { return this->outputIndex; }
void SetOutputIndex(int index) { this->outputIndex = index; }
inline bool operator <(const CZerocoinMint& a) const { return GetHeight() < a.GetHeight(); }
CZerocoinMint(const CZerocoinMint& other) {
denomination = other.GetDenomination();
nHeight = other.GetHeight();
value = other.GetValue();
randomness = other.GetRandomness();
serialNumber = other.GetSerialNumber();
txid = other.GetTxHash();
isUsed = other.IsUsed();
version = other.GetVersion();
privkey = other.privkey;
}
std::string ToString() const;
bool operator == (const CZerocoinMint& other) const
{
return this->GetValue() == other.GetValue();
}
// Copy another CZerocoinMint
inline CZerocoinMint& operator=(const CZerocoinMint& other) {
denomination = other.GetDenomination();
nHeight = other.GetHeight();
value = other.GetValue();
randomness = other.GetRandomness();
serialNumber = other.GetSerialNumber();
txid = other.GetTxHash();
isUsed = other.IsUsed();
version = other.GetVersion();
privkey = other.GetPrivKey();
return *this;
}
// why 6 below (SPOCK)
inline bool checkUnused(int denom, int Height) const {
if (IsUsed() == false && GetDenomination() == denomination && GetRandomness() != 0 && GetSerialNumber() != 0 && GetHeight() != -1 && GetHeight() != INT_MAX && GetHeight() >= 1 && (GetHeight() + 6 <= Height)) {
return true;
} else {
return false;
}
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(isUsed);
READWRITE(randomness);
READWRITE(serialNumber);
READWRITE(value);
READWRITE(denomination);
READWRITE(nHeight);
READWRITE(txid);
bool fVersionedMint = true;
try {
READWRITE(version);
} catch (...) {
fVersionedMint = false;
}
if (version > CURRENT_VERSION) {
version = 1;
fVersionedMint = false;
}
if (fVersionedMint)
READWRITE(privkey);
};
};
class CZerocoinSpend
{
private:
CBigNum coinSerial;
uint256 hashTx;
CBigNum pubCoin;
libzerocoin::CoinDenomination denomination;
unsigned int nAccumulatorChecksum;
int nMintCount; //memory only - the amount of mints that belong to the accumulator this is spent from
public:
CZerocoinSpend()
{
SetNull();
}
CZerocoinSpend(CBigNum coinSerial, uint256 hashTx, CBigNum pubCoin, libzerocoin::CoinDenomination denomination, unsigned int nAccumulatorChecksum)
{
this->coinSerial = coinSerial;
this->hashTx = hashTx;
this->pubCoin = pubCoin;
this->denomination = denomination;
this->nAccumulatorChecksum = nAccumulatorChecksum;
}
void SetNull()
{
coinSerial = 0;
hashTx = 0;
pubCoin = 0;
denomination = libzerocoin::ZQ_ERROR;
}
CBigNum GetSerial() const { return coinSerial; }
uint256 GetTxHash() const { return hashTx; }
void SetTxHash(uint256 hash) { this->hashTx = hash; }
CBigNum GetPubCoin() const { return pubCoin; }
libzerocoin::CoinDenomination GetDenomination() const { return denomination; }
unsigned int GetAccumulatorChecksum() const { return this->nAccumulatorChecksum; }
uint256 GetHash() const;
void SetMintCount(int nMintsAdded) { this->nMintCount = nMintsAdded; }
int GetMintCount() const { return nMintCount; }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(coinSerial);
READWRITE(hashTx);
READWRITE(pubCoin);
READWRITE(denomination);
READWRITE(nAccumulatorChecksum);
};
};
class CZerocoinSpendReceipt
{
private:
std::string strStatusMessage;
int nStatus;
int nNeededSpends;
std::vector<CZerocoinSpend> vSpends;
public:
void AddSpend(const CZerocoinSpend& spend);
std::vector<CZerocoinSpend> GetSpends();
void SetStatus(std::string strStatus, int nStatus, int nNeededSpends = 0);
std::string GetStatusMessage();
int GetStatus();
int GetNeededSpends();
};
/**
* Wrapped serials attack inflation, only for mainnet.
* FUTURE: Move this to another file..
* @param denom
* @return
*/
int GetWrapppedSerialInflation(libzerocoin::CoinDenomination denom);
int64_t GetWrapppedSerialInflationAmount();
#endif //Agrarian_ZEROCOIN_H