Make cost_of_change part of CoinSelectionParams

This commit is contained in:
Andrew Chow 2021-05-07 18:13:40 -04:00
parent af5867c896
commit d97d25d950
2 changed files with 23 additions and 16 deletions

View file

@ -2409,17 +2409,10 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
effective_feerate = coin_selection_params.m_effective_feerate; effective_feerate = coin_selection_params.m_effective_feerate;
} }
// Cost of change is the cost of creating the change output + cost of spending the change output in the future.
// For creating the change output now, we use the effective feerate.
// For spending the change output in the future, we use the discard feerate for now.
// So cost of change = (change output size * effective feerate) + (size of spending change output * discard feerate)
const CAmount change_fee = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
const CAmount cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + change_fee;
if (coin_selection_params.use_bnb) { if (coin_selection_params.use_bnb) {
std::vector<OutputGroup> positive_groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, coin_selection_params.m_long_term_feerate, eligibility_filter, true /* positive_only */); std::vector<OutputGroup> positive_groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, coin_selection_params.m_long_term_feerate, eligibility_filter, true /* positive_only */);
bnb_used = true; bnb_used = true;
return SelectCoinsBnB(positive_groups, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees); return SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change, setCoinsRet, nValueRet, not_input_fees);
} else { } else {
// The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here. // The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here.
// The knapsack solver currently does not use effective values, so we give GroupOutputs feerates of 0 so it sets the effective values to be the same as the real value. // The knapsack solver currently does not use effective values, so we give GroupOutputs feerates of 0 so it sets the effective values to be the same as the real value.
@ -2885,6 +2878,16 @@ bool CWallet::CreateTransactionInternal(
CTxOut change_prototype_txout(0, scriptChange); CTxOut change_prototype_txout(0, scriptChange);
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout); coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
// Get size of spending the change output
int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
// If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
// as lower-bound to allow BnB to do it's thing
if (change_spend_size == -1) {
coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
} else {
coin_selection_params.change_spend_size = (size_t)change_spend_size;
}
// Set discard feerate // Set discard feerate
coin_selection_params.m_discard_feerate = GetDiscardRate(*this); coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
@ -2907,6 +2910,14 @@ bool CWallet::CreateTransactionInternal(
cc_temp.m_confirm_target = chain().estimateMaxBlocks(); cc_temp.m_confirm_target = chain().estimateMaxBlocks();
coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr); coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr);
// Calculate the cost of change
// Cost of change is the cost of creating the change output + cost of spending the change output in the future.
// For creating the change output now, we use the effective feerate.
// For spending the change output in the future, we use the discard feerate for now.
// So cost of change = (change output size * effective feerate) + (size of spending change output * discard feerate)
coin_selection_params.m_change_fee = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
coin_selection_params.m_cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_change_fee;
nFeeRet = 0; nFeeRet = 0;
bool pick_new_inputs = true; bool pick_new_inputs = true;
CAmount nValueIn = 0; CAmount nValueIn = 0;
@ -2972,14 +2983,6 @@ bool CWallet::CreateTransactionInternal(
if (pick_new_inputs) { if (pick_new_inputs) {
nValueIn = 0; nValueIn = 0;
setCoins.clear(); setCoins.clear();
int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
// If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
// as lower-bound to allow BnB to do it's thing
if (change_spend_size == -1) {
coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
} else {
coin_selection_params.change_spend_size = (size_t)change_spend_size;
}
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used)) 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. // If BnB was used, it was the first pass. No longer the first pass and continue loop with knapsack.

View file

@ -621,6 +621,10 @@ struct CoinSelectionParams
size_t change_output_size = 0; size_t change_output_size = 0;
/** Size of the input to spend a change output in virtual bytes. */ /** Size of the input to spend a change output in virtual bytes. */
size_t change_spend_size = 0; size_t change_spend_size = 0;
/** Cost of creating the change output. */
CAmount m_change_fee{0};
/** Cost of creating the change output + cost of spending the change output in the future. */
CAmount m_cost_of_change{0};
/** The targeted feerate of the transaction being built. */ /** The targeted feerate of the transaction being built. */
CFeeRate m_effective_feerate; CFeeRate m_effective_feerate;
/** The feerate estimate used to estimate an upper bound on what should be sufficient to spend /** The feerate estimate used to estimate an upper bound on what should be sufficient to spend