mirror of https://github.com/bitcoin/bitcoin.git
[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: 6da5de58ca
This commit is contained in:
parent
a02e0a401c
commit
66559d1a4a
|
@ -32,7 +32,7 @@ static constexpr unsigned int MAX_P2SH_SIGOPS{15};
|
||||||
/** The maximum number of sigops we're willing to relay/mine in a single tx */
|
/** The maximum number of sigops we're willing to relay/mine in a single tx */
|
||||||
static constexpr unsigned int MAX_STANDARD_TX_SIGOPS_COST{MAX_BLOCK_SIGOPS_COST/5};
|
static constexpr unsigned int MAX_STANDARD_TX_SIGOPS_COST{MAX_BLOCK_SIGOPS_COST/5};
|
||||||
/** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or replacement **/
|
/** 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 */
|
/** Default for -bytespersigop */
|
||||||
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20};
|
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20};
|
||||||
/** Default for -permitbaremultisig */
|
/** Default for -permitbaremultisig */
|
||||||
|
@ -54,7 +54,7 @@ static constexpr unsigned int MAX_STANDARD_SCRIPTSIG_SIZE{1650};
|
||||||
* outputs below the new threshold */
|
* outputs below the new threshold */
|
||||||
static constexpr unsigned int DUST_RELAY_TX_FEE{3000};
|
static constexpr unsigned int DUST_RELAY_TX_FEE{3000};
|
||||||
/** Default for -minrelaytxfee, minimum relay fee for transactions */
|
/** 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 */
|
/** Default for -limitancestorcount, max number of in-mempool ancestors */
|
||||||
static constexpr unsigned int DEFAULT_ANCESTOR_LIMIT{25};
|
static constexpr unsigned int DEFAULT_ANCESTOR_LIMIT{25};
|
||||||
/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */
|
/** 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.resize(1);
|
||||||
tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
|
tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
|
||||||
tx1.vout[0].nValue = 10 * COIN;
|
tx1.vout[0].nValue = 10 * COIN;
|
||||||
pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
|
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx1));
|
||||||
|
|
||||||
CMutableTransaction tx2 = CMutableTransaction();
|
CMutableTransaction tx2 = CMutableTransaction();
|
||||||
tx2.vin.resize(1);
|
tx2.vin.resize(1);
|
||||||
|
@ -451,7 +451,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
|
||||||
tx2.vout.resize(1);
|
tx2.vout.resize(1);
|
||||||
tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
|
tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
|
||||||
tx2.vout[0].nValue = 10 * COIN;
|
tx2.vout[0].nValue = 10 * COIN;
|
||||||
pool.addUnchecked(entry.Fee(5000LL).FromTx(tx2));
|
pool.addUnchecked(entry.Fee(500LL).FromTx(tx2));
|
||||||
|
|
||||||
pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
|
pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
|
||||||
BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash())));
|
BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash())));
|
||||||
|
@ -469,7 +469,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
|
||||||
tx3.vout.resize(1);
|
tx3.vout.resize(1);
|
||||||
tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
|
tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
|
||||||
tx3.vout[0].nValue = 10 * COIN;
|
tx3.vout[0].nValue = 10 * COIN;
|
||||||
pool.addUnchecked(entry.Fee(20000LL).FromTx(tx3));
|
pool.addUnchecked(entry.Fee(2000LL).FromTx(tx3));
|
||||||
|
|
||||||
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
|
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
|
||||||
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx1.GetHash())));
|
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(tx2.GetHash())));
|
||||||
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx3.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);
|
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE);
|
||||||
|
|
||||||
CMutableTransaction tx4 = CMutableTransaction();
|
CMutableTransaction tx4 = CMutableTransaction();
|
||||||
|
@ -532,10 +532,10 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
|
||||||
tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
|
tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
|
||||||
tx7.vout[1].nValue = 10 * COIN;
|
tx7.vout[1].nValue = 10 * COIN;
|
||||||
|
|
||||||
pool.addUnchecked(entry.Fee(7000LL).FromTx(tx4));
|
pool.addUnchecked(entry.Fee(700LL).FromTx(tx4));
|
||||||
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
|
pool.addUnchecked(entry.Fee(100LL).FromTx(tx5));
|
||||||
pool.addUnchecked(entry.Fee(1100LL).FromTx(tx6));
|
pool.addUnchecked(entry.Fee(110LL).FromTx(tx6));
|
||||||
pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
|
pool.addUnchecked(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
|
// 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);
|
pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
|
||||||
|
@ -544,8 +544,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
|
||||||
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
|
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
|
||||||
|
|
||||||
if (!pool.exists(GenTxid::Txid(tx5.GetHash())))
|
if (!pool.exists(GenTxid::Txid(tx5.GetHash())))
|
||||||
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
|
pool.addUnchecked(entry.Fee(100LL).FromTx(tx5));
|
||||||
pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
|
pool.addUnchecked(entry.Fee(900LL).FromTx(tx7));
|
||||||
|
|
||||||
pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
|
pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
|
||||||
BOOST_CHECK(pool.exists(GenTxid::Txid(tx4.GetHash())));
|
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(tx6.GetHash())));
|
||||||
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
|
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
|
||||||
|
|
||||||
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
|
pool.addUnchecked(entry.Fee(100LL).FromTx(tx5));
|
||||||
pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
|
pool.addUnchecked(entry.Fee(900LL).FromTx(tx7));
|
||||||
|
|
||||||
std::vector<CTransactionRef> vtx;
|
std::vector<CTransactionRef> vtx;
|
||||||
SetMockTime(42);
|
SetMockTime(42);
|
||||||
|
|
|
@ -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, 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());
|
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
|
// 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 + 1, 11, 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 + 1, 10, 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 + 2, 11, 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 + 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, incremental_relay_feerate, unused_txid).has_value());
|
||||||
BOOST_CHECK(PaysForRBF(low_fee, high_fee + 99999999, 99999999, incremental_relay_feerate, unused_txid) == std::nullopt);
|
BOOST_CHECK(PaysForRBF(low_fee, high_fee + 99999999, 99999999, incremental_relay_feerate, unused_txid) == std::nullopt);
|
||||||
|
|
||||||
|
|
|
@ -703,7 +703,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
||||||
|
|
||||||
# Higher fee, higher feerate, different txid, but the replacement does not provide a relay
|
# 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.
|
# 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
|
tx.vout[0].nValue -= 1
|
||||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex())
|
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex())
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,7 @@ class MempoolLimitTest(BitcoinTestFramework):
|
||||||
# coin is no longer available, but the cache could still contains the tx.
|
# coin is no longer available, but the cache could still contains the tx.
|
||||||
cpfp_parent = self.wallet.create_self_transfer(
|
cpfp_parent = self.wallet.create_self_transfer(
|
||||||
utxo_to_spend=mempool_evicted_tx["new_utxo"],
|
utxo_to_spend=mempool_evicted_tx["new_utxo"],
|
||||||
fee_rate=mempoolmin_feerate - Decimal('0.00001'),
|
fee_rate=mempoolmin_feerate / 2,
|
||||||
confirmed_only=True)
|
confirmed_only=True)
|
||||||
package_hex.append(cpfp_parent["hex"])
|
package_hex.append(cpfp_parent["hex"])
|
||||||
parent_utxos.append(cpfp_parent["new_utxo"])
|
parent_utxos.append(cpfp_parent["new_utxo"])
|
||||||
|
@ -153,7 +153,7 @@ class MempoolLimitTest(BitcoinTestFramework):
|
||||||
# Specific number of satoshis to fit within a small window. The parent_cpfp + child package needs to be
|
# 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 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.
|
# - 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
|
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)
|
child = self.wallet.create_self_transfer_multi(utxos_to_spend=parent_utxos, fee_per_output=cpfp_satoshis)
|
||||||
|
@ -206,7 +206,7 @@ class MempoolLimitTest(BitcoinTestFramework):
|
||||||
# coin is no longer available, but the cache could still contain the tx.
|
# coin is no longer available, but the cache could still contain the tx.
|
||||||
cpfp_parent = self.wallet.create_self_transfer(
|
cpfp_parent = self.wallet.create_self_transfer(
|
||||||
utxo_to_spend=replaced_tx["new_utxo"],
|
utxo_to_spend=replaced_tx["new_utxo"],
|
||||||
fee_rate=mempoolmin_feerate - Decimal('0.00001'),
|
fee_rate=mempoolmin_feerate - Decimal('0.000001'),
|
||||||
confirmed_only=True)
|
confirmed_only=True)
|
||||||
|
|
||||||
self.wallet.rescan_utxos()
|
self.wallet.rescan_utxos()
|
||||||
|
@ -311,9 +311,9 @@ class MempoolLimitTest(BitcoinTestFramework):
|
||||||
target_weight_each = 200000
|
target_weight_each = 200000
|
||||||
assert_greater_than(target_weight_each * 2, node.getmempoolinfo()["maxmempool"] - node.getmempoolinfo()["bytes"])
|
assert_greater_than(target_weight_each * 2, node.getmempoolinfo()["maxmempool"] - node.getmempoolinfo()["bytes"])
|
||||||
# Should be a true CPFP: parent's feerate is just below mempool min feerate
|
# 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
|
# 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.
|
# However, when eviction is triggered, these transactions should be at the bottom.
|
||||||
# This assertion assumes parent and child are the same size.
|
# This assertion assumes parent and child are the same size.
|
||||||
miniwallet.rescan_utxos()
|
miniwallet.rescan_utxos()
|
||||||
|
|
|
@ -170,13 +170,13 @@ class PackageRBFTest(BitcoinTestFramework):
|
||||||
self.log.info("Check replacement pays for incremental bandwidth")
|
self.log.info("Check replacement pays for incremental bandwidth")
|
||||||
_, placeholder_txns3 = self.create_simple_package(coin)
|
_, placeholder_txns3 = self.create_simple_package(coin)
|
||||||
package_3_size = sum([tx.get_vsize() for tx in placeholder_txns3])
|
package_3_size = sum([tx.get_vsize() for tx in placeholder_txns3])
|
||||||
incremental_sats_required = Decimal(package_3_size) / COIN
|
incremental_sats_required = (Decimal(package_3_size * 0.1) / COIN).quantize(Decimal("0.00000001"))
|
||||||
incremental_sats_short = incremental_sats_required - 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
|
# 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)
|
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]))
|
assert_equal(package_3_size, sum([tx.get_vsize() for tx in failure_package_txns3]))
|
||||||
pkg_results3 = node.submitpackage(failure_package_hex3)
|
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)
|
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)
|
success_package_hex3, success_package_txns3 = self.create_simple_package(coin, parent_fee=DEFAULT_FEE, child_fee=DEFAULT_CHILD_FEE + incremental_sats_required)
|
||||||
|
@ -570,12 +570,13 @@ class PackageRBFTest(BitcoinTestFramework):
|
||||||
)
|
)
|
||||||
|
|
||||||
node.sendrawtransaction(grandparent_result["hex"])
|
node.sendrawtransaction(grandparent_result["hex"])
|
||||||
|
minrelayfeerate = node.getnetworkinfo()["relayfee"]
|
||||||
|
|
||||||
# Now make package of two descendants that looks
|
# Now make package of two descendants that looks
|
||||||
# like a cpfp where the parent can't get in on its own
|
# like a cpfp where the parent can't get in on its own
|
||||||
self.ctr += 1
|
self.ctr += 1
|
||||||
parent_result = self.wallet.create_self_transfer(
|
parent_result = self.wallet.create_self_transfer(
|
||||||
fee_rate=Decimal('0.00001000'),
|
fee_rate=minrelayfeerate,
|
||||||
utxo_to_spend=grandparent_result["new_utxo"],
|
utxo_to_spend=grandparent_result["new_utxo"],
|
||||||
sequence=MAX_BIP125_RBF_SEQUENCE - self.ctr,
|
sequence=MAX_BIP125_RBF_SEQUENCE - self.ctr,
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,9 +13,11 @@ from decimal import Decimal
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
|
||||||
from test_framework.mempool_util import (
|
from test_framework.mempool_util import (
|
||||||
|
DEFAULT_MIN_RELAY_TX_FEE,
|
||||||
fill_mempool,
|
fill_mempool,
|
||||||
)
|
)
|
||||||
from test_framework.messages import (
|
from test_framework.messages import (
|
||||||
|
COIN,
|
||||||
msg_tx,
|
msg_tx,
|
||||||
)
|
)
|
||||||
from test_framework.p2p import (
|
from test_framework.p2p import (
|
||||||
|
@ -31,9 +33,6 @@ from test_framework.wallet import (
|
||||||
MiniWalletMode,
|
MiniWalletMode,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 1sat/vB feerate denominated in BTC/KvB
|
|
||||||
FEERATE_1SAT_VB = Decimal("0.00001000")
|
|
||||||
|
|
||||||
class PackageRelayTest(BitcoinTestFramework):
|
class PackageRelayTest(BitcoinTestFramework):
|
||||||
def set_test_params(self):
|
def set_test_params(self):
|
||||||
self.setup_clean_chain = True
|
self.setup_clean_chain = True
|
||||||
|
@ -51,12 +50,12 @@ class PackageRelayTest(BitcoinTestFramework):
|
||||||
|
|
||||||
self.log.debug("Check that all nodes' mempool minimum feerates are above min relay feerate")
|
self.log.debug("Check that all nodes' mempool minimum feerates are above min relay feerate")
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
assert_equal(node.getmempoolinfo()['minrelaytxfee'], FEERATE_1SAT_VB)
|
assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN)
|
||||||
assert_greater_than(node.getmempoolinfo()['mempoolminfee'], FEERATE_1SAT_VB)
|
assert_greater_than(node.getmempoolinfo()['mempoolminfee'], Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN)
|
||||||
|
|
||||||
def create_basic_1p1c(self, wallet):
|
def create_basic_1p1c(self, wallet):
|
||||||
low_fee_parent = wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB, confirmed_only=True)
|
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*FEERATE_1SAT_VB)
|
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"]]
|
package_hex_basic = [low_fee_parent["hex"], high_fee_child["hex"]]
|
||||||
return package_hex_basic, low_fee_parent["tx"], high_fee_child["tx"]
|
return package_hex_basic, low_fee_parent["tx"], high_fee_child["tx"]
|
||||||
|
|
||||||
|
@ -87,8 +86,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"]
|
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):
|
def create_package_2p1c(self, wallet):
|
||||||
parent1 = wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB*10, 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=FEERATE_1SAT_VB*20, 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(
|
child = wallet.create_self_transfer_multi(
|
||||||
utxos_to_spend=[parent1["new_utxo"], parent2["new_utxo"]],
|
utxos_to_spend=[parent1["new_utxo"], parent2["new_utxo"]],
|
||||||
fee_per_output=999*parent1["tx"].get_vsize(),
|
fee_per_output=999*parent1["tx"].get_vsize(),
|
||||||
|
|
|
@ -28,8 +28,8 @@ from test_framework.p2p import (
|
||||||
)
|
)
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
|
||||||
MAX_FEE_FILTER = Decimal(9170997) / COIN
|
MAX_FEE_FILTER = Decimal(9936506) / COIN
|
||||||
NORMAL_FEE_FILTER = Decimal(100) / COIN
|
NORMAL_FEE_FILTER = Decimal(10) / COIN
|
||||||
|
|
||||||
|
|
||||||
class P2PIBDTxRelayTest(BitcoinTestFramework):
|
class P2PIBDTxRelayTest(BitcoinTestFramework):
|
||||||
|
@ -37,8 +37,8 @@ class P2PIBDTxRelayTest(BitcoinTestFramework):
|
||||||
self.setup_clean_chain = True
|
self.setup_clean_chain = True
|
||||||
self.num_nodes = 2
|
self.num_nodes = 2
|
||||||
self.extra_args = [
|
self.extra_args = [
|
||||||
["-minrelaytxfee={}".format(NORMAL_FEE_FILTER)],
|
["-minrelaytxfee={:.8f}".format(NORMAL_FEE_FILTER)],
|
||||||
["-minrelaytxfee={}".format(NORMAL_FEE_FILTER)],
|
["-minrelaytxfee={:.8f}".format(NORMAL_FEE_FILTER)],
|
||||||
]
|
]
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
|
|
|
@ -9,10 +9,12 @@ Test opportunistic 1p1c package submission logic.
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import time
|
import time
|
||||||
from test_framework.mempool_util import (
|
from test_framework.mempool_util import (
|
||||||
|
DEFAULT_MIN_RELAY_TX_FEE,
|
||||||
fill_mempool,
|
fill_mempool,
|
||||||
)
|
)
|
||||||
from test_framework.messages import (
|
from test_framework.messages import (
|
||||||
CInv,
|
CInv,
|
||||||
|
COIN,
|
||||||
CTxInWitness,
|
CTxInWitness,
|
||||||
MAX_BIP125_RBF_SEQUENCE,
|
MAX_BIP125_RBF_SEQUENCE,
|
||||||
MSG_WTX,
|
MSG_WTX,
|
||||||
|
@ -65,13 +67,13 @@ class PackageRelayTest(BitcoinTestFramework):
|
||||||
self.supports_cli = False
|
self.supports_cli = False
|
||||||
|
|
||||||
def create_tx_below_mempoolminfee(self, wallet):
|
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 so that subsequent calls to this function result in unique transactions."""
|
||||||
|
|
||||||
self.sequence -= 1
|
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
|
@cleanup
|
||||||
def test_basic_child_then_parent(self):
|
def test_basic_child_then_parent(self):
|
||||||
|
|
|
@ -18,16 +18,16 @@ from .wallet import (
|
||||||
)
|
)
|
||||||
|
|
||||||
# Default for -minrelaytxfee in sat/kvB
|
# 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 for -incrementalrelayfee in sat/kvB
|
||||||
DEFAULT_INCREMENTAL_RELAY_FEE = 1000
|
DEFAULT_INCREMENTAL_RELAY_FEE = 100
|
||||||
|
|
||||||
def fill_mempool(test_framework, node, *, tx_sync_fun=None):
|
def fill_mempool(test_framework, node, *, tx_sync_fun=None):
|
||||||
"""Fill mempool until eviction.
|
"""Fill mempool until eviction.
|
||||||
|
|
||||||
Allows for simpler testing of scenarios with floating mempoolminfee > minrelay
|
Allows for simpler testing of scenarios with floating mempoolminfee > minrelay
|
||||||
Requires -datacarriersize=100000 and -maxmempool=5 and assumes -minrelaytxfee
|
Requires -datacarriersize=100000 and -maxmempool=5 and assumes -minrelaytxfee
|
||||||
is 1 sat/vbyte.
|
is 100 sat/kvB.
|
||||||
To avoid unintentional tx dependencies, the mempool filling txs are created with a
|
To avoid unintentional tx dependencies, the mempool filling txs are created with a
|
||||||
tagged ephemeral miniwallet instance.
|
tagged ephemeral miniwallet instance.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -534,7 +534,7 @@ def test_dust_to_fee(self, rbf_node, dest_address):
|
||||||
|
|
||||||
def test_settxfee(self, rbf_node, dest_address):
|
def test_settxfee(self, rbf_node, dest_address):
|
||||||
self.log.info('Test settxfee')
|
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'))
|
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)
|
# check that bumpfee reacts correctly to the use of settxfee (paytxfee)
|
||||||
rbfid = spend_one_input(rbf_node, dest_address)
|
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
|
# 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)
|
# 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)
|
# 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})
|
rbf_node.bumpfee(tx["txid"], {"fee_rate": 3})
|
||||||
|
|
Loading…
Reference in New Issue