mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-25 10:43:19 -03:00
wallet: SelectCoins, return early if wallet's UTXOs cannot cover the target
The CoinsResult class will now count the raw total amount and the effective total amount internally (inside the 'CoinsResult::Add' and 'CoinsResult::Erase' methods). So there is no discrepancy between what we add/erase and the total values. (which is what was happening on the coinselector_test because the 'CoinsResult' object is manually created there, and we were not keeping the total amount in sync with the outputs being added/removed).
This commit is contained in:
parent
cac2725fd0
commit
c4e3b7d6a1
3 changed files with 25 additions and 4 deletions
|
@ -110,6 +110,8 @@ public:
|
||||||
assert(effective_value.has_value());
|
assert(effective_value.has_value());
|
||||||
return effective_value.value();
|
return effective_value.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasEffectiveValue() const { return effective_value.has_value(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Parameters for one iteration of Coin Selection. */
|
/** Parameters for one iteration of Coin Selection. */
|
||||||
|
|
|
@ -106,7 +106,13 @@ void CoinsResult::Erase(const std::unordered_set<COutPoint, SaltedOutpointHasher
|
||||||
{
|
{
|
||||||
for (auto& [type, vec] : coins) {
|
for (auto& [type, vec] : coins) {
|
||||||
auto remove_it = std::remove_if(vec.begin(), vec.end(), [&](const COutput& coin) {
|
auto remove_it = std::remove_if(vec.begin(), vec.end(), [&](const COutput& coin) {
|
||||||
return coins_to_remove.count(coin.outpoint) == 1;
|
// remove it if it's on the set
|
||||||
|
if (coins_to_remove.count(coin.outpoint) == 0) return false;
|
||||||
|
|
||||||
|
// update cached amounts
|
||||||
|
total_amount -= coin.txout.nValue;
|
||||||
|
if (coin.HasEffectiveValue()) total_effective_amount = *total_effective_amount - coin.GetEffectiveValue();
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
vec.erase(remove_it, vec.end());
|
vec.erase(remove_it, vec.end());
|
||||||
}
|
}
|
||||||
|
@ -122,6 +128,11 @@ void CoinsResult::Shuffle(FastRandomContext& rng_fast)
|
||||||
void CoinsResult::Add(OutputType type, const COutput& out)
|
void CoinsResult::Add(OutputType type, const COutput& out)
|
||||||
{
|
{
|
||||||
coins[type].emplace_back(out);
|
coins[type].emplace_back(out);
|
||||||
|
total_amount += out.txout.nValue;
|
||||||
|
if (out.HasEffectiveValue()) {
|
||||||
|
total_effective_amount = total_effective_amount.has_value() ?
|
||||||
|
*total_effective_amount + out.GetEffectiveValue() : out.GetEffectiveValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static OutputType GetOutputType(TxoutType type, bool is_from_p2sh)
|
static OutputType GetOutputType(TxoutType type, bool is_from_p2sh)
|
||||||
|
@ -319,8 +330,6 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
||||||
result.Add(GetOutputType(type, is_from_p2sh),
|
result.Add(GetOutputType(type, is_from_p2sh),
|
||||||
COutput(outpoint, output, nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate));
|
COutput(outpoint, output, nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate));
|
||||||
|
|
||||||
// Cache total amount as we go
|
|
||||||
result.total_amount += output.nValue;
|
|
||||||
// Checks the sum amount of all UTXO's.
|
// Checks the sum amount of all UTXO's.
|
||||||
if (params.min_sum_amount != MAX_MONEY) {
|
if (params.min_sum_amount != MAX_MONEY) {
|
||||||
if (result.total_amount >= params.min_sum_amount) {
|
if (result.total_amount >= params.min_sum_amount) {
|
||||||
|
@ -575,6 +584,14 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
|
||||||
return result;
|
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.total_amount :
|
||||||
|
(available_coins.total_effective_amount.has_value() ? *available_coins.total_effective_amount : 0);
|
||||||
|
if (selection_target > available_coins_total_amount) {
|
||||||
|
return std::nullopt; // Insufficient funds
|
||||||
|
}
|
||||||
|
|
||||||
// Start wallet Coin Selection procedure
|
// Start wallet Coin Selection procedure
|
||||||
auto op_selection_result = AutomaticCoinSelection(wallet, available_coins, selection_target, coin_control, coin_selection_params);
|
auto op_selection_result = AutomaticCoinSelection(wallet, available_coins, selection_target, coin_control, coin_selection_params);
|
||||||
if (!op_selection_result) return op_selection_result;
|
if (!op_selection_result) return op_selection_result;
|
||||||
|
|
|
@ -51,8 +51,10 @@ struct CoinsResult {
|
||||||
void Shuffle(FastRandomContext& rng_fast);
|
void Shuffle(FastRandomContext& rng_fast);
|
||||||
void Add(OutputType type, const COutput& out);
|
void Add(OutputType type, const COutput& out);
|
||||||
|
|
||||||
/** Sum of all available coins */
|
/** Sum of all available coins raw value */
|
||||||
CAmount total_amount{0};
|
CAmount total_amount{0};
|
||||||
|
/** Sum of all available coins effective value (each output value minus fees required to spend it) */
|
||||||
|
std::optional<CAmount> total_effective_amount{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CoinFilterParams {
|
struct CoinFilterParams {
|
||||||
|
|
Loading…
Add table
Reference in a new issue