diff options
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r-- | src/wallet/wallet.cpp | 110 |
1 files changed, 56 insertions, 54 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6cf4c53da5..13da7bb2c2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2469,9 +2469,9 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC CReserveKey reservekey(this); CWalletTx wtx; - if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, &coinControl, false)) + if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) { return false; - + } if (nChangePosInOut != -1) tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); @@ -2502,7 +2502,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC } bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, - int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) + int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign) { CAmount nValue = 0; int nChangePosRequest = nChangePosInOut; @@ -2567,7 +2567,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT LOCK2(cs_main, cs_wallet); { std::vector<COutput> vAvailableCoins; - AvailableCoins(vAvailableCoins, true, coinControl); + AvailableCoins(vAvailableCoins, true, &coin_control); // Create change script that will be used if we need change // TODO: pass in scriptChange instead of reservekey so @@ -2575,12 +2575,9 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT CScript scriptChange; // coin control: send change to custom address - if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange)) - scriptChange = GetScriptForDestination(coinControl->destChange); - - // no coin control: send change to newly generated address - else - { + if (!boost::get<CNoDestination>(&coin_control.destChange)) { + scriptChange = GetScriptForDestination(coin_control.destChange); + } else { // no coin control: send change to newly generated address // Note: We use a new key here to keep it from being obvious which side is the change. // The drawback is that by not reusing a previous key, the change may be lost if a // backup is restored, if the backup doesn't have the new private key for the change. @@ -2654,7 +2651,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT if (pick_new_inputs) { nValueIn = 0; setCoins.clear(); - if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, &coin_control)) { strFailReason = _("Insufficient funds"); return false; @@ -2705,8 +2702,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." - bool rbf = coinControl ? coinControl->signalRbf : fWalletRbf; - const uint32_t nSequence = rbf ? MAX_BIP125_RBF_SEQUENCE : (std::numeric_limits<unsigned int>::max() - 1); + const uint32_t nSequence = coin_control.signalRbf ? MAX_BIP125_RBF_SEQUENCE : (std::numeric_limits<unsigned int>::max() - 1); for (const auto& coin : setCoins) txNew.vin.push_back(CTxIn(coin.outpoint,CScript(), nSequence)); @@ -2725,17 +2721,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT vin.scriptWitness.SetNull(); } - // Allow to override the default confirmation target over the CoinControl instance - int currentConfirmationTarget = nTxConfirmTarget; - if (coinControl && coinControl->nConfirmTarget > 0) - currentConfirmationTarget = coinControl->nConfirmTarget; - - // Allow to override the default fee estimate mode over the CoinControl instance - bool conservative_estimate = CalculateEstimateType(coinControl ? coinControl->m_fee_mode : FeeEstimateMode::UNSET, rbf); - - CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc, false /* ignoreGlobalPayTxFee */, conservative_estimate); - if (coinControl && coinControl->fOverrideFeeRate) - nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + CAmount nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc); // 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 allowed fee. @@ -2760,7 +2746,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT // 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, currentConfirmationTarget, ::mempool, ::feeEstimator, nullptr, false /* ignoreGlobalPayTxFee */, conservative_estimate); + 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 max_excess_fee = fee_needed_for_change + minimum_value_for_change; if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { @@ -2936,33 +2922,61 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes) return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); } -CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee, bool conservative_estimate) +CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc) { - // payTxFee is the user-set global for desired feerate - CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); - // User didn't set: use -txconfirmtarget to estimate... - if (nFeeNeeded == 0 || ignoreGlobalPayTxFee) { - nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, feeCalc, pool, conservative_estimate).GetFee(nTxBytes); - // ... unless we don't have enough mempool data for estimatefee, then use fallbackFee - if (nFeeNeeded == 0) { - nFeeNeeded = fallbackFee.GetFee(nTxBytes); + /* User control of how to calculate fee uses the following parameter precedence: + 1. coin_control.m_feerate + 2. coin_control.m_confirm_target + 3. payTxFee (user-set global variable) + 4. nTxConfirmTarget (user-set global variable) + The first parameter that is set is used. + */ + CAmount fee_needed; + if (coin_control.m_feerate) { // 1. + fee_needed = coin_control.m_feerate->GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; + // Allow to override automatic min/max check over coin control instance + if (coin_control.fOverrideFeeRate) return fee_needed; + } + else if (!coin_control.m_confirm_target && ::payTxFee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for global payTxFee + fee_needed = ::payTxFee.GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; + } + else { // 2. or 4. + // We will use smart fee estimation + unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : ::nTxConfirmTarget; + // By default estimates are economical iff we are signaling opt-in-RBF + bool conservative_estimate = !coin_control.signalRbf; + // Allow to override the default fee estimate mode over the CoinControl instance + if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true; + else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false; + + fee_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate).GetFee(nTxBytes); + if (fee_needed == 0) { + // if we don't have enough data for estimateSmartFee, then use fallbackFee + fee_needed = fallbackFee.GetFee(nTxBytes); if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; } - } else { - if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; + // Obey mempool min fee when using smart fee estimation + CAmount min_mempool_fee = pool.GetMinFee(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; + } } + // prevent user from paying a fee below minRelayTxFee or minTxFee - CAmount requiredFee = GetRequiredFee(nTxBytes); - if (requiredFee > nFeeNeeded) { - nFeeNeeded = requiredFee; + CAmount required_fee = GetRequiredFee(nTxBytes); + if (required_fee > fee_needed) { + fee_needed = required_fee; if (feeCalc) feeCalc->reason = FeeReason::REQUIRED; } // But always obey the maximum - if (nFeeNeeded > maxTxFee) { - nFeeNeeded = maxTxFee; + if (fee_needed > maxTxFee) { + fee_needed = maxTxFee; if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE; } - return nFeeNeeded; + return fee_needed; } @@ -4200,15 +4214,3 @@ bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& { return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee); } - -bool CalculateEstimateType(FeeEstimateMode mode, bool opt_in_rbf) { - switch (mode) { - case FeeEstimateMode::UNSET: - return !opt_in_rbf; // Allow for lower fees if RBF is an option - case FeeEstimateMode::CONSERVATIVE: - return true; - case FeeEstimateMode::ECONOMICAL: - return false; - } - return true; -} |