Skip to content

Commit 51ff73a

Browse files
meshcolliderPastaPastaPasta
authored andcommitted
Merge bitcoin#17458: Refactor OutputGroup effective value calculations and filtering to occur within the struct
9adc2f8 Refactor OutputGroups to handle effective values, fees, and filtering (Andrew Chow) 7d07e86 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: reACK 9adc2f8 fjahr: re-ACK 9adc2f8 meshcollider: Light code review ACK 9adc2f8 Tree-SHA512: 7445c94b7295b45bcd83a6f8a5c8f6961a89453fcc856335192d4b4a66aec7724513616b04e5111588ab208c89b311055399d6279cd9c4ce452aefb85f04b64a
1 parent 6853849 commit 51ff73a

File tree

3 files changed

+56
-23
lines changed

3 files changed

+56
-23
lines changed

src/wallet/coinselection.cpp

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <wallet/coinselection.h>
66

7+
#include <policy/feerate.h>
78
#include <util/system.h>
89
#include <util/moneystr.h>
910

@@ -365,7 +366,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
365366
void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants) {
366367
m_outputs.push_back(output);
367368
m_from_me &= from_me;
368-
m_value += output.effective_value;
369+
m_value += output.txout.nValue;
369370
m_depth = std::min(m_depth, depth);
370371
// ancestors here express the number of ancestors the new coin will end up having, which is
371372
// the sum, rather than the max; this will overestimate in the cases where multiple inputs
@@ -374,15 +375,19 @@ void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size
374375
// descendants is the count as seen from the top ancestor, not the descendants as seen from the
375376
// coin itself; thus, this value is counted as the max, not the sum
376377
m_descendants = std::max(m_descendants, descendants);
377-
effective_value = m_value;
378+
effective_value += output.effective_value;
379+
fee += output.m_fee;
380+
long_term_fee += output.m_long_term_fee;
378381
}
379382

380383
std::vector<CInputCoin>::iterator OutputGroup::Discard(const CInputCoin& output) {
381384
auto it = m_outputs.begin();
382385
while (it != m_outputs.end() && it->outpoint != output.outpoint) ++it;
383386
if (it == m_outputs.end()) return it;
384-
m_value -= output.effective_value;
387+
m_value -= output.txout.nValue;
385388
effective_value -= output.effective_value;
389+
fee -= output.m_fee;
390+
long_term_fee -= output.m_long_term_fee;
386391
return m_outputs.erase(it);
387392
}
388393

@@ -401,3 +406,35 @@ bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_f
401406
&& m_ancestors <= eligibility_filter.max_ancestors
402407
&& m_descendants <= eligibility_filter.max_descendants;
403408
}
409+
410+
void OutputGroup::SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate)
411+
{
412+
fee = 0;
413+
long_term_fee = 0;
414+
effective_value = 0;
415+
for (CInputCoin& coin : m_outputs) {
416+
coin.m_fee = coin.m_input_bytes < 0 ? 0 : effective_feerate.GetFee(coin.m_input_bytes);
417+
fee += coin.m_fee;
418+
419+
coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
420+
long_term_fee += coin.m_long_term_fee;
421+
422+
coin.effective_value = coin.txout.nValue - coin.m_fee;
423+
effective_value += coin.effective_value;
424+
}
425+
}
426+
427+
OutputGroup OutputGroup::GetPositiveOnlyGroup()
428+
{
429+
OutputGroup group(*this);
430+
for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) {
431+
const CInputCoin& coin = *it;
432+
// Only include outputs that are positive effective value (i.e. not dust)
433+
if (coin.effective_value <= 0) {
434+
it = group.Discard(coin);
435+
} else {
436+
++it;
437+
}
438+
}
439+
return group;
440+
}

src/wallet/coinselection.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <primitives/transaction.h>
1010
#include <random.h>
1111

12+
class CFeeRate;
13+
1214
//! target minimum change amount
1315
static constexpr CAmount MIN_CHANGE{COIN / 100};
1416
//! final minimum change amount after paying for fees
@@ -36,6 +38,8 @@ class CInputCoin {
3638
COutPoint outpoint;
3739
CTxOut txout;
3840
CAmount effective_value;
41+
CAmount m_fee{0};
42+
CAmount m_long_term_fee{0};
3943

4044
/** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
4145
int m_input_bytes{-1};
@@ -92,6 +96,10 @@ struct OutputGroup
9296
std::vector<CInputCoin>::iterator Discard(const CInputCoin& output);
9397
bool IsLockedByInstantSend() const;
9498
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
99+
100+
//! Update the OutputGroup's fee, long_term_fee, and effective_value based on the given feerates
101+
void SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate);
102+
OutputGroup GetPositiveOnlyGroup();
95103
};
96104

97105
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);

src/wallet/wallet.cpp

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,27 +2745,15 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
27452745
for (OutputGroup& group : groups) {
27462746
if (!group.EligibleForSpending(eligibility_filter)) continue;
27472747

2748-
group.fee = 0;
2749-
group.long_term_fee = 0;
2750-
group.effective_value = 0;
2751-
for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) {
2752-
const CInputCoin& coin = *it;
2753-
CAmount effective_value = coin.txout.nValue - (coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes));
2754-
// Only include outputs that are positive effective value (i.e. not dust)
2755-
if (effective_value > 0) {
2756-
group.fee += coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
2757-
group.long_term_fee += coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
2758-
if (coin_selection_params.m_subtract_fee_outputs) {
2759-
group.effective_value += coin.txout.nValue;
2760-
} else {
2761-
group.effective_value += effective_value;
2762-
}
2763-
++it;
2764-
} else {
2765-
it = group.Discard(coin);
2766-
}
2748+
if (coin_selection_params.m_subtract_fee_outputs) {
2749+
// 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
2750+
group.SetFees(CFeeRate(0) /* effective_feerate */, long_term_feerate);
2751+
} else {
2752+
group.SetFees(coin_selection_params.effective_fee, long_term_feerate);
27672753
}
2768-
if (group.effective_value > 0) utxo_pool.push_back(group);
2754+
2755+
OutputGroup pos_group = group.GetPositiveOnlyGroup();
2756+
if (pos_group.effective_value > 0) utxo_pool.push_back(pos_group);
27692757
}
27702758
// Calculate the fees for things that aren't inputs
27712759
CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);

0 commit comments

Comments
 (0)