mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
Merge #21520: [0.21] wallet: Avoid requesting fee rates multiple times during coin selection
d61fb07da7
Rename CoinSelectionParams::effective_fee to m_effective_feerate (Andrew Chow)5fc381e443
wallet: Move discard feerate fetching to CreateTransaction (Andrew Chow)bcd716670b
wallet: Move long term feerate setting to CreateTransaction (Andrew Chow)34c89f92f3
wallet: Replace nFeeRateNeeded with effective_fee (Andrew Chow)48fc675163
wallet: Use existing feerate instead of getting a new one (Andrew Chow) Pull request description: Backport of #21083 ACKs for top commit: MarcoFalke: cherry-pick-only re-ACKd61fb07da7
🔙 instagibbs: utACKd61fb07da7
Tree-SHA512: 23b212301bb467153dd9723903918ae01dd520525c81d541c411e7a4381e46594fe032e2a7c06ddcff7dc56dcb546991d50187c33fcff08ec45bd835cc01bd19
This commit is contained in:
commit
0fe5b6130c
4 changed files with 58 additions and 34 deletions
|
@ -50,7 +50,10 @@ static void CoinSelection(benchmark::Bench& bench)
|
|||
}
|
||||
|
||||
const CoinEligibilityFilter filter_standard(1, 6, 0);
|
||||
const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0);
|
||||
const CoinSelectionParams coin_selection_params(/* use_bnb= */ true, /* change_output_size= */ 34,
|
||||
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
|
||||
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
|
||||
/* tx_no_inputs_size= */ 0);
|
||||
bench.run([&] {
|
||||
std::set<CInputCoin> setCoinsRet;
|
||||
CAmount nValueRet;
|
||||
|
|
|
@ -35,7 +35,10 @@ static CAmount balance = 0;
|
|||
CoinEligibilityFilter filter_standard(1, 6, 0);
|
||||
CoinEligibilityFilter filter_confirmed(1, 1, 0);
|
||||
CoinEligibilityFilter filter_standard_extra(6, 6, 0);
|
||||
CoinSelectionParams coin_selection_params(false, 0, 0, CFeeRate(0), 0);
|
||||
CoinSelectionParams coin_selection_params(/* use_bnb= */ false, /* change_output_size= */ 0,
|
||||
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0),
|
||||
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
|
||||
/* tx_no_inputs_size= */ 0);
|
||||
|
||||
static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set)
|
||||
{
|
||||
|
@ -262,7 +265,10 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
|
|||
}
|
||||
|
||||
// Make sure that effective value is working in SelectCoinsMinConf when BnB is used
|
||||
CoinSelectionParams coin_selection_params_bnb(true, 0, 0, CFeeRate(3000), 0);
|
||||
CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 0,
|
||||
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000),
|
||||
/* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000),
|
||||
/* tx_no_inputs_size= */ 0);
|
||||
CoinSet setCoinsRet;
|
||||
CAmount nValueRet;
|
||||
bool bnb_used;
|
||||
|
@ -294,7 +300,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
|
|||
CCoinControl coin_control;
|
||||
coin_control.fAllowOtherInputs = true;
|
||||
coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
|
||||
coin_selection_params_bnb.effective_fee = CFeeRate(0);
|
||||
coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
|
||||
BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb, bnb_used));
|
||||
BOOST_CHECK(bnb_used);
|
||||
BOOST_CHECK(coin_selection_params_bnb.use_bnb);
|
||||
|
@ -632,8 +638,14 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
|
|||
CAmount target = rand.randrange(balance - 1000) + 1000;
|
||||
|
||||
// Perform selection
|
||||
CoinSelectionParams coin_selection_params_knapsack(false, 34, 148, CFeeRate(0), 0);
|
||||
CoinSelectionParams coin_selection_params_bnb(true, 34, 148, CFeeRate(0), 0);
|
||||
CoinSelectionParams coin_selection_params_knapsack(/* use_bnb= */ false, /* change_output_size= */ 34,
|
||||
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
|
||||
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
|
||||
/* tx_no_inputs_size= */ 0);
|
||||
CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 34,
|
||||
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
|
||||
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
|
||||
/* tx_no_inputs_size= */ 0);
|
||||
CoinSet out_set;
|
||||
CAmount out_value = 0;
|
||||
bool bnb_used = false;
|
||||
|
|
|
@ -2362,14 +2362,8 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
|
|||
|
||||
std::vector<OutputGroup> utxo_pool;
|
||||
if (coin_selection_params.use_bnb) {
|
||||
// Get long term estimate
|
||||
FeeCalculation feeCalc;
|
||||
CCoinControl temp;
|
||||
temp.m_confirm_target = 1008;
|
||||
CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, &feeCalc);
|
||||
|
||||
// Calculate cost of change
|
||||
CAmount cost_of_change = GetDiscardRate(*this).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
|
||||
CAmount cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
|
||||
|
||||
// Filter by the min conf specs and add to utxo_pool and calculate effective value
|
||||
for (OutputGroup& group : groups) {
|
||||
|
@ -2377,16 +2371,16 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
|
|||
|
||||
if (coin_selection_params.m_subtract_fee_outputs) {
|
||||
// Set the effective feerate to 0 as we don't want to use the effective value since the fees will be deducted from the output
|
||||
group.SetFees(CFeeRate(0) /* effective_feerate */, long_term_feerate);
|
||||
group.SetFees(CFeeRate(0) /* effective_feerate */, coin_selection_params.m_long_term_feerate);
|
||||
} else {
|
||||
group.SetFees(coin_selection_params.effective_fee, long_term_feerate);
|
||||
group.SetFees(coin_selection_params.m_effective_feerate, coin_selection_params.m_long_term_feerate);
|
||||
}
|
||||
|
||||
OutputGroup pos_group = group.GetPositiveOnlyGroup();
|
||||
if (pos_group.effective_value > 0) utxo_pool.push_back(pos_group);
|
||||
}
|
||||
// Calculate the fees for things that aren't inputs
|
||||
CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);
|
||||
CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
|
||||
bnb_used = true;
|
||||
return SelectCoinsBnB(utxo_pool, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees);
|
||||
} else {
|
||||
|
@ -2443,7 +2437,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
|
|||
if (coin.m_input_bytes <= 0) {
|
||||
return false; // Not solvable, can't estimate size for fee
|
||||
}
|
||||
coin.effective_value = coin.txout.nValue - coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
|
||||
coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
|
||||
if (coin_selection_params.use_bnb) {
|
||||
value_to_select -= coin.effective_value;
|
||||
} else {
|
||||
|
@ -2811,16 +2805,27 @@ bool CWallet::CreateTransactionInternal(
|
|||
CTxOut change_prototype_txout(0, scriptChange);
|
||||
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
|
||||
|
||||
CFeeRate discard_rate = GetDiscardRate(*this);
|
||||
// Set discard feerate
|
||||
coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
|
||||
|
||||
// Get the fee rate to use effective values in coin selection
|
||||
CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
|
||||
coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc);
|
||||
// Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
|
||||
// provided one
|
||||
if (coin_control.m_feerate && nFeeRateNeeded > *coin_control.m_feerate) {
|
||||
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), nFeeRateNeeded.ToString(FeeEstimateMode::SAT_VB));
|
||||
if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
|
||||
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
|
||||
return false;
|
||||
}
|
||||
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
|
||||
// eventually allow a fallback fee
|
||||
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get long term estimate
|
||||
CCoinControl cc_temp;
|
||||
cc_temp.m_confirm_target = chain().estimateMaxBlocks();
|
||||
coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr);
|
||||
|
||||
nFeeRet = 0;
|
||||
bool pick_new_inputs = true;
|
||||
|
@ -2895,7 +2900,6 @@ bool CWallet::CreateTransactionInternal(
|
|||
} else {
|
||||
coin_selection_params.change_spend_size = (size_t)change_spend_size;
|
||||
}
|
||||
coin_selection_params.effective_fee = nFeeRateNeeded;
|
||||
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used))
|
||||
{
|
||||
// If BnB was used, it was the first pass. No longer the first pass and continue loop with knapsack.
|
||||
|
@ -2921,7 +2925,7 @@ bool CWallet::CreateTransactionInternal(
|
|||
// Never create dust outputs; if we would, just
|
||||
// add the dust to the fee.
|
||||
// The nChange when BnB is used is always going to go to fees.
|
||||
if (IsDust(newTxOut, discard_rate) || bnb_used)
|
||||
if (IsDust(newTxOut, coin_selection_params.m_discard_feerate) || bnb_used)
|
||||
{
|
||||
nChangePosInOut = -1;
|
||||
nFeeRet += nChange;
|
||||
|
@ -2958,13 +2962,7 @@ bool CWallet::CreateTransactionInternal(
|
|||
return false;
|
||||
}
|
||||
|
||||
nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, &feeCalc);
|
||||
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
|
||||
// eventually allow a fallback fee
|
||||
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
|
||||
return false;
|
||||
}
|
||||
|
||||
nFeeNeeded = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
||||
if (nFeeRet >= nFeeNeeded) {
|
||||
// Reduce fee to only the needed amount if possible. This
|
||||
// prevents potential overpayment in fees if the coins
|
||||
|
@ -2978,8 +2976,8 @@ bool CWallet::CreateTransactionInternal(
|
|||
// change output. Only try this once.
|
||||
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
|
||||
unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size
|
||||
CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, nullptr);
|
||||
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
|
||||
CAmount fee_needed_with_change = coin_selection_params.m_effective_feerate.GetFee(tx_size_with_change);
|
||||
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, coin_selection_params.m_discard_feerate);
|
||||
if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) {
|
||||
pick_new_inputs = false;
|
||||
nFeeRet = fee_needed_with_change;
|
||||
|
|
|
@ -606,12 +606,23 @@ struct CoinSelectionParams
|
|||
bool use_bnb = true;
|
||||
size_t change_output_size = 0;
|
||||
size_t change_spend_size = 0;
|
||||
CFeeRate effective_fee = CFeeRate(0);
|
||||
CFeeRate m_effective_feerate;
|
||||
CFeeRate m_long_term_feerate;
|
||||
CFeeRate m_discard_feerate;
|
||||
size_t tx_noinputs_size = 0;
|
||||
//! Indicate that we are subtracting the fee from outputs
|
||||
bool m_subtract_fee_outputs = false;
|
||||
|
||||
CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size) : use_bnb(use_bnb), change_output_size(change_output_size), change_spend_size(change_spend_size), effective_fee(effective_fee), tx_noinputs_size(tx_noinputs_size) {}
|
||||
CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_feerate,
|
||||
CFeeRate long_term_feerate, CFeeRate discard_feerate, size_t tx_noinputs_size) :
|
||||
use_bnb(use_bnb),
|
||||
change_output_size(change_output_size),
|
||||
change_spend_size(change_spend_size),
|
||||
m_effective_feerate(effective_feerate),
|
||||
m_long_term_feerate(long_term_feerate),
|
||||
m_discard_feerate(discard_feerate),
|
||||
tx_noinputs_size(tx_noinputs_size)
|
||||
{}
|
||||
CoinSelectionParams() {}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue