mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 09:46:52 -05:00
Merge #17458: Refactor OutputGroup effective value calculations and filtering to occur within the struct
9adc2f80fc
Refactor OutputGroups to handle effective values, fees, and filtering (Andrew Chow)7d07e864b8
Use real value when calculating OutputGroup value (Andrew Chow) Pull request description: Currently, the effective values and filtering for positive effective values is done outside of the OutputGroup. We should instead have functions in Outputgroup to do this and call those for each OutputGroup. So this PR does that. This makes future changes for effective values in coin selection much easier. ACKs for top commit: instagibbs: reACK9adc2f80fc
fjahr: re-ACK9adc2f80fc
meshcollider: Light code review ACK9adc2f80fc
Tree-SHA512: 7445c94b7295b45bcd83a6f8a5c8f6961a89453fcc856335192d4b4a66aec7724513616b04e5111588ab208c89b311055399d6279cd9c4ce452aefb85f04b64a
This commit is contained in:
commit
f269165edc
3 changed files with 56 additions and 23 deletions
|
@ -5,6 +5,7 @@
|
||||||
#include <wallet/coinselection.h>
|
#include <wallet/coinselection.h>
|
||||||
|
|
||||||
#include <optional.h>
|
#include <optional.h>
|
||||||
|
#include <policy/feerate.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
|
|
||||||
|
@ -302,7 +303,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
|
||||||
void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants) {
|
void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants) {
|
||||||
m_outputs.push_back(output);
|
m_outputs.push_back(output);
|
||||||
m_from_me &= from_me;
|
m_from_me &= from_me;
|
||||||
m_value += output.effective_value;
|
m_value += output.txout.nValue;
|
||||||
m_depth = std::min(m_depth, depth);
|
m_depth = std::min(m_depth, depth);
|
||||||
// ancestors here express the number of ancestors the new coin will end up having, which is
|
// ancestors here express the number of ancestors the new coin will end up having, which is
|
||||||
// the sum, rather than the max; this will overestimate in the cases where multiple inputs
|
// the sum, rather than the max; this will overestimate in the cases where multiple inputs
|
||||||
|
@ -311,15 +312,19 @@ void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size
|
||||||
// descendants is the count as seen from the top ancestor, not the descendants as seen from the
|
// descendants is the count as seen from the top ancestor, not the descendants as seen from the
|
||||||
// coin itself; thus, this value is counted as the max, not the sum
|
// coin itself; thus, this value is counted as the max, not the sum
|
||||||
m_descendants = std::max(m_descendants, descendants);
|
m_descendants = std::max(m_descendants, descendants);
|
||||||
effective_value = m_value;
|
effective_value += output.effective_value;
|
||||||
|
fee += output.m_fee;
|
||||||
|
long_term_fee += output.m_long_term_fee;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CInputCoin>::iterator OutputGroup::Discard(const CInputCoin& output) {
|
std::vector<CInputCoin>::iterator OutputGroup::Discard(const CInputCoin& output) {
|
||||||
auto it = m_outputs.begin();
|
auto it = m_outputs.begin();
|
||||||
while (it != m_outputs.end() && it->outpoint != output.outpoint) ++it;
|
while (it != m_outputs.end() && it->outpoint != output.outpoint) ++it;
|
||||||
if (it == m_outputs.end()) return it;
|
if (it == m_outputs.end()) return it;
|
||||||
m_value -= output.effective_value;
|
m_value -= output.txout.nValue;
|
||||||
effective_value -= output.effective_value;
|
effective_value -= output.effective_value;
|
||||||
|
fee -= output.m_fee;
|
||||||
|
long_term_fee -= output.m_long_term_fee;
|
||||||
return m_outputs.erase(it);
|
return m_outputs.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,3 +334,35 @@ bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_f
|
||||||
&& m_ancestors <= eligibility_filter.max_ancestors
|
&& m_ancestors <= eligibility_filter.max_ancestors
|
||||||
&& m_descendants <= eligibility_filter.max_descendants;
|
&& m_descendants <= eligibility_filter.max_descendants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OutputGroup::SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate)
|
||||||
|
{
|
||||||
|
fee = 0;
|
||||||
|
long_term_fee = 0;
|
||||||
|
effective_value = 0;
|
||||||
|
for (CInputCoin& coin : m_outputs) {
|
||||||
|
coin.m_fee = coin.m_input_bytes < 0 ? 0 : effective_feerate.GetFee(coin.m_input_bytes);
|
||||||
|
fee += coin.m_fee;
|
||||||
|
|
||||||
|
coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
|
||||||
|
long_term_fee += coin.m_long_term_fee;
|
||||||
|
|
||||||
|
coin.effective_value = coin.txout.nValue - coin.m_fee;
|
||||||
|
effective_value += coin.effective_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputGroup OutputGroup::GetPositiveOnlyGroup()
|
||||||
|
{
|
||||||
|
OutputGroup group(*this);
|
||||||
|
for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) {
|
||||||
|
const CInputCoin& coin = *it;
|
||||||
|
// Only include outputs that are positive effective value (i.e. not dust)
|
||||||
|
if (coin.effective_value <= 0) {
|
||||||
|
it = group.Discard(coin);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <random.h>
|
#include <random.h>
|
||||||
|
|
||||||
|
class CFeeRate;
|
||||||
|
|
||||||
//! target minimum change amount
|
//! target minimum change amount
|
||||||
static constexpr CAmount MIN_CHANGE{COIN / 100};
|
static constexpr CAmount MIN_CHANGE{COIN / 100};
|
||||||
//! final minimum change amount after paying for fees
|
//! final minimum change amount after paying for fees
|
||||||
|
@ -36,6 +38,8 @@ public:
|
||||||
COutPoint outpoint;
|
COutPoint outpoint;
|
||||||
CTxOut txout;
|
CTxOut txout;
|
||||||
CAmount effective_value;
|
CAmount effective_value;
|
||||||
|
CAmount m_fee{0};
|
||||||
|
CAmount m_long_term_fee{0};
|
||||||
|
|
||||||
/** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
|
/** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
|
||||||
int m_input_bytes{-1};
|
int m_input_bytes{-1};
|
||||||
|
@ -91,6 +95,10 @@ struct OutputGroup
|
||||||
void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants);
|
void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants);
|
||||||
std::vector<CInputCoin>::iterator Discard(const CInputCoin& output);
|
std::vector<CInputCoin>::iterator Discard(const CInputCoin& output);
|
||||||
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
|
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
|
||||||
|
|
||||||
|
//! Update the OutputGroup's fee, long_term_fee, and effective_value based on the given feerates
|
||||||
|
void SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate);
|
||||||
|
OutputGroup GetPositiveOnlyGroup();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret, CAmount not_input_fees);
|
bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret, CAmount not_input_fees);
|
||||||
|
|
|
@ -2320,27 +2320,15 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
|
||||||
for (OutputGroup& group : groups) {
|
for (OutputGroup& group : groups) {
|
||||||
if (!group.EligibleForSpending(eligibility_filter)) continue;
|
if (!group.EligibleForSpending(eligibility_filter)) continue;
|
||||||
|
|
||||||
group.fee = 0;
|
if (coin_selection_params.m_subtract_fee_outputs) {
|
||||||
group.long_term_fee = 0;
|
// Set the effective feerate to 0 as we don't want to use the effective value since the fees will be deducted from the output
|
||||||
group.effective_value = 0;
|
group.SetFees(CFeeRate(0) /* effective_feerate */, long_term_feerate);
|
||||||
for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) {
|
} else {
|
||||||
const CInputCoin& coin = *it;
|
group.SetFees(coin_selection_params.effective_fee, long_term_feerate);
|
||||||
CAmount effective_value = coin.txout.nValue - (coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes));
|
|
||||||
// Only include outputs that are positive effective value (i.e. not dust)
|
|
||||||
if (effective_value > 0) {
|
|
||||||
group.fee += coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
|
|
||||||
group.long_term_fee += coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
|
|
||||||
if (coin_selection_params.m_subtract_fee_outputs) {
|
|
||||||
group.effective_value += coin.txout.nValue;
|
|
||||||
} else {
|
|
||||||
group.effective_value += effective_value;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
} else {
|
|
||||||
it = group.Discard(coin);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (group.effective_value > 0) utxo_pool.push_back(group);
|
|
||||||
|
OutputGroup pos_group = group.GetPositiveOnlyGroup();
|
||||||
|
if (pos_group.effective_value > 0) utxo_pool.push_back(pos_group);
|
||||||
}
|
}
|
||||||
// Calculate the fees for things that aren't inputs
|
// Calculate the fees for things that aren't inputs
|
||||||
CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);
|
CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);
|
||||||
|
|
Loading…
Add table
Reference in a new issue