Skip to content

Commit f269165

Browse files
committed
Merge #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
2 parents 30dd562 + 9adc2f8 commit f269165

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
@@ -5,6 +5,7 @@
55
#include <wallet/coinselection.h>
66

77
#include <optional.h>
8+
#include <policy/feerate.h>
89
#include <util/system.h>
910
#include <util/moneystr.h>
1011

@@ -302,7 +303,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
302303
void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants) {
303304
m_outputs.push_back(output);
304305
m_from_me &= from_me;
305-
m_value += output.effective_value;
306+
m_value += output.txout.nValue;
306307
m_depth = std::min(m_depth, depth);
307308
// ancestors here express the number of ancestors the new coin will end up having, which is
308309
// 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
311312
// descendants is the count as seen from the top ancestor, not the descendants as seen from the
312313
// coin itself; thus, this value is counted as the max, not the sum
313314
m_descendants = std::max(m_descendants, descendants);
314-
effective_value = m_value;
315+
effective_value += output.effective_value;
316+
fee += output.m_fee;
317+
long_term_fee += output.m_long_term_fee;
315318
}
316319

317320
std::vector<CInputCoin>::iterator OutputGroup::Discard(const CInputCoin& output) {
318321
auto it = m_outputs.begin();
319322
while (it != m_outputs.end() && it->outpoint != output.outpoint) ++it;
320323
if (it == m_outputs.end()) return it;
321-
m_value -= output.effective_value;
324+
m_value -= output.txout.nValue;
322325
effective_value -= output.effective_value;
326+
fee -= output.m_fee;
327+
long_term_fee -= output.m_long_term_fee;
323328
return m_outputs.erase(it);
324329
}
325330

@@ -329,3 +334,35 @@ bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_f
329334
&& m_ancestors <= eligibility_filter.max_ancestors
330335
&& m_descendants <= eligibility_filter.max_descendants;
331336
}
337+
338+
void OutputGroup::SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate)
339+
{
340+
fee = 0;
341+
long_term_fee = 0;
342+
effective_value = 0;
343+
for (CInputCoin& coin : m_outputs) {
344+
coin.m_fee = coin.m_input_bytes < 0 ? 0 : effective_feerate.GetFee(coin.m_input_bytes);
345+
fee += coin.m_fee;
346+
347+
coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
348+
long_term_fee += coin.m_long_term_fee;
349+
350+
coin.effective_value = coin.txout.nValue - coin.m_fee;
351+
effective_value += coin.effective_value;
352+
}
353+
}
354+
355+
OutputGroup OutputGroup::GetPositiveOnlyGroup()
356+
{
357+
OutputGroup group(*this);
358+
for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) {
359+
const CInputCoin& coin = *it;
360+
// Only include outputs that are positive effective value (i.e. not dust)
361+
if (coin.effective_value <= 0) {
362+
it = group.Discard(coin);
363+
} else {
364+
++it;
365+
}
366+
}
367+
return group;
368+
}

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};
@@ -91,6 +95,10 @@ struct OutputGroup
9195
void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants);
9296
std::vector<CInputCoin>::iterator Discard(const CInputCoin& output);
9397
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
98+
99+
//! Update the OutputGroup's fee, long_term_fee, and effective_value based on the given feerates
100+
void SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate);
101+
OutputGroup GetPositiveOnlyGroup();
94102
};
95103

96104
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
@@ -2320,27 +2320,15 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
23202320
for (OutputGroup& group : groups) {
23212321
if (!group.EligibleForSpending(eligibility_filter)) continue;
23222322

2323-
group.fee = 0;
2324-
group.long_term_fee = 0;
2325-
group.effective_value = 0;
2326-
for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) {
2327-
const CInputCoin& coin = *it;
2328-
CAmount effective_value = coin.txout.nValue - (coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes));
2329-
// Only include outputs that are positive effective value (i.e. not dust)
2330-
if (effective_value > 0) {
2331-
group.fee += coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
2332-
group.long_term_fee += coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
2333-
if (coin_selection_params.m_subtract_fee_outputs) {
2334-
group.effective_value += coin.txout.nValue;
2335-
} else {
2336-
group.effective_value += effective_value;
2337-
}
2338-
++it;
2339-
} else {
2340-
it = group.Discard(coin);
2341-
}
2323+
if (coin_selection_params.m_subtract_fee_outputs) {
2324+
// 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
2325+
group.SetFees(CFeeRate(0) /* effective_feerate */, long_term_feerate);
2326+
} else {
2327+
group.SetFees(coin_selection_params.effective_fee, long_term_feerate);
23422328
}
2343-
if (group.effective_value > 0) utxo_pool.push_back(group);
2329+
2330+
OutputGroup pos_group = group.GetPositiveOnlyGroup();
2331+
if (pos_group.effective_value > 0) utxo_pool.push_back(pos_group);
23442332
}
23452333
// Calculate the fees for things that aren't inputs
23462334
CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);

0 commit comments

Comments
 (0)