aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp561
1 files changed, 380 insertions, 181 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 15001776f..a5e8752fc 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -13,7 +13,7 @@
#include "consensus/validation.h"
#include "key.h"
#include "keystore.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
#include "policy/policy.h"
#include "primitives/block.h"
@@ -75,7 +75,7 @@ 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));
}
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
@@ -400,7 +400,7 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> 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
@@ -411,6 +411,13 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const
return result;
}
+bool CWallet::HasWalletSpend(const uint256& txid) const
+{
+ AssertLockHeld(cs_wallet);
+ auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0));
+ return (iter != mapTxSpends.end() && iter->first.hash == txid);
+}
+
void CWallet::Flush(bool shutdown)
{
bitdb.Flush(shutdown);
@@ -552,7 +559,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);
}
@@ -795,7 +802,7 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo
for (map<uint256, CWalletTx>::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;
@@ -826,6 +833,35 @@ void CWallet::MarkDirty()
}
}
+bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
+{
+ LOCK(cs_wallet);
+
+ auto mi = mapWallet.find(originalHash);
+
+ // There is a bug if MarkReplaced is not called on an existing wallet transaction.
+ assert(mi != mapWallet.end());
+
+ CWalletTx& wtx = (*mi).second;
+
+ // Ensure for now that we're not overwriting data
+ assert(wtx.mapValue.count("replaced_by_txid") == 0);
+
+ wtx.mapValue["replaced_by_txid"] = newHash.ToString();
+
+ CWalletDB walletdb(strWalletFile, "r+");
+
+ bool success = true;
+ if (!walletdb.WriteTx(wtx)) {
+ LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetHash().ToString());
+ success = false;
+ }
+
+ NotifyTransactionChanged(this, originalHash, CT_UPDATED);
+
+ return success;
+}
+
bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
{
LOCK(cs_wallet);
@@ -954,7 +990,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()) {
@@ -967,9 +1003,17 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
}
/**
- * Add a transaction to the wallet, or update it.
- * pblock is optional, but should be provided if the transaction is known to be in a block.
+ * Add a transaction to the wallet, or update it. pIndex and posInBlock should
+ * be set when the transaction was known to be included in a block. When
+ * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not
+ * updated in AddToWallet, but notifications happen and cached balances are
+ * marked dirty.
* If fUpdate is true, existing transactions will be updated.
+ * TODO: One exception to this is that the abandoned state is cleared under the
+ * assumption that any further notification of a transaction that was considered
+ * abandoned is an indication that it is not safe to be considered abandoned.
+ * Abandoned state should probably be more carefuly tracked via different
+ * posInBlock signals or by checking mempool presence when necessary.
*/
bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
{
@@ -993,7 +1037,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)
@@ -1009,8 +1053,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
{
LOCK2(cs_main, cs_wallet);
- // Do not flush the wallet here for performance reasons
- CWalletDB walletdb(strWalletFile, "r+", false);
+ CWalletDB walletdb(strWalletFile, "r+");
std::set<uint256> todo;
std::set<uint256> done;
@@ -1052,7 +1095,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();
@@ -1113,7 +1156,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();
@@ -1148,13 +1191,15 @@ 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;
}
+// Note that this function doesn't distinguish between a 0-valued input,
+// and a not-"is mine" (according to the filter) input.
CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
{
{
@@ -1163,9 +1208,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;
@@ -1237,6 +1282,27 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co
return nDebit;
}
+bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const
+{
+ LOCK(cs_wallet);
+
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ auto mi = mapWallet.find(txin.prevout.hash);
+ if (mi == mapWallet.end())
+ return false; // any unknown inputs can't be from us
+
+ const CWalletTx& prev = (*mi).second;
+
+ if (txin.prevout.n >= prev.tx->vout.size())
+ return false; // invalid input!
+
+ if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter))
+ return false;
+ }
+ return true;
+}
+
CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const
{
CAmount nCredit = 0;
@@ -1380,14 +1446,14 @@ void CWalletTx::GetAmounts(list<COutputEntry>& 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)
@@ -1480,12 +1546,12 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
pindex = chainActive.Next(pindex);
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
- double dProgressStart = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false);
- double dProgressTip = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip(), false);
+ double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
+ double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip());
while (pindex)
{
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0)
- ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
+ ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
CBlock block;
ReadBlockFromDisk(block, pindex, Params().GetConsensus());
@@ -1498,7 +1564,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
pindex = chainActive.Next(pindex);
if (GetTime() >= nNow + 60) {
nNow = GetTime();
- LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex));
+ LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
}
}
ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI
@@ -1542,9 +1608,11 @@ void CWallet::ReacceptWalletTransactions()
bool CWalletTx::RelayWalletTransaction(CConnman* connman)
{
assert(pwallet->GetBroadcastTransactions());
- if (!IsCoinBase())
+ if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0)
{
- if (GetDepthInMainChain() == 0 && !isAbandoned() && InMempool()) {
+ CValidationState state;
+ /* GetDepthInMainChain already catches known conflicts. */
+ if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) {
LogPrintf("Relaying wtx %s\n", GetHash().ToString());
if (connman) {
CInv inv(MSG_TX, GetHash());
@@ -1573,7 +1641,7 @@ set<uint256> CWalletTx::GetConflicts() const
CAmount CWalletTx::GetDebit(const isminefilter& filter) const
{
- if (vin.empty())
+ if (tx->vin.empty())
return 0;
CAmount debit = 0;
@@ -1663,11 +1731,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("CWalletTx::GetAvailableCredit() : value out of range");
@@ -1706,11 +1774,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("CWalletTx::GetAvailableCredit() : value out of range");
@@ -1758,23 +1826,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);
@@ -1957,10 +2025,41 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
if (nDepth == 0 && !pcoin->InMempool())
continue;
- for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
- isminetype mine = IsMine(pcoin->vout[i]);
+ // We should not consider coins from transactions that are replacing
+ // other transactions.
+ //
+ // Example: There is a transaction A which is replaced by bumpfee
+ // transaction B. In this case, we want to prevent creation of
+ // a transaction B' which spends an output of B.
+ //
+ // Reason: If transaction A were initially confirmed, transactions B
+ // and B' would no longer be valid, so the user would have to create
+ // a new transaction C to replace B'. However, in the case of a
+ // one-block reorg, transactions B' and C might BOTH be accepted,
+ // when the user only wanted one of them. Specifically, there could
+ // be a 1-block reorg away from the chain where transactions A and C
+ // were accepted to another chain where B, B', and C were all
+ // accepted.
+ if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) {
+ continue;
+ }
+
+ // Similarly, we should not consider coins from transactions that
+ // have been replaced. In the example above, we would want to prevent
+ // creation of a transaction A' spending an output of A, because if
+ // transaction B were initially confirmed, conflicting with A and
+ // A', we wouldn't want to the user to create a transaction D
+ // intending to replace A', but potentially resulting in a scenario
+ // where A, A', and D could all be accepted (instead of just B and
+ // D, or just A and A' like the user would want).
+ if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) {
+ continue;
+ }
+
+ for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
+ isminetype mine = IsMine(pcoin->tx->vout[i]);
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
- !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) &&
+ !IsLockedCoin((*it).first, i) && (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) ||
@@ -2017,7 +2116,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns
}
}
-bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,
+bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, vector<COutput> vCoins,
set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
{
setCoinsRet.clear();
@@ -2042,8 +2141,11 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs))
continue;
+ if (!mempool.TransactionWithinChainLimit(pcoin->GetHash(), nMaxAncestors))
+ continue;
+
int i = output.i;
- CAmount n = pcoin->vout[i].nValue;
+ CAmount n = pcoin->tx->vout[i].nValue;
pair<CAmount,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
@@ -2130,7 +2232,7 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
{
if (!out.fSpendable)
continue;
- nValueRet += out.tx->vout[out.i].nValue;
+ nValueRet += out.tx->tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i));
}
return (nValueRet >= nTargetValue);
@@ -2150,9 +2252,9 @@ bool CWallet::SelectCoins(const vector<COutput>& 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
@@ -2167,10 +2269,17 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
++it;
}
+ size_t nMaxChainLength = std::min(GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT));
+ bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
+
bool res = nTargetValue <= nValueFromPresetInputs ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, vCoins, setCoinsRet, nValueRet) ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, vCoins, setCoinsRet, nValueRet) ||
- (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, vCoins, setCoinsRet, nValueRet));
+ SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength/3), vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength/2, vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits<uint64_t>::max(), vCoins, setCoinsRet, nValueRet));
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
@@ -2181,14 +2290,15 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
return res;
}
-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange)
+bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange)
{
vector<CRecipient> vecSend;
// Turn the txout set into a CRecipient vector
- BOOST_FOREACH(const CTxOut& txOut, tx.vout)
+ for (size_t idx = 0; idx < tx.vout.size(); idx++)
{
- CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false};
+ const CTxOut& txOut = tx.vout[idx];
+ CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1};
vecSend.push_back(recipient);
}
@@ -2208,10 +2318,14 @@ 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]);
+
+ // Copy output sizes from new transaction; they may have had the fee subtracted from them
+ for (unsigned int idx = 0; idx < tx.vout.size(); idx++)
+ tx.vout[idx].nValue = wtx.tx->vout[idx].nValue;
// 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))
{
@@ -2225,6 +2339,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov
}
}
+ // optionally keep the change output key
+ if (keepReserveKey)
+ reservekey.KeepKey();
+
return true;
}
@@ -2234,7 +2352,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
CAmount nValue = 0;
int nChangePosRequest = nChangePosInOut;
unsigned int nSubtractFeeFromAmount = 0;
- BOOST_FOREACH (const CRecipient& recipient, vecSend)
+ for (const auto& recipient : vecSend)
{
if (nValue < 0 || recipient.nAmount < 0)
{
@@ -2289,6 +2407,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
{
+ set<pair<const CWalletTx*,unsigned int> > setCoins;
LOCK2(cs_main, cs_wallet);
{
std::vector<COutput> vAvailableCoins;
@@ -2301,7 +2420,6 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
nChangePosInOut = nChangePosRequest;
txNew.vin.clear();
txNew.vout.clear();
- txNew.wit.SetNull();
wtxNew.fFromMe = true;
bool fFirst = true;
@@ -2310,7 +2428,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
nValueToSelect += nFeeRet;
double dPriority = 0;
// vouts to the payees
- BOOST_FOREACH (const CRecipient& recipient, vecSend)
+ for (const auto& recipient : vecSend)
{
CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
@@ -2325,7 +2443,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
}
- if (txout.IsDust(::minRelayTxFee))
+ if (txout.IsDust(dustRelayFee))
{
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
@@ -2342,16 +2460,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
// Choose coins to use
- set<pair<const CWalletTx*,unsigned int> > setCoins;
CAmount nValueIn = 0;
+ setCoins.clear();
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl))
{
strFailReason = _("Insufficient funds");
return false;
}
- BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+ for (const auto& 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.
@@ -2389,7 +2507,11 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
CPubKey vchPubKey;
bool ret;
ret = reservekey.GetReservedKey(vchPubKey);
- assert(ret); // should never fail, as we just unlocked
+ if (!ret)
+ {
+ strFailReason = _("Keypool ran out, please call keypoolrefill first");
+ return false;
+ }
scriptChange = GetScriptForDestination(vchPubKey.GetID());
}
@@ -2399,16 +2521,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// We do not move dust-change to fees, because the sender would end up paying more than requested.
// This would be against the purpose of the all-inclusive feature.
// So instead we raise the change and deduct from the recipient.
- if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee))
+ if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee))
{
- CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue;
+ CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue;
newTxOut.nValue += nDust; // raise change until no more dust
for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient
{
if (vecSend[i].fSubtractFeeFromAmount)
{
txNew.vout[i].nValue -= nDust;
- if (txNew.vout[i].IsDust(::minRelayTxFee))
+ if (txNew.vout[i].IsDust(dustRelayFee))
{
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
return false;
@@ -2420,7 +2542,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// Never create dust outputs; if we would, just
// add the dust to the fee.
- if (newTxOut.IsDust(::minRelayTxFee))
+ if (newTxOut.IsDust(dustRelayFee))
{
nChangePosInOut = -1;
nFeeRet += nChange;
@@ -2454,57 +2576,29 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// BIP125 defines opt-in RBF as any nSequence < maxint-1, so
// we use the highest possible value in that range (maxint-2)
// to avoid conflicting with other possible uses of nSequence,
- // and in the spirit of "smallest posible change from prior
+ // and in the spirit of "smallest possible change from prior
// behavior."
- BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
+ for (const auto& coin : setCoins)
txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),
std::numeric_limits<unsigned int>::max() - (fWalletRbf ? 2 : 1)));
- // Sign
- int nIn = 0;
- CTransaction txNewConst(txNew);
- BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
- {
- bool signSuccess;
- const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey;
- SignatureData sigdata;
- if (sign)
- signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
- else
- signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata);
-
- if (!signSuccess)
- {
- strFailReason = _("Signing transaction failed");
- return false;
- } else {
- UpdateTransaction(txNew, nIn, sigdata);
- }
-
- nIn++;
+ // Fill in dummy signatures for fee calculation.
+ if (!DummySignTx(txNew, setCoins)) {
+ strFailReason = _("Signing transaction failed");
+ return false;
}
unsigned int nBytes = GetVirtualTransactionSize(txNew);
- // Remove scriptSigs if we used dummy signatures for fee calculation
- if (!sign) {
- BOOST_FOREACH (CTxIn& vin, txNew.vin)
- vin.scriptSig = CScript();
- txNew.wit.SetNull();
- }
-
- // Embed the constructed transaction data in wtxNew.
- *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew);
+ CTransaction txNewConst(txNew);
+ dPriority = txNewConst.ComputePriority(dPriority, nBytes);
- // Limit size
- if (GetTransactionWeight(txNew) >= MAX_STANDARD_TX_WEIGHT)
- {
- strFailReason = _("Transaction too large");
- return false;
+ // Remove scriptSigs to eliminate the fee calculation dummy signatures
+ for (auto& vin : txNew.vin) {
+ vin.scriptSig = CScript();
+ vin.scriptWitness.SetNull();
}
- dPriority = wtxNew.ComputePriority(dPriority, nBytes);
-
// Allow to override the default confirmation target over the CoinControl instance
int currentConfirmationTarget = nTxConfirmTarget;
if (coinControl && coinControl->nConfirmTarget > 0)
@@ -2535,16 +2629,91 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
return false;
}
- if (nFeeRet >= nFeeNeeded)
+ if (nFeeRet >= nFeeNeeded) {
+ // Reduce fee to only the needed amount if we have change
+ // output to increase. This prevents potential overpayment
+ // in fees if the coins selected to meet nFeeNeeded result
+ // in a transaction that requires less fee than the prior
+ // iteration.
+ // TODO: The case where nSubtractFeeFromAmount > 0 remains
+ // to be addressed because it requires returning the fee to
+ // the payees and not the change output.
+ // TODO: The case where there is no change output remains
+ // to be addressed so we avoid creating too small an output.
+ if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
+ CAmount extraFeePaid = nFeeRet - nFeeNeeded;
+ vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
+ change_position->nValue += extraFeePaid;
+ nFeeRet -= extraFeePaid;
+ }
break; // Done, enough fee included.
+ }
+
+ // Try to reduce change to include necessary fee
+ if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
+ CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet;
+ vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
+ // Only reduce change if remaining amount is still a large enough output.
+ if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) {
+ change_position->nValue -= additionalFeeNeeded;
+ nFeeRet += additionalFeeNeeded;
+ break; // Done, able to increase fee from change
+ }
+ }
// Include more fee and try again.
nFeeRet = nFeeNeeded;
continue;
}
}
+
+ if (sign)
+ {
+ CTransaction txNewConst(txNew);
+ int nIn = 0;
+ for (const auto& coin : setCoins)
+ {
+ const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
+ SignatureData sigdata;
+
+ if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata))
+ {
+ strFailReason = _("Signing transaction failed");
+ return false;
+ } else {
+ UpdateTransaction(txNew, nIn, sigdata);
+ }
+
+ nIn++;
+ }
+ }
+
+ // Embed the constructed transaction data in wtxNew.
+ wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
+
+ // Limit size
+ if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
+ {
+ strFailReason = _("Transaction too large");
+ return false;
+ }
}
+ if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
+ // Lastly, ensure this tx will pass the mempool's chain limits
+ LockPoints lp;
+ CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp);
+ CTxMemPool::setEntries setAncestors;
+ size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
+ size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
+ size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
+ std::string errString;
+ if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
+ strFailReason = _("Transaction has too long of a mempool chain");
+ return false;
+ }
+ }
return true;
}
@@ -2555,7 +2724,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();
@@ -2565,7 +2734,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
AddToWallet(wtxNew);
// Notify that old coins are spent
- BOOST_FOREACH(const CTxIn& txin, wtxNew.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtxNew.tx->vin)
{
CWalletTx &coin = mapWallet[txin.prevout.hash];
coin.BindWallet(this);
@@ -2580,11 +2749,11 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
{
// Broadcast
if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) {
- // This must not fail. The transaction has already been signed and recorded.
- LogPrintf("CommitTransaction(): Error: Transaction not valid, %s\n", state.GetRejectReason());
- return false;
+ LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason());
+ // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
+ } else {
+ wtxNew.RelayWalletTransaction(connman);
}
- wtxNew.RelayWalletTransaction(connman);
}
}
return true;
@@ -2621,8 +2790,13 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
{
- // payTxFee is user-set "I want to pay this much"
- CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
+ // payTxFee is the user-set global for desired feerate
+ return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes));
+}
+
+CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee)
+{
+ CAmount nFeeNeeded = targetFee;
// User didn't set: use -txconfirmtarget to estimate...
if (nFeeNeeded == 0) {
int estimateFoundTarget = nConfirmTarget;
@@ -2939,15 +3113,15 @@ std::map<CTxDestination, CAmount> 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;
@@ -2969,16 +3143,16 @@ set< set<CTxDestination> > 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;
@@ -2987,7 +3161,7 @@ set< set<CTxDestination> > 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;
@@ -3004,11 +3178,11 @@ set< set<CTxDestination> > 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);
@@ -3275,7 +3449,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &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) {
@@ -3369,21 +3543,14 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE));
strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET));
strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB));
+ strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS));
}
return strUsage;
}
-bool CWallet::InitLoadWallet()
+CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
{
- if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
- pwalletMain = NULL;
- LogPrintf("Wallet disabled!\n");
- return true;
- }
-
- std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
-
// needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
@@ -3393,7 +3560,8 @@ bool CWallet::InitLoadWallet()
CWallet *tempWallet = new CWallet(walletFile);
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) {
- return InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ return NULL;
}
delete tempWallet;
@@ -3408,23 +3576,29 @@ bool CWallet::InitLoadWallet()
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK)
{
- if (nLoadWalletRet == DB_CORRUPT)
- return InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ if (nLoadWalletRet == DB_CORRUPT) {
+ InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ return NULL;
+ }
else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
{
InitWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect."),
walletFile));
}
- else if (nLoadWalletRet == DB_TOO_NEW)
- return InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"),
- walletFile, _(PACKAGE_NAME)));
+ else if (nLoadWalletRet == DB_TOO_NEW) {
+ InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));
+ return NULL;
+ }
else if (nLoadWalletRet == DB_NEED_REWRITE)
{
- return InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
+ InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
+ return NULL;
+ }
+ else {
+ InitError(strprintf(_("Error loading %s"), walletFile));
+ return NULL;
}
- else
- return InitError(strprintf(_("Error loading %s"), walletFile));
}
if (GetBoolArg("-upgradewallet", fFirstRun))
@@ -3440,7 +3614,8 @@ bool CWallet::InitLoadWallet()
LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
if (nMaxVersion < walletInstance->GetVersion())
{
- return InitError(_("Cannot downgrade wallet"));
+ InitError(_("Cannot downgrade wallet"));
+ return NULL;
}
walletInstance->SetMaxVersion(nMaxVersion);
}
@@ -3457,18 +3632,24 @@ bool CWallet::InitLoadWallet()
CPubKey newDefaultKey;
if (walletInstance->GetKeyFromPool(newDefaultKey)) {
walletInstance->SetDefaultKey(newDefaultKey);
- if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive"))
- return InitError(_("Cannot write default address") += "\n");
+ if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) {
+ InitError(_("Cannot write default address") += "\n");
+ return NULL;
+ }
}
walletInstance->SetBestChain(chainActive.GetLocator());
}
- else if (mapArgs.count("-usehd")) {
+ else if (IsArgSet("-usehd")) {
bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET);
- if (walletInstance->IsHDEnabled() && !useHD)
- return InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile));
- if (!walletInstance->IsHDEnabled() && useHD)
- return InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile));
+ if (walletInstance->IsHDEnabled() && !useHD) {
+ InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile));
+ return NULL;
+ }
+ if (!walletInstance->IsHDEnabled() && useHD) {
+ InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile));
+ return NULL;
+ }
}
LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart);
@@ -3498,8 +3679,10 @@ bool CWallet::InitLoadWallet()
while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA) && block->pprev->nTx > 0 && pindexRescan != block)
block = block->pprev;
- if (pindexRescan != block)
- return InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
+ if (pindexRescan != block) {
+ InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
+ return NULL;
+ }
}
uiInterface.InitMessage(_("Rescanning..."));
@@ -3544,11 +3727,30 @@ bool CWallet::InitLoadWallet()
LogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size());
}
- pwalletMain = walletInstance;
+ return walletInstance;
+}
+
+bool CWallet::InitLoadWallet()
+{
+ if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
+ pwalletMain = NULL;
+ LogPrintf("Wallet disabled!\n");
+ return true;
+ }
+
+ std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
+
+ CWallet * const pwallet = CreateWalletFromFile(walletFile);
+ if (!pwallet) {
+ return false;
+ }
+ pwalletMain = pwallet;
return true;
}
+std::atomic<bool> CWallet::fFlushThreadRunning(false);
+
void CWallet::postInitProcess(boost::thread_group& threadGroup)
{
// Add wallet transactions that aren't already in a block to mempool
@@ -3556,7 +3758,9 @@ void CWallet::postInitProcess(boost::thread_group& threadGroup)
ReacceptWalletTransactions();
// Run a thread to flush wallet periodically
- threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(this->strWalletFile)));
+ if (!CWallet::fFlushThreadRunning.exchange(true)) {
+ threadGroup.create_thread(ThreadFlushWalletDB);
+ }
}
bool CWallet::ParameterInteraction()
@@ -3587,31 +3791,31 @@ bool CWallet::ParameterInteraction()
InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
_("The wallet will avoid paying less than the minimum relay fee."));
- if (mapArgs.count("-mintxfee"))
+ if (IsArgSet("-mintxfee"))
{
CAmount n = 0;
- if (!ParseMoney(mapArgs["-mintxfee"], n) || 0 == n)
- return InitError(AmountErrMsg("mintxfee", mapArgs["-mintxfee"]));
+ if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n)
+ return InitError(AmountErrMsg("mintxfee", GetArg("-mintxfee", "")));
if (n > HIGH_TX_FEE_PER_KB)
InitWarning(AmountHighWarn("-mintxfee") + " " +
_("This is the minimum transaction fee you pay on every transaction."));
CWallet::minTxFee = CFeeRate(n);
}
- if (mapArgs.count("-fallbackfee"))
+ if (IsArgSet("-fallbackfee"))
{
CAmount nFeePerK = 0;
- if (!ParseMoney(mapArgs["-fallbackfee"], nFeePerK))
- return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), mapArgs["-fallbackfee"]));
+ if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK))
+ return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), GetArg("-fallbackfee", "")));
if (nFeePerK > HIGH_TX_FEE_PER_KB)
InitWarning(AmountHighWarn("-fallbackfee") + " " +
_("This is the transaction fee you may pay when fee estimates are not available."));
CWallet::fallbackFee = CFeeRate(nFeePerK);
}
- if (mapArgs.count("-paytxfee"))
+ if (IsArgSet("-paytxfee"))
{
CAmount nFeePerK = 0;
- if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK))
- return InitError(AmountErrMsg("paytxfee", mapArgs["-paytxfee"]));
+ if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK))
+ return InitError(AmountErrMsg("paytxfee", GetArg("-paytxfee", "")));
if (nFeePerK > HIGH_TX_FEE_PER_KB)
InitWarning(AmountHighWarn("-paytxfee") + " " +
_("This is the transaction fee you will pay if you send a transaction."));
@@ -3620,21 +3824,21 @@ bool CWallet::ParameterInteraction()
if (payTxFee < ::minRelayTxFee)
{
return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
- mapArgs["-paytxfee"], ::minRelayTxFee.ToString()));
+ GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
}
}
- if (mapArgs.count("-maxtxfee"))
+ if (IsArgSet("-maxtxfee"))
{
CAmount nMaxFee = 0;
- if (!ParseMoney(mapArgs["-maxtxfee"], nMaxFee))
- return InitError(AmountErrMsg("maxtxfee", mapArgs["-maxtxfee"]));
+ if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee))
+ return InitError(AmountErrMsg("maxtxfee", GetArg("-maxtxfee", "")));
if (nMaxFee > HIGH_MAX_TX_FEE)
InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
maxTxFee = nMaxFee;
if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee)
{
return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
- mapArgs["-maxtxfee"], ::minRelayTxFee.ToString()));
+ GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
}
}
nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
@@ -3642,6 +3846,9 @@ bool CWallet::ParameterInteraction()
fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS);
fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
+ if (fSendFreeTransactions && GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0)
+ return InitError("Creation of free transactions with their relay disabled is not supported.");
+
return true;
}
@@ -3702,21 +3909,13 @@ CWalletKey::CWalletKey(int64_t nExpires)
nTimeExpires = nExpires;
}
-int CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
+void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
{
- AssertLockHeld(cs_main);
-
// Update the tx's hashBlock
hashBlock = pindex->GetBlockHash();
// set the position of the transaction in the block
nIndex = posInBlock;
-
- // Is the tx in a block that's in the main chain
- if (!chainActive.Contains(pindex))
- return 0;
-
- return chainActive.Height() - pindex->nHeight + 1;
}
int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
@@ -3748,5 +3947,5 @@ int CMerkleTx::GetBlocksToMaturity() const
bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
- return ::AcceptToMemoryPool(mempool, state, *this, true, NULL, false, nAbsurdFee);
+ return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
}