Skip to content

Commit c99f5c5

Browse files
committed
Merge bitcoin/bitcoin#33106: policy: lower the default blockmintxfee, incrementalrelayfee, minrelaytxfee
ba84a25 [doc] update mempool-replacements.md for incremental relay feerate change (glozow) 18720bc [doc] release note for min feerate changes (glozow) 6da5de5 [policy] lower default minrelaytxfee and incrementalrelayfee to 100sat/kvB (glozow) 2e515d2 [prep/test] make wallet_fundrawtransaction's minrelaytxfee assumption explicit (glozow) 457cfb6 [prep/util] help MockMempoolMinFee handle more precise feerates (glozow) 3eab8b7 [prep/test] replace magic number 1000 with respective feerate vars (glozow) 5f2df0e [miner] lower default -blockmintxfee to 1sat/kvB (glozow) d6213d6 [doc] assert that default min relay feerate and incremental are the same (glozow) 1fbee5d [test] explicitly check default -minrelaytxfee and -incrementalrelayfee (glozow) 72dc184 [test] RBF rule 4 for various incrementalrelayfee settings (glozow) 85f4988 [test] check bypass of minrelay for various minrelaytxfee settings (glozow) e5f896b [test] check miner doesn't select 0fee transactions (glozow) Pull request description: ML post for discussion about the general concept, how this impacts the wider ecosystem, philosophy about minimum feerates, etc: https://delvingbitcoin.org/t/changing-the-minimum-relay-feerate/1886 This PR is inspired by #13922 and #32959 to lower the minimum relay feerate in response to bitcoin's exchange rate changes in the last ~10 years. It lowers the default `-minrelaytxfee` and `-incrementalrelayfee`, and knocks `-blockmintxfee` down to the minimum nonzero setting. Also adds some tests for the settings and pulls in #32750. The minimum relay feerate is a DoS protection rule, representing a price on the network bandwidth used to relay transactions that have no PoW. While relay nodes don't all collect fees, the assumption is that if nodes on the network use their resources to relay this transaction, it will reach a miner and the attacker's money will be spent once it is mined. The incremental relay feerate is similar: it's used to price the relay of replacement transactions (the additional fees need to cover the new transactions at this feerate) and evicted transactions (following a trim, the new mempool minimum feerate is the package feerate of what was removed + incremental). Also note that many nodes on the network have elected to relay/mine lower feerate transactions. Miners (some say up to 85%) are choosing to mine these low feerate transactions instead of leaving block space unfilled, but these blocks have extremely poor compact block reconstruction rates with nodes that rejected or didn't hear about those transactions earlier. - bitcoin/bitcoin#33106 (comment) - https://x.com/caesrcd/status/1947022514267230302 - https://mempool.space/block/00000000000000000001305770e0aa279dcd8ba8be18c3d5cf736a26f77e06fd - https://mempool.space/block/00000000000000000001b491649ec030aa8e003e1f4f9d3b24bb99ba16f91e97 - https://x.com/mononautical/status/1949452586391855121 While it wouldn't make sense to loosen DoS restrictions recklessly in response to these events, I think the current price is higher than necessary, and this motivates us changing the default soon. Since the minimum relay feerate defines an amount as too small based on what it costs the attacker, it makes sense to consider BTC's conversion rate to what resources you can buy in the "real world." Going off of [this comment](bitcoin/bitcoin#32959 (comment)) and [this comment](bitcoin/bitcoin#33106 (comment)) - Let's say an attacker wants to use/exhaust the network's bandwidth, and has the choice between renting resources from a commercial provider and getting the network to "spam" itself it by sending unconfirmed transactions. We'd like the latter to be more expensive than the former. - The bandwidth for relaying a transaction across the network is roughly its serialized size (plus relay overhead) x number of nodes. A 1000vB transaction is 1000-4000B serialized. With 100k nodes, that's 0.1-0.4GB - If the going rate for ec2 bandwidth is 10c/GB, that's like 1-4c per kvB of transaction data - Then a 1000vB transaction should pay at least 4c - $0.04 USD is 40 satoshis at 100k USD/BTC - Baking in some margin for changes in USD/BTC conversion rate, number of nodes (and thus bandwidth), and commercial service costs, I think 50-100 satoshis is on the conservative end but in the right ballpark - At least 97% of the recent sub-1sat/vB transactions would be accepted with a new threshold of 0.1sat/vB: bitcoin/bitcoin#33106 (comment) List of feerates that are changed and why: - min relay feerate: significant conversion rate changes, see above - incremental relay feerate: should follow min relay feerate, see above - block minimum feerate: shouldn’t be above min relay feerate, otherwise the node accepts transactions it will never mine. I've knocked it down to the bare minimum of 1sat/kvB. Now that we no longer have coin age priority (removed in v0.15), I think we can leave it to the `CheckFeeRate` policy rule to enforce a minimum entry price, and the block assembly code should just fill up the block with whatever it finds in mempool. List of feerates that are not changed and why: - dust feerate: this feerate cannot be changed as flexibly as the minrelay feerate. A much longer record of low feerate transactions being mined is needed to motivate a decrease there. - maxfeerate (RPC, wallet): I think the conversion rate is relevant as well, but out of scope for this PR - minimum feerate returned by fee estimator: should be done later. In the past, we've excluded new policy defaults from fee estimation until we feel confident they represent miner policy (e.g. #9519). Also, the fee estimator itself doesn't have support for sub-1sat/vB yet. - all wallet feerates (mintxfee, fallbackfee, discardfee, consolidatefeerate, WALLET_INCREMENTAL_RELAY_FEE, etc.): should be done later. Our standard procedure is to do wallet changes at least 1 release after policy changes. ACKs for top commit: achow101: ACK ba84a25 gmaxwell: ACK ba84a25 jsarenik: Tested ACK ba84a25 darosior: ACK ba84a25 ajtowns: ACK ba84a25 davidgumberg: crACK bitcoin/bitcoin@ba84a25 w0xlt: ACK bitcoin/bitcoin@ba84a25 caesrcd: reACK ba84a25 ismaelsadeeq: re-ACK ba84a25 Tree-SHA512: b4c35e8b506b1184db466551a7e2e48bb1e535972a8dbcaa145ce3a8bfdcc70a8807dc129460f129a9d31024174d34077154a387c32f1a3e6831f6fa5e9c399e
2 parents 578b512 + ba84a25 commit c99f5c5

21 files changed

+212
-79
lines changed

doc/policy/mempool-replacements.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ other consensus and policy rules, each of the following conditions are met:
3232
4. The additional fees (difference between absolute fee paid by the replacement transaction and the
3333
sum paid by the original transactions) pays for the replacement transaction's bandwidth at or
3434
above the rate set by the node's incremental relay feerate. For example, if the incremental relay
35-
feerate is 1 satoshi/vB and the replacement transaction is 500 virtual bytes total, then the
36-
replacement pays a fee at least 500 satoshis higher than the sum of the original transactions.
35+
feerate is 0.1 satoshi/vB and the replacement transaction is 500 virtual bytes total, then the
36+
replacement pays a fee at least 50 satoshis higher than the sum of the original transactions.
3737

3838
*Rationale*: Try to prevent DoS attacks where an attacker causes the network to repeatedly relay
3939
transactions each paying a tiny additional amount in fees, e.g. just 1 satoshi.
@@ -77,3 +77,5 @@ This set of rules is similar but distinct from BIP125.
7777
* Full replace-by-fee is the default policy as of **v28.0** ([PR #30493](https://github.com/bitcoin/bitcoin/pull/30493)).
7878

7979
* Signaling for replace-by-fee is no longer required as of [PR 30592](https://github.com/bitcoin/bitcoin/pull/30592).
80+
81+
* The incremental relay feerate default is 0.1sat/vB ([PR #33106](https://github.com/bitcoin/bitcoin/pull/33106)).

doc/release-notes-33106.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Mining and Transaction Relay Policy
2+
=========================
3+
4+
The minimum block feerate (`-blockmintxfee`) has been changed to 1 satoshi per kvB. It can still be changed using the
5+
configuration option.
6+
7+
The default minimum relay feerate (`-minrelaytxfee`) and incremental relay feerate (`-incrementalrelayfee`) have been
8+
changed to 100 satoshis per kvB. They can still be changed using their respective configuration options, but it is
9+
recommended to change both together if you decide to do so.
10+
11+
Other minimum feerates (e.g. the dust feerate, the minimum returned by the fee estimator, and all feerates used by the
12+
wallet) remain unchanged. The mempool minimum feerate still changes in response to high volume but more gradually, as a
13+
result of the change to the incremental relay feerate.
14+
15+
Note that unless these lower defaults are widely adopted across the network, transactions created with lower fee rates
16+
are not guaranteed to propagate or confirm. The wallet feerates remain unchanged; `-mintxfee` must be changed before
17+
attempting to create transactions with lower feerates using the wallet.

src/node/mempool_args.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
6565
}
6666
}
6767

68+
static_assert(DEFAULT_MIN_RELAY_TX_FEE == DEFAULT_INCREMENTAL_RELAY_FEE);
6869
if (const auto arg{argsman.GetArg("-minrelaytxfee")}) {
6970
if (std::optional<CAmount> min_relay_feerate = ParseMoney(*arg)) {
7071
// High fee check is done afterward in CWallet::Create()

src/policy/policy.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ static constexpr unsigned int DEFAULT_BLOCK_RESERVED_WEIGHT{8000};
2929
* Setting a lower value is prevented at startup. */
3030
static constexpr unsigned int MINIMUM_BLOCK_RESERVED_WEIGHT{2000};
3131
/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/
32-
static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1000};
32+
static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1};
3333
/** The maximum weight for transactions we're willing to relay/mine */
3434
static constexpr int32_t MAX_STANDARD_TX_WEIGHT{400000};
3535
/** The minimum non-witness size for transactions we're willing to relay/mine: one larger than 64 */
@@ -41,7 +41,7 @@ static constexpr unsigned int MAX_STANDARD_TX_SIGOPS_COST{MAX_BLOCK_SIGOPS_COST/
4141
/** The maximum number of potentially executed legacy signature operations in a single standard tx */
4242
static constexpr unsigned int MAX_TX_LEGACY_SIGOPS{2'500};
4343
/** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or replacement **/
44-
static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE{1000};
44+
static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE{100};
4545
/** Default for -bytespersigop */
4646
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20};
4747
/** Default for -permitbaremultisig */
@@ -63,7 +63,7 @@ static constexpr unsigned int MAX_STANDARD_SCRIPTSIG_SIZE{1650};
6363
* outputs below the new threshold */
6464
static constexpr unsigned int DUST_RELAY_TX_FEE{3000};
6565
/** Default for -minrelaytxfee, minimum relay fee for transactions */
66-
static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE{1000};
66+
static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE{100};
6767
/** Default for -limitancestorcount, max number of in-mempool ancestors */
6868
static constexpr unsigned int DEFAULT_ANCESTOR_LIMIT{25};
6969
/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */

src/test/mempool_tests.cpp

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -443,15 +443,15 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
443443
tx1.vout.resize(1);
444444
tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
445445
tx1.vout[0].nValue = 10 * COIN;
446-
AddToMempool(pool, entry.Fee(10000LL).FromTx(tx1));
446+
AddToMempool(pool, entry.Fee(1000LL).FromTx(tx1));
447447

448448
CMutableTransaction tx2 = CMutableTransaction();
449449
tx2.vin.resize(1);
450450
tx2.vin[0].scriptSig = CScript() << OP_2;
451451
tx2.vout.resize(1);
452452
tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
453453
tx2.vout[0].nValue = 10 * COIN;
454-
AddToMempool(pool, entry.Fee(5000LL).FromTx(tx2));
454+
AddToMempool(pool, entry.Fee(500LL).FromTx(tx2));
455455

456456
pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
457457
BOOST_CHECK(pool.exists(tx1.GetHash()));
@@ -469,7 +469,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
469469
tx3.vout.resize(1);
470470
tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
471471
tx3.vout[0].nValue = 10 * COIN;
472-
AddToMempool(pool, entry.Fee(20000LL).FromTx(tx3));
472+
AddToMempool(pool, entry.Fee(2000LL).FromTx(tx3));
473473

474474
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
475475
BOOST_CHECK(!pool.exists(tx1.GetHash()));
@@ -481,8 +481,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
481481
BOOST_CHECK(!pool.exists(tx2.GetHash()));
482482
BOOST_CHECK(!pool.exists(tx3.GetHash()));
483483

484-
CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2)));
485-
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
484+
CFeeRate maxFeeRateRemoved(2500, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2)));
485+
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE);
486486

487487
CMutableTransaction tx4 = CMutableTransaction();
488488
tx4.vin.resize(2);
@@ -532,10 +532,10 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
532532
tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
533533
tx7.vout[1].nValue = 10 * COIN;
534534

535-
AddToMempool(pool, entry.Fee(7000LL).FromTx(tx4));
536-
AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5));
537-
AddToMempool(pool, entry.Fee(1100LL).FromTx(tx6));
538-
AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7));
535+
AddToMempool(pool, entry.Fee(700LL).FromTx(tx4));
536+
AddToMempool(pool, entry.Fee(100LL).FromTx(tx5));
537+
AddToMempool(pool, entry.Fee(110LL).FromTx(tx6));
538+
AddToMempool(pool, entry.Fee(900LL).FromTx(tx7));
539539

540540
// we only require this to remove, at max, 2 txn, because it's not clear what we're really optimizing for aside from that
541541
pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
@@ -544,43 +544,43 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
544544
BOOST_CHECK(!pool.exists(tx7.GetHash()));
545545

546546
if (!pool.exists(tx5.GetHash()))
547-
AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5));
548-
AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7));
547+
AddToMempool(pool, entry.Fee(100LL).FromTx(tx5));
548+
AddToMempool(pool, entry.Fee(900LL).FromTx(tx7));
549549

550550
pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
551551
BOOST_CHECK(pool.exists(tx4.GetHash()));
552552
BOOST_CHECK(!pool.exists(tx5.GetHash()));
553553
BOOST_CHECK(pool.exists(tx6.GetHash()));
554554
BOOST_CHECK(!pool.exists(tx7.GetHash()));
555555

556-
AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5));
557-
AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7));
556+
AddToMempool(pool, entry.Fee(100LL).FromTx(tx5));
557+
AddToMempool(pool, entry.Fee(900LL).FromTx(tx7));
558558

559559
std::vector<CTransactionRef> vtx;
560560
SetMockTime(42);
561561
SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
562-
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
562+
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE);
563563
// ... we should keep the same min fee until we get a block
564564
pool.removeForBlock(vtx, 1);
565565
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
566-
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0));
566+
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/2.0));
567567
// ... then feerate should drop 1/2 each halflife
568568

569569
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2);
570-
BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0));
570+
BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/4.0));
571571
// ... with a 1/2 halflife when mempool is < 1/2 its target size
572572

573573
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
574-
BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0));
574+
BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/8.0));
575575
// ... with a 1/4 halflife when mempool is < 1/4 its target size
576576

577577
SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
578-
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000);
579-
// ... but feerate should never drop below 1000
578+
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), DEFAULT_INCREMENTAL_RELAY_FEE);
579+
// ... but feerate should never drop below DEFAULT_INCREMENTAL_RELAY_FEE
580580

581581
SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
582582
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
583-
// ... unless it has gone all the way to 0 (after getting past 1000/2)
583+
// ... unless it has gone all the way to 0 (after getting past DEFAULT_INCREMENTAL_RELAY_FEE/2)
584584
}
585585

586586
inline CTransactionRef make_tx(std::vector<CAmount>&& output_values, std::vector<CTransactionRef>&& inputs=std::vector<CTransactionRef>(), std::vector<uint32_t>&& input_indices=std::vector<uint32_t>())

src/test/miner_tests.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <node/miner.h>
1313
#include <policy/policy.h>
1414
#include <test/util/random.h>
15+
#include <test/util/transaction_utils.h>
1516
#include <test/util/txmempool.h>
1617
#include <txmempool.h>
1718
#include <uint256.h>
@@ -216,6 +217,9 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
216217
tx.vout.resize(2);
217218
tx.vout[0].nValue = 5000000000LL - 100000000;
218219
tx.vout[1].nValue = 100000000; // 1BTC output
220+
// Increase size to avoid rounding errors: when the feerate is extremely small (i.e. 1sat/kvB), evaluating the fee
221+
// at a smaller transaction size gives us a rounded value of 0.
222+
BulkTransaction(tx, 4000);
219223
Txid hashFreeTx2 = tx.GetHash();
220224
AddToMempool(tx_mempool, entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
221225

src/test/rbf_tests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,10 @@ BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
238238
BOOST_CHECK(PaysForRBF(high_fee, high_fee - 1, 1, CFeeRate(0), unused_txid).has_value());
239239
BOOST_CHECK(PaysForRBF(high_fee + 1, high_fee, 1, CFeeRate(0), unused_txid).has_value());
240240
// Additional fees must cover the replacement's vsize at incremental relay fee
241-
BOOST_CHECK(PaysForRBF(high_fee, high_fee + 1, 2, incremental_relay_feerate, unused_txid).has_value());
242-
BOOST_CHECK(PaysForRBF(high_fee, high_fee + 2, 2, incremental_relay_feerate, unused_txid) == std::nullopt);
243-
BOOST_CHECK(PaysForRBF(high_fee, high_fee + 2, 2, higher_relay_feerate, unused_txid).has_value());
244-
BOOST_CHECK(PaysForRBF(high_fee, high_fee + 4, 2, higher_relay_feerate, unused_txid) == std::nullopt);
241+
BOOST_CHECK(PaysForRBF(high_fee, high_fee + 1, 11, incremental_relay_feerate, unused_txid).has_value());
242+
BOOST_CHECK(PaysForRBF(high_fee, high_fee + 1, 10, incremental_relay_feerate, unused_txid) == std::nullopt);
243+
BOOST_CHECK(PaysForRBF(high_fee, high_fee + 2, 11, higher_relay_feerate, unused_txid).has_value());
244+
BOOST_CHECK(PaysForRBF(high_fee, high_fee + 4, 20, higher_relay_feerate, unused_txid) == std::nullopt);
245245
BOOST_CHECK(PaysForRBF(low_fee, high_fee, 99999999, incremental_relay_feerate, unused_txid).has_value());
246246
BOOST_CHECK(PaysForRBF(low_fee, high_fee + 99999999, 99999999, incremental_relay_feerate, unused_txid) == std::nullopt);
247247

src/test/util/setup_common.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <test/util/coverage.h>
4141
#include <test/util/net.h>
4242
#include <test/util/random.h>
43+
#include <test/util/transaction_utils.h>
4344
#include <test/util/txmempool.h>
4445
#include <txdb.h>
4546
#include <txmempool.h>
@@ -586,6 +587,9 @@ void TestChain100Setup::MockMempoolMinFee(const CFeeRate& target_feerate)
586587
CMutableTransaction mtx = CMutableTransaction();
587588
mtx.vin.emplace_back(COutPoint{Txid::FromUint256(m_rng.rand256()), 0});
588589
mtx.vout.emplace_back(1 * COIN, GetScriptForDestination(WitnessV0ScriptHash(CScript() << OP_TRUE)));
590+
// Set a large size so that the fee evaluated at target_feerate (which is usually in sats/kvB) is an integer.
591+
// Otherwise, GetMinFee() may end up slightly different from target_feerate.
592+
BulkTransaction(mtx, 4000);
589593
const auto tx{MakeTransactionRef(mtx)};
590594
LockPoints lp;
591595
// The new mempool min feerate is equal to the removed package's feerate + incremental feerate.

test/functional/feature_rbf.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
from test_framework.test_framework import BitcoinTestFramework
1414
from test_framework.util import (
1515
assert_equal,
16+
assert_greater_than,
17+
assert_greater_than_or_equal,
1618
assert_raises_rpc_error,
19+
get_fee,
1720
)
1821
from test_framework.wallet import MiniWallet
1922
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
@@ -74,6 +77,9 @@ def run_test(self):
7477
self.log.info("Running test full replace by fee...")
7578
self.test_fullrbf()
7679

80+
self.log.info("Running test incremental relay feerates...")
81+
self.test_incremental_relay_feerates()
82+
7783
self.log.info("Passed")
7884

7985
def make_utxo(self, node, amount, *, confirmed=True, scriptPubKey=None):
@@ -579,10 +585,42 @@ def test_replacement_relay_fee(self):
579585

580586
# Higher fee, higher feerate, different txid, but the replacement does not provide a relay
581587
# fee conforming to node's `incrementalrelayfee` policy of 1000 sat per KB.
582-
assert_equal(self.nodes[0].getmempoolinfo()["incrementalrelayfee"], Decimal("0.00001"))
588+
assert_equal(self.nodes[0].getmempoolinfo()["incrementalrelayfee"], Decimal("0.000001"))
583589
tx.vout[0].nValue -= 1
584590
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex())
585591

592+
def test_incremental_relay_feerates(self):
593+
self.log.info("Test that incremental relay fee is applied correctly in RBF for various settings...")
594+
node = self.nodes[0]
595+
for incremental_setting in (0, 5, 10, 50, 100, 234, 1000, 5000, 21000):
596+
incremental_setting_decimal = incremental_setting / Decimal(COIN)
597+
self.log.info(f"-> Test -incrementalrelayfee={incremental_setting_decimal:.8f}sat/kvB...")
598+
self.restart_node(0, extra_args=[f"-incrementalrelayfee={incremental_setting_decimal:.8f}", "-persistmempool=0"])
599+
600+
# When incremental relay feerate is higher than min relay feerate, min relay feerate is automatically increased.
601+
min_relay_feerate = node.getmempoolinfo()["minrelaytxfee"]
602+
assert_greater_than_or_equal(min_relay_feerate, incremental_setting_decimal)
603+
604+
low_feerate = min_relay_feerate * 2
605+
confirmed_utxo = self.wallet.get_utxo(confirmed_only=True)
606+
replacee_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee_rate=low_feerate, target_vsize=5000)
607+
node.sendrawtransaction(replacee_tx['hex'])
608+
609+
replacement_placeholder_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo)
610+
replacement_expected_size = replacement_placeholder_tx['tx'].get_vsize()
611+
replacement_required_fee = get_fee(replacement_expected_size, incremental_setting_decimal) + replacee_tx['fee']
612+
613+
# Should always be required to pay additional fees
614+
if incremental_setting > 0:
615+
assert_greater_than(replacement_required_fee, replacee_tx['fee'])
616+
617+
# 1 satoshi shy of the required fee
618+
failed_replacement_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee=replacement_required_fee - Decimal("0.00000001"))
619+
assert_raises_rpc_error(-26, "insufficient fee", node.sendrawtransaction, failed_replacement_tx['hex'])
620+
621+
replacement_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee=replacement_required_fee)
622+
node.sendrawtransaction(replacement_tx['hex'])
623+
586624
def test_fullrbf(self):
587625
# BIP125 signaling is not respected
588626

test/functional/mempool_accept.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111
from test_framework.test_framework import BitcoinTestFramework
1212
from test_framework.blocktools import MAX_STANDARD_TX_WEIGHT
13+
from test_framework.mempool_util import (
14+
DEFAULT_MIN_RELAY_TX_FEE,
15+
DEFAULT_INCREMENTAL_RELAY_FEE,
16+
)
1317
from test_framework.messages import (
1418
MAX_BIP125_RBF_SEQUENCE,
1519
COIN,
@@ -85,6 +89,11 @@ def run_test(self):
8589
assert_equal(node.getblockcount(), 200)
8690
assert_equal(node.getmempoolinfo()['size'], self.mempool_size)
8791

92+
self.log.info("Check default settings")
93+
# Settings are listed in BTC/kvB
94+
assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN)
95+
assert_equal(node.getmempoolinfo()['incrementalrelayfee'], Decimal(DEFAULT_INCREMENTAL_RELAY_FEE) / COIN)
96+
8897
self.log.info('Should not accept garbage to testmempoolaccept')
8998
assert_raises_rpc_error(-3, 'JSON value of type string is not of expected type array', lambda: node.testmempoolaccept(rawtxs='ff00baar'))
9099
assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=['ff22']*26))

0 commit comments

Comments
 (0)