mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-07 00:40:41 +01:00
Use waste metric for deciding which selection to use
Instead of always choosing BnB if it finds a solution, always do both BnB and KnapsackSolver and choose the one which has the least waste.
This commit is contained in:
parent
b3df0caf7c
commit
86beee0579
2 changed files with 31 additions and 4 deletions
|
@ -357,17 +357,44 @@ bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilit
|
||||||
{
|
{
|
||||||
setCoinsRet.clear();
|
setCoinsRet.clear();
|
||||||
nValueRet = 0;
|
nValueRet = 0;
|
||||||
|
// Vector of results for use with waste calculation
|
||||||
|
// In order: calculated waste, selected inputs, selected input value (sum of input values)
|
||||||
|
// TODO: Use a struct representing the selection result
|
||||||
|
std::vector<std::tuple<CAmount, std::set<CInputCoin>, CAmount>> results;
|
||||||
|
|
||||||
// Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output.
|
// Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output.
|
||||||
std::vector<OutputGroup> positive_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, true /* positive_only */);
|
std::vector<OutputGroup> positive_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, true /* positive_only */);
|
||||||
if (SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change, setCoinsRet, nValueRet)) {
|
std::set<CInputCoin> bnb_coins;
|
||||||
return true;
|
CAmount bnb_value;
|
||||||
|
if (SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change, bnb_coins, bnb_value)) {
|
||||||
|
const auto waste = GetSelectionWaste(bnb_coins, /* cost of change */ CAmount(0), nTargetValue, !coin_selection_params.m_subtract_fee_outputs);
|
||||||
|
results.emplace_back(std::make_tuple(waste, std::move(bnb_coins), bnb_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
std::vector<OutputGroup> all_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, false /* positive_only */);
|
std::vector<OutputGroup> all_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, false /* positive_only */);
|
||||||
// While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output.
|
// While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output.
|
||||||
// So we need to include that for KnapsackSolver as well, as we are expecting to create a change output.
|
// So we need to include that for KnapsackSolver as well, as we are expecting to create a change output.
|
||||||
return KnapsackSolver(nTargetValue + coin_selection_params.m_change_fee, all_groups, setCoinsRet, nValueRet);
|
std::set<CInputCoin> knapsack_coins;
|
||||||
|
CAmount knapsack_value;
|
||||||
|
if (KnapsackSolver(nTargetValue + coin_selection_params.m_change_fee, all_groups, knapsack_coins, knapsack_value)) {
|
||||||
|
const auto waste = GetSelectionWaste(knapsack_coins, coin_selection_params.m_cost_of_change, nTargetValue + coin_selection_params.m_change_fee, !coin_selection_params.m_subtract_fee_outputs);
|
||||||
|
results.emplace_back(std::make_tuple(waste, std::move(knapsack_coins), knapsack_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.size() == 0) {
|
||||||
|
// No solution found
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose the result with the least waste
|
||||||
|
// If the waste is the same, choose the one which spends more inputs.
|
||||||
|
const auto& best_result = std::min_element(results.begin(), results.end(), [](const auto& a, const auto& b) {
|
||||||
|
return std::get<0>(a) < std::get<0>(b) || (std::get<0>(a) == std::get<0>(b) && std::get<1>(a).size() > std::get<1>(b).size());
|
||||||
|
});
|
||||||
|
setCoinsRet = std::get<1>(*best_result);
|
||||||
|
nValueRet = std::get<2>(*best_result);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const
|
bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const
|
||||||
|
|
|
@ -543,7 +543,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||||
self.nodes[1].getnewaddress()
|
self.nodes[1].getnewaddress()
|
||||||
self.nodes[1].getrawchangeaddress()
|
self.nodes[1].getrawchangeaddress()
|
||||||
inputs = []
|
inputs = []
|
||||||
outputs = {self.nodes[0].getnewaddress():1.09999500}
|
outputs = {self.nodes[0].getnewaddress():1.19999500}
|
||||||
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
|
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
|
||||||
# fund a transaction that does not require a new key for the change output
|
# fund a transaction that does not require a new key for the change output
|
||||||
self.nodes[1].fundrawtransaction(rawtx)
|
self.nodes[1].fundrawtransaction(rawtx)
|
||||||
|
|
Loading…
Add table
Reference in a new issue