Files
agrarian/src/stakeinput.cpp
T
2022-02-03 23:45:47 -08:00

273 lines
8.3 KiB
C++

// 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;
}