From 4e3cfa660d1ab1552881f2eedb5123ab7d0fe0b0 Mon Sep 17 00:00:00 2001 From: glozow Date: Thu, 31 Jul 2025 11:29:49 -0400 Subject: [PATCH 01/13] [test] check miner doesn't select 0fee transactions Github-Pull: #33106 Rebased-From: e5f896bb1f052fb8c7811c6024cb49143b427512 --- test/functional/mining_basic.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py index fea7ced3752c..c354e2c71c48 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, @@ -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, 1, 5, 10, 50, 100, 500, 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']] From 03da7aff996eaaddce04448f8461d4f3d15d2a4a Mon Sep 17 00:00:00 2001 From: glozow Date: Thu, 31 Jul 2025 12:38:36 -0400 Subject: [PATCH 02/13] [test] check bypass of minrelay for various minrelaytxfee settings Github-Pull: #33106 Rebased-From: 85f498893f54ea7d84f2bdf12aa35d198edf8a72 --- test/functional/mempool_truc.py | 49 ++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) 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__": From 6b5396c4b1b3561f79e0c4881dad5eb0ca7b44ae Mon Sep 17 00:00:00 2001 From: glozow Date: Thu, 31 Jul 2025 13:53:57 -0400 Subject: [PATCH 03/13] [test] RBF rule 4 for various incrementalrelayfee settings Github-Pull: #33106 Rebased-From: 72dc18467dbfc16cdbda2dd109b087243b397799 --- test/functional/feature_rbf.py | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index c7f0cc5e4325..3da41602c907 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): @@ -587,6 +593,38 @@ def test_replacement_relay_fee(self): 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 From 567c3ee3cb937de09d3fe8b7e82e431993fac7c7 Mon Sep 17 00:00:00 2001 From: glozow Date: Mon, 11 Aug 2025 16:58:21 -0400 Subject: [PATCH 04/13] [test] explicitly check default -minrelaytxfee and -incrementalrelayfee Github-Pull: #33106 Rebased-From: 1fbee5d7b61b83e68e4230c8a97ca308de92c4c3 --- test/functional/mempool_accept.py | 9 +++++++++ test/functional/test_framework/mempool_util.py | 4 ++++ 2 files changed, 13 insertions(+) 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/test_framework/mempool_util.py b/test/functional/test_framework/mempool_util.py index 0e9c821e2ead..1587c82e2f80 100644 --- a/test/functional/test_framework/mempool_util.py +++ b/test/functional/test_framework/mempool_util.py @@ -20,6 +20,10 @@ ) ORPHAN_TX_EXPIRE_TIME = 1200 +# Default for -minrelaytxfee in sat/kvB +DEFAULT_MIN_RELAY_TX_FEE = 1000 +# Default for -incrementalrelayfee in sat/kvB +DEFAULT_INCREMENTAL_RELAY_FEE = 1000 def assert_mempool_contents(test_framework, node, expected=None, sync=True): """Assert that all transactions in expected are in the mempool, From 3a7e093f948571e058db31dd971dc628d9729232 Mon Sep 17 00:00:00 2001 From: glozow Date: Mon, 11 Aug 2025 16:50:58 -0400 Subject: [PATCH 05/13] [doc] assert that default min relay feerate and incremental are the same Github-Pull: #33106 Rebased-From: d6213d6aa114aeed6804a585491d741386fd2739 --- src/node/mempool_args.cpp | 1 + 1 file changed, 1 insertion(+) 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() From 1c1970fb45896dc5fa7b16370408c34964ed4c19 Mon Sep 17 00:00:00 2001 From: glozow Date: Tue, 29 Jul 2025 13:32:38 -0400 Subject: [PATCH 06/13] [miner] lower default -blockmintxfee to 1sat/kvB Back when we implemented coin age priority as a miner policy, miners mempools might admit transactions paying very low fees, but then want to set a higher fee for block inclusion. However, since coin age priority was removed in v0.15, the block assembly policy is solely based on fees, so we do not need to apply minimum feerate rules in multiple places. In fact, the block assembly policy ignoring transactions that are added to the mempool is likely undesirable as we waste resources accepting and storing this transaction. Instead, rely on mempool policy to enforce a minimum entry feerate to the mempool (minrelaytxfee). Set the minimum block feerate to the minimum non-zero amount (1sat/kvB) so it collects everything it finds in mempool into the block. Github-Pull: #33106 Rebased-From: 5f2df0ef78be7b24798d0983c9b962740608f1f4 --- src/policy/policy.h | 2 +- src/test/miner_tests.cpp | 4 ++++ test/functional/mining_basic.py | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/policy/policy.h b/src/policy/policy.h index dab39aa4f8b5..fe8006729bb4 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 */ 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/test/functional/mining_basic.py b/test/functional/mining_basic.py index c354e2c71c48..72f92c85a09c 100755 --- a/test/functional/mining_basic.py +++ b/test/functional/mining_basic.py @@ -49,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): @@ -100,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, 1, 5, 10, 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)...") From a0ae3fc8a764121b17e11e3a99330e73c0e44c2d Mon Sep 17 00:00:00 2001 From: glozow Date: Tue, 29 Jul 2025 14:08:06 -0400 Subject: [PATCH 07/13] [prep/test] replace magic number 1000 with respective feerate vars Github-Pull: #33106 Rebased-From: 3eab8b724044dc321f70e5eed66b149713158a04 --- src/test/mempool_tests.cpp | 16 ++++++++-------- test/functional/mempool_limit.py | 9 +++------ test/functional/test_framework/mempool_util.py | 15 ++++++--------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index f0094dce59f8..e96dd6e430bf 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -482,7 +482,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) 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); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE); CMutableTransaction tx4 = CMutableTransaction(); tx4.vin.resize(2); @@ -559,28 +559,28 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) 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/test/functional/mempool_limit.py b/test/functional/mempool_limit.py index 85d158d6111c..737b3c4b52b8 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() @@ -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() @@ -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) diff --git a/test/functional/test_framework/mempool_util.py b/test/functional/test_framework/mempool_util.py index 1587c82e2f80..e00fea23d99c 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, @@ -52,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 @@ -74,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] @@ -84,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 @@ -103,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.""" From da30ca0efadd3861016f6435636d9b399da65162 Mon Sep 17 00:00:00 2001 From: glozow Date: Wed, 30 Jul 2025 15:48:34 -0400 Subject: [PATCH 08/13] [prep/util] help MockMempoolMinFee handle more precise feerates Use a virtual size of 1000 to keep precision when using a feerate (which is rounded to the nearest satoshi per kvb) that isn't just an integer. Github-Pull: #33106 Rebased-From: 457cfb61b5323a13218b3cfb5a6a6d8b3a7c5f7f --- src/test/util/setup_common.cpp | 4 ++++ 1 file changed, 4 insertions(+) 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. From bbdab3ef7b7e8f3da4e571f35692bc3264c384db Mon Sep 17 00:00:00 2001 From: glozow Date: Wed, 30 Jul 2025 15:55:07 -0400 Subject: [PATCH 09/13] [prep/test] make wallet_fundrawtransaction's minrelaytxfee assumption explicit Github-Pull: #33106 Rebased-From: 2e515d2897eaa5a9b012eb78aef105e1cf80d42b --- test/functional/wallet_fundrawtransaction.py | 4 ++++ 1 file changed, 4 insertions(+) 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 From 9dd7efc8c3fc30cd65ae18a8a91e292c71fe7c16 Mon Sep 17 00:00:00 2001 From: glozow Date: Tue, 29 Jul 2025 14:37:16 -0400 Subject: [PATCH 10/13] [policy] lower default minrelaytxfee and incrementalrelayfee to 100sat/kvB 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 commercial services is 10c/GB, that's like 1-4c per kvB of transaction data, so a 1000vB transaction should pay at least $0.04. At a price of 120k USD/BTC, 100sat is about $0.12. This price allows us to tolerate a large decrease in the conversion rate or increase in the number of nodes. Github-Pull: #33106 Rebased-From: 6da5de58cabc4133c379baa50845e30e5bc6b3e4 --- src/policy/policy.h | 4 ++-- src/test/mempool_tests.cpp | 24 +++++++++---------- src/test/rbf_tests.cpp | 8 +++---- test/functional/feature_rbf.py | 2 +- test/functional/mempool_ephemeral_dust.py | 2 +- test/functional/mempool_limit.py | 16 ++++++------- test/functional/mempool_package_rbf.py | 9 +++---- test/functional/p2p_1p1c_network.py | 17 +++++++------ test/functional/p2p_ibd_txrelay.py | 8 +++---- test/functional/p2p_opportunistic_1p1c.py | 8 ++++--- .../functional/test_framework/mempool_util.py | 4 ++-- test/functional/wallet_bumpfee.py | 4 ++-- 12 files changed, 54 insertions(+), 52 deletions(-) diff --git a/src/policy/policy.h b/src/policy/policy.h index fe8006729bb4..bf6224af3db6 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -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 e96dd6e430bf..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,7 +481,7 @@ 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))); + 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(); @@ -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,8 +553,8 @@ 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); 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/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index 3da41602c907..81f117a759d9 100755 --- a/test/functional/feature_rbf.py +++ b/test/functional/feature_rbf.py @@ -589,7 +589,7 @@ 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()) 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 737b3c4b52b8..5051f8a03016 100755 --- a/test/functional/mempool_limit.py +++ b/test/functional/mempool_limit.py @@ -185,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() @@ -215,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"]) @@ -230,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): @@ -249,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) @@ -302,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() @@ -408,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/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 e00fea23d99c..56a9b4d262e7 100644 --- a/test/functional/test_framework/mempool_util.py +++ b/test/functional/test_framework/mempool_util.py @@ -20,9 +20,9 @@ ORPHAN_TX_EXPIRE_TIME = 1200 # Default for -minrelaytxfee in sat/kvB -DEFAULT_MIN_RELAY_TX_FEE = 1000 +DEFAULT_MIN_RELAY_TX_FEE = 100 # Default for -incrementalrelayfee in sat/kvB -DEFAULT_INCREMENTAL_RELAY_FEE = 1000 +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, 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}) From f9f1ca5445fc216c770b583c1db999aaf910f96f Mon Sep 17 00:00:00 2001 From: glozow Date: Wed, 20 Aug 2025 09:24:49 -0400 Subject: [PATCH 11/13] [doc] update release notes Release notes are from 18720bc5d5b4d3acf91060859180d72cbfdf59b7 --- doc/release-notes.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) 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 From eb1574af0c6cba4918957d704c5c0f17235a10b5 Mon Sep 17 00:00:00 2001 From: glozow Date: Wed, 20 Aug 2025 09:41:47 -0400 Subject: [PATCH 12/13] [build] bump version to 29.1rc2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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") From 0034dcfba9dc599449e7569ed1b30e58d4f4434f Mon Sep 17 00:00:00 2001 From: glozow Date: Wed, 20 Aug 2025 09:45:13 -0400 Subject: [PATCH 13/13] [doc] man pages for 29.1rc2 --- doc/man/bitcoin-cli.1 | 6 +++--- doc/man/bitcoin-qt.1 | 10 +++++----- doc/man/bitcoin-tx.1 | 6 +++--- doc/man/bitcoin-util.1 | 6 +++--- doc/man/bitcoin-wallet.1 | 6 +++--- doc/man/bitcoind.1 | 10 +++++----- 6 files changed, 22 insertions(+), 22 deletions(-) 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