Skip to content

Commit 459272d

Browse files
committed
Merge bitcoin/bitcoin#26152: Bump unconfirmed ancestor transactions to target feerate
f18f9ef Amend bumpfee for inputs with overlapping ancestry (Murch) 2e35e94 Bump unconfirmed parent txs to target feerate (Murch) 3e3e052 coinselection: Move GetSelectionWaste into SelectionResult (Andrew Chow) c57889d [node] interface to get bump fees (glozow) c24851b Make MiniMinerMempoolEntry fields private (Murch) ac6030e Remove unused imports (Murch) d2f90c3 Fix calculation of ancestor set feerates in test (Murch) a1f7d98 Match tx names to index in miniminer overlap test (Murch) Pull request description: Includes some commits to address follow-ups from #27021: bitcoin/bitcoin#27021 (comment) Reduces the effective value of unconfirmed UTXOs by the fees necessary to bump their ancestor transactions to the same feerate. While the individual UTXOs always account for their full ancestry before coin-selection, we can correct potential overestimates with a second pass where we establish the ancestry and bump fee for the whole input set collectively. Fixes #9645 Fixes #9864 Fixes #15553 ACKs for top commit: S3RK: ACK f18f9ef ismaelsadeeq: ACK f18f9ef achow101: ACK f18f9ef brunoerg: crACK f18f9ef t-bast: ACK bitcoin/bitcoin@f18f9ef, I reviewed the latest changes and run e2e tests against eclair, everything looks good 👍 Tree-SHA512: b65180c4243b1f9d13c311ada7a1c9f2f055d530d6c533b78c2068b50b8c29ac1321e89e85675b15515760d4f1b653ebd9da77b37c7be52d9bc565a3538f0aa6
2 parents 541976b + f18f9ef commit 459272d

File tree

14 files changed

+1094
-325
lines changed

14 files changed

+1094
-325
lines changed

src/bench/coin_selection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ static void CoinSelection(benchmark::Bench& bench)
7979
};
8080
auto group = wallet::GroupOutputs(wallet, available_coins, coin_selection_params, {{filter_standard}})[filter_standard];
8181
bench.run([&] {
82-
auto result = AttemptSelection(1003 * COIN, group, coin_selection_params, /*allow_mixed_output_types=*/true);
82+
auto result = AttemptSelection(wallet.chain(), 1003 * COIN, group, coin_selection_params, /*allow_mixed_output_types=*/true);
8383
assert(result);
8484
assert(result->GetSelectedValue() == 1003 * COIN);
8585
assert(result->GetInputSet().size() == 2);

src/interfaces/chain.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,43 @@ class Chain
216216
//! Calculate mempool ancestor and descendant counts for the given transaction.
217217
virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) = 0;
218218

219+
//! For each outpoint, calculate the fee-bumping cost to spend this outpoint at the specified
220+
// feerate, including bumping its ancestors. For example, if the target feerate is 10sat/vbyte
221+
// and this outpoint refers to a mempool transaction at 3sat/vbyte, the bump fee includes the
222+
// cost to bump the mempool transaction to 10sat/vbyte (i.e. 7 * mempooltx.vsize). If that
223+
// transaction also has, say, an unconfirmed parent with a feerate of 1sat/vbyte, the bump fee
224+
// includes the cost to bump the parent (i.e. 9 * parentmempooltx.vsize).
225+
//
226+
// If the outpoint comes from an unconfirmed transaction that is already above the target
227+
// feerate or bumped by its descendant(s) already, it does not need to be bumped. Its bump fee
228+
// is 0. Likewise, if any of the transaction's ancestors are already bumped by a transaction
229+
// in our mempool, they are not included in the transaction's bump fee.
230+
//
231+
// Also supported is bump-fee calculation in the case of replacements. If an outpoint
232+
// conflicts with another transaction in the mempool, it is assumed that the goal is to replace
233+
// that transaction. As such, the calculation will exclude the to-be-replaced transaction, but
234+
// will include the fee-bumping cost. If bump fees of descendants of the to-be-replaced
235+
// transaction are requested, the value will be 0. Fee-related RBF rules are not included as
236+
// they are logically distinct.
237+
//
238+
// Any outpoints that are otherwise unavailable from the mempool (e.g. UTXOs from confirmed
239+
// transactions or transactions not yet broadcast by the wallet) are given a bump fee of 0.
240+
//
241+
// If multiple outpoints come from the same transaction (which would be very rare because
242+
// it means that one transaction has multiple change outputs or paid the same wallet using multiple
243+
// outputs in the same transaction) or have shared ancestry, the bump fees are calculated
244+
// independently, i.e. as if only one of them is spent. This may result in double-fee-bumping. This
245+
// caveat can be rectified per use of the sister-function CalculateCombinedBumpFee(…).
246+
virtual std::map<COutPoint, CAmount> CalculateIndividualBumpFees(const std::vector<COutPoint>& outpoints, const CFeeRate& target_feerate) = 0;
247+
248+
//! Calculate the combined bump fee for an input set per the same strategy
249+
// as in CalculateIndividualBumpFees(…).
250+
// Unlike CalculateIndividualBumpFees(…), this does not return individual
251+
// bump fees per outpoint, but a single bump fee for the shared ancestry.
252+
// The combined bump fee may be used to correct overestimation due to
253+
// shared ancestry by multiple UTXOs after coin selection.
254+
virtual std::optional<CAmount> CalculateCombinedBumpFee(const std::vector<COutPoint>& outpoints, const CFeeRate& target_feerate) = 0;
255+
219256
//! Get the node's package limits.
220257
//! Currently only returns the ancestor and descendant count limits, but could be enhanced to
221258
//! return more policy settings.

src/node/interfaces.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <node/coin.h>
2929
#include <node/context.h>
3030
#include <node/interface_ui.h>
31+
#include <node/mini_miner.h>
3132
#include <node/transaction.h>
3233
#include <policy/feerate.h>
3334
#include <policy/fees.h>
@@ -665,6 +666,26 @@ class ChainImpl : public Chain
665666
if (!m_node.mempool) return;
666667
m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants, ancestorsize, ancestorfees);
667668
}
669+
670+
std::map<COutPoint, CAmount> CalculateIndividualBumpFees(const std::vector<COutPoint>& outpoints, const CFeeRate& target_feerate) override
671+
{
672+
if (!m_node.mempool) {
673+
std::map<COutPoint, CAmount> bump_fees;
674+
for (const auto& outpoint : outpoints) {
675+
bump_fees.emplace(std::make_pair(outpoint, 0));
676+
}
677+
return bump_fees;
678+
}
679+
return MiniMiner(*m_node.mempool, outpoints).CalculateBumpFees(target_feerate);
680+
}
681+
682+
std::optional<CAmount> CalculateCombinedBumpFee(const std::vector<COutPoint>& outpoints, const CFeeRate& target_feerate) override
683+
{
684+
if (!m_node.mempool) {
685+
return 0;
686+
}
687+
return MiniMiner(*m_node.mempool, outpoints).CalculateTotalBumpFees(target_feerate);
688+
}
668689
void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override
669690
{
670691
const CTxMemPool::Limits default_limits{};

src/node/mini_miner.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
#include <consensus/amount.h>
88
#include <policy/feerate.h>
99
#include <primitives/transaction.h>
10-
#include <timedata.h>
1110
#include <util/check.h>
12-
#include <util/moneystr.h>
1311

1412
#include <algorithm>
1513
#include <numeric>
@@ -171,9 +169,8 @@ void MiniMiner::DeleteAncestorPackage(const std::set<MockEntryMap::iterator, Ite
171169
for (auto& descendant : it->second) {
172170
// If these fail, we must be double-deducting.
173171
Assume(descendant->second.GetModFeesWithAncestors() >= anc->second.GetModifiedFee());
174-
Assume(descendant->second.vsize_with_ancestors >= anc->second.GetTxSize());
175-
descendant->second.fee_with_ancestors -= anc->second.GetModifiedFee();
176-
descendant->second.vsize_with_ancestors -= anc->second.GetTxSize();
172+
Assume(descendant->second.GetSizeWithAncestors() >= anc->second.GetTxSize());
173+
descendant->second.UpdateAncestorState(-anc->second.GetTxSize(), -anc->second.GetModifiedFee());
177174
}
178175
}
179176
// Delete these entries.

src/node/mini_miner.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ class MiniMinerMempoolEntry
1919
const CAmount fee_individual;
2020
const CTransactionRef tx;
2121
const int64_t vsize_individual;
22+
CAmount fee_with_ancestors;
23+
int64_t vsize_with_ancestors;
2224

2325
// This class must be constructed while holding mempool.cs. After construction, the object's
2426
// methods can be called without holding that lock.
27+
2528
public:
26-
CAmount fee_with_ancestors;
27-
int64_t vsize_with_ancestors;
2829
explicit MiniMinerMempoolEntry(CTxMemPool::txiter entry) :
2930
fee_individual{entry->GetModifiedFee()},
3031
tx{entry->GetSharedTx()},
@@ -38,6 +39,10 @@ class MiniMinerMempoolEntry
3839
int64_t GetTxSize() const { return vsize_individual; }
3940
int64_t GetSizeWithAncestors() const { return vsize_with_ancestors; }
4041
const CTransaction& GetTx() const LIFETIMEBOUND { return *tx; }
42+
void UpdateAncestorState(int64_t vsize_change, CAmount fee_change) {
43+
vsize_with_ancestors += vsize_change;
44+
fee_with_ancestors += fee_change;
45+
}
4146
};
4247

4348
// Comparator needed for std::set<MockEntryMap::iterator>

0 commit comments

Comments
 (0)