Skip to content

Commit 01dc8eb

Browse files
committed
Have KnapsackSolver actually use effective values
Although the CreateTransaction loop currently remains, it should be largely unused. KnapsackSolver will now account for transaction fees when doing its selection. In the previous commit, SelectCoinsMinConf was refactored to have some calculations become shared for KnapsackSolver and SelectCoinsBnB. In this commit, KnapsackSolver will now use the not_input_fees and effective_feerate so that it include the fee for non-input things (excluding a change output) so that the algorithm will select enough to cover those fees. This is necessary for selecting on effective values. Additionally, the OutputGroups created for KnapsackSolver will actually have their effective values calculated and set, and KnapsackSolver will do its selection on those effective values. Lastly, SelectCoins is modified to use the same value for preselected inputs for BnB and KnapsackSolver. While it will still use the real value when subtracting the fee from outputs, this behavior will be the same regardless of the algo used for selecting additional inputs.
1 parent bf26e01 commit 01dc8eb

File tree

2 files changed

+10
-18
lines changed

2 files changed

+10
-18
lines changed

src/wallet/coinselection.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,14 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
227227
Shuffle(groups.begin(), groups.end(), FastRandomContext());
228228

229229
for (const OutputGroup& group : groups) {
230-
if (group.m_value == nTargetValue) {
230+
if (group.effective_value == nTargetValue) {
231231
util::insert(setCoinsRet, group.m_outputs);
232232
nValueRet += group.m_value;
233233
return true;
234-
} else if (group.m_value < nTargetValue + MIN_CHANGE) {
234+
} else if (group.effective_value < nTargetValue + MIN_CHANGE) {
235235
applicable_groups.push_back(group);
236-
nTotalLower += group.m_value;
237-
} else if (!lowest_larger || group.m_value < lowest_larger->m_value) {
236+
nTotalLower += group.effective_value;
237+
} else if (!lowest_larger || group.effective_value < lowest_larger->effective_value) {
238238
lowest_larger = group;
239239
}
240240
}
@@ -267,7 +267,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
267267
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
268268
// or the next bigger coin is closer), return the bigger coin
269269
if (lowest_larger &&
270-
((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->m_value <= nBest)) {
270+
((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->effective_value <= nBest)) {
271271
util::insert(setCoinsRet, lowest_larger->m_outputs);
272272
nValueRet += lowest_larger->m_value;
273273
} else {

src/wallet/wallet.cpp

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,13 +2413,11 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
24132413
return SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change, setCoinsRet, nValueRet);
24142414
} else {
24152415
// 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.
2416-
// 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.
2417-
std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, CFeeRate(0), CFeeRate(0), eligibility_filter, false /* positive_only */);
2418-
2416+
std::vector<OutputGroup> all_groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, coin_selection_params.m_long_term_feerate, eligibility_filter, false /* positive_only */);
24192417
bnb_used = false;
24202418
// While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output.
24212419
// So we need to include that for KnapsackSolver as well, as we are expecting to create a change output.
2422-
return KnapsackSolver(nTargetValue + coin_selection_params.m_change_fee, groups, setCoinsRet, nValueRet);
2420+
return KnapsackSolver(nTargetValue + coin_selection_params.m_change_fee, all_groups, setCoinsRet, nValueRet);
24232421
}
24242422
}
24252423

@@ -2467,10 +2465,10 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
24672465
return false; // Not solvable, can't estimate size for fee
24682466
}
24692467
coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
2470-
if (coin_selection_params.use_bnb) {
2471-
value_to_select -= coin.effective_value;
2472-
} else {
2468+
if (coin_selection_params.m_subtract_fee_outputs) {
24732469
value_to_select -= coin.txout.nValue;
2470+
} else {
2471+
value_to_select -= coin.effective_value;
24742472
}
24752473
setPresetCoins.insert(coin);
24762474
} else {
@@ -2956,12 +2954,6 @@ bool CWallet::CreateTransactionInternal(
29562954
const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
29572955
CAmount nValueToSelect = nValue + not_input_fees;
29582956

2959-
// For KnapsackSolver, when we are not subtracting the fee from the recipients, we also want to include the fees for the
2960-
// inputs that we found in the previous iteration.
2961-
if (!coin_selection_params.use_bnb && nSubtractFeeFromAmount == 0) {
2962-
nValueToSelect += std::max(CAmount(0), nFeeRet - not_input_fees);
2963-
}
2964-
29652957
// Choose coins to use
29662958
bool bnb_used = false;
29672959
nValueIn = 0;

0 commit comments

Comments
 (0)