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.cpp489
1 files changed, 291 insertions, 198 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 6f1894d430..c32726aeb0 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -12,6 +12,7 @@
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "fs.h"
+#include "init.h"
#include "key.h"
#include "keystore.h"
#include "validation.h"
@@ -57,6 +58,8 @@ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
*/
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
+CFeeRate CWallet::m_discard_rate = CFeeRate(DEFAULT_DISCARD_FEE);
+
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
/** @defgroup mapWallet
@@ -78,12 +81,44 @@ std::string COutput::ToString() const
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
}
+class CAffectedKeysVisitor : public boost::static_visitor<void> {
+private:
+ const CKeyStore &keystore;
+ std::vector<CKeyID> &vKeys;
+
+public:
+ CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {}
+
+ void Process(const CScript &script) {
+ txnouttype type;
+ std::vector<CTxDestination> vDest;
+ int nRequired;
+ if (ExtractDestinations(script, type, vDest, nRequired)) {
+ for (const CTxDestination &dest : vDest)
+ boost::apply_visitor(*this, dest);
+ }
+ }
+
+ void operator()(const CKeyID &keyId) {
+ if (keystore.HaveKey(keyId))
+ vKeys.push_back(keyId);
+ }
+
+ void operator()(const CScriptID &scriptId) {
+ CScript script;
+ if (keystore.GetCScript(scriptId, script))
+ Process(script);
+ }
+
+ void operator()(const CNoDestination &none) {}
+};
+
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
LOCK(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
if (it == mapWallet.end())
- return NULL;
+ return nullptr;
return &(it->second);
}
@@ -180,10 +215,10 @@ bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey& secret, const
pwalletdbEncryption = &walletdb;
}
if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) {
- if (needsDB) pwalletdbEncryption = NULL;
+ if (needsDB) pwalletdbEncryption = nullptr;
return false;
}
- if (needsDB) pwalletdbEncryption = NULL;
+ if (needsDB) pwalletdbEncryption = nullptr;
// check if we need to remove from watch-only
CScript script;
@@ -461,16 +496,31 @@ void CWallet::Flush(bool shutdown)
bool CWallet::Verify()
{
- if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
+ if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
return true;
uiInterface.InitMessage(_("Verifying wallet(s)..."));
+ // Keep track of each wallet absolute path to detect duplicates.
+ std::set<fs::path> wallet_paths;
+
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
if (boost::filesystem::path(walletFile).filename() != walletFile) {
- return InitError(_("-wallet parameter must only specify a filename (not a path)"));
- } else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) {
- return InitError(_("Invalid characters in -wallet filename"));
+ return InitError(strprintf(_("Error loading wallet %s. -wallet parameter must only specify a filename (not a path)."), walletFile));
+ }
+
+ if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) {
+ return InitError(strprintf(_("Error loading wallet %s. Invalid characters in -wallet filename."), walletFile));
+ }
+
+ fs::path wallet_path = fs::absolute(walletFile, GetDataDir());
+
+ if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) {
+ return InitError(strprintf(_("Error loading wallet %s. -wallet filename must be a regular file."), walletFile));
+ }
+
+ if (!wallet_paths.insert(wallet_path).second) {
+ return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), walletFile));
}
std::string strError;
@@ -478,7 +528,7 @@ bool CWallet::Verify()
return InitError(strError);
}
- if (GetBoolArg("-salvagewallet", false)) {
+ if (gArgs.GetBoolArg("-salvagewallet", false)) {
// Recover readable keypairs:
CWallet dummyWallet;
std::string backup_filename;
@@ -508,7 +558,7 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
// So: find smallest nOrderPos:
int nMinOrderPos = std::numeric_limits<int>::max();
- const CWalletTx* copyFrom = NULL;
+ const CWalletTx* copyFrom = nullptr;
for (TxSpends::iterator it = range.first; it != range.second; ++it)
{
const uint256& hash = it->second;
@@ -573,8 +623,9 @@ void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
void CWallet::AddToSpends(const uint256& wtxid)
{
- assert(mapWallet.count(wtxid));
- CWalletTx& thisTx = mapWallet[wtxid];
+ auto it = mapWallet.find(wtxid);
+ assert(it != mapWallet.end());
+ CWalletTx& thisTx = it->second;
if (thisTx.IsCoinBase()) // Coinbases don't spend anything!
return;
@@ -623,7 +674,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
pwalletdbEncryption = new CWalletDB(*dbw);
if (!pwalletdbEncryption->TxnBegin()) {
delete pwalletdbEncryption;
- pwalletdbEncryption = NULL;
+ pwalletdbEncryption = nullptr;
return false;
}
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
@@ -648,7 +699,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
}
delete pwalletdbEncryption;
- pwalletdbEncryption = NULL;
+ pwalletdbEncryption = nullptr;
Lock();
Unlock(strWalletPassphrase);
@@ -689,13 +740,13 @@ DBErrors CWallet::ReorderTransactions()
for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
CWalletTx* wtx = &((*it).second);
- txByTime.insert(std::make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0)));
+ txByTime.insert(std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr)));
}
std::list<CAccountingEntry> acentries;
walletdb.ListAccountCreditDebit("", acentries);
for (CAccountingEntry& entry : acentries)
{
- txByTime.insert(std::make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
+ txByTime.insert(std::make_pair(entry.nTime, TxPair(nullptr, &entry)));
}
nOrderPosNext = 0;
@@ -704,7 +755,7 @@ DBErrors CWallet::ReorderTransactions()
{
CWalletTx *const pwtx = (*it).second.first;
CAccountingEntry *const pacentry = (*it).second.second;
- int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;
+ int64_t& nOrderPos = (pwtx != nullptr) ? pwtx->nOrderPos : pacentry->nOrderPos;
if (nOrderPos == -1)
{
@@ -889,7 +940,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
{
wtx.nTimeReceived = GetAdjustedTime();
wtx.nOrderPos = IncOrderPosNext(&walletdb);
- wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
+ wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr)));
wtx.nTimeSmart = ComputeTimeSmart(wtx);
AddToSpends(hash);
}
@@ -936,7 +987,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
// notify an external script when a wallet transaction comes in or is updated
- std::string strCmd = GetArg("-walletnotify", "");
+ std::string strCmd = gArgs.GetArg("-walletnotify", "");
if ( !strCmd.empty())
{
@@ -954,11 +1005,12 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
mapWallet[hash] = wtxIn;
CWalletTx& wtx = mapWallet[hash];
wtx.BindWallet(this);
- wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
+ wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr)));
AddToSpends(hash);
for (const CTxIn& txin : wtx.tx->vin) {
- if (mapWallet.count(txin.prevout.hash)) {
- CWalletTx& prevtx = mapWallet[txin.prevout.hash];
+ auto it = mapWallet.find(txin.prevout.hash);
+ if (it != mapWallet.end()) {
+ CWalletTx& prevtx = it->second;
if (prevtx.nIndex == -1 && !prevtx.hashUnset()) {
MarkConflicted(prevtx.hashBlock, wtx.GetHash());
}
@@ -971,7 +1023,7 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
/**
* 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
- * pIndex == NULL, then wallet state is not updated in AddToWallet, but
+ * pIndex == nullptr, 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.
@@ -987,7 +1039,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI
{
AssertLockHeld(cs_wallet);
- if (pIndex != NULL) {
+ if (pIndex != nullptr) {
for (const CTxIn& txin : tx.vin) {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);
while (range.first != range.second) {
@@ -1004,10 +1056,34 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI
if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
+ /* Check if any keys in the wallet keypool that were supposed to be unused
+ * have appeared in a new transaction. If so, remove those keys from the keypool.
+ * This can happen when restoring an old wallet backup that does not contain
+ * the mostly recently created transactions from newer versions of the wallet.
+ */
+
+ // loop though all outputs
+ for (const CTxOut& txout: tx.vout) {
+ // extract addresses and check if they match with an unused keypool key
+ std::vector<CKeyID> vAffected;
+ CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
+ for (const CKeyID &keyid : vAffected) {
+ std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);
+ if (mi != m_pool_key_to_index.end()) {
+ LogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
+ MarkReserveKeysAsUsed(mi->second);
+
+ if (!TopUpKeyPool()) {
+ LogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
+ }
+ }
+ }
+ }
+
CWalletTx wtx(this, ptx);
// Get merkle branch if transaction was found in a block
- if (pIndex != NULL)
+ if (pIndex != nullptr)
wtx.SetMerkleBranch(pIndex, posInBlock);
return AddToWallet(wtx, false);
@@ -1033,8 +1109,9 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
std::set<uint256> done;
// Can't mark abandoned if confirmed or in mempool
- assert(mapWallet.count(hashTx));
- CWalletTx& origtx = mapWallet[hashTx];
+ auto it = mapWallet.find(hashTx);
+ assert(it != mapWallet.end());
+ CWalletTx& origtx = it->second;
if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) {
return false;
}
@@ -1045,8 +1122,9 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
uint256 now = *todo.begin();
todo.erase(now);
done.insert(now);
- assert(mapWallet.count(now));
- CWalletTx& wtx = mapWallet[now];
+ auto it = mapWallet.find(now);
+ assert(it != mapWallet.end());
+ CWalletTx& wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain();
// If the orig tx was not in block, none of its spends can be
assert(currentconfirm <= 0);
@@ -1071,8 +1149,10 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
// available of the outputs it spends. So force those to be recomputed
for (const CTxIn& txin : wtx.tx->vin)
{
- if (mapWallet.count(txin.prevout.hash))
- mapWallet[txin.prevout.hash].MarkDirty();
+ auto it = mapWallet.find(txin.prevout.hash);
+ if (it != mapWallet.end()) {
+ it->second.MarkDirty();
+ }
}
}
}
@@ -1110,8 +1190,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
uint256 now = *todo.begin();
todo.erase(now);
done.insert(now);
- assert(mapWallet.count(now));
- CWalletTx& wtx = mapWallet[now];
+ auto it = mapWallet.find(now);
+ assert(it != mapWallet.end());
+ CWalletTx& wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain();
if (conflictconfirms < currentconfirm) {
// Block is 'more conflicted' than current confirm; update.
@@ -1130,10 +1211,11 @@ 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
- for (const CTxIn& txin : wtx.tx->vin)
- {
- if (mapWallet.count(txin.prevout.hash))
- mapWallet[txin.prevout.hash].MarkDirty();
+ for (const CTxIn& txin : wtx.tx->vin) {
+ auto it = mapWallet.find(txin.prevout.hash);
+ if (it != mapWallet.end()) {
+ it->second.MarkDirty();
+ }
}
}
}
@@ -1148,10 +1230,11 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pin
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be
// recomputed, also:
- for (const CTxIn& txin : tx.vin)
- {
- if (mapWallet.count(txin.prevout.hash))
- mapWallet[txin.prevout.hash].MarkDirty();
+ for (const CTxIn& txin : tx.vin) {
+ auto it = mapWallet.find(txin.prevout.hash);
+ if (it != mapWallet.end()) {
+ it->second.MarkDirty();
+ }
}
}
@@ -1631,7 +1714,7 @@ bool CWalletTx::RelayWalletTransaction(CConnman* connman)
std::set<uint256> CWalletTx::GetConflicts() const
{
std::set<uint256> result;
- if (pwallet != NULL)
+ if (pwallet != nullptr)
{
uint256 myHash = GetHash();
result = pwallet->GetConflicts(myHash);
@@ -1720,7 +1803,7 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
{
- if (pwallet == 0)
+ if (pwallet == nullptr)
return 0;
// Must wait until coinbase is safely deep enough in the chain before valuing it
@@ -1764,7 +1847,7 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const
CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
{
- if (pwallet == 0)
+ if (pwallet == nullptr)
return 0;
// Must wait until coinbase is safely deep enough in the chain before valuing it
@@ -1828,7 +1911,7 @@ bool CWalletTx::IsTrusted() const
{
// Transactions not sent by us: not trusted
const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash);
- if (parent == NULL)
+ if (parent == nullptr)
return false;
const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE)
@@ -1851,6 +1934,7 @@ std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime, CCon
std::vector<uint256> result;
LOCK(cs_wallet);
+
// Sort them in chronological order
std::multimap<unsigned int, CWalletTx*> mapSorted;
for (std::pair<const uint256, CWalletTx>& item : mapWallet)
@@ -2426,8 +2510,8 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
++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);
+ size_t nMaxChainLength = std::min(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT));
+ bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
bool res = nTargetValue <= nValueFromPresetInputs ||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) ||
@@ -2471,7 +2555,7 @@ bool CWallet::SignTransaction(CMutableTransaction &tx)
return true;
}
-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl, bool keepReserveKey)
+bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
{
std::vector<CRecipient> vecSend;
@@ -2493,8 +2577,13 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
return false;
}
- if (nChangePosInOut != -1)
+
+ if (nChangePosInOut != -1) {
tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]);
+ // we don't have the normal Create/Commit cycle, and don't want to risk reusing change,
+ // so just remove the key from the keypool here.
+ reservekey.KeepKey();
+ }
// 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++)
@@ -2515,13 +2604,21 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
}
}
- // optionally keep the change output key
- if (keepReserveKey)
- reservekey.KeepKey();
return true;
}
+static CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator)
+{
+ unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */);
+ // Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate
+ discard_rate = (discard_rate == CFeeRate(0)) ? CWallet::m_discard_rate : std::min(discard_rate, CWallet::m_discard_rate);
+ // Discard rate must be at least dustRelayFee
+ discard_rate = std::max(discard_rate, ::dustRelayFee);
+ return discard_rate;
+}
+
bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{
@@ -2621,6 +2718,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
CTxOut change_prototype_txout(0, scriptChange);
size_t change_prototype_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0);
+ CFeeRate discard_rate = GetDiscardRate(::feeEstimator);
nFeeRet = 0;
bool pick_new_inputs = true;
CAmount nValueIn = 0;
@@ -2688,7 +2786,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// Never create dust outputs; if we would, just
// add the dust to the fee.
- if (IsDust(newTxOut, ::dustRelayFee))
+ if (IsDust(newTxOut, discard_rate))
{
nChangePosInOut = -1;
nFeeRet += nChange;
@@ -2723,7 +2821,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// to avoid conflicting with other possible uses of nSequence,
// and in the spirit of "smallest possible change from prior
// behavior."
- const uint32_t nSequence = coin_control.signalRbf ? MAX_BIP125_RBF_SEQUENCE : (std::numeric_limits<unsigned int>::max() - 1);
+ const uint32_t nSequence = coin_control.signalRbf ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
for (const auto& coin : setCoins)
txNew.vin.push_back(CTxIn(coin.outpoint,CScript(),
nSequence));
@@ -2758,17 +2856,13 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// 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.
-
// If we have no change and a big enough excess fee, then
// try to construct transaction again only without picking
// new inputs. We now know we only need the smaller fee
// (because of reduced tx size) and so we should add a
// change output. Only try this once.
CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, coin_control, ::mempool, ::feeEstimator, nullptr);
- CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, ::dustRelayFee);
+ CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
CAmount max_excess_fee = fee_needed_for_change + minimum_value_for_change;
if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
pick_new_inputs = false;
@@ -2788,6 +2882,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
else if (!pick_new_inputs) {
// This shouldn't happen, we should have had enough excess
// fee to pay for the new output and still meet nFeeNeeded
+ // Or we should have just subtracted fee from recipients and
+ // nFeeNeeded should not have changed
strFailReason = _("Transaction fee and change calculation failed");
return false;
}
@@ -2804,6 +2900,12 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
}
}
+ // If subtracting fee from recipients, we now know what fee we
+ // need to subtract, we have no reason to reselect inputs
+ if (nSubtractFeeFromAmount > 0) {
+ pick_new_inputs = false;
+ }
+
// Include more fee and try again.
nFeeRet = nFeeNeeded;
continue;
@@ -2844,15 +2946,15 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
}
}
- if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
+ if (gArgs.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, 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;
+ size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
+ size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
+ size_t nLimitDescendantSize = gArgs.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");
@@ -2933,7 +3035,7 @@ bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwa
laccentries.push_back(acentry);
CAccountingEntry & entry = laccentries.back();
- wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
+ wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair(nullptr, &entry)));
return true;
}
@@ -2979,7 +3081,7 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_c
if (feeCalc) feeCalc->reason = FeeReason::FALLBACK;
}
// Obey mempool min fee when using smart fee estimation
- CAmount min_mempool_fee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes);
+ CAmount min_mempool_fee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes);
if (fee_needed < min_mempool_fee) {
fee_needed = min_mempool_fee;
if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN;
@@ -3014,15 +3116,18 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
LOCK(cs_wallet);
setInternalKeyPool.clear();
setExternalKeyPool.clear();
+ m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
}
}
+ // This wallet is in its first run if all of these are empty
+ fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty();
+
if (nLoadWalletRet != DB_LOAD_OK)
return nLoadWalletRet;
- fFirstRunRet = !vchDefaultKey.IsValid();
uiInterface.LoadWallet(this);
@@ -3032,7 +3137,6 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
{
AssertLockHeld(cs_wallet); // mapWallet
- vchDefaultKey = CPubKey();
DBErrors nZapSelectTxRet = CWalletDB(*dbw,"cr+").ZapSelectTx(vHashIn, vHashOut);
for (uint256 hash : vHashOut)
mapWallet.erase(hash);
@@ -3043,6 +3147,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
{
setInternalKeyPool.clear();
setExternalKeyPool.clear();
+ m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
@@ -3060,7 +3165,6 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
{
- vchDefaultKey = CPubKey();
DBErrors nZapWalletTxRet = CWalletDB(*dbw,"cr+").ZapWalletTx(vWtx);
if (nZapWalletTxRet == DB_NEED_REWRITE)
{
@@ -3069,6 +3173,7 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
LOCK(cs_wallet);
setInternalKeyPool.clear();
setExternalKeyPool.clear();
+ m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
@@ -3135,14 +3240,6 @@ const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const
return DEFAULT_ACCOUNT_NAME;
}
-bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
-{
- if (!CWalletDB(*dbw).WriteDefaultKey(vchPubKey))
- return false;
- vchDefaultKey = vchPubKey;
- return true;
-}
-
/**
* Mark old keypool keys as used,
* and generate all new keys
@@ -3163,6 +3260,8 @@ bool CWallet::NewKeyPool()
}
setExternalKeyPool.clear();
+ m_pool_key_to_index.clear();
+
if (!TopUpKeyPool()) {
return false;
}
@@ -3177,6 +3276,25 @@ size_t CWallet::KeypoolCountExternalKeys()
return setExternalKeyPool.size();
}
+void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
+{
+ AssertLockHeld(cs_wallet);
+ if (keypool.fInternal) {
+ setInternalKeyPool.insert(nIndex);
+ } else {
+ setExternalKeyPool.insert(nIndex);
+ }
+ m_max_keypool_index = std::max(m_max_keypool_index, nIndex);
+ m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex;
+
+ // If no metadata exists yet, create a default with the pool key's
+ // creation time. Note that this may be overwritten by actually
+ // stored metadata for that key later, which is fine.
+ CKeyID keyid = keypool.vchPubKey.GetID();
+ if (mapKeyMetadata.count(keyid) == 0)
+ mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
+}
+
bool CWallet::TopUpKeyPool(unsigned int kpSize)
{
{
@@ -3190,7 +3308,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
if (kpSize > 0)
nTargetSize = kpSize;
else
- nTargetSize = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
+ nTargetSize = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
// count amount of available keys (internal, external)
// make sure the keypool of external and internal keys fits the user selected target (-keypool)
@@ -3206,27 +3324,24 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
CWalletDB walletdb(*dbw);
for (int64_t i = missingInternal + missingExternal; i--;)
{
- int64_t nEnd = 1;
if (i < missingInternal) {
internal = true;
}
- if (!setInternalKeyPool.empty()) {
- nEnd = *(setInternalKeyPool.rbegin()) + 1;
- }
- if (!setExternalKeyPool.empty()) {
- nEnd = std::max(nEnd, *(setExternalKeyPool.rbegin()) + 1);
- }
+ assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys?
+ int64_t index = ++m_max_keypool_index;
- if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(walletdb, internal), internal))) {
+ CPubKey pubkey(GenerateNewKey(walletdb, internal));
+ if (!walletdb.WritePool(index, CKeyPool(pubkey, internal))) {
throw std::runtime_error(std::string(__func__) + ": writing generated key failed");
}
if (internal) {
- setInternalKeyPool.insert(nEnd);
+ setInternalKeyPool.insert(index);
} else {
- setExternalKeyPool.insert(nEnd);
+ setExternalKeyPool.insert(index);
}
+ m_pool_key_to_index[pubkey.GetID()] = index;
}
if (missingInternal + missingExternal > 0) {
LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size());
@@ -3268,6 +3383,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
}
assert(keypool.vchPubKey.IsValid());
+ m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
LogPrintf("keypool reserve %d\n", nIndex);
}
}
@@ -3280,7 +3396,7 @@ void CWallet::KeepKey(int64_t nIndex)
LogPrintf("keypool keep %d\n", nIndex);
}
-void CWallet::ReturnKey(int64_t nIndex, bool fInternal)
+void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
{
// Return to key pool
{
@@ -3290,6 +3406,7 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal)
} else {
setExternalKeyPool.insert(nIndex);
}
+ m_pool_key_to_index[pubkey.GetID()] = nIndex;
}
LogPrintf("keypool return %d\n", nIndex);
}
@@ -3519,38 +3636,32 @@ void CReserveKey::KeepKey()
void CReserveKey::ReturnKey()
{
if (nIndex != -1) {
- pwallet->ReturnKey(nIndex, fInternal);
+ pwallet->ReturnKey(nIndex, fInternal, vchPubKey);
}
nIndex = -1;
vchPubKey = CPubKey();
}
-static void LoadReserveKeysToSet(std::set<CKeyID>& setAddress, const std::set<int64_t>& setKeyPool, CWalletDB& walletdb) {
- for (const int64_t& id : setKeyPool)
- {
- CKeyPool keypool;
- if (!walletdb.ReadPool(id, keypool))
- throw std::runtime_error(std::string(__func__) + ": read failed");
- assert(keypool.vchPubKey.IsValid());
- CKeyID keyID = keypool.vchPubKey.GetID();
- setAddress.insert(keyID);
- }
-}
-
-void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
+void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
{
- setAddress.clear();
+ AssertLockHeld(cs_wallet);
+ bool internal = setInternalKeyPool.count(keypool_id);
+ if (!internal) assert(setExternalKeyPool.count(keypool_id));
+ std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool;
+ auto it = setKeyPool->begin();
CWalletDB walletdb(*dbw);
+ while (it != std::end(*setKeyPool)) {
+ const int64_t& index = *(it);
+ if (index > keypool_id) break; // set*KeyPool is ordered
- LOCK2(cs_main, cs_wallet);
- LoadReserveKeysToSet(setAddress, setInternalKeyPool, walletdb);
- LoadReserveKeysToSet(setAddress, setExternalKeyPool, walletdb);
-
- for (const CKeyID& keyID : setAddress) {
- if (!HaveKey(keyID)) {
- throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
+ CKeyPool keypool;
+ if (walletdb.ReadPool(index, keypool)) { //TODO: This should be unnecessary
+ m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
}
+ walletdb.ErasePool(index);
+ LogPrintf("keypool index %d removed\n", index);
+ it = setKeyPool->erase(it);
}
}
@@ -3603,38 +3714,6 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
/** @} */ // end of Actions
-class CAffectedKeysVisitor : public boost::static_visitor<void> {
-private:
- const CKeyStore &keystore;
- std::vector<CKeyID> &vKeys;
-
-public:
- CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {}
-
- void Process(const CScript &script) {
- txnouttype type;
- std::vector<CTxDestination> vDest;
- int nRequired;
- if (ExtractDestinations(script, type, vDest, nRequired)) {
- for (const CTxDestination &dest : vDest)
- boost::apply_visitor(*this, dest);
- }
- }
-
- void operator()(const CKeyID &keyId) {
- if (keystore.HaveKey(keyId))
- vKeys.push_back(keyId);
- }
-
- void operator()(const CScriptID &scriptId) {
- CScript script;
- if (keystore.GetCScript(scriptId, script))
- Process(script);
- }
-
- void operator()(const CNoDestination &none) {}
-};
-
void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const {
AssertLockHeld(cs_wallet); // mapKeyMetadata
mapKeyBirth.clear();
@@ -3813,6 +3892,9 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE));
strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)));
+ strUsage += HelpMessageOpt("-discardfee=<amt>", strprintf(_("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
+ "Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target"),
+ CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)));
strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));
strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
@@ -3848,7 +3930,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
// needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
- if (GetBoolArg("-zapwallettxes", false)) {
+ if (gArgs.GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
@@ -3856,11 +3938,11 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
- return NULL;
+ return nullptr;
}
delete tempWallet;
- tempWallet = NULL;
+ tempWallet = nullptr;
}
uiInterface.InitMessage(_("Loading wallet..."));
@@ -3874,7 +3956,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
{
if (nLoadWalletRet == DB_CORRUPT) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
- return NULL;
+ return nullptr;
}
else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
{
@@ -3884,22 +3966,22 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
}
else if (nLoadWalletRet == DB_TOO_NEW) {
InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));
- return NULL;
+ return nullptr;
}
else if (nLoadWalletRet == DB_NEED_REWRITE)
{
InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
- return NULL;
+ return nullptr;
}
else {
InitError(strprintf(_("Error loading %s"), walletFile));
- return NULL;
+ return nullptr;
}
}
- if (GetBoolArg("-upgradewallet", fFirstRun))
+ if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
{
- int nMaxVersion = GetArg("-upgradewallet", 0);
+ int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
if (nMaxVersion == 0) // the -upgradewallet without argument case
{
LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
@@ -3911,7 +3993,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
if (nMaxVersion < walletInstance->GetVersion())
{
InitError(_("Cannot downgrade wallet"));
- return NULL;
+ return nullptr;
}
walletInstance->SetMaxVersion(nMaxVersion);
}
@@ -3919,7 +4001,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
if (fFirstRun)
{
// Create new keyUser and set as default key
- if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) {
+ if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) {
// ensure this wallet.dat can only be opened by clients supporting HD with chain split
walletInstance->SetMinVersion(FEATURE_HD_SPLIT);
@@ -3929,26 +4011,24 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
if (!walletInstance->SetHDMasterKey(masterPubKey))
throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
}
- CPubKey newDefaultKey;
- if (walletInstance->GetKeyFromPool(newDefaultKey, false)) {
- walletInstance->SetDefaultKey(newDefaultKey);
- if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) {
- InitError(_("Cannot write default address") += "\n");
- return NULL;
- }
+
+ // Top up the keypool
+ if (!walletInstance->TopUpKeyPool()) {
+ InitError(_("Unable to generate initial keys") += "\n");
+ return NULL;
}
walletInstance->SetBestChain(chainActive.GetLocator());
}
- else if (IsArgSet("-usehd")) {
- bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET);
+ else if (gArgs.IsArgSet("-usehd")) {
+ bool useHD = gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET);
if (walletInstance->IsHDEnabled() && !useHD) {
InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile));
- return NULL;
+ return nullptr;
}
if (!walletInstance->IsHDEnabled() && useHD) {
InitError(strprintf(_("Error loading %s: You can't enable HD on an already existing non-HD wallet"), walletFile));
- return NULL;
+ return nullptr;
}
}
@@ -3956,8 +4036,11 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
RegisterValidationInterface(walletInstance);
+ // Try to top up keypool. No-op if the wallet is locked.
+ walletInstance->TopUpKeyPool();
+
CBlockIndex *pindexRescan = chainActive.Genesis();
- if (!GetBoolArg("-rescan", false))
+ if (!gArgs.GetBoolArg("-rescan", false))
{
CWalletDB walletdb(*walletInstance->dbw);
CBlockLocator locator;
@@ -3977,7 +4060,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
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;
+ return nullptr;
}
}
@@ -3997,7 +4080,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
walletInstance->dbw->IncrementUpdateCounter();
// Restore wallet transaction metadata after -zapwallettxes=1
- if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2")
+ if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2")
{
CWalletDB walletdb(*walletInstance->dbw);
@@ -4021,7 +4104,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
}
}
}
- walletInstance->SetBroadcastTransactions(GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
+ walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
{
LOCK(walletInstance->cs_wallet);
@@ -4035,7 +4118,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
bool CWallet::InitLoadWallet()
{
- if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
+ if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
LogPrintf("Wallet disabled!\n");
return true;
}
@@ -4067,29 +4150,29 @@ void CWallet::postInitProcess(CScheduler& scheduler)
bool CWallet::ParameterInteraction()
{
- SoftSetArg("-wallet", DEFAULT_WALLET_DAT);
+ gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT);
const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
- if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
+ if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
return true;
- if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) {
+ if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
}
- if (GetBoolArg("-salvagewallet", false)) {
+ if (gArgs.GetBoolArg("-salvagewallet", false)) {
if (is_multiwallet) {
return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet"));
}
// Rewrite just private keys: rescan to find transactions
- if (SoftSetBoolArg("-rescan", true)) {
+ if (gArgs.SoftSetBoolArg("-rescan", true)) {
LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
}
}
- int zapwallettxes = GetArg("-zapwallettxes", 0);
+ int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0);
// -zapwallettxes implies dropping the mempool on startup
- if (zapwallettxes != 0 && SoftSetBoolArg("-persistmempool", false)) {
+ if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) {
LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes);
}
@@ -4098,51 +4181,61 @@ bool CWallet::ParameterInteraction()
if (is_multiwallet) {
return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes"));
}
- if (SoftSetBoolArg("-rescan", true)) {
+ if (gArgs.SoftSetBoolArg("-rescan", true)) {
LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes);
}
}
if (is_multiwallet) {
- if (GetBoolArg("-upgradewallet", false)) {
+ if (gArgs.GetBoolArg("-upgradewallet", false)) {
return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet"));
}
}
- if (GetBoolArg("-sysperms", false))
+ if (gArgs.GetBoolArg("-sysperms", false))
return InitError("-sysperms is not allowed in combination with enabled wallet functionality");
- if (GetArg("-prune", 0) && GetBoolArg("-rescan", false))
+ if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false))
return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));
if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB)
InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
_("The wallet will avoid paying less than the minimum relay fee."));
- if (IsArgSet("-mintxfee"))
+ if (gArgs.IsArgSet("-mintxfee"))
{
CAmount n = 0;
- if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n)
- return InitError(AmountErrMsg("mintxfee", GetArg("-mintxfee", "")));
+ if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n)
+ return InitError(AmountErrMsg("mintxfee", gArgs.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 (IsArgSet("-fallbackfee"))
+ if (gArgs.IsArgSet("-fallbackfee"))
{
CAmount nFeePerK = 0;
- if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK))
- return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), GetArg("-fallbackfee", "")));
+ if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK))
+ return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.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 (IsArgSet("-paytxfee"))
+ if (gArgs.IsArgSet("-discardfee"))
+ {
+ CAmount nFeePerK = 0;
+ if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK))
+ return InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")));
+ if (nFeePerK > HIGH_TX_FEE_PER_KB)
+ InitWarning(AmountHighWarn("-discardfee") + " " +
+ _("This is the transaction fee you may discard if change is smaller than dust at this level"));
+ CWallet::m_discard_rate = CFeeRate(nFeePerK);
+ }
+ if (gArgs.IsArgSet("-paytxfee"))
{
CAmount nFeePerK = 0;
- if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK))
- return InitError(AmountErrMsg("paytxfee", GetArg("-paytxfee", "")));
+ if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK))
+ return InitError(AmountErrMsg("paytxfee", gArgs.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."));
@@ -4151,26 +4244,26 @@ bool CWallet::ParameterInteraction()
if (payTxFee < ::minRelayTxFee)
{
return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
- GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
+ gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
}
}
- if (IsArgSet("-maxtxfee"))
+ if (gArgs.IsArgSet("-maxtxfee"))
{
CAmount nMaxFee = 0;
- if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee))
- return InitError(AmountErrMsg("maxtxfee", GetArg("-maxtxfee", "")));
+ if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee))
+ return InitError(AmountErrMsg("maxtxfee", gArgs.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)"),
- GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
+ gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
}
}
- nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
- bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
- fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
+ nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
+ bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
+ fWalletRbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
return true;
}
@@ -4237,5 +4330,5 @@ int CMerkleTx::GetBlocksToMaturity() const
bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
- return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
+ return ::AcceptToMemoryPool(mempool, state, tx, true, nullptr, nullptr, false, nAbsurdFee);
}