mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-02-08 18:49:28 +08:00
walllet: use CoinsResult instead of PreSelectedInputs
PreSelectedInputs is confusing to use. it's `total_amount`
might store total amount or effective amount based on SFFO.
ex: we might accidentally sum preselected inputs effective
amount (named `total_amount`) with automatically selected
inputs actual total amount.
CoinsResult has a cleaner interface with separate fields
for both these amounts.
2 behavioural changes:
1. no more default assert error if effective value is unset
- previously PreSelectedInputs::Insert() called
COutput::GetEffectiveValue() which assert failed
if the optional was unset.
- now we don't default assert anymore.
* in GUI/getAvailableBalance better not to assert.
* SelectCoins's preselected inputs always contain a
feerate, so effective amount should be set.
explicitly added an assertion to ensure this.
2. FetchSelectedInputs uses OutputType::UNKNOWN as key to
populate CoinsResult's coins map. it's discarded later.
This commit is contained in:
@@ -411,7 +411,7 @@ public:
|
||||
CoinSelectionParams params(rng);
|
||||
// Note: for now, swallow any error.
|
||||
if (auto res = FetchSelectedInputs(*m_wallet, coin_control, params)) {
|
||||
total_amount += res->total_amount;
|
||||
total_amount += res->GetTotalAmount();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -266,10 +266,10 @@ static OutputType GetOutputType(TxoutType type, bool is_from_p2sh)
|
||||
|
||||
// Fetch and validate the coin control selected inputs.
|
||||
// Coins could be internal (from the wallet) or external.
|
||||
util::Result<PreSelectedInputs> FetchSelectedInputs(const CWallet& wallet, const CCoinControl& coin_control,
|
||||
util::Result<CoinsResult> FetchSelectedInputs(const CWallet& wallet, const CCoinControl& coin_control,
|
||||
const CoinSelectionParams& coin_selection_params)
|
||||
{
|
||||
PreSelectedInputs result;
|
||||
CoinsResult result;
|
||||
const bool can_grind_r = wallet.CanGrindR();
|
||||
std::map<COutPoint, CAmount> map_of_bump_fees = wallet.chain().calculateIndividualBumpFees(coin_control.ListSelected(), coin_selection_params.m_effective_feerate);
|
||||
for (const COutPoint& outpoint : coin_control.ListSelected()) {
|
||||
@@ -312,7 +312,7 @@ util::Result<PreSelectedInputs> FetchSelectedInputs(const CWallet& wallet, const
|
||||
/* Set some defaults for depth, solvable, safe, time, and from_me as these don't matter for preset inputs since no selection is being done. */
|
||||
COutput output(outpoint, txout, /*depth=*/0, input_bytes, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, coin_selection_params.m_effective_feerate);
|
||||
output.ApplyBumpFee(map_of_bump_fees.at(output.outpoint));
|
||||
result.Insert(output, coin_selection_params.m_subtract_fee_outputs);
|
||||
result.Add(OutputType::UNKNOWN, output);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -812,12 +812,12 @@ util::Result<SelectionResult> ChooseSelectionResult(interfaces::Chain& chain, co
|
||||
return *std::min_element(results.begin(), results.end());
|
||||
}
|
||||
|
||||
util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& available_coins, const PreSelectedInputs& pre_set_inputs,
|
||||
util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& available_coins, const CoinsResult& pre_set_inputs,
|
||||
const CAmount& nTargetValue, const CCoinControl& coin_control,
|
||||
const CoinSelectionParams& coin_selection_params)
|
||||
{
|
||||
// Deduct preset inputs amount from the search target
|
||||
CAmount selection_target = nTargetValue - pre_set_inputs.total_amount;
|
||||
CAmount selection_target = nTargetValue - pre_set_inputs.GetAppropriateTotal(coin_selection_params.m_subtract_fee_outputs).value_or(0);
|
||||
|
||||
// Return if automatic coin selection is disabled, and we don't cover the selection target
|
||||
if (!coin_control.m_allow_other_inputs && selection_target > 0) {
|
||||
@@ -825,18 +825,22 @@ util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& av
|
||||
"Please allow other inputs to be automatically selected or include more coins manually")};
|
||||
}
|
||||
|
||||
OutputSet preset_coin_set;
|
||||
for (const auto& output: pre_set_inputs.All()) {
|
||||
preset_coin_set.insert(std::make_shared<COutput>(output));
|
||||
}
|
||||
|
||||
// Return if we can cover the target only with the preset inputs
|
||||
if (selection_target <= 0) {
|
||||
SelectionResult result(nTargetValue, SelectionAlgorithm::MANUAL);
|
||||
result.AddInputs(pre_set_inputs.coins, coin_selection_params.m_subtract_fee_outputs);
|
||||
result.AddInputs(preset_coin_set, coin_selection_params.m_subtract_fee_outputs);
|
||||
result.RecalculateWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Return early if we cannot cover the target with the wallet's UTXO.
|
||||
// We use the total effective value if we are not subtracting fee from outputs and 'available_coins' contains the data.
|
||||
CAmount available_coins_total_amount = coin_selection_params.m_subtract_fee_outputs ? available_coins.GetTotalAmount() :
|
||||
(available_coins.GetEffectiveTotalAmount().has_value() ? *available_coins.GetEffectiveTotalAmount() : 0);
|
||||
CAmount available_coins_total_amount = available_coins.GetAppropriateTotal(coin_selection_params.m_subtract_fee_outputs).value_or(0);
|
||||
if (selection_target > available_coins_total_amount) {
|
||||
return util::Error(); // Insufficient funds
|
||||
}
|
||||
@@ -847,8 +851,10 @@ util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& av
|
||||
|
||||
// If needed, add preset inputs to the automatic coin selection result
|
||||
if (!pre_set_inputs.coins.empty()) {
|
||||
SelectionResult preselected(pre_set_inputs.total_amount, SelectionAlgorithm::MANUAL);
|
||||
preselected.AddInputs(pre_set_inputs.coins, coin_selection_params.m_subtract_fee_outputs);
|
||||
auto preset_total = pre_set_inputs.GetAppropriateTotal(coin_selection_params.m_subtract_fee_outputs);
|
||||
assert(preset_total.has_value());
|
||||
SelectionResult preselected(preset_total.value(), SelectionAlgorithm::MANUAL);
|
||||
preselected.AddInputs(preset_coin_set, coin_selection_params.m_subtract_fee_outputs);
|
||||
op_selection_result->Merge(preselected);
|
||||
op_selection_result->RecalculateWaste(coin_selection_params.min_viable_change,
|
||||
coin_selection_params.m_cost_of_change,
|
||||
@@ -1183,7 +1189,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
|
||||
}
|
||||
|
||||
// Fetch manually selected coins
|
||||
PreSelectedInputs preset_inputs;
|
||||
CoinsResult preset_inputs;
|
||||
if (coin_control.HasSelected()) {
|
||||
auto res_fetch_inputs = FetchSelectedInputs(wallet, coin_control, coin_selection_params);
|
||||
if (!res_fetch_inputs) return util::Error{util::ErrorString(res_fetch_inputs)};
|
||||
|
||||
@@ -180,7 +180,7 @@ struct PreSelectedInputs
|
||||
* Fetch and validate coin control selected inputs.
|
||||
* Coins could be internal (from the wallet) or external.
|
||||
*/
|
||||
util::Result<PreSelectedInputs> FetchSelectedInputs(const CWallet& wallet, const CCoinControl& coin_control,
|
||||
util::Result<CoinsResult> FetchSelectedInputs(const CWallet& wallet, const CCoinControl& coin_control,
|
||||
const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
|
||||
/**
|
||||
@@ -202,7 +202,7 @@ util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coin
|
||||
* Select all coins from coin_control, and if coin_control 'm_allow_other_inputs=true', call 'AutomaticCoinSelection' to
|
||||
* select a set of coins such that nTargetValue - pre_set_inputs.total_amount is met.
|
||||
*/
|
||||
util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& available_coins, const PreSelectedInputs& pre_set_inputs,
|
||||
util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& available_coins, const CoinsResult& pre_set_inputs,
|
||||
const CAmount& nTargetValue, const CCoinControl& coin_control,
|
||||
const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
|
||||
|
||||
@@ -222,8 +222,8 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
|
||||
coin_control.m_allow_other_inputs = true;
|
||||
COutput select_coin = available_coins.All().at(0);
|
||||
coin_control.Select(select_coin.outpoint);
|
||||
PreSelectedInputs selected_input;
|
||||
selected_input.Insert(select_coin, coin_selection_params_bnb.m_subtract_fee_outputs);
|
||||
CoinsResult selected_input;
|
||||
selected_input.Add(OutputType::BECH32, select_coin);
|
||||
available_coins.Erase({available_coins.coins[OutputType::BECH32].begin()->outpoint});
|
||||
|
||||
LOCK(wallet->cs_wallet);
|
||||
@@ -253,8 +253,8 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
|
||||
coin_control.m_allow_other_inputs = true;
|
||||
COutput select_coin = available_coins.All().at(1); // pre select 9 coin
|
||||
coin_control.Select(select_coin.outpoint);
|
||||
PreSelectedInputs selected_input;
|
||||
selected_input.Insert(select_coin, coin_selection_params_bnb.m_subtract_fee_outputs);
|
||||
CoinsResult selected_input;
|
||||
selected_input.Add(OutputType::BECH32, select_coin);
|
||||
available_coins.Erase({(++available_coins.coins[OutputType::BECH32].begin())->outpoint});
|
||||
const auto result13 = SelectCoins(*wallet, available_coins, selected_input, 10 * CENT, coin_control, coin_selection_params_bnb);
|
||||
BOOST_CHECK(EquivalentResult(expected_result, *result13));
|
||||
|
||||
Reference in New Issue
Block a user