Browse Source

Cheatcatcher

pull/4/head
Michael Toutonghi 6 years ago
parent
commit
df756d24ba
  1. 3
      src/cc/StakeGuard.cpp
  2. 25
      src/komodo_utils.h
  3. 162
      src/main.cpp
  4. 50
      src/main.h
  5. 123
      src/miner.cpp
  6. 20
      src/transaction_builder.cpp
  7. 7
      src/transaction_builder.h
  8. 1
      src/wallet/asyncrpcoperation_shieldcoinbase.h
  9. 2
      src/wallet/walletdb.cpp

3
src/cc/StakeGuard.cpp

@ -287,7 +287,7 @@ bool MakeCheatEvidence(CMutableTransaction &mtx, const CTransaction &ccTx, uint3
CCcontract_info *cp,C;
std::vector<unsigned char> vch;
CDataStream s = CDataStream(SER_DISK, CLIENT_VERSION);
bool isCheater;
bool isCheater = false;
if (ValidateMatchingStake(ccTx, voutNum, cheatTx, isCheater) && isCheater)
{
@ -301,6 +301,7 @@ bool MakeCheatEvidence(CMutableTransaction &mtx, const CTransaction &ccTx, uint3
vOut.nValue = 0;
mtx.vout.push_back(vOut);
}
return isCheater;
}
typedef struct ccFulfillmentCheck {

25
src/komodo_utils.h

@ -13,6 +13,7 @@
* *
******************************************************************************/
#include "komodo_defs.h"
#include "key_io.h"
#include <string.h>
#ifdef _WIN32
@ -1650,10 +1651,32 @@ void komodo_args(char *argv0)
extern const char *Notaries_elected1[][2];
std::string name,addn; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[256],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,baseid,len,n,extralen = 0;
IS_KOMODO_NOTARY = GetBoolArg("-notary", false);
if ( GetBoolArg("-gen", false) != 0 )
if ( GetBoolArg("-gen", false) != 0 )\
{
KOMODO_MININGTHREADS = GetArg("-genproclimit",-1);
if (KOMODO_MININGTHREADS == 0)
mapArgs["-gen"] = "0";
}
else KOMODO_MININGTHREADS = 0;
VERUS_MINTBLOCKS = GetBoolArg("-mint", false);
// if we are supposed to catch stake cheaters, there must be a valid sapling parameter, store the Sapling address here
extern boost::optional<libzcash::SaplingPaymentAddress> cheatCatcher;
if (mapArgs["-cheatcatcher"].size() == 77)
{
libzcash::PaymentAddress addr = DecodePaymentAddress(mapArgs["-cheatcatcher"]);
if (IsValidPaymentAddress(addr))
{
cheatCatcher = boost::get<libzcash::SaplingPaymentAddress>(addr);
}
else
{
fprintf(stderr, "-cheatcatcher parameter is invalid Sapling payment address");
}
}
if ( (KOMODO_EXCHANGEWALLET= GetBoolArg("-exchange", false)) != 0 )
fprintf(stderr,"KOMODO_EXCHANGEWALLET mode active\n");
DONATION_PUBKEY = GetArg("-donation", "");

162
src/main.cpp

@ -38,7 +38,9 @@
#include <algorithm>
#include <atomic>
#include <sstream>
#include <map>
#include <unordered_map>
#include <vector>
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
@ -120,6 +122,120 @@ CScript COINBASE_FLAGS;
const string strMessageMagic = "Komodo Signed Message:\n";
CCheatList cheatList;
boost::optional<libzcash::SaplingPaymentAddress> cheatCatcher;
uint32_t CCheatList::Prune(uint32_t height)
{
uint32_t count;
pair<std::multimap<const uint32_t, CTxHolder>::iterator, std::multimap<const uint32_t, CTxHolder>::iterator> range;
std::vector<CTxHolder *> toPrune;
if (NetworkUpgradeActive(height, Params().GetConsensus(), Consensus::UPGRADE_SAPLING))
{
LOCK(cs_cheat);
for (auto it = orderedCheatCandidates.begin(); it != orderedCheatCandidates.end() && it->second.height <= height; it--)
{
toPrune.push_back(&it->second);
}
count = toPrune.size();
for (auto ptxHolder : toPrune)
{
Remove(*ptxHolder);
}
}
return count; // return how many removed
}
bool GetStakeParams(const CTransaction &stakeTx, CStakeParams &stakeParams);
bool CCheatList::IsCheatInList(const CTransaction &tx, CTransaction *cheatTx)
{
// for a tx to be cheat, it needs to spend the same UTXO and be for a different prior block
// the list should be pruned before this call
// we return the first valid cheat we find
CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
hw << tx.vin[0].prevout.hash;
hw << tx.vin[0].prevout.n;
uint256 utxo = hw.GetHash();
pair<std::multimap<const uint256, CTxHolder *>::iterator, std::multimap<const uint256, CTxHolder *>::iterator> range;
CStakeParams p, s;
if (GetStakeParams(tx, p))
{
LOCK(cs_cheat);
range = indexedCheatCandidates.equal_range(utxo);
for (auto it = range.first; it != range.second; it++)
{
// we assume height is valid, as we should have pruned the list before checking. since the tx came out of a valid block,
// what matters is if the prior hash matches
CTransaction &cTx = it->second->tx;
// need both parameters to check
if (GetStakeParams(cTx, s))
{
if (p.prevHash != s.prevHash)
{
cheatTx = &cTx;
return true;
}
}
}
}
return false;
}
bool CCheatList::Add(CTxHolder &txh)
{
CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
if (NetworkUpgradeActive(txh.height, Params().GetConsensus(), Consensus::UPGRADE_SAPLING))
{
LOCK(cs_cheat);
auto it = orderedCheatCandidates.insert(pair<const uint32_t, CTxHolder>(txh.height, txh));
indexedCheatCandidates.insert(pair<const uint256, CTxHolder *>(txh.utxo, &it->second));
}
}
void CCheatList::Remove(const CTxHolder &txh)
{
// first narrow by source tx, then compare with tx hash
uint32_t count;
pair<std::multimap<const uint256, CTxHolder *>::iterator, std::multimap<const uint256, CTxHolder *>::iterator> range;
std::vector<std::multimap<const uint256, CTxHolder *>::iterator> utxoPrune;
std::vector<std::multimap<const int32_t, CTxHolder>::iterator> heightPrune;
{
LOCK(cs_cheat);
range = indexedCheatCandidates.equal_range(txh.utxo);
for (auto it = range.first; it != range.second; it++)
{
uint256 hash = txh.tx.GetHash();
if (hash == it->second->tx.GetHash())
{
utxoPrune.push_back(it);
auto hrange = orderedCheatCandidates.equal_range(it->second->height);
for (auto hit = hrange.first; hit != hrange.second; hit++)
{
if (hit->second.tx.GetHash() == hash)
{
heightPrune.push_back(hit);
}
}
}
}
for (auto it : utxoPrune)
{
indexedCheatCandidates.erase(it);
}
for (auto it : heightPrune)
{
orderedCheatCandidates.erase(it);
}
}
}
// Internal stuff
namespace {
@ -152,8 +268,10 @@ namespace {
* missing the data for the block.
*/
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
/** Number of nodes with fSyncStarted. */
int nSyncStarted = 0;
/** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions.
* Pruned nodes may have entries where B is missing data.
*/
@ -1527,7 +1645,14 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return false;
}
}
// if this is a stake transaction with a stake opreturn, reject it if not staking a block. don't check coinbase or actual stake tx
CStakeParams p;
if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING) && ValidateStakeTransaction(tx, p, false))
{
return error("AcceptToMemoryPool: attempt to add staking transaction that is not staking");
}
auto verifier = libzcash::ProofVerifier::Strict();
if ( komodo_validate_interest(tx,chainActive.LastTip()->GetHeight()+1,chainActive.LastTip()->GetMedianTimePast() + 777,0) < 0 )
{
@ -3701,6 +3826,14 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) {
{
mempool.remove(tx, removed, true);
}
// if this is a staking tx, and we are on Verus Sapling with nothing at stake solution,
// save staking tx as a possible cheat
if ((i == (block.vtx.size() - 1)) && (block.IsVerusPOSBlock()))
{
CTxHolder txh(block.vtx[i], pindexDelete->GetHeight());
cheatList.Add(txh);
}
}
if (sproutAnchorBeforeDisconnect != sproutAnchorAfterDisconnect) {
// The anchor may not change between block disconnects,
@ -4522,8 +4655,10 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C
}
//fprintf(stderr,"done putting block's tx into mempool\n");
}
BOOST_FOREACH(const CTransaction& tx, block.vtx)
for (uint32_t i = 0; i < block.vtx.size(); i++)
{
const CTransaction& tx = block.vtx[i];
if ( komodo_validate_interest(tx,height == 0 ? komodo_block2height((CBlock *)&block) : height,block.nTime,0) < 0 )
return error("CheckBlock: komodo_validate_interest failed");
if (!CheckTransaction(tx, state, verifier))
@ -4633,15 +4768,24 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->GetHeight() + 1;
const Consensus::Params& consensusParams = Params().GetConsensus();
bool sapling = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_SAPLING);
// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
for (uint32_t i = 0; i < block.vtx.size(); i++) {
const CTransaction& tx = block.vtx[i];
// Check transaction contextually against consensus rules at block height
if (!ContextualCheckTransaction(tx, state, nHeight, 100)) {
return false; // Failure reason has been set in validation state object
}
// if this is a stake transaction with a stake opreturn, reject it if not staking a block. don't check coinbase or actual stake tx
CStakeParams p;
if (sapling && i > 0 && i < (block.vtx.size() - 1) && ValidateStakeTransaction(tx, p, false))
{
return state.DoS(10, error("%s: attempt to submit block with staking transaction that is not staking", __func__), REJECT_INVALID, "bad-txns-staking");
}
int nLockTimeFlags = 0;
int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST)
? pindexPrev->GetMedianTimePast()
@ -4958,11 +5102,7 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo
}
// Store to disk
CBlockIndex *pindex = NULL;
//if ( 1 )
//{
// // without the komodo_ensure call, it is quite possible to get a non-error but null pindex returned from AcceptBlockHeader. In a 2 node network, it will be a long time before that block is reprocessed. Even though restarting makes it rescan, it seems much better to keep the nodes in sync
// komodo_ensure(pblock, hash);
//}
bool ret = AcceptBlock(&futureblock,*pblock, state, &pindex, fRequested, dbp);
if (pindex && pfrom) {
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
@ -4977,6 +5117,10 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo
return error("%s: ActivateBestChain failed", __func__);
//fprintf(stderr,"finished ProcessBlock %d\n",(int32_t)chainActive.LastTip()->GetHeight());
// when we succeed here, we prune all cheat candidates in the cheat list to 250 blocks ago, as they should be used or not
// useful by then
cheatList.Prune(height - 250);
return true;
}

50
src/main.h

@ -639,6 +639,56 @@ struct CDiskTxPos : public CDiskBlockPos
}
};
class CTxHolder
{
public:
uint256 utxo;
uint32_t height;
CTransaction tx;
CTxHolder(const CTransaction &_tx, uint32_t _height) : height(_height), tx(_tx) {
CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
hw << tx.vin[0].prevout.hash;
hw << tx.vin[0].prevout.n;
utxo = hw.GetHash();
}
};
class CCheatList
{
private:
std::multimap<const int32_t, CTxHolder> orderedCheatCandidates;
std::multimap<const uint256, CTxHolder *> indexedCheatCandidates;
CCriticalSection cs_cheat;
public:
CCheatList() {}
// prune all transactions in the list below height
uint32_t Prune(uint32_t height);
// check to see if a transaction that could be a cheat for the passed transaction is in our list
bool IsCheatInList(const CTransaction &tx, CTransaction *pcheatTx);
// check to see if there are cheat candidates of the same or greater block height in list
bool IsHeightOrGreaterInList(uint32_t height)
{
auto range = orderedCheatCandidates.equal_range(height);
return (range.first == orderedCheatCandidates.end());
}
// add a potential cheat transaction to the list. we do this for all stake transactions from orphaned stakes
bool Add(CTxHolder &txh);
// remove a transaction from the the list
void Remove(const CTxHolder &txh);
};
extern CCheatList cheatList;
extern boost::optional<libzcash::SaplingPaymentAddress> cheatCatcher;
CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);

123
src/miner.cpp

@ -35,6 +35,9 @@
#include "wallet/wallet.h"
#endif
#include "zcash/Address.hpp"
#include "transaction_builder.h"
#include "sodium.h"
#include <boost/thread.hpp>
@ -152,7 +155,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params();
//fprintf(stderr,"create new block\n");
// Create new block
// Create new block
if ( gpucount < 0 )
gpucount = KOMODO_MAXGPUCOUNT;
std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
@ -189,12 +192,20 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
// Collect memory pool transactions into the block
CAmount nFees = 0;
// we will attempt to spend any cheats we see
CTransaction cheatTx;
boost::optional<CTransaction> cheatSpend;
uint256 cbHash;
CBlockIndex* pindexPrev = 0;
{
LOCK2(cs_main, mempool.cs);
pindexPrev = chainActive.LastTip();
const int nHeight = pindexPrev->GetHeight() + 1;
uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus());
const Consensus::Params &consensusParams = chainparams.GetConsensus();
uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, consensusParams);
bool sapling = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_SAPLING);
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
uint32_t proposedTime = GetAdjustedTime();
@ -224,7 +235,78 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
// This vector will be sorted into a priority queue:
vector<TxPriority> vecPriority;
vecPriority.reserve(mempool.mapTx.size());
vecPriority.reserve(mempool.mapTx.size() + 1);
// check if we should add cheat transaction
CBlockIndex *ppast;
if (cheatCatcher &&
sapling && chainActive.Height() > 100 &&
(ppast = chainActive[nHeight - 100]) &&
ppast->IsVerusPOSBlock() &&
cheatList.IsHeightOrGreaterInList(nHeight))
{
// get the block and see if there is a cheat candidate for the stake tx
CBlock b;
if (!(fHavePruned && !(ppast->nStatus & BLOCK_HAVE_DATA) && ppast->nTx > 0) && ReadBlockFromDisk(b, ppast, 1))
{
CTransaction &stakeTx = b.vtx[b.vtx.size() - 1];
if (cheatList.IsCheatInList(stakeTx, &cheatTx))
{
// make and sign the cheat transaction to spend the coinbase to our address
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
// send to the same pub key as the destination of this block reward
if (MakeCheatEvidence(mtx, b.vtx[0], stakeTx.vin[0].prevout.n, cheatTx))
{
extern CWallet *pwalletMain;
LOCK(pwalletMain->cs_wallet);
TransactionBuilder tb = TransactionBuilder(consensusParams, nHeight, pwalletMain);
CTransaction cb = b.vtx[0];
cbHash = cb.GetHash();
bool hasInput = false;
for (uint32_t i = 0; i < cb.vout.size(); i++)
{
// add the spends with the cheat
if (cb.vout[0].nValue > 0)
{
tb.AddTransparentInput(COutPoint(cbHash,i), cb.vout[0].scriptPubKey, cb.vout[0].nValue);
hasInput = true;
}
}
if (hasInput)
{
// this is a send from a t-address to a sapling address, which we don't have an ovk for.
// Instead, generate a common one from the HD seed. This ensures the data is
// recoverable, at least for us, while keeping it logically separate from the ZIP 32
// Sapling key hierarchy, which the user might not be using.
uint256 ovk;
HDSeed seed;
if (pwalletMain->GetHDSeed(seed)) {
ovk = ovkForShieldingFromTaddr(seed);
tb.AddSaplingOutput(ovk, cheatCatcher.value, cb.vout[0].nValue);
tb.AddOpRet(mtx.vout[mtx.vout.size - 1].scriptPubKey);
cheatSpend = tb.Build();
if (cheatSpend)
{
cheatTx = boost::get<CTransaction>(cheatSpend);
unsigned int nTxSize = ::GetSerializeSize(cheatTx, SER_NETWORK, PROTOCOL_VERSION);
double dPriority = cheatTx.ComputePriority(dPriority, nTxSize);
CFeeRate feeRate(DEFAULT_TRANSACTION_MAXFEE, nTxSize);
vecPriority.push_back(TxPriority(dPriority, feeRate, &cheatTx));
}
}
}
}
}
}
}
// now add transations from the mem pool
for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
mi != mempool.mapTx.end(); ++mi)
{
@ -244,6 +326,21 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
//fprintf(stderr,"CreateNewBlock: komodo_validate_interest failure nHeight.%d nTime.%u vs locktime.%u\n",nHeight,(uint32_t)pblock->nTime,(uint32_t)tx.nLockTime);
continue;
}
if (cheatSpend)
{
bool skip = false;
for (const CTxIn &txin : tx.vin)
{
if (txin.prevout.hash == cbHash)
{
skip = true;
break;
}
}
if (skip)
continue;
}
COrphan* porphan = NULL;
double dPriority = 0;
CAmount nTotalIn = 0;
@ -316,7 +413,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
else
vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx())));
}
// Collect transactions into block
uint64_t nBlockSize = 1000;
uint64_t nBlockTx = 0;
@ -440,7 +537,6 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
int32_t stakeHeight = chainActive.Height() + 1;
bool extendedStake = (Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight <= stakeHeight);
//LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x\n", nBlockSize,blocktime,pblock->nBits);
if ( ASSETCHAINS_SYMBOL[0] != 0 && isStake )
@ -473,7 +569,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
// after Sapling, stake transactions have a fee, but it is recovered in the reward
// this ensures that a rebroadcast goes through quickly to begin staking again
txfees = extendedStake ? DEFAULT_STAKE_TXFEE : 0;
txfees = sapling ? DEFAULT_STAKE_TXFEE : 0;
pblock->vtx.push_back(txStaked);
pblocktemplate->vTxFees.push_back(txfees);
@ -485,17 +581,17 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
}
// Create coinbase tx
CMutableTransaction txNew = CreateNewContextualCMutableTransaction(chainparams.GetConsensus(), nHeight);
CMutableTransaction txNew = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
txNew.vout.resize(1);
txNew.vout[0].scriptPubKey = scriptPubKeyIn;
txNew.vout[0].nValue = GetBlockSubsidy(nHeight,chainparams.GetConsensus()) + nFees;
txNew.vout[0].nValue = GetBlockSubsidy(nHeight,consensusParams) + nFees;
// once we get to Sapling, enable CC StakeGuard for stake transactions
if (isStake && extendedStake)
if (isStake && sapling)
{
// if there is a specific destination, use it
CTransaction stakeTx = pblock->vtx[pblock->vtx.size() - 1];
@ -769,7 +865,6 @@ static bool ProcessBlockFound(CBlock* pblock)
// Found a solution
{
//LOCK(cs_main);
if (pblock->hashPrevBlock != chainActive.LastTip()->GetBlockHash())
{
uint256 hash; int32_t i;
@ -986,9 +1081,9 @@ void static VerusStaker(CWallet *pwallet)
uint256 hashTarget = ArithToUint256(arith_uint256().SetCompact(pblock->nBits));
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
UpdateTime(pblock, consensusParams, pindexPrev);
ProcessBlockFound(pblock, *pwallet, reservekey);
@ -1745,8 +1840,8 @@ void static BitcoinMiner()
/*if ( NOTARY_PUBKEY33[0] == 0 )
{
int32_t percPoS;
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks)
UpdateTime(pblock, consensusParams, pindexPrev);
if (consensusParams.fPowAllowMinDifficultyBlocks)
{
// Changing pblock->nTime can change work required on testnet:
HASHTarget.SetCompact(pblock->nBits);

20
src/transaction_builder.cpp

@ -79,6 +79,23 @@ bool TransactionBuilder::AddTransparentOutput(CTxDestination& to, CAmount value)
return true;
}
bool TransactionBuilder::AddOpRetLast()
{
CScript s;
if (opReturn)
{
s = opReturn.value;
}
CTxOut out(0, s);
mtx.vout.push_back(out);
return true;
}
void TransactionBuilder::AddOpRet(CScript &s)
{
opReturn.emplace(CScript(s));
}
void TransactionBuilder::SetFee(CAmount fee)
{
this->fee = fee;
@ -230,6 +247,9 @@ boost::optional<CTransaction> TransactionBuilder::Build()
mtx.vShieldedOutput.push_back(odesc);
}
// add op_return if there is one to add
AddOpRetLast();
//
// Signatures
//

7
src/transaction_builder.h

@ -67,6 +67,9 @@ private:
boost::optional<std::pair<uint256, libzcash::SaplingPaymentAddress>> zChangeAddr;
boost::optional<CTxDestination> tChangeAddr;
boost::optional<CScript> opReturn;
bool AddOpRetLast(CScript &s);
public:
TransactionBuilder() {}
@ -93,6 +96,10 @@ public:
bool AddTransparentOutput(CTxDestination& to, CAmount value);
void AddOpRet(CScript &s);
bool AddOpRetLast();
void SendChangeTo(libzcash::SaplingPaymentAddress changeAddr, uint256 ovk);
bool SendChangeTo(CTxDestination& changeAddr);

1
src/wallet/asyncrpcoperation_shieldcoinbase.h

@ -63,6 +63,7 @@ public:
virtual UniValue getStatus() const;
bool testmode = false; // Set to true to disable sending txs and generating proofs
bool cheatSpend = false; // set when this is shielding a cheating coinbase
bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database.

2
src/wallet/walletdb.cpp

@ -967,7 +967,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
if (wss.fAnyUnordered)
result = ReorderTransactions(pwallet);
return result;
}

Loading…
Cancel
Save