diff --git a/CMakeLists.txt b/CMakeLists.txt index 13ceac65f2bd..50160dede3d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ set(CLIENT_NAME "Bitcoin Core") set(CLIENT_VERSION_MAJOR 29) set(CLIENT_VERSION_MINOR 1) set(CLIENT_VERSION_BUILD 0) -set(CLIENT_VERSION_RC 1) +set(CLIENT_VERSION_RC 2) set(CLIENT_VERSION_IS_RELEASE "true") set(COPYRIGHT_YEAR "2025") diff --git a/doc/man/bitcoin-cli.1 b/doc/man/bitcoin-cli.1 index dfc4d5ad40c0..7a5523b75895 100644 --- a/doc/man/bitcoin-cli.1 +++ b/doc/man/bitcoin-cli.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN-CLI "1" "July 2025" "bitcoin-cli v29.1.0rc1" "User Commands" +.TH BITCOIN-CLI "1" "August 2025" "bitcoin-cli v29.1.0rc2" "User Commands" .SH NAME -bitcoin-cli \- manual page for bitcoin-cli v29.1.0rc1 +bitcoin-cli \- manual page for bitcoin-cli v29.1.0rc2 .SH SYNOPSIS .B bitcoin-cli [\fI\,options\/\fR] \fI\, \/\fR[\fI\,params\/\fR] @@ -15,7 +15,7 @@ bitcoin-cli \- manual page for bitcoin-cli v29.1.0rc1 .B bitcoin-cli [\fI\,options\/\fR] \fI\,help \/\fR .SH DESCRIPTION -Bitcoin Core RPC client version v29.1.0rc1 +Bitcoin Core RPC client version v29.1.0rc2 .PP The bitcoin\-cli utility provides a command line interface to interact with a Bitcoin Core RPC server. .PP diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1 index ca2bc8eb8640..68db7bd3ad8f 100644 --- a/doc/man/bitcoin-qt.1 +++ b/doc/man/bitcoin-qt.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN-QT "1" "July 2025" "bitcoin-qt v29.1.0rc1" "User Commands" +.TH BITCOIN-QT "1" "August 2025" "bitcoin-qt v29.1.0rc2" "User Commands" .SH NAME -bitcoin-qt \- manual page for bitcoin-qt v29.1.0rc1 +bitcoin-qt \- manual page for bitcoin-qt v29.1.0rc2 .SH SYNOPSIS .B bitcoin-qt [\fI\,options\/\fR] [\fI\,URI\/\fR] .SH DESCRIPTION -Bitcoin Core version v29.1.0rc1 +Bitcoin Core version v29.1.0rc2 .PP The bitcoin\-qt application provides a graphical interface for interacting with Bitcoin Core. .PP @@ -702,7 +702,7 @@ this size or less (default: 83) \fB\-minrelaytxfee=\fR .IP Fees (in BTC/kvB) smaller than this are considered zero fee for -relaying, mining and transaction creation (default: 0.00001) +relaying, mining and transaction creation (default: 0.000001) .HP \fB\-permitbaremultisig\fR .IP @@ -729,7 +729,7 @@ Set maximum BIP141 block weight (default: 4000000) \fB\-blockmintxfee=\fR .IP Set lowest fee rate (in BTC/kvB) for transactions to be included in -block creation. (default: 0.00001) +block creation. (default: 0.00000001) .HP \fB\-blockreservedweight=\fR .IP diff --git a/doc/man/bitcoin-tx.1 b/doc/man/bitcoin-tx.1 index db199ff7408e..288e8bf3e504 100644 --- a/doc/man/bitcoin-tx.1 +++ b/doc/man/bitcoin-tx.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN-TX "1" "July 2025" "bitcoin-tx v29.1.0rc1" "User Commands" +.TH BITCOIN-TX "1" "August 2025" "bitcoin-tx v29.1.0rc2" "User Commands" .SH NAME -bitcoin-tx \- manual page for bitcoin-tx v29.1.0rc1 +bitcoin-tx \- manual page for bitcoin-tx v29.1.0rc2 .SH SYNOPSIS .B bitcoin-tx [\fI\,options\/\fR] \fI\, \/\fR[\fI\,commands\/\fR] @@ -9,7 +9,7 @@ bitcoin-tx \- manual page for bitcoin-tx v29.1.0rc1 .B bitcoin-tx [\fI\,options\/\fR] \fI\,-create \/\fR[\fI\,commands\/\fR] .SH DESCRIPTION -Bitcoin Core bitcoin\-tx utility version v29.1.0rc1 +Bitcoin Core bitcoin\-tx utility version v29.1.0rc2 .PP The bitcoin\-tx tool is used for creating and modifying bitcoin transactions. .PP diff --git a/doc/man/bitcoin-util.1 b/doc/man/bitcoin-util.1 index 07d61d78454e..ebe0641225d6 100644 --- a/doc/man/bitcoin-util.1 +++ b/doc/man/bitcoin-util.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN-UTIL "1" "July 2025" "bitcoin-util v29.1.0rc1" "User Commands" +.TH BITCOIN-UTIL "1" "August 2025" "bitcoin-util v29.1.0rc2" "User Commands" .SH NAME -bitcoin-util \- manual page for bitcoin-util v29.1.0rc1 +bitcoin-util \- manual page for bitcoin-util v29.1.0rc2 .SH SYNOPSIS .B bitcoin-util [\fI\,options\/\fR] [\fI\,command\/\fR] @@ -9,7 +9,7 @@ bitcoin-util \- manual page for bitcoin-util v29.1.0rc1 .B bitcoin-util [\fI\,options\/\fR] \fI\,grind \/\fR .SH DESCRIPTION -Bitcoin Core bitcoin\-util utility version v29.1.0rc1 +Bitcoin Core bitcoin\-util utility version v29.1.0rc2 .PP The bitcoin\-util tool provides bitcoin related functionality that does not rely on the ability to access a running node. Available [commands] are listed below. .SH OPTIONS diff --git a/doc/man/bitcoin-wallet.1 b/doc/man/bitcoin-wallet.1 index 636e8c5906d7..4110979bf3be 100644 --- a/doc/man/bitcoin-wallet.1 +++ b/doc/man/bitcoin-wallet.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN-WALLET "1" "July 2025" "bitcoin-wallet v29.1.0rc1" "User Commands" +.TH BITCOIN-WALLET "1" "August 2025" "bitcoin-wallet v29.1.0rc2" "User Commands" .SH NAME -bitcoin-wallet \- manual page for bitcoin-wallet v29.1.0rc1 +bitcoin-wallet \- manual page for bitcoin-wallet v29.1.0rc2 .SH SYNOPSIS .B bitcoin-wallet [\fI\,options\/\fR] \fI\,\/\fR .SH DESCRIPTION -Bitcoin Core bitcoin\-wallet utility version v29.1.0rc1 +Bitcoin Core bitcoin\-wallet utility version v29.1.0rc2 .PP bitcoin\-wallet is an offline tool for creating and interacting with Bitcoin Core wallet files. .PP diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1 index 8e41a3a763af..da7af79b20f9 100644 --- a/doc/man/bitcoind.1 +++ b/doc/man/bitcoind.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIND "1" "July 2025" "bitcoind v29.1.0rc1" "User Commands" +.TH BITCOIND "1" "August 2025" "bitcoind v29.1.0rc2" "User Commands" .SH NAME -bitcoind \- manual page for bitcoind v29.1.0rc1 +bitcoind \- manual page for bitcoind v29.1.0rc2 .SH SYNOPSIS .B bitcoind [\fI\,options\/\fR] .SH DESCRIPTION -Bitcoin Core daemon version v29.1.0rc1 +Bitcoin Core daemon version v29.1.0rc2 .PP The Bitcoin Core daemon (bitcoind) is a headless program that connects to the Bitcoin network to validate and relay transactions and blocks, as well as relaying addresses. .PP @@ -702,7 +702,7 @@ this size or less (default: 83) \fB\-minrelaytxfee=\fR .IP Fees (in BTC/kvB) smaller than this are considered zero fee for -relaying, mining and transaction creation (default: 0.00001) +relaying, mining and transaction creation (default: 0.000001) .HP \fB\-permitbaremultisig\fR .IP @@ -729,7 +729,7 @@ Set maximum BIP141 block weight (default: 4000000) \fB\-blockmintxfee=\fR .IP Set lowest fee rate (in BTC/kvB) for transactions to be included in -block creation. (default: 0.00001) +block creation. (default: 0.00000001) .HP \fB\-blockreservedweight=\fR .IP diff --git a/doc/release-notes.md b/doc/release-notes.md index b384763e155b..f5a91e1adad9 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,6 +1,6 @@ -Bitcoin Core version 29.1rc1 is now available from: +Bitcoin Core version 29.1rc2 is now available from: - + This release includes various bug fixes and performance improvements, as well as updated translations. @@ -48,6 +48,20 @@ Notable changes - #32521 policy: make pathological transactions packed with legacy sigops non-standard +- The minimum block feerate (`-blockmintxfee`) has been changed to 1 satoshi per kvB. It can still be changed using the +configuration option. + +- The default minimum relay feerate (`-minrelaytxfee`) and incremental relay feerate (`-incrementalrelayfee`) have been +changed to 100 satoshis per kvB. They can still be changed using their respective configuration options, but it is +recommended to change both together if you decide to do so. + - Other minimum feerates (e.g. the dust feerate, the minimum returned by the fee estimator, and all feerates used by + the wallet) remain unchanged. The mempool minimum feerate still changes in response to high volume. + - Note that unless these lower defaults are widely adopted across the network, transactions created with lower fee + rates are not guaranteed to propagate or confirm. The wallet feerates remain unchanged; `-mintxfee` must be changed + before attempting to create transactions with lower feerates using the wallet. + +- #33106 policy: lower the default blockmintxfee, incrementalrelayfee, minrelaytxfee + ### Logging Unconditional logging to disk is now rate limited by giving each source location @@ -181,6 +195,7 @@ Thanks to everyone who directly contributed to this release: - enirox001 - fanquake - furszy +- glozow - instagibbs - Hennadii Stepanov - hodlinator diff --git a/src/node/mempool_args.cpp b/src/node/mempool_args.cpp index 33ae697168e6..f2241f0caf51 100644 --- a/src/node/mempool_args.cpp +++ b/src/node/mempool_args.cpp @@ -65,6 +65,7 @@ util::Result ApplyArgsManOptions(const ArgsManager& argsman, const CChainP } } + static_assert(DEFAULT_MIN_RELAY_TX_FEE == DEFAULT_INCREMENTAL_RELAY_FEE); if (argsman.IsArgSet("-minrelaytxfee")) { if (std::optional min_relay_feerate = ParseMoney(argsman.GetArg("-minrelaytxfee", ""))) { // High fee check is done afterward in CWallet::Create() diff --git a/src/policy/policy.h b/src/policy/policy.h index dab39aa4f8b5..bf6224af3db6 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -29,7 +29,7 @@ static constexpr unsigned int DEFAULT_BLOCK_RESERVED_WEIGHT{8000}; * Setting a lower value is prevented at startup. */ static constexpr unsigned int MINIMUM_BLOCK_RESERVED_WEIGHT{2000}; /** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/ -static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1000}; +static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1}; /** The maximum weight for transactions we're willing to relay/mine */ static constexpr int32_t MAX_STANDARD_TX_WEIGHT{400000}; /** 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/ /** The maximum number of potentially executed legacy signature operations in a single standard tx */ static constexpr unsigned int MAX_TX_LEGACY_SIGOPS{2'500}; /** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or replacement **/ -static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE{1000}; +static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE{100}; /** Default for -bytespersigop */ static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20}; /** Default for -permitbaremultisig */ @@ -63,7 +63,7 @@ static constexpr unsigned int MAX_STANDARD_SCRIPTSIG_SIZE{1650}; * outputs below the new threshold */ static constexpr unsigned int DUST_RELAY_TX_FEE{3000}; /** Default for -minrelaytxfee, minimum relay fee for transactions */ -static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE{1000}; +static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE{100}; /** Default for -limitancestorcount, max number of in-mempool ancestors */ static constexpr unsigned int DEFAULT_ANCESTOR_LIMIT{25}; /** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */ diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index f0094dce59f8..aa6333321f1f 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -443,7 +443,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) tx1.vout.resize(1); tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; tx1.vout[0].nValue = 10 * COIN; - AddToMempool(pool, entry.Fee(10000LL).FromTx(tx1)); + AddToMempool(pool, entry.Fee(1000LL).FromTx(tx1)); CMutableTransaction tx2 = CMutableTransaction(); tx2.vin.resize(1); @@ -451,7 +451,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) tx2.vout.resize(1); tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; tx2.vout[0].nValue = 10 * COIN; - AddToMempool(pool, entry.Fee(5000LL).FromTx(tx2)); + AddToMempool(pool, entry.Fee(500LL).FromTx(tx2)); pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash()))); @@ -469,7 +469,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) tx3.vout.resize(1); tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; tx3.vout[0].nValue = 10 * COIN; - AddToMempool(pool, entry.Fee(20000LL).FromTx(tx3)); + AddToMempool(pool, entry.Fee(2000LL).FromTx(tx3)); pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP) BOOST_CHECK(!pool.exists(GenTxid::Txid(tx1.GetHash()))); @@ -481,8 +481,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) BOOST_CHECK(!pool.exists(GenTxid::Txid(tx2.GetHash()))); BOOST_CHECK(!pool.exists(GenTxid::Txid(tx3.GetHash()))); - CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2))); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000); + CFeeRate maxFeeRateRemoved(2500, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2))); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE); CMutableTransaction tx4 = CMutableTransaction(); tx4.vin.resize(2); @@ -532,10 +532,10 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL; tx7.vout[1].nValue = 10 * COIN; - AddToMempool(pool, entry.Fee(7000LL).FromTx(tx4)); - AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5)); - AddToMempool(pool, entry.Fee(1100LL).FromTx(tx6)); - AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7)); + AddToMempool(pool, entry.Fee(700LL).FromTx(tx4)); + AddToMempool(pool, entry.Fee(100LL).FromTx(tx5)); + AddToMempool(pool, entry.Fee(110LL).FromTx(tx6)); + AddToMempool(pool, entry.Fee(900LL).FromTx(tx7)); // we only require this to remove, at max, 2 txn, because it's not clear what we're really optimizing for aside from that pool.TrimToSize(pool.DynamicMemoryUsage() - 1); @@ -544,8 +544,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash()))); if (!pool.exists(GenTxid::Txid(tx5.GetHash()))) - AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5)); - AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7)); + AddToMempool(pool, entry.Fee(100LL).FromTx(tx5)); + AddToMempool(pool, entry.Fee(900LL).FromTx(tx7)); pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7 BOOST_CHECK(pool.exists(GenTxid::Txid(tx4.GetHash()))); @@ -553,34 +553,34 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) BOOST_CHECK(pool.exists(GenTxid::Txid(tx6.GetHash()))); BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash()))); - AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5)); - AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7)); + AddToMempool(pool, entry.Fee(100LL).FromTx(tx5)); + AddToMempool(pool, entry.Fee(900LL).FromTx(tx7)); std::vector vtx; SetMockTime(42); SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE); // ... we should keep the same min fee until we get a block pool.removeForBlock(vtx, 1); SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0)); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/2.0)); // ... then feerate should drop 1/2 each halflife SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0)); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/4.0)); // ... with a 1/2 halflife when mempool is < 1/2 its target size SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0)); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/8.0)); // ... with a 1/4 halflife when mempool is < 1/4 its target size SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000); - // ... but feerate should never drop below 1000 + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), DEFAULT_INCREMENTAL_RELAY_FEE); + // ... but feerate should never drop below DEFAULT_INCREMENTAL_RELAY_FEE SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0); - // ... unless it has gone all the way to 0 (after getting past 1000/2) + // ... unless it has gone all the way to 0 (after getting past DEFAULT_INCREMENTAL_RELAY_FEE/2) } inline CTransactionRef make_tx(std::vector&& output_values, std::vector&& inputs=std::vector(), std::vector&& input_indices=std::vector()) diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 5bfcde771832..10f8be4b324d 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -210,6 +211,9 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const tx.vout.resize(2); tx.vout[0].nValue = 5000000000LL - 100000000; tx.vout[1].nValue = 100000000; // 1BTC output + // Increase size to avoid rounding errors: when the feerate is extremely small (i.e. 1sat/kvB), evaluating the fee + // at a smaller transaction size gives us a rounded value of 0. + BulkTransaction(tx, 4000); Txid hashFreeTx2 = tx.GetHash(); AddToMempool(tx_mempool, entry.Fee(0).SpendsCoinbase(true).FromTx(tx)); diff --git a/src/test/rbf_tests.cpp b/src/test/rbf_tests.cpp index c6d06c937c58..db328f87412e 100644 --- a/src/test/rbf_tests.cpp +++ b/src/test/rbf_tests.cpp @@ -238,10 +238,10 @@ BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup) BOOST_CHECK(PaysForRBF(high_fee, high_fee - 1, 1, CFeeRate(0), unused_txid).has_value()); BOOST_CHECK(PaysForRBF(high_fee + 1, high_fee, 1, CFeeRate(0), unused_txid).has_value()); // Additional fees must cover the replacement's vsize at incremental relay fee - BOOST_CHECK(PaysForRBF(high_fee, high_fee + 1, 2, incremental_relay_feerate, unused_txid).has_value()); - BOOST_CHECK(PaysForRBF(high_fee, high_fee + 2, 2, incremental_relay_feerate, unused_txid) == std::nullopt); - BOOST_CHECK(PaysForRBF(high_fee, high_fee + 2, 2, higher_relay_feerate, unused_txid).has_value()); - BOOST_CHECK(PaysForRBF(high_fee, high_fee + 4, 2, higher_relay_feerate, unused_txid) == std::nullopt); + BOOST_CHECK(PaysForRBF(high_fee, high_fee + 1, 11, incremental_relay_feerate, unused_txid).has_value()); + BOOST_CHECK(PaysForRBF(high_fee, high_fee + 1, 10, incremental_relay_feerate, unused_txid) == std::nullopt); + BOOST_CHECK(PaysForRBF(high_fee, high_fee + 2, 11, higher_relay_feerate, unused_txid).has_value()); + BOOST_CHECK(PaysForRBF(high_fee, high_fee + 4, 20, higher_relay_feerate, unused_txid) == std::nullopt); BOOST_CHECK(PaysForRBF(low_fee, high_fee, 99999999, incremental_relay_feerate, unused_txid).has_value()); BOOST_CHECK(PaysForRBF(low_fee, high_fee + 99999999, 99999999, incremental_relay_feerate, unused_txid) == std::nullopt); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index bf26997c0767..29327d461ba2 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -571,6 +572,9 @@ void TestChain100Setup::MockMempoolMinFee(const CFeeRate& target_feerate) CMutableTransaction mtx = CMutableTransaction(); mtx.vin.emplace_back(COutPoint{Txid::FromUint256(m_rng.rand256()), 0}); mtx.vout.emplace_back(1 * COIN, GetScriptForDestination(WitnessV0ScriptHash(CScript() << OP_TRUE))); + // Set a large size so that the fee evaluated at target_feerate (which is usually in sats/kvB) is an integer. + // Otherwise, GetMinFee() may end up slightly different from target_feerate. + BulkTransaction(mtx, 4000); const auto tx{MakeTransactionRef(mtx)}; LockPoints lp; // The new mempool min feerate is equal to the removed package's feerate + incremental feerate. diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index c7f0cc5e4325..81f117a759d9 100755 --- a/test/functional/feature_rbf.py +++ b/test/functional/feature_rbf.py @@ -13,7 +13,10 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, + assert_greater_than, + assert_greater_than_or_equal, assert_raises_rpc_error, + get_fee, ) from test_framework.wallet import MiniWallet from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE @@ -77,6 +80,9 @@ def run_test(self): self.log.info("Running test full replace by fee...") self.test_fullrbf() + self.log.info("Running test incremental relay feerates...") + self.test_incremental_relay_feerates() + self.log.info("Passed") def make_utxo(self, node, amount, *, confirmed=True, scriptPubKey=None): @@ -583,10 +589,42 @@ def test_replacement_relay_fee(self): # Higher fee, higher feerate, different txid, but the replacement does not provide a relay # fee conforming to node's `incrementalrelayfee` policy of 1000 sat per KB. - assert_equal(self.nodes[0].getmempoolinfo()["incrementalrelayfee"], Decimal("0.00001")) + assert_equal(self.nodes[0].getmempoolinfo()["incrementalrelayfee"], Decimal("0.000001")) tx.vout[0].nValue -= 1 assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex()) + def test_incremental_relay_feerates(self): + self.log.info("Test that incremental relay fee is applied correctly in RBF for various settings...") + node = self.nodes[0] + for incremental_setting in (0, 5, 10, 50, 100, 234, 1000, 5000, 21000): + incremental_setting_decimal = incremental_setting / Decimal(COIN) + self.log.info(f"-> Test -incrementalrelayfee={incremental_setting_decimal:.8f}sat/kvB...") + self.restart_node(0, extra_args=[f"-incrementalrelayfee={incremental_setting_decimal:.8f}", "-datacarriersize=5000", "-persistmempool=0"]) + + # When incremental relay feerate is higher than min relay feerate, min relay feerate is automatically increased. + min_relay_feerate = node.getmempoolinfo()["minrelaytxfee"] + assert_greater_than_or_equal(min_relay_feerate, incremental_setting_decimal) + + low_feerate = min_relay_feerate * 2 + confirmed_utxo = self.wallet.get_utxo(confirmed_only=True) + replacee_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee_rate=low_feerate, target_vsize=5000) + node.sendrawtransaction(replacee_tx['hex']) + + replacement_placeholder_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo) + replacement_expected_size = replacement_placeholder_tx['tx'].get_vsize() + replacement_required_fee = get_fee(replacement_expected_size, incremental_setting_decimal) + replacee_tx['fee'] + + # Should always be required to pay additional fees + if incremental_setting > 0: + assert_greater_than(replacement_required_fee, replacee_tx['fee']) + + # 1 satoshi shy of the required fee + failed_replacement_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee=replacement_required_fee - Decimal("0.00000001")) + assert_raises_rpc_error(-26, "insufficient fee", node.sendrawtransaction, failed_replacement_tx['hex']) + + replacement_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee=replacement_required_fee) + node.sendrawtransaction(replacement_tx['hex']) + def test_fullrbf(self): # BIP125 signaling is not respected diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py index 27ecc3b4a848..2155b8de6b1b 100755 --- a/test/functional/mempool_accept.py +++ b/test/functional/mempool_accept.py @@ -9,6 +9,10 @@ import math from test_framework.test_framework import BitcoinTestFramework +from test_framework.mempool_util import ( + DEFAULT_MIN_RELAY_TX_FEE, + DEFAULT_INCREMENTAL_RELAY_FEE, +) from test_framework.messages import ( MAX_BIP125_RBF_SEQUENCE, COIN, @@ -81,6 +85,11 @@ def run_test(self): assert_equal(node.getblockcount(), 200) assert_equal(node.getmempoolinfo()['size'], self.mempool_size) + self.log.info("Check default settings") + # Settings are listed in BTC/kvB + assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN) + assert_equal(node.getmempoolinfo()['incrementalrelayfee'], Decimal(DEFAULT_INCREMENTAL_RELAY_FEE) / COIN) + self.log.info('Should not accept garbage to testmempoolaccept') assert_raises_rpc_error(-3, 'JSON value of type string is not of expected type array', lambda: node.testmempoolaccept(rawtxs='ff00baar')) assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=['ff22']*26)) diff --git a/test/functional/mempool_ephemeral_dust.py b/test/functional/mempool_ephemeral_dust.py index 004db3656ed5..8963c3b5cb01 100755 --- a/test/functional/mempool_ephemeral_dust.py +++ b/test/functional/mempool_ephemeral_dust.py @@ -215,7 +215,7 @@ def test_non_truc(self): res = self.nodes[0].submitpackage([dusty_tx["hex"], sweep_tx["hex"]]) assert_equal(res["package_msg"], "transaction failed") - assert_equal(res["tx-results"][dusty_tx["wtxid"]]["error"], "min relay fee not met, 0 < 147") + assert_equal(res["tx-results"][dusty_tx["wtxid"]]["error"], "min relay fee not met, 0 < 15") assert_equal(self.nodes[0].getrawmempool(), []) diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py index 85d158d6111c..5051f8a03016 100755 --- a/test/functional/mempool_limit.py +++ b/test/functional/mempool_limit.py @@ -94,8 +94,7 @@ def test_mid_package_eviction_success(self): assert_equal(node.getrawmempool(), []) # Restarting the node resets mempool minimum feerate - assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) - assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) + assert_equal(node.getmempoolinfo()['minrelaytxfee'], node.getmempoolinfo()["mempoolminfee"]) fill_mempool(self, node) current_info = node.getmempoolinfo() @@ -186,8 +185,8 @@ def test_mid_package_eviction(self): self.restart_node(0, extra_args=self.extra_args[0]) # Restarting the node resets mempool minimum feerate - assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) - assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) + assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00000100')) + assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00000100')) fill_mempool(self, node) current_info = node.getmempoolinfo() @@ -216,7 +215,7 @@ def test_mid_package_eviction(self): # coin is no longer available, but the cache could still contains the tx. cpfp_parent = self.wallet.create_self_transfer( utxo_to_spend=mempool_evicted_tx["new_utxo"], - fee_rate=mempoolmin_feerate - Decimal('0.00001'), + fee_rate=mempoolmin_feerate / 2, confirmed_only=True) package_hex.append(cpfp_parent["hex"]) parent_utxos.append(cpfp_parent["new_utxo"]) @@ -231,7 +230,7 @@ def test_mid_package_eviction(self): # Need to be large enough to trigger eviction # (note that the mempool usage of a tx is about three times its vsize) assert_greater_than(parent_vsize * num_big_parents * 3, current_info["maxmempool"] - current_info["bytes"]) - parent_feerate = 100 * mempoolmin_feerate + parent_feerate = 10 * mempoolmin_feerate big_parent_txids = [] for i in range(num_big_parents): @@ -250,7 +249,7 @@ def test_mid_package_eviction(self): # Specific number of satoshis to fit within a small window. The parent_cpfp + child package needs to be # - When there is mid-package eviction, high enough feerate to meet the new mempoolminfee # - When there is no mid-package eviction, low enough feerate to be evicted immediately after submission. - magic_satoshis = 1200 + magic_satoshis = 120 cpfp_satoshis = int(cpfp_fee * COIN) + magic_satoshis child = self.wallet.create_self_transfer_multi(utxos_to_spend=parent_utxos, fee_per_output=cpfp_satoshis) @@ -278,8 +277,7 @@ def test_mid_package_replacement(self): self.restart_node(0, extra_args=self.extra_args[0]) # Restarting the node resets mempool minimum feerate - assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) - assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) + assert_equal(node.getmempoolinfo()['minrelaytxfee'], node.getmempoolinfo()["mempoolminfee"]) fill_mempool(self, node) current_info = node.getmempoolinfo() @@ -304,7 +302,7 @@ def test_mid_package_replacement(self): # coin is no longer available, but the cache could still contain the tx. cpfp_parent = self.wallet.create_self_transfer( utxo_to_spend=replaced_tx["new_utxo"], - fee_rate=mempoolmin_feerate - Decimal('0.00001'), + fee_rate=mempoolmin_feerate - Decimal('0.000001'), confirmed_only=True) self.wallet.rescan_utxos() @@ -352,8 +350,7 @@ def run_test(self): relayfee = node.getnetworkinfo()['relayfee'] self.log.info('Check that mempoolminfee is minrelaytxfee') - assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) - assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) + assert_equal(node.getmempoolinfo()['minrelaytxfee'], node.getmempoolinfo()["mempoolminfee"]) fill_mempool(self, node) @@ -411,9 +408,9 @@ def run_test(self): target_vsize_each = 50000 assert_greater_than(target_vsize_each * 2 * 3, node.getmempoolinfo()["maxmempool"] - node.getmempoolinfo()["bytes"]) # Should be a true CPFP: parent's feerate is just below mempool min feerate - parent_feerate = mempoolmin_feerate - Decimal("0.000001") # 0.1 sats/vbyte below min feerate + parent_feerate = mempoolmin_feerate - Decimal("0.0000001") # 0.01 sats/vbyte below min feerate # Parent + child is above mempool minimum feerate - child_feerate = (worst_feerate_btcvb * 1000) - Decimal("0.000001") # 0.1 sats/vbyte below worst feerate + child_feerate = (worst_feerate_btcvb * 1000) - Decimal("0.0000001") # 0.01 sats/vbyte below worst feerate # However, when eviction is triggered, these transactions should be at the bottom. # This assertion assumes parent and child are the same size. miniwallet.rescan_utxos() diff --git a/test/functional/mempool_package_rbf.py b/test/functional/mempool_package_rbf.py index 4dc6f8fe3649..892d819a0e28 100755 --- a/test/functional/mempool_package_rbf.py +++ b/test/functional/mempool_package_rbf.py @@ -163,13 +163,13 @@ def test_package_rbf_additional_fees(self): self.log.info("Check replacement pays for incremental bandwidth") _, placeholder_txns3 = self.create_simple_package(coin) package_3_size = sum([tx.get_vsize() for tx in placeholder_txns3]) - incremental_sats_required = Decimal(package_3_size) / COIN - incremental_sats_short = incremental_sats_required - Decimal("0.00000001") + incremental_sats_required = (Decimal(package_3_size * 0.1) / COIN).quantize(Decimal("0.00000001")) + incremental_sats_short = incremental_sats_required - Decimal("0.00000005") # Recreate the package with slightly higher fee once we know the size of the new package, but still short of required fee failure_package_hex3, failure_package_txns3 = self.create_simple_package(coin, parent_fee=DEFAULT_FEE, child_fee=DEFAULT_CHILD_FEE + incremental_sats_short) assert_equal(package_3_size, sum([tx.get_vsize() for tx in failure_package_txns3])) pkg_results3 = node.submitpackage(failure_package_hex3) - assert_equal(f"package RBF failed: insufficient anti-DoS fees, rejecting replacement {failure_package_txns3[1].rehash()}, not enough additional fees to relay; {incremental_sats_short} < {incremental_sats_required}", pkg_results3["package_msg"]) + assert_equal(f"package RBF failed: insufficient anti-DoS fees, rejecting replacement {failure_package_txns3[1].rehash()}, not enough additional fees to relay; {incremental_sats_short:.8f} < {incremental_sats_required:.8f}", pkg_results3["package_msg"]) self.assert_mempool_contents(expected=package_txns1) success_package_hex3, success_package_txns3 = self.create_simple_package(coin, parent_fee=DEFAULT_FEE, child_fee=DEFAULT_CHILD_FEE + incremental_sats_required) @@ -563,12 +563,13 @@ def test_child_conflicts_parent_mempool_ancestor(self): ) node.sendrawtransaction(grandparent_result["hex"]) + minrelayfeerate = node.getnetworkinfo()["relayfee"] # Now make package of two descendants that looks # like a cpfp where the parent can't get in on its own self.ctr += 1 parent_result = self.wallet.create_self_transfer( - fee_rate=Decimal('0.00001000'), + fee_rate=minrelayfeerate, utxo_to_spend=grandparent_result["new_utxo"], sequence=MAX_BIP125_RBF_SEQUENCE - self.ctr, ) diff --git a/test/functional/mempool_truc.py b/test/functional/mempool_truc.py index 435b61f24f33..8850ba800289 100755 --- a/test/functional/mempool_truc.py +++ b/test/functional/mempool_truc.py @@ -10,6 +10,7 @@ assert_greater_than, assert_greater_than_or_equal, assert_raises_rpc_error, + get_fee, ) from test_framework.wallet import ( COIN, @@ -594,12 +595,57 @@ def test_reorg_sibling_eviction_1p2c(self): ) self.check_mempool([tx_with_multi_children["txid"], tx_with_sibling3_rbf["txid"], tx_with_sibling2["txid"]]) + @cleanup(extra_args=None) + def test_minrelay_in_package_combos(self): + node = self.nodes[0] + self.log.info("Test that only TRUC transactions can be under minrelaytxfee for various settings...") + + for minrelay_setting in (0, 5, 10, 100, 500, 1000, 5000, 333333, 2500000): + self.log.info(f"-> Test -minrelaytxfee={minrelay_setting}sat/kvB...") + setting_decimal = minrelay_setting / Decimal(COIN) + self.restart_node(0, extra_args=[f"-minrelaytxfee={setting_decimal:.8f}", "-persistmempool=0"]) + minrelayfeerate = node.getmempoolinfo()["minrelaytxfee"] + high_feerate = minrelayfeerate * 50 + + tx_v3_0fee_parent = self.wallet.create_self_transfer(fee=0, fee_rate=0, confirmed_only=True, version=3) + tx_v3_child = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_0fee_parent["new_utxo"], fee_rate=high_feerate, version=3) + total_v3_fee = tx_v3_child["fee"] + tx_v3_0fee_parent["fee"] + total_v3_size = tx_v3_child["tx"].get_vsize() + tx_v3_0fee_parent["tx"].get_vsize() + assert_greater_than_or_equal(total_v3_fee, get_fee(total_v3_size, minrelayfeerate)) + if minrelayfeerate > 0: + assert_greater_than(get_fee(tx_v3_0fee_parent["tx"].get_vsize(), minrelayfeerate), 0) + # Always need to pay at least 1 satoshi for entry, even if minimum feerate is very low + assert_greater_than(total_v3_fee, 0) + + tx_v2_0fee_parent = self.wallet.create_self_transfer(fee=0, fee_rate=0, confirmed_only=True, version=2) + tx_v2_child = self.wallet.create_self_transfer(utxo_to_spend=tx_v2_0fee_parent["new_utxo"], fee_rate=high_feerate, version=2) + total_v2_fee = tx_v2_child["fee"] + tx_v2_0fee_parent["fee"] + total_v2_size = tx_v2_child["tx"].get_vsize() + tx_v2_0fee_parent["tx"].get_vsize() + assert_greater_than_or_equal(total_v2_fee, get_fee(total_v2_size, minrelayfeerate)) + if minrelayfeerate > 0: + assert_greater_than(get_fee(tx_v2_0fee_parent["tx"].get_vsize(), minrelayfeerate), 0) + # Always need to pay at least 1 satoshi for entry, even if minimum feerate is very low + assert_greater_than(total_v2_fee, 0) + + result_truc = node.submitpackage([tx_v3_0fee_parent["hex"], tx_v3_child["hex"]], maxfeerate=0) + assert_equal(result_truc["package_msg"], "success") + + result_non_truc = node.submitpackage([tx_v2_0fee_parent["hex"], tx_v2_child["hex"]], maxfeerate=0) + if minrelayfeerate > 0: + assert_equal(result_non_truc["package_msg"], "transaction failed") + min_fee_parent = int(get_fee(tx_v2_0fee_parent["tx"].get_vsize(), minrelayfeerate) * COIN) + assert_equal(result_non_truc["tx-results"][tx_v2_0fee_parent["wtxid"]]["error"], f"min relay fee not met, 0 < {min_fee_parent}") + self.check_mempool([tx_v3_0fee_parent["txid"], tx_v3_child["txid"]]) + else: + assert_equal(result_non_truc["package_msg"], "success") + self.check_mempool([tx_v2_0fee_parent["txid"], tx_v2_child["txid"], tx_v3_0fee_parent["txid"], tx_v3_child["txid"]]) + def run_test(self): self.log.info("Generate blocks to create UTXOs") node = self.nodes[0] self.wallet = MiniWallet(node) - self.generate(self.wallet, 120) + self.generate(self.wallet, 200) self.test_truc_max_vsize() self.test_truc_acceptance() self.test_truc_replacement() @@ -613,6 +659,7 @@ def run_test(self): self.test_reorg_2child_rbf() self.test_truc_sibling_eviction() self.test_reorg_sibling_eviction_1p2c() + self.test_minrelay_in_package_combos() if __name__ == "__main__": diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py index fea7ced3752c..72f92c85a09c 100755 --- a/test/functional/mining_basic.py +++ b/test/functional/mining_basic.py @@ -36,6 +36,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, + assert_greater_than, assert_greater_than_or_equal, assert_raises_rpc_error, get_fee, @@ -48,7 +49,7 @@ MAX_TIMEWARP = 600 VERSIONBITS_TOP_BITS = 0x20000000 VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28 -DEFAULT_BLOCK_MIN_TX_FEE = 1000 # default `-blockmintxfee` setting [sat/kvB] +DEFAULT_BLOCK_MIN_TX_FEE = 1 # default `-blockmintxfee` setting [sat/kvB] def assert_template(node, block, expect, rehash=True): @@ -99,7 +100,7 @@ def test_blockmintxfee_parameter(self): node = self.nodes[0] # test default (no parameter), zero and a bunch of arbitrary blockmintxfee rates [sat/kvB] - for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 50, 100, 500, 2500, 5000, 21000, 333333, 2500000): + for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 5, 10, 50, 100, 500, 1000, 2500, 5000, 21000, 333333, 2500000): blockmintxfee_btc_kvb = blockmintxfee_sat_kvb / Decimal(COIN) if blockmintxfee_sat_kvb == DEFAULT_BLOCK_MIN_TX_FEE: self.log.info(f"-> Default -blockmintxfee setting ({blockmintxfee_sat_kvb} sat/kvB)...") @@ -110,19 +111,27 @@ def test_blockmintxfee_parameter(self): self.wallet.rescan_utxos() # to avoid spending outputs of txs that are not in mempool anymore after restart # submit one tx with exactly the blockmintxfee rate, and one slightly below - tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb) + tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True) assert_equal(tx_with_min_feerate["fee"], get_fee(tx_with_min_feerate["tx"].get_vsize(), blockmintxfee_btc_kvb)) - if blockmintxfee_btc_kvb > 0: + if blockmintxfee_sat_kvb > 5: lowerfee_btc_kvb = blockmintxfee_btc_kvb - Decimal(10)/COIN # 0.01 sat/vbyte lower - tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb) + tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb, confirmed_only=True) assert_equal(tx_below_min_feerate["fee"], get_fee(tx_below_min_feerate["tx"].get_vsize(), lowerfee_btc_kvb)) else: # go below zero fee by using modified fees - tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb) + tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True) node.prioritisetransaction(tx_below_min_feerate["txid"], 0, -1) # check that tx below specified fee-rate is neither in template nor in the actual block block_template = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS) block_template_txids = [tx['txid'] for tx in block_template['transactions']] + + # Unless blockmintxfee is 0, the template shouldn't contain free transactions. + # Note that the real block assembler uses package feerates, but we didn't create dependent transactions so it's ok to use base feerate. + if blockmintxfee_btc_kvb > 0: + for txid in block_template_txids: + tx = node.getmempoolentry(txid) + assert_greater_than(tx['fees']['base'], 0) + self.generate(self.wallet, 1, sync_fun=self.no_op) block = node.getblock(node.getbestblockhash(), verbosity=2) block_txids = [tx['txid'] for tx in block['tx']] diff --git a/test/functional/p2p_1p1c_network.py b/test/functional/p2p_1p1c_network.py index e48e5b88b676..4f03542168d8 100755 --- a/test/functional/p2p_1p1c_network.py +++ b/test/functional/p2p_1p1c_network.py @@ -13,9 +13,11 @@ from math import ceil from test_framework.mempool_util import ( + DEFAULT_MIN_RELAY_TX_FEE, fill_mempool, ) from test_framework.messages import ( + COIN, msg_tx, ) from test_framework.p2p import ( @@ -31,9 +33,6 @@ MiniWalletMode, ) -# 1sat/vB feerate denominated in BTC/KvB -FEERATE_1SAT_VB = Decimal("0.00001000") - class PackageRelayTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True @@ -51,12 +50,12 @@ def raise_network_minfee(self): self.log.debug("Check that all nodes' mempool minimum feerates are above min relay feerate") for node in self.nodes: - assert_equal(node.getmempoolinfo()['minrelaytxfee'], FEERATE_1SAT_VB) - assert_greater_than(node.getmempoolinfo()['mempoolminfee'], FEERATE_1SAT_VB) + assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN) + assert_greater_than(node.getmempoolinfo()['mempoolminfee'], Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN) def create_basic_1p1c(self, wallet): - low_fee_parent = wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB, confirmed_only=True) - high_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=999*FEERATE_1SAT_VB) + low_fee_parent = wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN, confirmed_only=True) + high_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=999*Decimal(DEFAULT_MIN_RELAY_TX_FEE)/ COIN) package_hex_basic = [low_fee_parent["hex"], high_fee_child["hex"]] return package_hex_basic, low_fee_parent["tx"], high_fee_child["tx"] @@ -87,8 +86,8 @@ def create_package_2outs(self, wallet): return [low_fee_parent_2outs["hex"], high_fee_child_2outs["hex"]], low_fee_parent_2outs["tx"], high_fee_child_2outs["tx"] def create_package_2p1c(self, wallet): - parent1 = wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB*10, confirmed_only=True) - parent2 = wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB*20, confirmed_only=True) + parent1 = wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN * 10, confirmed_only=True) + parent2 = wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN * 20, confirmed_only=True) child = wallet.create_self_transfer_multi( utxos_to_spend=[parent1["new_utxo"], parent2["new_utxo"]], fee_per_output=999*parent1["tx"].get_vsize(), diff --git a/test/functional/p2p_ibd_txrelay.py b/test/functional/p2p_ibd_txrelay.py index 870324d76c3e..0cd0ac05b2c7 100755 --- a/test/functional/p2p_ibd_txrelay.py +++ b/test/functional/p2p_ibd_txrelay.py @@ -28,8 +28,8 @@ ) from test_framework.test_framework import BitcoinTestFramework -MAX_FEE_FILTER = Decimal(9170997) / COIN -NORMAL_FEE_FILTER = Decimal(100) / COIN +MAX_FEE_FILTER = Decimal(9936506) / COIN +NORMAL_FEE_FILTER = Decimal(10) / COIN class P2PIBDTxRelayTest(BitcoinTestFramework): @@ -37,8 +37,8 @@ def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [ - ["-minrelaytxfee={}".format(NORMAL_FEE_FILTER)], - ["-minrelaytxfee={}".format(NORMAL_FEE_FILTER)], + ["-minrelaytxfee={:.8f}".format(NORMAL_FEE_FILTER)], + ["-minrelaytxfee={:.8f}".format(NORMAL_FEE_FILTER)], ] def run_test(self): diff --git a/test/functional/p2p_opportunistic_1p1c.py b/test/functional/p2p_opportunistic_1p1c.py index 5cf616b3ef8f..5fdbf74a5730 100755 --- a/test/functional/p2p_opportunistic_1p1c.py +++ b/test/functional/p2p_opportunistic_1p1c.py @@ -9,10 +9,12 @@ from decimal import Decimal import time from test_framework.mempool_util import ( + DEFAULT_MIN_RELAY_TX_FEE, fill_mempool, ) from test_framework.messages import ( CInv, + COIN, CTxInWitness, MAX_BIP125_RBF_SEQUENCE, MSG_WTX, @@ -65,13 +67,13 @@ def set_test_params(self): self.supports_cli = False def create_tx_below_mempoolminfee(self, wallet): - """Create a 1-input 1sat/vB transaction using a confirmed UTXO. Decrement and use + """Create a 1-input 0.1sat/vB transaction using a confirmed UTXO. Decrement and use self.sequence so that subsequent calls to this function result in unique transactions.""" self.sequence -= 1 - assert_greater_than(self.nodes[0].getmempoolinfo()["mempoolminfee"], FEERATE_1SAT_VB) + assert_greater_than(self.nodes[0].getmempoolinfo()["mempoolminfee"], Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN) - return wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB, sequence=self.sequence, confirmed_only=True) + return wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN, sequence=self.sequence, confirmed_only=True) @cleanup def test_basic_child_then_parent(self): diff --git a/test/functional/test_framework/mempool_util.py b/test/functional/test_framework/mempool_util.py index 0e9c821e2ead..56a9b4d262e7 100644 --- a/test/functional/test_framework/mempool_util.py +++ b/test/functional/test_framework/mempool_util.py @@ -3,7 +3,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Helpful routines for mempool testing.""" -from decimal import Decimal from .blocktools import ( COINBASE_MATURITY, @@ -20,6 +19,10 @@ ) ORPHAN_TX_EXPIRE_TIME = 1200 +# Default for -minrelaytxfee in sat/kvB +DEFAULT_MIN_RELAY_TX_FEE = 100 +# Default for -incrementalrelayfee in sat/kvB +DEFAULT_INCREMENTAL_RELAY_FEE = 100 def assert_mempool_contents(test_framework, node, expected=None, sync=True): """Assert that all transactions in expected are in the mempool, @@ -48,9 +51,7 @@ def fill_mempool(test_framework, node, *, tx_sync_fun=None): """ test_framework.log.info("Fill the mempool until eviction is triggered and the mempoolminfee rises") txouts = gen_return_txouts() - relayfee = node.getnetworkinfo()['relayfee'] - - assert_equal(relayfee, Decimal('0.00001000')) + minrelayfee = node.getnetworkinfo()['relayfee'] tx_batch_size = 1 num_of_batches = 75 @@ -70,7 +71,7 @@ def fill_mempool(test_framework, node, *, tx_sync_fun=None): test_framework.log.debug("Create a mempool tx that will be evicted") tx_to_be_evicted_id = ephemeral_miniwallet.send_self_transfer( - from_node=node, utxo_to_spend=confirmed_utxos.pop(0), fee_rate=relayfee)["txid"] + from_node=node, utxo_to_spend=confirmed_utxos.pop(0), fee_rate=minrelayfee)["txid"] def send_batch(fee): utxos = confirmed_utxos[:tx_batch_size] @@ -80,14 +81,14 @@ def send_batch(fee): # Increase the tx fee rate to give the subsequent transactions a higher priority in the mempool # The tx has an approx. vsize of 65k, i.e. multiplying the previous fee rate (in sats/kvB) # by 130 should result in a fee that corresponds to 2x of that fee rate - base_fee = relayfee * 130 + base_fee = minrelayfee * 130 batch_fees = [(i + 1) * base_fee for i in range(num_of_batches)] test_framework.log.debug("Fill up the mempool with txs with higher fee rate") for fee in batch_fees[:-3]: send_batch(fee) tx_sync_fun() if tx_sync_fun else test_framework.sync_mempools() # sync before any eviction - assert_equal(node.getmempoolinfo()["mempoolminfee"], Decimal("0.00001000")) + assert_equal(node.getmempoolinfo()["mempoolminfee"], minrelayfee) for fee in batch_fees[-3:]: send_batch(fee) tx_sync_fun() if tx_sync_fun else test_framework.sync_mempools() # sync after all evictions @@ -99,8 +100,8 @@ def send_batch(fee): assert tx_to_be_evicted_id not in node.getrawmempool() test_framework.log.debug("Check that mempoolminfee is larger than minrelaytxfee") - assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) - assert_greater_than(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) + assert_equal(node.getmempoolinfo()['minrelaytxfee'], minrelayfee) + assert_greater_than(node.getmempoolinfo()['mempoolminfee'], minrelayfee) def tx_in_orphanage(node, tx: CTransaction) -> bool: """Returns true if the transaction is in the orphanage.""" diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index 061e9f2caa1f..9cec77fb1845 100755 --- a/test/functional/wallet_bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -534,7 +534,7 @@ def test_dust_to_fee(self, rbf_node, dest_address): def test_settxfee(self, rbf_node, dest_address): self.log.info('Test settxfee') - assert_raises_rpc_error(-8, "txfee cannot be less than min relay tx fee", rbf_node.settxfee, Decimal('0.000005')) + assert_raises_rpc_error(-8, "txfee cannot be less than min relay tx fee", rbf_node.settxfee, Decimal('0.0000005')) assert_raises_rpc_error(-8, "txfee cannot be less than wallet min fee", rbf_node.settxfee, Decimal('0.000015')) # check that bumpfee reacts correctly to the use of settxfee (paytxfee) rbfid = spend_one_input(rbf_node, dest_address) @@ -846,7 +846,7 @@ def test_bumpfee_with_feerate_ignores_walletincrementalrelayfee(self, rbf_node, # Ensure you can not fee bump if the fee_rate is more than original fee_rate but the total fee from new fee_rate is # less than (original fee + incrementalrelayfee) - assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 2.8}) + assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 2.05}) # You can fee bump as long as the new fee set from fee_rate is at least (original fee + incrementalrelayfee) rbf_node.bumpfee(tx["txid"], {"fee_rate": 3}) diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py index 827f27b431c7..2c67aaafb918 100755 --- a/test/functional/wallet_fundrawtransaction.py +++ b/test/functional/wallet_fundrawtransaction.py @@ -44,6 +44,10 @@ def add_options(self, parser): def set_test_params(self): self.num_nodes = 4 + self.extra_args = [[ + "-deprecatedrpc=settxfee", + "-minrelaytxfee=0.00001000", + ] for i in range(self.num_nodes)] self.setup_clean_chain = True # whitelist peers to speed up tx relay / mempool sync self.noban_tx_relay = True