Skip to content

Commit 94d6a18

Browse files
committed
Merge #16507: feefilter: Compute the absolute fee rather than stored rate
eb7b781 modify p2p_feefilter test to catch rounding error (Gregory Sanders) 6a51f79 Disallow implicit conversion for CFeeRate constructor (Gregory Sanders) 8e59af5 feefilter: Compute the absolute fee rather than stored rate to match mempool acceptance logic (Gregory Sanders) Pull request description: This means we will use the rounding-down behavior in `GetFee` to match both mempool acceptance and wallet logic, with minimal changes. Fixes bitcoin/bitcoin#16499 Replacement PR for bitcoin/bitcoin#16500 ACKs for top commit: ajtowns: ACK eb7b781 code review only naumenkogs: utACK eb7b781 achow101: re ACK eb7b781 promag: ACK eb7b781. Tree-SHA512: 484a11c8f0e825f0c983b1f7e71cf6252b1bba6858194abfe4c088da3bae8a418ec539ef6c4181bf30940e277a95c08d493595d59dfcc6ddf77c65b05563dd7e
2 parents 94e6e9f + eb7b781 commit 94d6a18

File tree

6 files changed

+34
-21
lines changed

6 files changed

+34
-21
lines changed

src/net_processing.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3847,10 +3847,10 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
38473847
if (fSendTrickle && pto->m_tx_relay->fSendMempool) {
38483848
auto vtxinfo = mempool.infoAll();
38493849
pto->m_tx_relay->fSendMempool = false;
3850-
CAmount filterrate = 0;
3850+
CFeeRate filterrate;
38513851
{
38523852
LOCK(pto->m_tx_relay->cs_feeFilter);
3853-
filterrate = pto->m_tx_relay->minFeeFilter;
3853+
filterrate = CFeeRate(pto->m_tx_relay->minFeeFilter);
38543854
}
38553855

38563856
LOCK(pto->m_tx_relay->cs_filter);
@@ -3859,9 +3859,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
38593859
const uint256& hash = txinfo.tx->GetHash();
38603860
CInv inv(MSG_TX, hash);
38613861
pto->m_tx_relay->setInventoryTxToSend.erase(hash);
3862-
if (filterrate) {
3863-
if (txinfo.feeRate.GetFeePerK() < filterrate)
3864-
continue;
3862+
// Don't send transactions that peers will not put into their mempool
3863+
if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
3864+
continue;
38653865
}
38663866
if (pto->m_tx_relay->pfilter) {
38673867
if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
@@ -3884,10 +3884,10 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
38843884
for (std::set<uint256>::iterator it = pto->m_tx_relay->setInventoryTxToSend.begin(); it != pto->m_tx_relay->setInventoryTxToSend.end(); it++) {
38853885
vInvTx.push_back(it);
38863886
}
3887-
CAmount filterrate = 0;
3887+
CFeeRate filterrate;
38883888
{
38893889
LOCK(pto->m_tx_relay->cs_feeFilter);
3890-
filterrate = pto->m_tx_relay->minFeeFilter;
3890+
filterrate = CFeeRate(pto->m_tx_relay->minFeeFilter);
38913891
}
38923892
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
38933893
// A heap is used so that not all items need sorting if only a few are being sent.
@@ -3914,7 +3914,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
39143914
if (!txinfo.tx) {
39153915
continue;
39163916
}
3917-
if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) {
3917+
// Peer told you to not send transactions at that feerate? Don't bother sending it.
3918+
if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
39183919
continue;
39193920
}
39203921
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;

src/policy/feerate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class CFeeRate
2525
/** Fee rate of 0 satoshis per kB */
2626
CFeeRate() : nSatoshisPerK(0) { }
2727
template<typename I>
28-
CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) {
28+
explicit CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) {
2929
// We've previously had bugs creep in from silent double->int conversion...
3030
static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats");
3131
}

src/txmempool.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) const
773773
}
774774

775775
static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it) {
776-
return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), CFeeRate(it->GetFee(), it->GetTxSize()), it->GetModifiedFee() - it->GetFee()};
776+
return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), it->GetFee(), it->GetTxSize(), it->GetModifiedFee() - it->GetFee()};
777777
}
778778

779779
std::vector<TxMempoolInfo> CTxMemPool::infoAll() const

src/txmempool.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,11 @@ struct TxMempoolInfo
334334
/** Time the transaction entered the mempool. */
335335
std::chrono::seconds m_time;
336336

337-
/** Feerate of the transaction. */
338-
CFeeRate feeRate;
337+
/** Fee of the transaction. */
338+
CAmount fee;
339+
340+
/** Virtual size of the transaction. */
341+
size_t vsize;
339342

340343
/** The fee delta. */
341344
int64_t nFeeDelta;

src/wallet/rpcwallet.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2327,7 +2327,7 @@ static UniValue settxfee(const JSONRPCRequest& request)
23272327

23282328
CAmount nAmount = AmountFromValue(request.params[0]);
23292329
CFeeRate tx_fee_rate(nAmount, 1000);
2330-
if (tx_fee_rate == 0) {
2330+
if (tx_fee_rate == CFeeRate(0)) {
23312331
// automatic selection
23322332
} else if (tx_fee_rate < pwallet->chain().relayMinFee()) {
23332333
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than min relay tx fee (%s)", pwallet->chain().relayMinFee().ToString()));
@@ -3386,7 +3386,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
33863386
}
33873387
} else if (options.exists("fee_rate")) {
33883388
CFeeRate fee_rate(AmountFromValue(options["fee_rate"]));
3389-
if (fee_rate <= 0) {
3389+
if (fee_rate <= CFeeRate(0)) {
33903390
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid fee_rate %s (must be greater than 0)", fee_rate.ToString()));
33913391
}
33923392
coin_control.m_feerate = fee_rate;

test/functional/p2p_feefilter.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ def clear_invs(self):
4141
class FeeFilterTest(BitcoinTestFramework):
4242
def set_test_params(self):
4343
self.num_nodes = 2
44+
# We lower the various required feerates for this test
45+
# to catch a corner-case where feefilter used to slightly undercut
46+
# mempool and wallet feerate calculation based on GetFee
47+
# rounding down 3 places, leading to stranded transactions.
48+
# See issue #16499
49+
self.extra_args = [["-minrelaytxfee=0.00000100", "-mintxfee=0.00000100"]]*self.num_nodes
4450

4551
def skip_test_if_missing_module(self):
4652
self.skip_if_no_wallet()
@@ -54,22 +60,25 @@ def run_test(self):
5460

5561
self.nodes[0].add_p2p_connection(TestP2PConn())
5662

57-
# Test that invs are received for all txs at feerate of 20 sat/byte
58-
node1.settxfee(Decimal("0.00020000"))
63+
# Test that invs are received by test connection for all txs at
64+
# feerate of .2 sat/byte
65+
node1.settxfee(Decimal("0.00000200"))
5966
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
6067
assert allInvsMatch(txids, self.nodes[0].p2p)
6168
self.nodes[0].p2p.clear_invs()
6269

63-
# Set a filter of 15 sat/byte
64-
self.nodes[0].p2p.send_and_ping(msg_feefilter(15000))
70+
# Set a filter of .15 sat/byte on test connection
71+
self.nodes[0].p2p.send_and_ping(msg_feefilter(150))
6572

66-
# Test that txs are still being received (paying 20 sat/byte)
73+
# Test that txs are still being received by test connection (paying .15 sat/byte)
74+
node1.settxfee(Decimal("0.00000150"))
6775
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
6876
assert allInvsMatch(txids, self.nodes[0].p2p)
6977
self.nodes[0].p2p.clear_invs()
7078

71-
# Change tx fee rate to 10 sat/byte and test they are no longer received
72-
node1.settxfee(Decimal("0.00010000"))
79+
# Change tx fee rate to .1 sat/byte and test they are no longer received
80+
# by the test connection
81+
node1.settxfee(Decimal("0.00000100"))
7382
[node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
7483
self.sync_mempools() # must be sure node 0 has received all txs
7584

0 commit comments

Comments
 (0)