r1
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
// 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 "zagr/accumulators.h"
|
||||
#include "chain.h"
|
||||
#include "zagr/deterministicmint.h"
|
||||
#include "main.h"
|
||||
#include "stakeinput.h"
|
||||
#include "wallet/wallet.h"
|
||||
|
||||
CZPivStake::CZPivStake(const libzerocoin::CoinSpend& spend)
|
||||
{
|
||||
this->nChecksum = spend.getAccumulatorChecksum();
|
||||
this->denom = spend.getDenomination();
|
||||
uint256 nSerial = spend.getCoinSerialNumber().getuint256();
|
||||
this->hashSerial = Hash(nSerial.begin(), nSerial.end());
|
||||
this->pindexFrom = nullptr;
|
||||
fMint = false;
|
||||
}
|
||||
|
||||
int CZPivStake::GetChecksumHeightFromMint()
|
||||
{
|
||||
int nHeightChecksum = chainActive.Height() - Params().Zerocoin_RequiredStakeDepth();
|
||||
|
||||
//Need to return the first occurance of this checksum in order for the validation process to identify a specific
|
||||
//block height
|
||||
uint32_t nChecksum = 0;
|
||||
nChecksum = ParseChecksum(chainActive[nHeightChecksum]->nAccumulatorCheckpoint, denom);
|
||||
return GetChecksumHeight(nChecksum, denom);
|
||||
}
|
||||
|
||||
int CZPivStake::GetChecksumHeightFromSpend()
|
||||
{
|
||||
return GetChecksumHeight(nChecksum, denom);
|
||||
}
|
||||
|
||||
uint32_t CZPivStake::GetChecksum()
|
||||
{
|
||||
return nChecksum;
|
||||
}
|
||||
|
||||
// The zAGR block index is the first appearance of the accumulator checksum that was used in the spend
|
||||
// note that this also means when staking that this checksum should be from a block that is beyond 60 minutes old and
|
||||
// 100 blocks deep.
|
||||
CBlockIndex* CZPivStake::GetIndexFrom()
|
||||
{
|
||||
if (pindexFrom)
|
||||
return pindexFrom;
|
||||
|
||||
int nHeightChecksum = 0;
|
||||
|
||||
if (fMint)
|
||||
nHeightChecksum = GetChecksumHeightFromMint();
|
||||
else
|
||||
nHeightChecksum = GetChecksumHeightFromSpend();
|
||||
|
||||
if (nHeightChecksum < Params().Zerocoin_StartHeight() || nHeightChecksum > chainActive.Height()) {
|
||||
pindexFrom = nullptr;
|
||||
} else {
|
||||
//note that this will be a nullptr if the height DNE
|
||||
pindexFrom = chainActive[nHeightChecksum];
|
||||
}
|
||||
|
||||
return pindexFrom;
|
||||
}
|
||||
|
||||
CAmount CZPivStake::GetValue()
|
||||
{
|
||||
return denom * COIN;
|
||||
}
|
||||
|
||||
//Use the first accumulator checkpoint that occurs 60 minutes after the block being staked from
|
||||
// In case of regtest, next accumulator of 60 blocks after the block being staked from
|
||||
bool CZPivStake::GetModifier(uint64_t& nStakeModifier)
|
||||
{
|
||||
CBlockIndex* pindex = GetIndexFrom();
|
||||
if (!pindex)
|
||||
return error("%s: failed to get index from", __func__);
|
||||
|
||||
if(Params().NetworkID() == CBaseChainParams::REGTEST) {
|
||||
// Stake modifier is fixed for now, move it to 60 blocks after this pindex in the future..
|
||||
nStakeModifier = pindexFrom->nStakeModifier;
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t nTimeBlockFrom = pindex->GetBlockTime();
|
||||
while (true) {
|
||||
if (pindex->GetBlockTime() - nTimeBlockFrom > 60 * 60) {
|
||||
nStakeModifier = pindex->nAccumulatorCheckpoint.Get64();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pindex->nHeight + 1 <= chainActive.Height())
|
||||
pindex = chainActive.Next(pindex);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CDataStream CZPivStake::GetUniqueness()
|
||||
{
|
||||
//The unique identifier for a zAGR is a hash of the serial
|
||||
CDataStream ss(SER_GETHASH, 0);
|
||||
ss << hashSerial;
|
||||
return ss;
|
||||
}
|
||||
|
||||
bool CZPivStake::CreateTxIn(CWallet* pwallet, CTxIn& txIn, uint256 hashTxOut)
|
||||
{
|
||||
CBlockIndex* pindexCheckpoint = GetIndexFrom();
|
||||
if (!pindexCheckpoint)
|
||||
return error("%s: failed to find checkpoint block index", __func__);
|
||||
|
||||
CZerocoinMint mint;
|
||||
if (!pwallet->GetMintFromStakeHash(hashSerial, mint))
|
||||
return error("%s: failed to fetch mint associated with serial hash %s", __func__, hashSerial.GetHex());
|
||||
|
||||
if (libzerocoin::ExtractVersionFromSerial(mint.GetSerialNumber()) < 2)
|
||||
return error("%s: serial extract is less than v2", __func__);
|
||||
|
||||
CZerocoinSpendReceipt receipt;
|
||||
if (!pwallet->MintToTxIn(mint, hashTxOut, txIn, receipt, libzerocoin::SpendType::STAKE, pindexCheckpoint))
|
||||
return error("%s\n", receipt.GetStatusMessage());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CZPivStake::CreateTxOuts(CWallet* pwallet, vector<CTxOut>& vout, CAmount nTotal)
|
||||
{
|
||||
//Create an output returning the zAGR that was staked
|
||||
CTxOut outReward;
|
||||
libzerocoin::CoinDenomination denomStaked = libzerocoin::AmountToZerocoinDenomination(this->GetValue());
|
||||
CDeterministicMint dMint;
|
||||
if (!pwallet->CreateZAGROutPut(denomStaked, outReward, dMint))
|
||||
return error("%s: failed to create zAGR output", __func__);
|
||||
vout.emplace_back(outReward);
|
||||
|
||||
//Add new staked denom to our wallet
|
||||
if (!pwallet->DatabaseMint(dMint))
|
||||
return error("%s: failed to database the staked zAGR", __func__);
|
||||
|
||||
for (unsigned int i = 0; i < 3; i++) {
|
||||
CTxOut out;
|
||||
CDeterministicMint dMintReward;
|
||||
if (!pwallet->CreateZAGROutPut(libzerocoin::CoinDenomination::ZQ_ONE, out, dMintReward))
|
||||
return error("%s: failed to create zAGR output", __func__);
|
||||
vout.emplace_back(out);
|
||||
|
||||
if (!pwallet->DatabaseMint(dMintReward))
|
||||
return error("%s: failed to database mint reward", __func__);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CZPivStake::GetTxFrom(CTransaction& tx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CZPivStake::MarkSpent(CWallet *pwallet, const uint256& txid)
|
||||
{
|
||||
CzAGRTracker* zagrTracker = pwallet->zagrTracker.get();
|
||||
CMintMeta meta;
|
||||
if (!zagrTracker->GetMetaFromStakeHash(hashSerial, meta))
|
||||
return error("%s: tracker does not have serialhash", __func__);
|
||||
|
||||
zagrTracker->SetPubcoinUsed(meta.hashPubcoin, txid);
|
||||
return true;
|
||||
}
|
||||
|
||||
//!AGR Stake
|
||||
bool CPivStake::SetInput(CTransaction txPrev, unsigned int n)
|
||||
{
|
||||
this->txFrom = txPrev;
|
||||
this->nPosition = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPivStake::GetTxFrom(CTransaction& tx)
|
||||
{
|
||||
tx = txFrom;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPivStake::CreateTxIn(CWallet* pwallet, CTxIn& txIn, uint256 hashTxOut)
|
||||
{
|
||||
txIn = CTxIn(txFrom.GetHash(), nPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
CAmount CPivStake::GetValue()
|
||||
{
|
||||
return txFrom.vout[nPosition].nValue;
|
||||
}
|
||||
|
||||
bool CPivStake::CreateTxOuts(CWallet* pwallet, vector<CTxOut>& vout, CAmount nTotal)
|
||||
{
|
||||
vector<valtype> vSolutions;
|
||||
txnouttype whichType;
|
||||
CScript scriptPubKeyKernel = txFrom.vout[nPosition].scriptPubKey;
|
||||
if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) {
|
||||
LogPrintf("CreateCoinStake : failed to parse kernel\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH)
|
||||
return false; // only support pay to public key and pay to address
|
||||
|
||||
CScript scriptPubKey;
|
||||
if (whichType == TX_PUBKEYHASH) // pay to address type
|
||||
{
|
||||
//convert to pay to public key type
|
||||
CKey key;
|
||||
CKeyID keyID = CKeyID(uint160(vSolutions[0]));
|
||||
if (!pwallet->GetKey(keyID, key))
|
||||
return false;
|
||||
|
||||
scriptPubKey << key.GetPubKey() << OP_CHECKSIG;
|
||||
} else
|
||||
scriptPubKey = scriptPubKeyKernel;
|
||||
|
||||
vout.emplace_back(CTxOut(0, scriptPubKey));
|
||||
|
||||
// Calculate if we need to split the output
|
||||
if (nTotal / 2 > (CAmount)(pwallet->nStakeSplitThreshold * COIN))
|
||||
vout.emplace_back(CTxOut(0, scriptPubKey));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPivStake::GetModifier(uint64_t& nStakeModifier)
|
||||
{
|
||||
int nStakeModifierHeight = 0;
|
||||
int64_t nStakeModifierTime = 0;
|
||||
GetIndexFrom();
|
||||
if (!pindexFrom)
|
||||
return error("%s: failed to get index from", __func__);
|
||||
|
||||
if (!GetKernelStakeModifier(pindexFrom->GetBlockHash(), nStakeModifier, nStakeModifierHeight, nStakeModifierTime, false))
|
||||
return error("CheckStakeKernelHash(): failed to get kernel stake modifier \n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CDataStream CPivStake::GetUniqueness()
|
||||
{
|
||||
//The unique identifier for a AGR stake is the outpoint
|
||||
CDataStream ss(SER_NETWORK, 0);
|
||||
ss << nPosition << txFrom.GetHash();
|
||||
return ss;
|
||||
}
|
||||
|
||||
//The block that the UTXO was added to the chain
|
||||
CBlockIndex* CPivStake::GetIndexFrom()
|
||||
{
|
||||
uint256 hashBlock = 0;
|
||||
CTransaction tx;
|
||||
if (GetTransaction(txFrom.GetHash(), tx, hashBlock, true)) {
|
||||
// If the index is in the chain, then set it as the "index from"
|
||||
if (mapBlockIndex.count(hashBlock)) {
|
||||
CBlockIndex* pindex = mapBlockIndex.at(hashBlock);
|
||||
if (chainActive.Contains(pindex))
|
||||
pindexFrom = pindex;
|
||||
}
|
||||
} else {
|
||||
LogPrintf("%s : failed to find tx %s\n", __func__, txFrom.GetHash().GetHex());
|
||||
}
|
||||
|
||||
return pindexFrom;
|
||||
}
|
||||
Reference in New Issue
Block a user