From 525c049316a8abb105363fb281e3a673b80c0d6a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 5 Dec 2016 08:01:20 +0100 Subject: [PATCH] Merge #8580: Make CTransaction actually immutable 81e3228 Make CTransaction actually immutable (Pieter Wuille) 42fd8de Make DecodeHexTx return a CMutableTransaction (Pieter Wuille) c3f5673 Make CWalletTx store a CTransactionRef instead of inheriting (Pieter Wuille) a188353 Switch GetTransaction to returning a CTransactionRef (Pieter Wuille) --- src/bench/coin_selection.cpp | 2 +- src/core_io.h | 3 +- src/core_read.cpp | 2 +- src/dash-tx.cpp | 6 +- src/governance-object.cpp | 16 +- src/instantx.cpp | 2 +- src/net_processing.cpp | 2 +- src/primitives/transaction.cpp | 26 +--- src/primitives/transaction.h | 34 ++--- src/privatesend-client.cpp | 6 +- src/privatesend-server.cpp | 4 +- src/qt/coincontroldialog.cpp | 18 +-- src/qt/transactiondesc.cpp | 30 ++-- src/qt/transactionrecord.cpp | 26 ++-- src/qt/walletmodel.cpp | 16 +- src/qt/walletmodeltransaction.cpp | 4 +- src/rest.cpp | 4 +- src/rpc/rawtransaction.cpp | 19 +-- src/script/dashconsensus.cpp | 3 +- src/test/bloom_tests.cpp | 6 +- src/test/script_tests.cpp | 14 +- src/test/serialize_tests.cpp | 6 +- src/test/sighash_tests.cpp | 6 +- src/test/transaction_tests.cpp | 6 +- src/validation.cpp | 11 +- src/validation.h | 2 +- src/wallet/rpcdump.cpp | 6 +- src/wallet/rpcwallet.cpp | 27 ++-- src/wallet/test/accounting_tests.cpp | 4 +- src/wallet/test/wallet_tests.cpp | 2 +- src/wallet/wallet.cpp | 220 +++++++++++++-------------- src/wallet/wallet.h | 35 +++-- 32 files changed, 275 insertions(+), 293 deletions(-) diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 7091ee3e1..32690fe48 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -19,7 +19,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, vectorToString()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } - if(txCollateral.vout.size() < 1) { - strError = strprintf("tx vout size less than 1 | %d", txCollateral.vout.size()); + if(txCollateral->vout.size() < 1) { + strError = strprintf("tx vout size less than 1 | %d", txCollateral->vout.size()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } @@ -532,7 +532,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC CScript findScript; findScript << OP_RETURN << ToByteVector(nExpectedHash); - DBG( cout << "IsCollateralValid: txCollateral.vout.size() = " << txCollateral.vout.size() << endl; ); + DBG( cout << "IsCollateralValid: txCollateral->vout.size() = " << txCollateral->vout.size() << endl; ); DBG( cout << "IsCollateralValid: findScript = " << ScriptToAsmStr( findScript, false ) << endl; ); @@ -540,13 +540,13 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC bool foundOpReturn = false; - BOOST_FOREACH(const CTxOut o, txCollateral.vout) { + BOOST_FOREACH(const CTxOut o, txCollateral->vout) { DBG( cout << "IsCollateralValid txout : " << o.ToString() << ", o.nValue = " << o.nValue << ", o.scriptPubKey = " << ScriptToAsmStr( o.scriptPubKey, false ) << endl; ); if(!o.scriptPubKey.IsPayToPublicKeyHash() && !o.scriptPubKey.IsUnspendable()) { - strError = strprintf("Invalid Script %s", txCollateral.ToString()); + strError = strprintf("Invalid Script %s", txCollateral->ToString()); LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } @@ -561,7 +561,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC } if(!foundOpReturn){ - strError = strprintf("Couldn't find opReturn %s in %s", nExpectedHash.ToString(), txCollateral.ToString()); + strError = strprintf("Couldn't find opReturn %s in %s", nExpectedHash.ToString(), txCollateral->ToString()); LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } diff --git a/src/instantx.cpp b/src/instantx.cpp index 4bed1da59..a2144e6ab 100644 --- a/src/instantx.cpp +++ b/src/instantx.cpp @@ -608,7 +608,7 @@ bool CInstantSend::ResolveConflicts(const CTxLockCandidate& txLockCandidate) } } // FOREACH // No conflicts were found so far, check to see if it was already included in block - CTransaction txTmp; + CTransactionRef txTmp; uint256 hashBlock; if(GetTransaction(txHash, txTmp, Params().GetConsensus(), hashBlock, true) && hashBlock != uint256()) { LogPrint("instantsend", "CInstantSend::ResolveConflicts -- Done, %s is included in block %s\n", txHash.ToString(), hashBlock.ToString()); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 92513f77c..4ee9e69ac 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1611,7 +1611,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, deque vWorkQueue; vector vEraseQueue; - CTransaction tx; + CMutableTransaction tx; CTxLockRequest txLockRequest; CDarksendBroadcastTx dstx; int nInvType = MSG_TX; diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 77861527c..f2afae047 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -84,29 +84,15 @@ std::string CMutableTransaction::ToString() const return str; } -void CTransaction::UpdateHash() const +uint256 CTransaction::ComputeHash() const { - *const_cast(&hash) = SerializeHash(*this); + return SerializeHash(*this); } -CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { } - -CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) { - UpdateHash(); -} - -CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime) { - UpdateHash(); -} - -CTransaction& CTransaction::operator=(const CTransaction &tx) { - *const_cast(&nVersion) = tx.nVersion; - *const_cast*>(&vin) = tx.vin; - *const_cast*>(&vout) = tx.vout; - *const_cast(&nLockTime) = tx.nLockTime; - *const_cast(&hash) = tx.hash; - return *this; -} +/* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ +CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), hash() {} +CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), hash(ComputeHash()) {} +CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), hash(ComputeHash()) {} CAmount CTransaction::GetValueOut() const { diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index e88ace319..397e38057 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -207,11 +207,6 @@ struct CMutableTransaction; */ class CTransaction { -private: - /** Memory only. */ - const uint256 hash; - void UpdateHash() const; - public: // Default transaction version. static const int32_t CURRENT_VERSION=1; @@ -232,6 +227,13 @@ public: const std::vector vout; const uint32_t nLockTime; +private: + /** Memory only. */ + const uint256 hash; + + uint256 ComputeHash() const; + +public: /** Construct a CTransaction that qualifies as IsNull() */ CTransaction(); @@ -239,20 +241,16 @@ public: CTransaction(const CMutableTransaction &tx); CTransaction(CMutableTransaction &&tx); - CTransaction& operator=(const CTransaction& tx); - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*const_cast(&this->nVersion)); - READWRITE(*const_cast*>(&vin)); - READWRITE(*const_cast*>(&vout)); - READWRITE(*const_cast(&nLockTime)); - if (ser_action.ForRead()) - UpdateHash(); + template + inline void Serialize(Stream& s) const { + s << this->nVersion; + s << vin; + s << vout; + s << nLockTime; } + /** This deserializing constructor is provided instead of an Unserialize method. + * Unserialize is not possible, since it would require overwriting const fields. */ template CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {} @@ -274,7 +272,7 @@ public: // Compute modified tx size for priority calculation (optionally given tx size) unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const; - + /** * Get the total transaction size in bytes, including witness data. * "Total Size" defined in BIP141 and BIP144. diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp index 33b6f415d..f0a2c05cf 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -155,8 +155,8 @@ void CPrivateSendClient::ProcessMessage(CNode* pfrom, std::string& strCommand, C } int nMsgSessionID; - CTransaction txNew; - vRecv >> nMsgSessionID >> txNew; + vRecv >> nMsgSessionID; + CTransaction txNew(deserialize, vRecv); if(nSessionID != nMsgSessionID) { LogPrint("privatesend", "DSFINALTX -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d\n", nSessionID, nMsgSessionID); @@ -1091,7 +1091,7 @@ bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std:: std::vector::iterator it2 = vCoins.begin(); while (it2 != vCoins.end()) { // we have matching inputs - if ((*it2).tx->vout[(*it2).i].nValue == nValueDenom) { + if ((*it2).tx->tx->vout[(*it2).i].nValue == nValueDenom) { // add new input in resulting vector vecTxDSInRet.push_back(*it); // remove corresponting items from initial vectors diff --git a/src/privatesend-server.cpp b/src/privatesend-server.cpp index 1a222deae..52a379669 100644 --- a/src/privatesend-server.cpp +++ b/src/privatesend-server.cpp @@ -39,8 +39,8 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, std::string& strCommand, C } int nDenom; - CTransaction txCollateral; - vRecv >> nDenom >> txCollateral; + vRecv >> nDenom; + CTransaction txCollateral(deserialize, vRecv); LogPrint("privatesend", "DSACCEPT -- nDenom %d (%s) txCollateral %s", nDenom, CPrivateSend::GetDenominationsToString(nDenom), txCollateral.ToString()); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index f71f152fd..3e271955a 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -524,14 +524,14 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nQuantity++; // Amount - nAmount += out.tx->vout[out.i].nValue; + nAmount += out.tx->tx->vout[out.i].nValue; // Priority - dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); + dPriorityInputs += (double)out.tx->tx->vout[out.i].nValue * (out.nDepth+1); // Bytes CTxDestination address; - if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address)) { CPubKey pubkey; CKeyID *keyid = boost::get(&address); @@ -731,7 +731,7 @@ void CoinControlDialog::updateView() CAmount nSum = 0; int nChildren = 0; BOOST_FOREACH(const COutput& out, coins.second) { - nSum += out.tx->vout[out.i].nValue; + nSum += out.tx->tx->vout[out.i].nValue; nChildren++; CCoinControlWidgetItem *itemOutput; @@ -743,7 +743,7 @@ void CoinControlDialog::updateView() // address CTxDestination outputAddress; QString sAddress = ""; - if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress)) + if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress)) { sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString()); @@ -770,9 +770,9 @@ void CoinControlDialog::updateView() } // amount - itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue)); - itemOutput->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue)); - itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->vout[out.i].nValue)); // padding so that sorting works correctly + itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue)); + itemOutput->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue)); + itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue)); // padding so that sorting works correctly // date itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime())); @@ -781,7 +781,7 @@ void CoinControlDialog::updateView() // PrivateSend rounds - COutPoint outpoint = COutPoint(out.tx->GetHash(), out.i); + COutPoint outpoint = COutPoint(out.tx->tx->GetHash(), out.i); int nRounds = pwalletMain->GetOutpointPrivateSendRounds(outpoint); if (nRounds >= 0 || fDebug) itemOutput->setText(COLUMN_PRIVATESEND_ROUNDS, QString::number(nRounds)); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index edbc3c7d0..f6069f24c 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -29,10 +29,10 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) AssertLockHeld(cs_main); if (!CheckFinalTx(wtx)) { - if (wtx.nLockTime < LOCKTIME_THRESHOLD) - return tr("Open for %n more block(s)", "", wtx.nLockTime - chainActive.Height()); + if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) + return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - chainActive.Height()); else - return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime)); + return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime)); } else { @@ -157,7 +157,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // Coinbase // CAmount nUnmatured = 0; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) nUnmatured += wallet->GetCredit(txout, ISMINE_ALL); strHTML += "" + tr("Credit") + ": "; if (wtx.IsInMainChain()) @@ -176,14 +176,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco else { isminetype fAllFromMe = ISMINE_SPENDABLE; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { isminetype mine = wallet->IsMine(txin); if(fAllFromMe > mine) fAllFromMe = mine; } isminetype fAllToMe = ISMINE_SPENDABLE; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) { isminetype mine = wallet->IsMine(txout); if(fAllToMe > mine) fAllToMe = mine; @@ -197,7 +197,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // // Debit // - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) { // Ignore change isminetype toSelf = wallet->IsMine(txout); @@ -236,7 +236,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "" + tr("Total credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "
"; } - CAmount nTxFee = nDebit - wtx.GetValueOut(); + CAmount nTxFee = nDebit - wtx.tx->GetValueOut(); if (nTxFee > 0) strHTML += "" + tr("Transaction fee") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "
"; } @@ -245,10 +245,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // // Mixed debit transaction // - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) if (wallet->IsMine(txin)) strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "
"; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) if (wallet->IsMine(txout)) strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
"; } @@ -266,7 +266,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "" + tr("Transaction ID") + ": " + rec->getTxID() + "
"; strHTML += "" + tr("Output index") + ": " + QString::number(rec->getOutputIndex()) + "
"; - strHTML += "" + tr("Transaction total size") + ": " + QString::number(wtx.GetTotalSize()) + " bytes
"; + strHTML += "" + tr("Transaction total size") + ": " + QString::number(wtx.tx->GetTotalSize()) + " bytes
"; // Message from normal dash:URI (dash:XyZ...?message=example) Q_FOREACH (const PAIRTYPE(std::string, std::string)& r, wtx.vOrderForm) @@ -300,20 +300,20 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (fDebug) { strHTML += "

" + tr("Debug information") + "

"; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) if(wallet->IsMine(txin)) strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "
"; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) if(wallet->IsMine(txout)) strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
"; strHTML += "
" + tr("Transaction") + ":
"; - strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); + strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true); strHTML += "
" + tr("Inputs") + ":"; strHTML += "
    "; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { COutPoint prevout = txin.prevout; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 54711d82e..de942e0a6 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -51,7 +51,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * // // Credit // - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) { isminetype mine = wallet->IsMine(txout); if(mine) @@ -89,7 +89,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * int nFromMe = 0; bool involvesWatchAddress = false; isminetype fAllFromMe = ISMINE_SPENDABLE; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { if(wallet->IsMine(txin)) { fAllFromMeDenom = fAllFromMeDenom && wallet->IsDenominated(txin.prevout); @@ -103,7 +103,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * isminetype fAllToMe = ISMINE_SPENDABLE; bool fAllToMeDenom = true; int nToMe = 0; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) { + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) { if(wallet->IsMine(txout)) { fAllToMeDenom = fAllToMeDenom && CPrivateSend::IsDenominatedAmount(txout.nValue); nToMe++; @@ -132,7 +132,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * { sub.type = TransactionRecord::PrivateSend; CTxDestination address; - if (ExtractDestination(wtx.vout[0].scriptPubKey, address)) + if (ExtractDestination(wtx.tx->vout[0].scriptPubKey, address)) { // Sent to Dash Address sub.address = CBitcoinAddress(address).ToString(); @@ -145,14 +145,14 @@ QList TransactionRecord::decomposeTransaction(const CWallet * } else { - for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) + for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++) { - const CTxOut& txout = wtx.vout[nOut]; + const CTxOut& txout = wtx.tx->vout[nOut]; sub.idx = parts.size(); if(CPrivateSend::IsCollateralAmount(txout.nValue)) sub.type = TransactionRecord::PrivateSendMakeCollaterals; if(CPrivateSend::IsDenominatedAmount(txout.nValue)) sub.type = TransactionRecord::PrivateSendCreateDenominations; - if(nDebit - wtx.GetValueOut() == CPrivateSend::GetCollateralAmount()) sub.type = TransactionRecord::PrivateSendCollateralPayment; + if(nDebit - wtx.tx->GetValueOut() == CPrivateSend::GetCollateralAmount()) sub.type = TransactionRecord::PrivateSendCollateralPayment; } } @@ -168,11 +168,11 @@ QList TransactionRecord::decomposeTransaction(const CWallet * // // Debit // - CAmount nTxFee = nDebit - wtx.GetValueOut(); + CAmount nTxFee = nDebit - wtx.tx->GetValueOut(); - for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) + for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++) { - const CTxOut& txout = wtx.vout[nOut]; + const CTxOut& txout = wtx.tx->vout[nOut]; TransactionRecord sub(hash, nTime); sub.idx = parts.size(); sub.involvesWatchAddress = involvesWatchAddress; @@ -252,15 +252,15 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) if (!CheckFinalTx(wtx)) { - if (wtx.nLockTime < LOCKTIME_THRESHOLD) + if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) { status.status = TransactionStatus::OpenUntilBlock; - status.open_for = wtx.nLockTime - chainActive.Height(); + status.open_for = wtx.tx->nLockTime - chainActive.Height(); } else { status.status = TransactionStatus::OpenUntilDate; - status.open_for = wtx.nLockTime; + status.open_for = wtx.tx->nLockTime; } } // For generated transactions, determine maturity diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index ec8482f6c..0dbe5c724 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -80,7 +80,7 @@ CAmount WalletModel::getBalance(const CCoinControl *coinControl) const wallet->AvailableCoins(vCoins, true, coinControl); BOOST_FOREACH(const COutput& out, vCoins) if(out.fSpendable) - nBalance += out.tx->vout[out.i].nValue; + nBalance += out.tx->tx->vout[out.i].nValue; return nBalance; } @@ -319,12 +319,12 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact transaction.reassignAmounts(nChangePosRet); if(recipients[0].fUseInstantSend) { - if(newTx->GetValueOut() > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) { + if(newTx->tx->GetValueOut() > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) { Q_EMIT message(tr("Send Coins"), tr("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH.").arg(sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } - if(newTx->vin.size() > CTxLockRequest::WARN_MANY_INPUTS) { + if(newTx->tx->vin.size() > CTxLockRequest::WARN_MANY_INPUTS) { Q_EMIT message(tr("Send Coins"), tr("Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge.").arg(CTxLockRequest::WARN_MANY_INPUTS), CClientUIInterface::MSG_WARNING); } @@ -682,7 +682,7 @@ void WalletModel::listCoins(std::map >& mapCoins) int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); - if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == ISMINE_SPENDABLE) + if (outpoint.n < out.tx->tx->vout.size() && wallet->IsMine(out.tx->tx->vout[outpoint.n]) == ISMINE_SPENDABLE) vCoins.push_back(out); } @@ -690,14 +690,14 @@ void WalletModel::listCoins(std::map >& mapCoins) { COutput cout = out; - while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) + while (wallet->IsChange(cout.tx->tx->vout[cout.i]) && cout.tx->tx->vin.size() > 0 && wallet->IsMine(cout.tx->tx->vin[0])) { - if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; - cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true, true); + if (!wallet->mapWallet.count(cout.tx->tx->vin[0].prevout.hash)) break; + cout = COutput(&wallet->mapWallet[cout.tx->tx->vin[0].prevout.hash], cout.tx->tx->vin[0].prevout.n, 0, true, true); } CTxDestination address; - if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) + if(!out.fSpendable || !ExtractDestination(cout.tx->tx->vout[cout.i].scriptPubKey, address)) continue; mapCoins[QString::fromStdString(CBitcoinAddress(address).ToString())].push_back(out); } diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 2db2ff331..a3465db3b 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -63,7 +63,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) if (out.amount() <= 0) continue; if (i == nChangePosRet) i++; - subtotal += walletTransaction->vout[i].nValue; + subtotal += walletTransaction->tx->vout[i].nValue; i++; } rcp.amount = subtotal; @@ -72,7 +72,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) { if (i == nChangePosRet) i++; - rcp.amount = walletTransaction->vout[i].nValue; + rcp.amount = walletTransaction->tx->vout[i].nValue; i++; } } diff --git a/src/rest.cpp b/src/rest.cpp index de3746138..91c7b488c 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -366,7 +366,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) if (!ParseHashStr(hashStr, hash)) return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); - CTransaction tx; + CTransactionRef tx; uint256 hashBlock = uint256(); if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); @@ -391,7 +391,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) case RF_JSON: { UniValue objTx(UniValue::VOBJ); - TxToJSON(tx, hashBlock, objTx); + TxToJSON(*tx, hashBlock, objTx); string strJSON = objTx.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 31543fe14..4a9819174 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -230,19 +230,19 @@ UniValue getrawtransaction(const JSONRPCRequest& request) } } - CTransaction tx; + CTransactionRef tx; uint256 hashBlock; if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); - string strHex = EncodeHexTx(tx); + string strHex = EncodeHexTx(*tx); if (!fVerbose) return strHex; UniValue result(UniValue::VOBJ); result.push_back(Pair("hex", strHex)); - TxToJSON(tx, hashBlock, result); + TxToJSON(*tx, hashBlock, result); return result; } @@ -307,7 +307,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request) if (pblockindex == NULL) { - CTransaction tx; + CTransactionRef tx; if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); if (!mapBlockIndex.count(hashBlock)) @@ -539,13 +539,13 @@ UniValue decoderawtransaction(const JSONRPCRequest& request) LOCK(cs_main); RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); - CTransaction tx; + CMutableTransaction mtx; - if (!DecodeHexTx(tx, request.params[0].get_str())) + if (!DecodeHexTx(mtx, request.params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); UniValue result(UniValue::VOBJ); - TxToJSON(tx, uint256(), result); + TxToJSON(CTransaction(std::move(mtx)), uint256(), result); return result; } @@ -896,9 +896,10 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)(UniValue::VBOOL)); // parse hex string from parameter - CTransaction tx; - if (!DecodeHexTx(tx, request.params[0].get_str())) + CMutableTransaction mtx; + if (!DecodeHexTx(mtx, request.params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + CTransaction tx(std::move(mtx)); uint256 hashTx = tx.GetHash(); bool fLimitFree = false; diff --git a/src/script/dashconsensus.cpp b/src/script/dashconsensus.cpp index 2a342d290..f9fab29b7 100644 --- a/src/script/dashconsensus.cpp +++ b/src/script/dashconsensus.cpp @@ -76,8 +76,7 @@ int dashconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int { try { TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); - CTransaction tx; - stream >> tx; + CTransaction tx(deserialize, stream); if (nIn >= tx.vin.size()) return set_error(err, dashconsensus_ERR_TX_INDEX); if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 3f2f53ea0..016833601 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -114,16 +114,14 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_key) BOOST_AUTO_TEST_CASE(bloom_match) { // Random real transaction (b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b) - CTransaction tx; CDataStream stream(ParseHex("01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000"), SER_DISK, CLIENT_VERSION); - stream >> tx; + CTransaction tx(deserialize, stream); // and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436) unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00}; vector vch(ch, ch + sizeof(ch) -1); CDataStream spendStream(vch, SER_DISK, CLIENT_VERSION); - CTransaction spendingTx; - spendStream >> spendingTx; + CTransaction spendingTx(deserialize, spendStream); CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(uint256S("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b")); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 439cdbc96..0a6ffac39 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -240,7 +240,7 @@ class TestBuilder { private: CScript scriptPubKey; - CTransaction creditTx; + CTransactionRef creditTx; CMutableTransaction spendTx; bool havePush; std::vector push; @@ -267,11 +267,11 @@ public: TestBuilder(const CScript& script_, const std::string& comment_, int flags_, bool P2SH = false) : scriptPubKey(script_), havePush(false), comment(comment_), flags(flags_), scriptError(SCRIPT_ERR_OK) { if (P2SH) { - creditTx = BuildCreditingTransaction(CScript() << OP_HASH160 << ToByteVector(CScriptID(script_)) << OP_EQUAL); + creditTx = MakeTransactionRef(BuildCreditingTransaction(CScript() << OP_HASH160 << ToByteVector(CScriptID(script_)) << OP_EQUAL)); } else { - creditTx = BuildCreditingTransaction(script_); + creditTx = MakeTransactionRef(BuildCreditingTransaction(script_)); } - spendTx = BuildSpendingTransaction(CScript(), creditTx); + spendTx = BuildSpendingTransaction(CScript(), *creditTx); } TestBuilder& ScriptError(ScriptError_t err) @@ -359,7 +359,7 @@ public: { TestBuilder copy = *this; // Make a copy so we can rollback the push. DoPush(); - DoTest(creditTx.vout[0].scriptPubKey, spendTx.vin[0].scriptSig, flags, comment, scriptError); + DoTest(creditTx->vout[0].scriptPubKey, spendTx.vin[0].scriptSig, flags, comment, scriptError); *this = copy; return *this; } @@ -369,7 +369,7 @@ public: DoPush(); UniValue array(UniValue::VARR); array.push_back(FormatScript(spendTx.vin[0].scriptSig)); - array.push_back(FormatScript(creditTx.vout[0].scriptPubKey)); + array.push_back(FormatScript(creditTx->vout[0].scriptPubKey)); array.push_back(FormatScriptFlags(flags)); array.push_back(FormatScriptError((ScriptError_t)scriptError)); array.push_back(comment); @@ -383,7 +383,7 @@ public: const CScript& GetScriptPubKey() { - return creditTx.vout[0].scriptPubKey; + return creditTx->vout[0].scriptPubKey; } }; diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 3d3d2e1e3..91a2c0d08 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -21,10 +21,10 @@ protected: bool boolval; std::string stringval; const char* charstrval; - CTransaction txval; + CTransactionRef txval; public: CSerializeMethodsTestSingle() = default; - CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, CTransaction txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), charstrval(charstrvalin), txval(txvalin){} + CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, CTransaction txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), charstrval(charstrvalin), txval(MakeTransactionRef(txvalin)){} ADD_SERIALIZE_METHODS; template @@ -42,7 +42,7 @@ public: boolval == rhs.boolval && \ stringval == rhs.stringval && \ strcmp(charstrval, rhs.charstrval) == 0 && \ - txval == rhs.txval; + *txval == *rhs.txval; } }; diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 703d13ffb..c98ed5775 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) std::string raw_tx, raw_script, sigHashHex; int nIn, nHashType; uint256 sh; - CTransaction tx; + CTransactionRef tx; CScript scriptCode = CScript(); try { @@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest); + BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest); BOOST_CHECK(state.IsValid()); std::vector raw = ParseHex(raw_script); @@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) continue; } - sh = SignatureHash(scriptCode, tx, nIn, nHashType); + sh = SignatureHash(scriptCode, *tx, nIn, nHashType); BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest); } } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index c8a480ac0..9c124967d 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -135,8 +135,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) string transaction = test[1].get_str(); CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION); - CTransaction tx; - stream >> tx; + CTransaction tx(deserialize, stream); CValidationState state; BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest); @@ -210,8 +209,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) string transaction = test[1].get_str(); CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION); - CTransaction tx; - stream >> tx; + CTransaction tx(deserialize, stream); CValidationState state; fValid = CheckTransaction(tx, state) && state.IsValid(); diff --git a/src/validation.cpp b/src/validation.cpp index 8f0ebefe5..770a62eba 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1096,7 +1096,7 @@ bool GetAddressUnspent(uint160 addressHash, int type, } /** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */ -bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow) +bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow) { CBlockIndex *pindexSlow = NULL; @@ -1105,7 +1105,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P CTransactionRef ptx = mempool.get(hash); if (ptx) { - txOut = *ptx; + txOut = ptx; return true; } @@ -1124,7 +1124,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } hashBlock = header.GetHash(); - if (txOut.GetHash() != hash) + if (txOut->GetHash() != hash) return error("%s: txid mismatch", __func__); return true; } @@ -1143,7 +1143,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) { for (const auto& tx : block.vtx) { if (tx->GetHash() == hash) { - txOut = *tx; + txOut = tx; hashBlock = pindexSlow->GetBlockHash(); return true; } @@ -4505,10 +4505,9 @@ bool LoadMempool(void) file >> num; double prioritydummy = 0; while (num--) { - CTransaction tx; int64_t nTime; int64_t nFeeDelta; - file >> tx; + CTransaction tx(deserialize, file); file >> nTime; file >> nFeeDelta; diff --git a/src/validation.h b/src/validation.h index c0d7e1f0f..10298b673 100644 --- a/src/validation.h +++ b/src/validation.h @@ -285,7 +285,7 @@ bool IsInitialBlockDownload(); */ std::string GetWarnings(const std::string& strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ -bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false); +bool GetTransaction(const uint256 &hash, CTransactionRef &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false); /** Find the best known block, and make it the tip of the block chain */ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 79c039eca..773fbc424 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -268,11 +268,11 @@ UniValue importprunedfunds(const JSONRPCRequest& request) "2. \"txoutproof\" (string, required) The hex output from gettxoutproof that contains the transaction\n" ); - CTransaction tx; + CMutableTransaction tx; if (!DecodeHexTx(tx, request.params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); uint256 hashTx = tx.GetHash(); - CWalletTx wtx(pwalletMain,tx); + CWalletTx wtx(pwalletMain, MakeTransactionRef(std::move(tx))); CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION); CMerkleBlock merkleBlock; @@ -305,7 +305,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request) LOCK2(cs_main, pwalletMain->cs_wallet); - if (pwalletMain->IsMine(tx)) { + if (pwalletMain->IsMine(wtx)) { pwalletMain->AddToWallet(wtx, false); return NullUniValue; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index cf00ee0f1..18e09403d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -658,10 +658,10 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) + if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) continue; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) if (txout.scriptPubKey == scriptPubKey) if (wtx.GetDepthInMainChain(fAddLockConf) >= nMinDepth) nAmount += txout.nValue; @@ -714,10 +714,10 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) + if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) continue; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) { CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) @@ -1153,14 +1153,14 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) + if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) continue; int nDepth = wtx.GetDepthInMainChain(fAddLockConf); if (nDepth < nMinDepth) continue; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) { CTxDestination address; if (!ExtractDestination(txout.scriptPubKey, address)) @@ -1800,7 +1800,7 @@ UniValue gettransaction(const JSONRPCRequest& request) CAmount nCredit = wtx.GetCredit(filter); CAmount nDebit = wtx.GetDebit(filter); CAmount nNet = nCredit - nDebit; - CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0); + CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0); entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); if (wtx.IsFromMe(filter)) @@ -2531,7 +2531,7 @@ UniValue listunspent(const JSONRPCRequest& request) continue; CTxDestination address; - const CScript& scriptPubKey = out.tx->vout[out.i].scriptPubKey; + const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; bool fValidAddress = ExtractDestination(scriptPubKey, address); if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) @@ -2556,7 +2556,7 @@ UniValue listunspent(const JSONRPCRequest& request) } entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); - entry.push_back(Pair("amount", ValueFromAmount(out.tx->vout[out.i].nValue))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); entry.push_back(Pair("confirmations", out.nDepth)); entry.push_back(Pair("spendable", out.fSpendable)); entry.push_back(Pair("solvable", out.fSolvable)); @@ -2669,17 +2669,16 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) } // parse hex string from parameter - CTransaction origTx; - if (!DecodeHexTx(origTx, request.params[0].get_str())) + CMutableTransaction tx; + if (!DecodeHexTx(tx, request.params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); - if (origTx.vout.size() == 0) + if (tx.vout.size() == 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output"); - if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > origTx.vout.size())) + if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > tx.vout.size())) throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds"); - CMutableTransaction tx(origTx); CAmount nFeeOut; string strFailReason; diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp index a833be13d..eb14d176b 100644 --- a/src/wallet/test/accounting_tests.cpp +++ b/src/wallet/test/accounting_tests.cpp @@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) { CMutableTransaction tx(wtx); --tx.nLockTime; // Just to change the hash :) - *static_cast(&wtx) = CTransaction(tx); + wtx.SetTx(MakeTransactionRef(std::move(tx))); } pwalletMain->AddToWallet(wtx); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); @@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) { CMutableTransaction tx(wtx); --tx.nLockTime; // Just to change the hash :) - *static_cast(&wtx) = CTransaction(tx); + wtx.SetTx(MakeTransactionRef(std::move(tx))); } pwalletMain->AddToWallet(wtx); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index acf980c78..3af2afead 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -42,7 +42,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() tx.vin.resize(1); } - CWalletTx* wtx = new CWalletTx(&wallet, tx); + CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx))); if (fIsFromMe) { wtx->fDebitCached = true; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 03502de32..2b179329d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -81,17 +81,17 @@ struct CompareValueOnly std::string COutput::ToString() const { - return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue)); + return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); } int COutput::Priority() const { BOOST_FOREACH(CAmount d, CPrivateSend::GetStandardDenominations()) - if(tx->vout[i].nValue == d) return 10000; - if(tx->vout[i].nValue < 1*COIN) return 20000; + if(tx->tx->vout[i].nValue == d) return 10000; + if(tx->tx->vout[i].nValue < 1*COIN) return 20000; //nondenom return largest first - return -(tx->vout[i].nValue/COIN); + return -(tx->tx->vout[i].nValue/COIN); } const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const @@ -578,7 +578,7 @@ set CWallet::GetConflicts(const uint256& txid) const std::pair range; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { if (mapTxSpends.count(txin.prevout) <= 1) continue; // No conflict if zero or one spends @@ -731,7 +731,7 @@ void CWallet::AddToSpends(const uint256& wtxid) if (thisTx.IsCoinBase()) // Coinbases don't spend anything! return; - BOOST_FOREACH(const CTxIn& txin, thisTx.vin) + BOOST_FOREACH(const CTxIn& txin, thisTx.tx->vin) AddToSpends(txin.prevout, wtxid); } @@ -1005,7 +1005,7 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo for (map::iterator it = mapWallet.begin(); it != mapWallet.end() && account.vchPubKey.IsValid(); ++it) - BOOST_FOREACH(const CTxOut& txout, (*it).second.vout) + BOOST_FOREACH(const CTxOut& txout, (*it).second.tx->vout) if (txout.scriptPubKey == scriptPubKey) { bForceNew = true; break; @@ -1103,8 +1103,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) wtxIn.hashBlock.ToString()); } AddToSpends(hash); - for(int i = 0; i < wtx.vout.size(); ++i) { - if (IsMine(wtx.vout[i]) && !IsSpent(hash, i)) { + for(int i = 0; i < wtx.tx->vout.size(); ++i) { + if (IsMine(wtx.tx->vout[i]) && !IsSpent(hash, i)) { setWalletUTXO.insert(COutPoint(hash, i)); } } @@ -1175,7 +1175,7 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn) wtx.BindWallet(this); wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); AddToSpends(hash); - BOOST_FOREACH(const CTxIn& txin, wtx.vin) { + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { if (mapWallet.count(txin.prevout.hash)) { CWalletTx& prevtx = mapWallet[txin.prevout.hash]; if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { @@ -1214,7 +1214,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex if (fExisted && !fUpdate) return false; if (fExisted || IsMine(tx) || IsFromMe(tx)) { - CWalletTx wtx(this,tx); + CWalletTx wtx(this, MakeTransactionRef(tx)); // Get merkle branch if transaction was found in a block if (posInBlock != -1) @@ -1273,7 +1273,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) } // If a transaction changes 'conflicted' state, that changes the balance // available of the outputs it spends. So force those to be recomputed - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { if (mapWallet.count(txin.prevout.hash)) mapWallet[txin.prevout.hash].MarkDirty(); @@ -1337,7 +1337,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) } // If a transaction changes 'conflicted' state, that changes the balance // available of the outputs it spends. So force those to be recomputed - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { if (mapWallet.count(txin.prevout.hash)) mapWallet[txin.prevout.hash].MarkDirty(); @@ -1378,8 +1378,8 @@ isminetype CWallet::IsMine(const CTxIn &txin) const if (mi != mapWallet.end()) { const CWalletTx& prev = (*mi).second; - if (txin.prevout.n < prev.vout.size()) - return IsMine(prev.vout[txin.prevout.n]); + if (txin.prevout.n < prev.tx->vout.size()) + return IsMine(prev.tx->vout[txin.prevout.n]); } } return ISMINE_NO; @@ -1393,9 +1393,9 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const if (mi != mapWallet.end()) { const CWalletTx& prev = (*mi).second; - if (txin.prevout.n < prev.vout.size()) - if (IsMine(prev.vout[txin.prevout.n]) & filter) - return prev.vout[txin.prevout.n].nValue; + if (txin.prevout.n < prev.tx->vout.size()) + if (IsMine(prev.tx->vout[txin.prevout.n]) & filter) + return prev.tx->vout[txin.prevout.n].nValue; } } return 0; @@ -1426,27 +1426,27 @@ int CWallet::GetRealOutpointPrivateSendRounds(const COutPoint& outpoint, int nRo // bounds check - if (nout >= wtx->vout.size()) { + if (nout >= wtx->tx->vout.size()) { // should never actually hit this LogPrint("privatesend", "GetRealOutpointPrivateSendRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, -4); return -4; } - if (CPrivateSend::IsCollateralAmount(wtx->vout[nout].nValue)) { + if (CPrivateSend::IsCollateralAmount(wtx->tx->vout[nout].nValue)) { mDenomWtxes[hash].vout[nout].nRounds = -3; LogPrint("privatesend", "GetRealOutpointPrivateSendRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds); return mDenomWtxes[hash].vout[nout].nRounds; } //make sure the final output is non-denominate - if (!CPrivateSend::IsDenominatedAmount(wtx->vout[nout].nValue)) { //NOT DENOM + if (!CPrivateSend::IsDenominatedAmount(wtx->tx->vout[nout].nValue)) { //NOT DENOM mDenomWtxes[hash].vout[nout].nRounds = -2; LogPrint("privatesend", "GetRealOutpointPrivateSendRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds); return mDenomWtxes[hash].vout[nout].nRounds; } bool fAllDenoms = true; - BOOST_FOREACH(CTxOut out, wtx->vout) { + BOOST_FOREACH(CTxOut out, wtx->tx->vout) { fAllDenoms = fAllDenoms && CPrivateSend::IsDenominatedAmount(out.nValue); } @@ -1460,7 +1460,7 @@ int CWallet::GetRealOutpointPrivateSendRounds(const COutPoint& outpoint, int nRo int nShortest = -10; // an initial value, should be no way to get this by calculations bool fDenomFound = false; // only denoms here so let's look up - BOOST_FOREACH(CTxIn txinNext, wtx->vin) { + BOOST_FOREACH(CTxIn txinNext, wtx->tx->vin) { if (IsMine(txinNext)) { int n = GetRealOutpointPrivateSendRounds(txinNext.prevout, nRounds + 1); // denom found, find the shortest chain or initially assign nShortest with the first found value @@ -1495,8 +1495,8 @@ bool CWallet::IsDenominated(const COutPoint& outpoint) const map::const_iterator mi = mapWallet.find(outpoint.hash); if (mi != mapWallet.end()) { const CWalletTx& prev = (*mi).second; - if (outpoint.n < prev.vout.size()) { - return CPrivateSend::IsDenominatedAmount(prev.vout[outpoint.n].nValue); + if (outpoint.n < prev.tx->vout.size()) { + return CPrivateSend::IsDenominatedAmount(prev.tx->vout[outpoint.n].nValue); } } @@ -1749,14 +1749,14 @@ void CWalletTx::GetAmounts(list& listReceived, CAmount nDebit = GetDebit(filter); if (nDebit > 0) // debit>0 means we signed/sent this transaction { - CAmount nValueOut = GetValueOut(); + CAmount nValueOut = tx->GetValueOut(); nFee = nDebit - nValueOut; } // Sent/received. - for (unsigned int i = 0; i < vout.size(); ++i) + for (unsigned int i = 0; i < tx->vout.size(); ++i) { - const CTxOut& txout = vout[i]; + const CTxOut& txout = tx->vout[i]; isminetype fIsMine = pwallet->IsMine(txout); // Only need to handle txouts if AT LEAST one of these is true: // 1) they debit from us (sent) @@ -1943,7 +1943,7 @@ set CWalletTx::GetConflicts() const CAmount CWalletTx::GetDebit(const isminefilter& filter) const { - if (vin.empty()) + if (tx->vin.empty()) return 0; CAmount debit = 0; @@ -2033,11 +2033,11 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const CAmount nCredit = 0; uint256 hashTx = GetHash(); - for (unsigned int i = 0; i < vout.size(); i++) + for (unsigned int i = 0; i < tx->vout.size(); i++) { if (!pwallet->IsSpent(hashTx, i)) { - const CTxOut &txout = vout[i]; + const CTxOut &txout = tx->vout[i]; nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) throw std::runtime_error(std::string(__func__) + ": value out of range"); @@ -2076,11 +2076,11 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const return nAvailableWatchCreditCached; CAmount nCredit = 0; - for (unsigned int i = 0; i < vout.size(); i++) + for (unsigned int i = 0; i < tx->vout.size(); i++) { if (!pwallet->IsSpent(GetHash(), i)) { - const CTxOut &txout = vout[i]; + const CTxOut &txout = tx->vout[i]; nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); if (!MoneyRange(nCredit)) throw std::runtime_error(std::string(__func__) + ": value out of range"); @@ -2106,9 +2106,9 @@ CAmount CWalletTx::GetAnonymizedCredit(bool fUseCache) const CAmount nCredit = 0; uint256 hashTx = GetHash(); - for (unsigned int i = 0; i < vout.size(); i++) + for (unsigned int i = 0; i < tx->vout.size(); i++) { - const CTxOut &txout = vout[i]; + const CTxOut &txout = tx->vout[i]; const COutPoint outpoint = COutPoint(hashTx, i); if(pwallet->IsSpent(hashTx, i) || !pwallet->IsDenominated(outpoint)) continue; @@ -2150,11 +2150,11 @@ CAmount CWalletTx::GetDenominatedCredit(bool unconfirmed, bool fUseCache) const CAmount nCredit = 0; uint256 hashTx = GetHash(); - for (unsigned int i = 0; i < vout.size(); i++) + for (unsigned int i = 0; i < tx->vout.size(); i++) { - const CTxOut &txout = vout[i]; + const CTxOut &txout = tx->vout[i]; - if(pwallet->IsSpent(hashTx, i) || !CPrivateSend::IsDenominatedAmount(vout[i].nValue)) continue; + if(pwallet->IsSpent(hashTx, i) || !CPrivateSend::IsDenominatedAmount(tx->vout[i].nValue)) continue; nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) @@ -2207,23 +2207,23 @@ bool CWalletTx::IsTrusted() const return false; // Trusted if all inputs are from us and are in the mempool: - BOOST_FOREACH(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, tx->vin) { // Transactions not sent by us: not trusted const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); if (parent == NULL) return false; - const CTxOut& parentOut = parent->vout[txin.prevout.n]; + const CTxOut& parentOut = parent->tx->vout[txin.prevout.n]; if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) return false; } return true; } -bool CWalletTx::IsEquivalentTo(const CWalletTx& tx) const +bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const { - CMutableTransaction tx1 = *this; - CMutableTransaction tx2 = tx; + CMutableTransaction tx1 = *this->tx; + CMutableTransaction tx2 = *_tx.tx; for (unsigned int i = 0; i < tx1.vin.size(); i++) tx1.vin[i].scriptSig = CScript(); for (unsigned int i = 0; i < tx2.vin.size(); i++) tx2.vin[i].scriptSig = CScript(); return CTransaction(tx1) == CTransaction(tx2); @@ -2386,7 +2386,7 @@ CAmount CWallet::GetNormalizedAnonymizedBalance() const if (it->second.GetDepthInMainChain() < 0) continue; int nRounds = GetOutpointPrivateSendRounds(outpoint); - nTotal += it->second.vout[outpoint.n].nValue * nRounds / privateSendClient.nPrivateSendRounds; + nTotal += it->second.tx->vout[outpoint.n].nValue * nRounds / privateSendClient.nPrivateSendRounds; } return nTotal; @@ -2543,26 +2543,26 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const if (nDepth == 0 && !pcoin->InMempool()) continue; - for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { bool found = false; if(nCoinType == ONLY_DENOMINATED) { - found = CPrivateSend::IsDenominatedAmount(pcoin->vout[i].nValue); + found = CPrivateSend::IsDenominatedAmount(pcoin->tx->vout[i].nValue); } else if(nCoinType == ONLY_NONDENOMINATED) { - if (CPrivateSend::IsCollateralAmount(pcoin->vout[i].nValue)) continue; // do not use collateral amounts - found = !CPrivateSend::IsDenominatedAmount(pcoin->vout[i].nValue); + if (CPrivateSend::IsCollateralAmount(pcoin->tx->vout[i].nValue)) continue; // do not use collateral amounts + found = !CPrivateSend::IsDenominatedAmount(pcoin->tx->vout[i].nValue); } else if(nCoinType == ONLY_1000) { - found = pcoin->vout[i].nValue == 1000*COIN; + found = pcoin->tx->vout[i].nValue == 1000*COIN; } else if(nCoinType == ONLY_PRIVATESEND_COLLATERAL) { - found = CPrivateSend::IsCollateralAmount(pcoin->vout[i].nValue); + found = CPrivateSend::IsCollateralAmount(pcoin->tx->vout[i].nValue); } else { found = true; } if(!found) continue; - isminetype mine = IsMine(pcoin->vout[i]); + isminetype mine = IsMine(pcoin->tx->vout[i]); if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && (!IsLockedCoin((*it).first, i) || nCoinType == ONLY_1000) && - (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && + (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) && (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) vCoins.push_back(COutput(pcoin, i, nDepth, ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || @@ -2632,8 +2632,8 @@ bool less_then_denom (const COutput& out1, const COutput& out2) bool found2 = false; BOOST_FOREACH(CAmount d, CPrivateSend::GetStandardDenominations()) // loop through predefined denoms { - if(pcoin1->vout[out1.i].nValue == d) found1 = true; - if(pcoin2->vout[out2.i].nValue == d) found2 = true; + if(pcoin1->tx->vout[out1.i].nValue == d) found1 = true; + if(pcoin2->tx->vout[out2.i].nValue == d) found2 = true; } return (!found1 && found2); } @@ -2676,7 +2676,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int continue; int i = output.i; - CAmount n = pcoin->vout[i].nValue; + CAmount n = pcoin->tx->vout[i].nValue; if (tryDenom == 0 && CPrivateSend::IsDenominatedAmount(n)) continue; // we don't want denom values on first run pair > coin = make_pair(n,make_pair(pcoin, i)); @@ -2784,7 +2784,7 @@ bool CWallet::SelectCoins(const vector& vAvailableCoins, const CAmount& // make sure it's actually anonymized if(nRounds < privateSendClient.nPrivateSendRounds) continue; } - nValueRet += out.tx->vout[out.i].nValue; + nValueRet += out.tx->tx->vout[out.i].nValue; setCoinsRet.insert(make_pair(out.tx, out.i)); } @@ -2801,7 +2801,7 @@ bool CWallet::SelectCoins(const vector& vAvailableCoins, const CAmount& BOOST_FOREACH(const COutput& out, vCoins) { //make sure it's the denom we're looking for, round the amount up to smallest denom - if(out.tx->vout[out.i].nValue == nDenom && nValueRet + nDenom < nTargetValue + nSmallestDenom) { + if(out.tx->tx->vout[out.i].nValue == nDenom && nValueRet + nDenom < nTargetValue + nSmallestDenom) { COutPoint outpoint = COutPoint(out.tx->GetHash(),out.i); int nRounds = GetOutpointPrivateSendRounds(outpoint); // make sure it's actually anonymized @@ -2827,9 +2827,9 @@ bool CWallet::SelectCoins(const vector& vAvailableCoins, const CAmount& { const CWalletTx* pcoin = &it->second; // Clearly invalid input, fail - if (pcoin->vout.size() <= outpoint.n) + if (pcoin->tx->vout.size() <= outpoint.n) return false; - nValueFromPresetInputs += pcoin->vout[outpoint.n].nValue; + nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue; setPresetCoins.insert(make_pair(pcoin, outpoint.n)); } else return false; // TODO: Allow non-wallet inputs @@ -2894,10 +2894,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov return false; if (nChangePosInOut != -1) - tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.vout[nChangePosInOut]); + tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); // Add new txins (keeping original txin scriptSig/order) - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { if (!coinControl.IsSelected(txin.prevout)) { @@ -2944,7 +2944,7 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount { // masternode-like input should not be selected by AvailableCoins now anyway //if(out.tx->vout[out.i].nValue == 1000*COIN) continue; - if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){ + if(nValueRet + out.tx->tx->vout[out.i].nValue <= nValueMax){ CTxIn txin = CTxIn(out.tx->GetHash(), out.i); @@ -2953,7 +2953,7 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount if(nRounds < nPrivateSendRoundsMin) continue; BOOST_FOREACH(int nBit, vecBits) { - if(out.tx->vout[out.i].nValue == vecPrivateSendDenominations[nBit]) { + if(out.tx->tx->vout[out.i].nValue == vecPrivateSendDenominations[nBit]) { if(nValueRet >= nValueMin) { //randomly reduce the max amount we'll submit (for anonymity) nValueMax -= insecure_rand.rand32(nValueMax/5); @@ -2961,8 +2961,8 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount int r = insecure_rand.rand32(vCoins.size()); if((int)vecTxDSInRet.size() > r) return true; } - nValueRet += out.tx->vout[out.i].nValue; - vecTxDSInRet.push_back(CTxDSIn(txin, out.tx->vout[out.i].scriptPubKey)); + nValueRet += out.tx->tx->vout[out.i].nValue; + vecTxDSInRet.push_back(CTxDSIn(txin, out.tx->tx->vout[out.i].scriptPubKey)); vCoinsRet.push_back(out); nDenomResult |= 1 << nBit; } @@ -3020,31 +3020,31 @@ bool CWallet::SelectCoinsGrouppedByAddresses(std::vector& vecT if(wtx.IsCoinBase() && wtx.GetBlocksToMaturity() > 0) continue; if(fSkipUnconfirmed && !wtx.IsTrusted()) continue; - for (unsigned int i = 0; i < wtx.vout.size(); i++) { + for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { CTxDestination txdest; - if (!ExtractDestination(wtx.vout[i].scriptPubKey, txdest)) continue; + if (!ExtractDestination(wtx.tx->vout[i].scriptPubKey, txdest)) continue; isminefilter mine = ::IsMine(*this, txdest); if(!(mine & filter)) continue; if(IsSpent(outpoint.hash, i) || IsLockedCoin(outpoint.hash, i)) continue; - if(fSkipDenominated && CPrivateSend::IsDenominatedAmount(wtx.vout[i].nValue)) continue; + if(fSkipDenominated && CPrivateSend::IsDenominatedAmount(wtx.tx->vout[i].nValue)) continue; if(fAnonymizable) { // ignore collaterals - if(CPrivateSend::IsCollateralAmount(wtx.vout[i].nValue)) continue; - if(fMasterNode && wtx.vout[i].nValue == 1000*COIN) continue; + if(CPrivateSend::IsCollateralAmount(wtx.tx->vout[i].nValue)) continue; + if(fMasterNode && wtx.tx->vout[i].nValue == 1000*COIN) continue; // ignore outputs that are 10 times smaller then the smallest denomination // otherwise they will just lead to higher fee / lower priority - if(wtx.vout[i].nValue <= nSmallestDenom/10) continue; + if(wtx.tx->vout[i].nValue <= nSmallestDenom/10) continue; // ignore anonymized if(GetOutpointPrivateSendRounds(COutPoint(outpoint.hash, i)) >= privateSendClient.nPrivateSendRounds) continue; } CompactTallyItem& item = mapTally[txdest]; item.txdest = txdest; - item.nAmount += wtx.vout[i].nValue; + item.nAmount += wtx.tx->vout[i].nValue; item.vecTxIn.push_back(CTxIn(outpoint.hash, i)); } } @@ -3097,19 +3097,19 @@ bool CWallet::SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector< BOOST_FOREACH(const COutput& out, vCoins) { //do not allow inputs less than 1/10th of minimum value - if(out.tx->vout[out.i].nValue < nValueMin/10) continue; + if(out.tx->tx->vout[out.i].nValue < nValueMin/10) continue; //do not allow collaterals to be selected - if(CPrivateSend::IsCollateralAmount(out.tx->vout[out.i].nValue)) continue; - if(fMasterNode && out.tx->vout[out.i].nValue == 1000*COIN) continue; //masternode input + if(CPrivateSend::IsCollateralAmount(out.tx->tx->vout[out.i].nValue)) continue; + if(fMasterNode && out.tx->tx->vout[out.i].nValue == 1000*COIN) continue; //masternode input - if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){ + if(nValueRet + out.tx->tx->vout[out.i].nValue <= nValueMax){ CTxIn txin = CTxIn(out.tx->GetHash(),out.i); int nRounds = GetOutpointPrivateSendRounds(txin.prevout); if(nRounds >= nPrivateSendRoundsMax) continue; if(nRounds < nPrivateSendRoundsMin) continue; - nValueRet += out.tx->vout[out.i].nValue; + nValueRet += out.tx->tx->vout[out.i].nValue; vecTxInRet.push_back(txin); } } @@ -3125,10 +3125,10 @@ bool CWallet::GetCollateralTxDSIn(CTxDSIn& txdsinRet, CAmount& nValueRet) const BOOST_FOREACH(const COutput& out, vCoins) { - if(CPrivateSend::IsCollateralAmount(out.tx->vout[out.i].nValue)) + if(CPrivateSend::IsCollateralAmount(out.tx->tx->vout[out.i].nValue)) { - txdsinRet = CTxDSIn(CTxIn(out.tx->GetHash(), out.i), out.tx->vout[out.i].scriptPubKey); - nValueRet = out.tx->vout[out.i].nValue; + txdsinRet = CTxDSIn(CTxIn(out.tx->tx->GetHash(), out.i), out.tx->tx->vout[out.i].scriptPubKey); + nValueRet = out.tx->tx->vout[out.i].nValue; return true; } } @@ -3172,7 +3172,7 @@ bool CWallet::GetOutpointAndKeysFromOutput(const COutput& out, COutPoint& outpoi CScript pubScript; outpointRet = COutPoint(out.tx->GetHash(), out.i); - pubScript = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + pubScript = out.tx->tx->vout[out.i].scriptPubKey; // the inputs PubKey CTxDestination address1; ExtractDestination(pubScript, address1); @@ -3204,13 +3204,13 @@ int CWallet::CountInputsWithAmount(CAmount nInputAmount) if (pcoin->IsTrusted()){ int nDepth = pcoin->GetDepthInMainChain(false); - for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { COutput out = COutput(pcoin, i, nDepth, true, true); COutPoint outpoint = COutPoint(out.tx->GetHash(), out.i); - if(out.tx->vout[out.i].nValue != nInputAmount) continue; - if(!CPrivateSend::IsDenominatedAmount(pcoin->vout[i].nValue)) continue; - if(IsSpent(out.tx->GetHash(), i) || IsMine(pcoin->vout[i]) != ISMINE_SPENDABLE || !IsDenominated(outpoint)) continue; + if(out.tx->tx->vout[out.i].nValue != nInputAmount) continue; + if(!CPrivateSend::IsDenominatedAmount(pcoin->tx->vout[i].nValue)) continue; + if(IsSpent(out.tx->GetHash(), i) || IsMine(pcoin->tx->vout[i]) != ISMINE_SPENDABLE || !IsDenominated(outpoint)) continue; nTotal++; } @@ -3264,11 +3264,11 @@ bool CWallet::CreateCollateralTransaction(CMutableTransaction& txCollateral, std return true; } -bool CWallet::GetBudgetSystemCollateralTX(CTransaction& tx, uint256 hash, CAmount amount, bool fUseInstantSend) +bool CWallet::GetBudgetSystemCollateralTX(CTransactionRef& tx, uint256 hash, CAmount amount, bool fUseInstantSend) { CWalletTx wtx; if(GetBudgetSystemCollateralTX(wtx, hash, amount, fUseInstantSend)){ - tx = (CTransaction)wtx; + tx = wtx.tx; return true; } return false; @@ -3304,8 +3304,8 @@ bool CWallet::ConvertList(std::vector vecTxIn, std::vector& vecA BOOST_FOREACH(CTxIn txin, vecTxIn) { if (mapWallet.count(txin.prevout.hash)) { CWalletTx& wtx = mapWallet[txin.prevout.hash]; - if(txin.prevout.n < wtx.vout.size()){ - vecAmounts.push_back(wtx.vout[txin.prevout.n].nValue); + if(txin.prevout.n < wtx.tx->vout.size()){ + vecAmounts.push_back(wtx.tx->vout[txin.prevout.n].nValue); } } else { LogPrintf("CWallet::ConvertList -- Couldn't find transaction\n"); @@ -3458,7 +3458,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { - CAmount nCredit = pcoin.first->vout[pcoin.second].nValue; + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; //The coin age after the next block (depth+1) is used instead of the current, //reflecting an assumption the user would accept a bit more delay for //a chance at a free transaction. @@ -3574,7 +3574,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins){ CTxIn txin = CTxIn(coin.first->GetHash(),coin.second,CScript(), std::numeric_limits::max()-1); - vecTxDSInTmp.push_back(CTxDSIn(txin, coin.first->vout[coin.second].scriptPubKey)); + vecTxDSInTmp.push_back(CTxDSIn(txin, coin.first->tx->vout[coin.second].scriptPubKey)); txNew.vin.push_back(txin); } @@ -3626,16 +3626,16 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt } // Embed the constructed transaction data in wtxNew. - *static_cast(&wtxNew) = CTransaction(txNew); + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); // Limit size - if (nBytes >= MAX_STANDARD_TX_SIZE) + if (::GetSerializeSize(wtxNew, SER_NETWORK, PROTOCOL_VERSION) >= MAX_STANDARD_TX_SIZE) { strFailReason = _("Transaction too large"); return false; } - dPriority = wtxNew.ComputePriority(dPriority, nBytes); + dPriority = wtxNew.tx->ComputePriority(dPriority, nBytes); // Allow to override the default confirmation target over the CoinControl instance int currentConfirmationTarget = nTxConfirmTarget; @@ -3694,7 +3694,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon { { LOCK2(cs_main, cs_wallet); - LogPrintf("CommitTransaction:\n%s", wtxNew.ToString()); + LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); { // Take key pair from key pool so it won't be used again reservekey.KeepKey(); @@ -3705,7 +3705,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon // Notify that old coins are spent set updated_hahes; - BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) + BOOST_FOREACH(const CTxIn& txin, wtxNew.tx->vin) { // notify only once if(updated_hahes.find(txin.prevout.hash) != updated_hahes.end()) continue; @@ -3806,8 +3806,8 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { LOCK2(cs_main, cs_wallet); for (auto& pair : mapWallet) { - for(int i = 0; i < pair.second.vout.size(); ++i) { - if (IsMine(pair.second.vout[i]) && !IsSpent(pair.first, i)) { + for(int i = 0; i < pair.second.tx->vout.size(); ++i) { + if (IsMine(pair.second.tx->vout[i]) && !IsSpent(pair.first, i)) { setWalletUTXO.insert(COutPoint(pair.first, i)); } } @@ -4167,15 +4167,15 @@ std::map CWallet::GetAddressBalances() if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) continue; - for (unsigned int i = 0; i < pcoin->vout.size(); i++) + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { CTxDestination addr; - if (!IsMine(pcoin->vout[i])) + if (!IsMine(pcoin->tx->vout[i])) continue; - if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr)) + if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) continue; - CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue; + CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue; if (!balances.count(addr)) balances[addr] = 0; @@ -4197,16 +4197,16 @@ set< set > CWallet::GetAddressGroupings() { CWalletTx *pcoin = &walletEntry.second; - if (pcoin->vin.size() > 0) + if (pcoin->tx->vin.size() > 0) { bool any_mine = false; // group all input addresses with each other - BOOST_FOREACH(CTxIn txin, pcoin->vin) + BOOST_FOREACH(CTxIn txin, pcoin->tx->vin) { CTxDestination address; if(!IsMine(txin)) /* If this input isn't mine, ignore it */ continue; - if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address)) + if(!ExtractDestination(mapWallet[txin.prevout.hash].tx->vout[txin.prevout.n].scriptPubKey, address)) continue; grouping.insert(address); any_mine = true; @@ -4215,7 +4215,7 @@ set< set > CWallet::GetAddressGroupings() // group change with input addresses if (any_mine) { - BOOST_FOREACH(CTxOut txout, pcoin->vout) + BOOST_FOREACH(CTxOut txout, pcoin->tx->vout) if (IsChange(txout)) { CTxDestination txoutAddr; @@ -4232,11 +4232,11 @@ set< set > CWallet::GetAddressGroupings() } // group lone addrs by themselves - for (unsigned int i = 0; i < pcoin->vout.size(); i++) - if (IsMine(pcoin->vout[i])) + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) + if (IsMine(pcoin->tx->vout[i])) { CTxDestination address; - if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address)) + if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, address)) continue; grouping.insert(address); groupings.insert(grouping); @@ -4530,7 +4530,7 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { // ... which are already in a block int nHeight = blit->second->nHeight; - BOOST_FOREACH(const CTxOut &txout, wtx.vout) { + BOOST_FOREACH(const CTxOut &txout, wtx.tx->vout) { // iterate over all their outputs CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); BOOST_FOREACH(const CKeyID &keyid, vAffected) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index efb617b61..3f764fe2c 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -200,13 +200,14 @@ struct COutputEntry }; /** A transaction with a merkle branch linking it to the block chain. */ -class CMerkleTx : public CTransaction +class CMerkleTx { private: /** Constant used in hashBlock to indicate tx has been abandoned */ static const uint256 ABANDON_HASH; public: + CTransactionRef tx; uint256 hashBlock; /* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest @@ -218,26 +219,37 @@ public: CMerkleTx() { + SetTx(MakeTransactionRef()); Init(); } - CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) + CMerkleTx(CTransactionRef arg) { + SetTx(std::move(arg)); Init(); } + /** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected. + * TODO: adapt callers and remove this operator. */ + operator const CTransaction&() const { return *tx; } + void Init() { hashBlock = uint256(); nIndex = -1; } + void SetTx(CTransactionRef arg) + { + tx = std::move(arg); + } + ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { std::vector vMerkleBranch; // For compatibility with older versions. - READWRITE(*(CTransaction*)this); + READWRITE(tx); READWRITE(hashBlock); READWRITE(vMerkleBranch); READWRITE(nIndex); @@ -260,6 +272,9 @@ public: bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); } bool isAbandoned() const { return (hashBlock == ABANDON_HASH); } void setAbandoned() { hashBlock = ABANDON_HASH; } + + const uint256& GetHash() const { return tx->GetHash(); } + bool IsCoinBase() const { return tx->IsCoinBase(); } }; /** @@ -312,17 +327,7 @@ public: Init(NULL); } - CWalletTx(const CWallet* pwalletIn) - { - Init(pwalletIn); - } - - CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn) - { - Init(pwalletIn); - } - - CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn) + CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg)) { Init(pwalletIn); } @@ -883,7 +888,7 @@ public: CAmount GetNeedsToBeAnonymizedBalance(CAmount nMinBalance = 0) const; CAmount GetDenominatedBalance(bool unconfirmed=false) const; - bool GetBudgetSystemCollateralTX(CTransaction& tx, uint256 hash, CAmount amount, bool fUseInstantSend); + bool GetBudgetSystemCollateralTX(CTransactionRef& tx, uint256 hash, CAmount amount, bool fUseInstantSend); bool GetBudgetSystemCollateralTX(CWalletTx& tx, uint256 hash, CAmount amount, bool fUseInstantSend); /**