Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show All 36 Lines | |||||
#include <boost/algorithm/string/replace.hpp> | #include <boost/algorithm/string/replace.hpp> | ||||
#include <cassert> | #include <cassert> | ||||
#include <future> | #include <future> | ||||
std::vector<CWalletRef> vpwallets; | std::vector<CWalletRef> vpwallets; | ||||
/** Transaction fee set by the user */ | |||||
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); | |||||
bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; | |||||
OutputType g_address_type = OutputType::NONE; | OutputType g_address_type = OutputType::NONE; | ||||
OutputType g_change_type = OutputType::NONE; | OutputType g_change_type = OutputType::NONE; | ||||
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; | const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; | ||||
/** | |||||
* Fees smaller than this (in satoshi) are considered zero fee (for transaction | |||||
* creation) | |||||
* Override with -mintxfee | |||||
*/ | |||||
CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE_PER_KB); | |||||
/** | |||||
* If fee estimation does not have enough data to provide estimates, use this | |||||
* fee instead. Has no effect if not using fee estimation. | |||||
* Override with -fallbackfee | |||||
*/ | |||||
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE); | |||||
const uint256 CMerkleTx::ABANDON_HASH(uint256S( | const uint256 CMerkleTx::ABANDON_HASH(uint256S( | ||||
"0000000000000000000000000000000000000000000000000000000000000001")); | "0000000000000000000000000000000000000000000000000000000000000001")); | ||||
/** @defgroup mapWallet | /** @defgroup mapWallet | ||||
* | * | ||||
* @{ | * @{ | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 2,068 Lines • ▼ Show 20 Lines | if (nDepth >= 1) { | ||||
return true; | return true; | ||||
} | } | ||||
if (nDepth < 0) { | if (nDepth < 0) { | ||||
return false; | return false; | ||||
} | } | ||||
// using wtx's cached debit | // using wtx's cached debit | ||||
if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) { | if (!pwallet->m_spend_zero_conf_change || !IsFromMe(ISMINE_ALL)) { | ||||
return false; | return false; | ||||
} | } | ||||
// Don't trust unconfirmed transactions from us unless they are in the | // Don't trust unconfirmed transactions from us unless they are in the | ||||
// mempool. | // mempool. | ||||
if (!InMempool()) { | if (!InMempool()) { | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 459 Lines • ▼ Show 20 Lines | bool CWallet::SelectCoinsMinConf( | ||||
setCoinsRet.clear(); | setCoinsRet.clear(); | ||||
nValueRet = Amount::zero(); | nValueRet = Amount::zero(); | ||||
std::vector<CInputCoin> utxo_pool; | std::vector<CInputCoin> utxo_pool; | ||||
if (coin_selection_params.use_bnb) { | if (coin_selection_params.use_bnb) { | ||||
// Get long term estimate | // Get long term estimate | ||||
CCoinControl temp; | CCoinControl temp; | ||||
temp.m_confirm_target = 1008; | temp.m_confirm_target = 1008; | ||||
CFeeRate long_term_feerate = GetMinimumFeeRate(temp, g_mempool); | CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, g_mempool); | ||||
// Calculate cost of change | // Calculate cost of change | ||||
Amount cost_of_change = | Amount cost_of_change = | ||||
dustRelayFee.GetFee(coin_selection_params.change_spend_size) + | dustRelayFee.GetFee(coin_selection_params.change_spend_size) + | ||||
coin_selection_params.effective_fee.GetFee( | coin_selection_params.effective_fee.GetFee( | ||||
coin_selection_params.change_output_size); | coin_selection_params.change_output_size); | ||||
// Filter by the min conf specs and add to utxo_pool and calculate | // Filter by the min conf specs and add to utxo_pool and calculate | ||||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | bool CWallet::SelectCoins(const std::vector<COutput> &vAvailableCoins, | ||||
bool res = | bool res = | ||||
nTargetValue <= nValueFromPresetInputs || | nTargetValue <= nValueFromPresetInputs || | ||||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, | SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, | ||||
CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, | CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, | ||||
nValueRet, coin_selection_params, bnb_used) || | nValueRet, coin_selection_params, bnb_used) || | ||||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, | SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, | ||||
CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, | CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, | ||||
nValueRet, coin_selection_params, bnb_used) || | nValueRet, coin_selection_params, bnb_used) || | ||||
(bSpendZeroConfChange && | (m_spend_zero_conf_change && | ||||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, | SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, | ||||
CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, | CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, | ||||
nValueRet, coin_selection_params, bnb_used)) || | nValueRet, coin_selection_params, bnb_used)) || | ||||
(bSpendZeroConfChange && | (m_spend_zero_conf_change && | ||||
SelectCoinsMinConf( | SelectCoinsMinConf( | ||||
nTargetValue - nValueFromPresetInputs, | nTargetValue - nValueFromPresetInputs, | ||||
CoinEligibilityFilter(0, 1, std::min<size_t>(4, max_ancestors / 3), | CoinEligibilityFilter(0, 1, std::min<size_t>(4, max_ancestors / 3), | ||||
std::min<size_t>(4, max_descendants / 3)), | std::min<size_t>(4, max_descendants / 3)), | ||||
vCoins, setCoinsRet, nValueRet, coin_selection_params, | vCoins, setCoinsRet, nValueRet, coin_selection_params, | ||||
bnb_used)) || | bnb_used)) || | ||||
(bSpendZeroConfChange && | (m_spend_zero_conf_change && | ||||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, | SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, | ||||
CoinEligibilityFilter(0, 1, max_ancestors / 2, | CoinEligibilityFilter(0, 1, max_ancestors / 2, | ||||
max_descendants / 2), | max_descendants / 2), | ||||
vCoins, setCoinsRet, nValueRet, | vCoins, setCoinsRet, nValueRet, | ||||
coin_selection_params, bnb_used)) || | coin_selection_params, bnb_used)) || | ||||
(bSpendZeroConfChange && | (m_spend_zero_conf_change && | ||||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, | SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, | ||||
CoinEligibilityFilter(0, 1, max_ancestors - 1, | CoinEligibilityFilter(0, 1, max_ancestors - 1, | ||||
max_descendants - 1), | max_descendants - 1), | ||||
vCoins, setCoinsRet, nValueRet, | vCoins, setCoinsRet, nValueRet, | ||||
coin_selection_params, bnb_used)) || | coin_selection_params, bnb_used)) || | ||||
(bSpendZeroConfChange && !fRejectLongChains && | (m_spend_zero_conf_change && !fRejectLongChains && | ||||
SelectCoinsMinConf( | SelectCoinsMinConf( | ||||
nTargetValue - nValueFromPresetInputs, | nTargetValue - nValueFromPresetInputs, | ||||
CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), | CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), | ||||
vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); | vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); | ||||
// Because SelectCoinsMinConf clears the setCoinsRet, we now add the | // Because SelectCoinsMinConf clears the setCoinsRet, we now add the | ||||
// possible inputs to the coinset. | // possible inputs to the coinset. | ||||
setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); | setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); | ||||
▲ Show 20 Lines • Show All 207 Lines • ▼ Show 20 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
scriptChange = GetScriptForDestination( | scriptChange = GetScriptForDestination( | ||||
GetDestinationForKey(vchPubKey, g_change_type)); | GetDestinationForKey(vchPubKey, g_change_type)); | ||||
} | } | ||||
CTxOut change_prototype_txout(Amount::zero(), scriptChange); | CTxOut change_prototype_txout(Amount::zero(), scriptChange); | ||||
coin_selection_params.change_output_size = | coin_selection_params.change_output_size = | ||||
GetSerializeSize(change_prototype_txout, SER_DISK, 0); | GetSerializeSize(change_prototype_txout, SER_DISK, 0); | ||||
// Get the fee rate to use effective values in coin selection | // Get the fee rate to use effective values in coin selection | ||||
CFeeRate nFeeRateNeeded = GetMinimumFeeRate(coinControl, g_mempool); | CFeeRate nFeeRateNeeded = | ||||
GetMinimumFeeRate(*this, coinControl, g_mempool); | |||||
nFeeRet = Amount::zero(); | nFeeRet = Amount::zero(); | ||||
bool pick_new_inputs = true; | bool pick_new_inputs = true; | ||||
Amount nValueIn = Amount::zero(); | Amount nValueIn = Amount::zero(); | ||||
// BnB selector is the only selector used when this is true. | // BnB selector is the only selector used when this is true. | ||||
// That should only happen on the first pass through the loop. | // That should only happen on the first pass through the loop. | ||||
// If we are doing subtract fee from recipient, then don't use BnB | // If we are doing subtract fee from recipient, then don't use BnB | ||||
▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
CTransaction txNewConst(txNew); | CTransaction txNewConst(txNew); | ||||
int nBytes = CalculateMaximumSignedTxSize(txNewConst, this); | int nBytes = CalculateMaximumSignedTxSize(txNewConst, this); | ||||
if (nBytes < 0) { | if (nBytes < 0) { | ||||
strFailReason = _("Signing transaction failed"); | strFailReason = _("Signing transaction failed"); | ||||
return false; | return false; | ||||
} | } | ||||
Amount nFeeNeeded = GetMinimumFee(nBytes, coinControl, g_mempool); | Amount nFeeNeeded = | ||||
GetMinimumFee(*this, nBytes, coinControl, g_mempool); | |||||
// If we made it here and we aren't even able to meet the relay fee | // If we made it here and we aren't even able to meet the relay fee | ||||
// on the next pass, give up because we must be at the maximum | // on the next pass, give up because we must be at the maximum | ||||
// allowed fee. | // allowed fee. | ||||
if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) { | if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) { | ||||
strFailReason = _("Transaction too large for fee policy"); | strFailReason = _("Transaction too large for fee policy"); | ||||
return false; | return false; | ||||
} | } | ||||
Show All 11 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
// once. | // once. | ||||
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && | if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && | ||||
pick_new_inputs) { | pick_new_inputs) { | ||||
// Add 2 as a buffer in case increasing # of outputs changes | // Add 2 as a buffer in case increasing # of outputs changes | ||||
// compact size | // compact size | ||||
unsigned int tx_size_with_change = | unsigned int tx_size_with_change = | ||||
nBytes + coin_selection_params.change_output_size + 2; | nBytes + coin_selection_params.change_output_size + 2; | ||||
Amount fee_needed_with_change = GetMinimumFee( | Amount fee_needed_with_change = GetMinimumFee( | ||||
tx_size_with_change, coinControl, g_mempool); | *this, tx_size_with_change, coinControl, g_mempool); | ||||
Amount minimum_value_for_change = | Amount minimum_value_for_change = | ||||
GetDustThreshold(change_prototype_txout, dustRelayFee); | GetDustThreshold(change_prototype_txout, dustRelayFee); | ||||
if (nFeeRet >= | if (nFeeRet >= | ||||
fee_needed_with_change + minimum_value_for_change) { | fee_needed_with_change + minimum_value_for_change) { | ||||
pick_new_inputs = false; | pick_new_inputs = false; | ||||
nFeeRet = fee_needed_with_change; | nFeeRet = fee_needed_with_change; | ||||
continue; | continue; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,159 Lines • ▼ Show 20 Lines | if (fFirstRun) { | ||||
if (!walletInstance->IsHDEnabled() && useHD) { | if (!walletInstance->IsHDEnabled() && useHD) { | ||||
InitError(strprintf(_("Error loading %s: You can't enable HD on an " | InitError(strprintf(_("Error loading %s: You can't enable HD on an " | ||||
"already existing non-HD wallet"), | "already existing non-HD wallet"), | ||||
walletFile)); | walletFile)); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
if (gArgs.IsArgSet("-mintxfee")) { | |||||
Amount n = Amount::zero(); | |||||
if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || | |||||
n == Amount::zero()) { | |||||
InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); | |||||
return nullptr; | |||||
} | |||||
if (n > HIGH_TX_FEE_PER_KB) { | |||||
InitWarning(AmountHighWarn("-mintxfee") + " " + | |||||
_("This is the minimum transaction fee you pay on " | |||||
"every transaction.")); | |||||
} | |||||
walletInstance->m_min_fee = CFeeRate(n); | |||||
} | |||||
if (gArgs.IsArgSet("-fallbackfee")) { | |||||
Amount nFeePerK = Amount::zero(); | |||||
if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) { | |||||
InitError( | |||||
strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), | |||||
gArgs.GetArg("-fallbackfee", ""))); | |||||
return nullptr; | |||||
} | |||||
if (nFeePerK > HIGH_TX_FEE_PER_KB) { | |||||
InitWarning(AmountHighWarn("-fallbackfee") + " " + | |||||
_("This is the transaction fee you may pay when fee " | |||||
"estimates are not available.")); | |||||
} | |||||
walletInstance->m_fallback_fee = CFeeRate(nFeePerK); | |||||
// disable fallback fee in case value was set to 0, enable if non-null | |||||
// value | |||||
walletInstance->m_allow_fallback_fee = (nFeePerK != Amount::zero()); | |||||
} | |||||
if (gArgs.IsArgSet("-paytxfee")) { | |||||
Amount nFeePerK = Amount::zero(); | |||||
if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) { | |||||
InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); | |||||
return nullptr; | |||||
} | |||||
if (nFeePerK > HIGH_TX_FEE_PER_KB) { | |||||
InitWarning(AmountHighWarn("-paytxfee") + " " + | |||||
_("This is the transaction fee you will pay if you " | |||||
"send a transaction.")); | |||||
} | |||||
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000); | |||||
if (walletInstance->m_pay_tx_fee < ::minRelayTxFee) { | |||||
InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' " | |||||
"(must be at least %s)"), | |||||
gArgs.GetArg("-paytxfee", ""), | |||||
::minRelayTxFee.ToString())); | |||||
return nullptr; | |||||
} | |||||
} | |||||
walletInstance->m_spend_zero_conf_change = | |||||
gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); | |||||
LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); | LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); | ||||
// Try to top up keypool. No-op if the wallet is locked. | // Try to top up keypool. No-op if the wallet is locked. | ||||
walletInstance->TopUpKeyPool(); | walletInstance->TopUpKeyPool(); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CBlockIndex *pindexRescan = chainActive.Genesis(); | CBlockIndex *pindexRescan = chainActive.Genesis(); | ||||
▲ Show 20 Lines • Show All 240 Lines • Show Last 20 Lines |