mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-16 08:22:46 +00:00
Merge bitcoin/bitcoin#33106: policy: lower the default blockmintxfee, incrementalrelayfee, minrelaytxfee
ba84a25deec0b3b9b94ee51b373e715fec995791 [doc] update mempool-replacements.md for incremental relay feerate change (glozow) 18720bc5d5b4d3acf91060859180d72cbfdf59b7 [doc] release note for min feerate changes (glozow) 6da5de58cabc4133c379baa50845e30e5bc6b3e4 [policy] lower default minrelaytxfee and incrementalrelayfee to 100sat/kvB (glozow) 2e515d2897eaa5a9b012eb78aef105e1cf80d42b [prep/test] make wallet_fundrawtransaction's minrelaytxfee assumption explicit (glozow) 457cfb61b5323a13218b3cfb5a6a6d8b3a7c5f7f [prep/util] help MockMempoolMinFee handle more precise feerates (glozow) 3eab8b724044dc321f70e5eed66b149713158a04 [prep/test] replace magic number 1000 with respective feerate vars (glozow) 5f2df0ef78be7b24798d0983c9b962740608f1f4 [miner] lower default -blockmintxfee to 1sat/kvB (glozow) d6213d6aa114aeed6804a585491d741386fd2739 [doc] assert that default min relay feerate and incremental are the same (glozow) 1fbee5d7b61b83e68e4230c8a97ca308de92c4c3 [test] explicitly check default -minrelaytxfee and -incrementalrelayfee (glozow) 72dc18467dbfc16cdbda2dd109b087243b397799 [test] RBF rule 4 for various incrementalrelayfee settings (glozow) 85f498893f54ea7d84f2bdf12aa35d198edf8a72 [test] check bypass of minrelay for various minrelaytxfee settings (glozow) e5f896bb1f052fb8c7811c6024cb49143b427512 [test] check miner doesn't select 0fee transactions (glozow) Pull request description: ML post for discussion about the general concept, how this impacts the wider ecosystem, philosophy about minimum feerates, etc: https://delvingbitcoin.org/t/changing-the-minimum-relay-feerate/1886 This PR is inspired by #13922 and #32959 to lower the minimum relay feerate in response to bitcoin's exchange rate changes in the last ~10 years. It lowers the default `-minrelaytxfee` and `-incrementalrelayfee`, and knocks `-blockmintxfee` down to the minimum nonzero setting. Also adds some tests for the settings and pulls in #32750. The minimum relay feerate is a DoS protection rule, representing a price on the network bandwidth used to relay transactions that have no PoW. While relay nodes don't all collect fees, the assumption is that if nodes on the network use their resources to relay this transaction, it will reach a miner and the attacker's money will be spent once it is mined. The incremental relay feerate is similar: it's used to price the relay of replacement transactions (the additional fees need to cover the new transactions at this feerate) and evicted transactions (following a trim, the new mempool minimum feerate is the package feerate of what was removed + incremental). Also note that many nodes on the network have elected to relay/mine lower feerate transactions. Miners (some say up to 85%) are choosing to mine these low feerate transactions instead of leaving block space unfilled, but these blocks have extremely poor compact block reconstruction rates with nodes that rejected or didn't hear about those transactions earlier. - https://github.com/bitcoin/bitcoin/pull/33106#issuecomment-3155627414 - https://x.com/caesrcd/status/1947022514267230302 - https://mempool.space/block/00000000000000000001305770e0aa279dcd8ba8be18c3d5cf736a26f77e06fd - https://mempool.space/block/00000000000000000001b491649ec030aa8e003e1f4f9d3b24bb99ba16f91e97 - https://x.com/mononautical/status/1949452586391855121 While it wouldn't make sense to loosen DoS restrictions recklessly in response to these events, I think the current price is higher than necessary, and this motivates us changing the default soon. Since the minimum relay feerate defines an amount as too small based on what it costs the attacker, it makes sense to consider BTC's conversion rate to what resources you can buy in the "real world." Going off of [this comment](https://github.com/bitcoin/bitcoin/pull/32959#issuecomment-3095260286) and [this comment](https://github.com/bitcoin/bitcoin/pull/33106#issuecomment-3142444090) - Let's say an attacker wants to use/exhaust the network's bandwidth, and has the choice between renting resources from a commercial provider and getting the network to "spam" itself it by sending unconfirmed transactions. We'd like the latter to be more expensive than the former. - The bandwidth for relaying a transaction across the network is roughly its serialized size (plus relay overhead) x number of nodes. A 1000vB transaction is 1000-4000B serialized. With 100k nodes, that's 0.1-0.4GB - If the going rate for ec2 bandwidth is 10c/GB, that's like 1-4c per kvB of transaction data - Then a 1000vB transaction should pay at least 4c - $0.04 USD is 40 satoshis at 100k USD/BTC - Baking in some margin for changes in USD/BTC conversion rate, number of nodes (and thus bandwidth), and commercial service costs, I think 50-100 satoshis is on the conservative end but in the right ballpark - At least 97% of the recent sub-1sat/vB transactions would be accepted with a new threshold of 0.1sat/vB: https://github.com/bitcoin/bitcoin/pull/33106#issuecomment-3156213089 List of feerates that are changed and why: - min relay feerate: significant conversion rate changes, see above - incremental relay feerate: should follow min relay feerate, see above - block minimum feerate: shouldn’t be above min relay feerate, otherwise the node accepts transactions it will never mine. I've knocked it down to the bare minimum of 1sat/kvB. Now that we no longer have coin age priority (removed in v0.15), I think we can leave it to the `CheckFeeRate` policy rule to enforce a minimum entry price, and the block assembly code should just fill up the block with whatever it finds in mempool. List of feerates that are not changed and why: - dust feerate: this feerate cannot be changed as flexibly as the minrelay feerate. A much longer record of low feerate transactions being mined is needed to motivate a decrease there. - maxfeerate (RPC, wallet): I think the conversion rate is relevant as well, but out of scope for this PR - minimum feerate returned by fee estimator: should be done later. In the past, we've excluded new policy defaults from fee estimation until we feel confident they represent miner policy (e.g. #9519). Also, the fee estimator itself doesn't have support for sub-1sat/vB yet. - all wallet feerates (mintxfee, fallbackfee, discardfee, consolidatefeerate, WALLET_INCREMENTAL_RELAY_FEE, etc.): should be done later. Our standard procedure is to do wallet changes at least 1 release after policy changes. ACKs for top commit: achow101: ACK ba84a25deec0b3b9b94ee51b373e715fec995791 gmaxwell: ACK ba84a25deec0b3b9b94ee51b373e715fec995791 jsarenik: Tested ACK ba84a25deec0b3b9b94ee51b373e715fec995791 darosior: ACK ba84a25deec0b3b9b94ee51b373e715fec995791 ajtowns: ACK ba84a25deec0b3b9b94ee51b373e715fec995791 davidgumberg: crACKba84a25deew0xlt: ACKba84a25deecaesrcd: reACK ba84a25deec0b3b9b94ee51b373e715fec995791 ismaelsadeeq: re-ACK ba84a25deec0b3b9b94ee51b373e715fec995791 Tree-SHA512: b4c35e8b506b1184db466551a7e2e48bb1e535972a8dbcaa145ce3a8bfdcc70a8807dc129460f129a9d31024174d34077154a387c32f1a3e6831f6fa5e9c399e
This commit is contained in:
commit
c99f5c5e1b
@ -32,8 +32,8 @@ other consensus and policy rules, each of the following conditions are met:
|
||||
4. The additional fees (difference between absolute fee paid by the replacement transaction and the
|
||||
sum paid by the original transactions) pays for the replacement transaction's bandwidth at or
|
||||
above the rate set by the node's incremental relay feerate. For example, if the incremental relay
|
||||
feerate is 1 satoshi/vB and the replacement transaction is 500 virtual bytes total, then the
|
||||
replacement pays a fee at least 500 satoshis higher than the sum of the original transactions.
|
||||
feerate is 0.1 satoshi/vB and the replacement transaction is 500 virtual bytes total, then the
|
||||
replacement pays a fee at least 50 satoshis higher than the sum of the original transactions.
|
||||
|
||||
*Rationale*: Try to prevent DoS attacks where an attacker causes the network to repeatedly relay
|
||||
transactions each paying a tiny additional amount in fees, e.g. just 1 satoshi.
|
||||
@ -77,3 +77,5 @@ This set of rules is similar but distinct from BIP125.
|
||||
* Full replace-by-fee is the default policy as of **v28.0** ([PR #30493](https://github.com/bitcoin/bitcoin/pull/30493)).
|
||||
|
||||
* Signaling for replace-by-fee is no longer required as of [PR 30592](https://github.com/bitcoin/bitcoin/pull/30592).
|
||||
|
||||
* The incremental relay feerate default is 0.1sat/vB ([PR #33106](https://github.com/bitcoin/bitcoin/pull/33106)).
|
||||
|
||||
17
doc/release-notes-33106.md
Normal file
17
doc/release-notes-33106.md
Normal file
@ -0,0 +1,17 @@
|
||||
Mining and Transaction Relay Policy
|
||||
=========================
|
||||
|
||||
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 but more gradually, as a
|
||||
result of the change to the incremental relay feerate.
|
||||
|
||||
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.
|
||||
@ -65,6 +65,7 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(DEFAULT_MIN_RELAY_TX_FEE == DEFAULT_INCREMENTAL_RELAY_FEE);
|
||||
if (const auto arg{argsman.GetArg("-minrelaytxfee")}) {
|
||||
if (std::optional<CAmount> min_relay_feerate = ParseMoney(*arg)) {
|
||||
// High fee check is done afterward in CWallet::Create()
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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(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(tx1.GetHash()));
|
||||
@ -481,8 +481,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
|
||||
BOOST_CHECK(!pool.exists(tx2.GetHash()));
|
||||
BOOST_CHECK(!pool.exists(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(tx7.GetHash()));
|
||||
|
||||
if (!pool.exists(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(tx4.GetHash()));
|
||||
@ -553,34 +553,34 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
|
||||
BOOST_CHECK(pool.exists(tx6.GetHash()));
|
||||
BOOST_CHECK(!pool.exists(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<CTransactionRef> 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<CAmount>&& output_values, std::vector<CTransactionRef>&& inputs=std::vector<CTransactionRef>(), std::vector<uint32_t>&& input_indices=std::vector<uint32_t>())
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <node/miner.h>
|
||||
#include <policy/policy.h>
|
||||
#include <test/util/random.h>
|
||||
#include <test/util/transaction_utils.h>
|
||||
#include <test/util/txmempool.h>
|
||||
#include <txmempool.h>
|
||||
#include <uint256.h>
|
||||
@ -216,6 +217,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));
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
#include <test/util/coverage.h>
|
||||
#include <test/util/net.h>
|
||||
#include <test/util/random.h>
|
||||
#include <test/util/transaction_utils.h>
|
||||
#include <test/util/txmempool.h>
|
||||
#include <txdb.h>
|
||||
#include <txmempool.h>
|
||||
@ -586,6 +587,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.
|
||||
|
||||
@ -13,7 +13,10 @@ from test_framework.messages import (
|
||||
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
|
||||
@ -74,6 +77,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
||||
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):
|
||||
@ -579,10 +585,42 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
||||
|
||||
# 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}", "-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
|
||||
|
||||
|
||||
@ -10,6 +10,10 @@ import math
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.blocktools import MAX_STANDARD_TX_WEIGHT
|
||||
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,
|
||||
@ -85,6 +89,11 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
||||
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))
|
||||
|
||||
@ -216,7 +216,7 @@ class EphemeralDustTest(BitcoinTestFramework):
|
||||
|
||||
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(), [])
|
||||
|
||||
|
||||
@ -92,8 +92,7 @@ class MempoolLimitTest(BitcoinTestFramework):
|
||||
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()
|
||||
@ -184,8 +183,7 @@ class MempoolLimitTest(BitcoinTestFramework):
|
||||
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()
|
||||
@ -208,7 +206,7 @@ class MempoolLimitTest(BitcoinTestFramework):
|
||||
# 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()
|
||||
@ -256,8 +254,7 @@ class MempoolLimitTest(BitcoinTestFramework):
|
||||
|
||||
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)
|
||||
|
||||
@ -315,9 +312,9 @@ class MempoolLimitTest(BitcoinTestFramework):
|
||||
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()
|
||||
|
||||
@ -162,13 +162,13 @@ class PackageRBFTest(BitcoinTestFramework):
|
||||
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].txid_hex}, 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].txid_hex}, 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)
|
||||
@ -562,12 +562,13 @@ class PackageRBFTest(BitcoinTestFramework):
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
@ -11,6 +11,7 @@ from test_framework.util import (
|
||||
assert_greater_than,
|
||||
assert_greater_than_or_equal,
|
||||
assert_raises_rpc_error,
|
||||
get_fee,
|
||||
)
|
||||
from test_framework.wallet import (
|
||||
COIN,
|
||||
@ -595,12 +596,57 @@ class MempoolTRUC(BitcoinTestFramework):
|
||||
)
|
||||
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()
|
||||
@ -614,6 +660,7 @@ class MempoolTRUC(BitcoinTestFramework):
|
||||
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__":
|
||||
|
||||
@ -39,6 +39,7 @@ from test_framework.p2p import P2PDataStore
|
||||
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,
|
||||
@ -54,7 +55,7 @@ MAX_FUTURE_BLOCK_TIME = 2 * 3600
|
||||
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]
|
||||
|
||||
class MiningTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
@ -143,7 +144,7 @@ class MiningTest(BitcoinTestFramework):
|
||||
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)...")
|
||||
@ -154,19 +155,27 @@ class MiningTest(BitcoinTestFramework):
|
||||
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']]
|
||||
|
||||
@ -13,9 +13,11 @@ from decimal import Decimal
|
||||
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 @@ from test_framework.wallet import (
|
||||
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
|
||||
@ -49,12 +48,12 @@ class PackageRelayTest(BitcoinTestFramework):
|
||||
|
||||
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"]
|
||||
|
||||
@ -85,8 +84,8 @@ class PackageRelayTest(BitcoinTestFramework):
|
||||
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(),
|
||||
|
||||
@ -28,8 +28,8 @@ from test_framework.p2p import (
|
||||
)
|
||||
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 @@ class P2PIBDTxRelayTest(BitcoinTestFramework):
|
||||
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):
|
||||
|
||||
@ -13,10 +13,12 @@ import time
|
||||
from test_framework.blocktools import MAX_STANDARD_TX_WEIGHT
|
||||
from test_framework.mempool_util import (
|
||||
create_large_orphan,
|
||||
DEFAULT_MIN_RELAY_TX_FEE,
|
||||
fill_mempool,
|
||||
)
|
||||
from test_framework.messages import (
|
||||
CInv,
|
||||
COIN,
|
||||
COutPoint,
|
||||
CTransaction,
|
||||
CTxIn,
|
||||
@ -79,13 +81,13 @@ class PackageRelayTest(BitcoinTestFramework):
|
||||
]]
|
||||
|
||||
def create_tx_below_mempoolminfee(self, wallet, utxo_to_spend=None):
|
||||
"""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, utxo_to_spend=utxo_to_spend, confirmed_only=True)
|
||||
return wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN, sequence=self.sequence, utxo_to_spend=utxo_to_spend, confirmed_only=True)
|
||||
|
||||
@cleanup
|
||||
def test_basic_child_then_parent(self):
|
||||
|
||||
@ -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
|
||||
import random
|
||||
|
||||
from .blocktools import (
|
||||
@ -30,6 +29,11 @@ from .wallet import (
|
||||
MiniWallet,
|
||||
)
|
||||
|
||||
# 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,
|
||||
and no additional ones exist. 'expected' is an array of
|
||||
@ -57,9 +61,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
|
||||
@ -79,7 +81,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]
|
||||
@ -89,14 +91,14 @@ def fill_mempool(test_framework, node, *, tx_sync_fun=None):
|
||||
# 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
|
||||
@ -108,8 +110,8 @@ def fill_mempool(test_framework, node, *, tx_sync_fun=None):
|
||||
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."""
|
||||
|
||||
@ -535,7 +535,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)
|
||||
@ -850,7 +850,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})
|
||||
|
||||
@ -43,7 +43,8 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 4
|
||||
self.extra_args = [[
|
||||
"-deprecatedrpc=settxfee"
|
||||
"-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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user