r1
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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(
|
||||
¶ms->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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user