|
14 | 14 | from test_framework.test_framework import BitcoinTestFramework |
15 | 15 | from test_framework.util import ( |
16 | 16 | assert_equal, |
| 17 | + assert_greater_than, |
| 18 | + assert_greater_than_or_equal, |
17 | 19 | assert_raises_rpc_error, |
| 20 | + get_fee, |
18 | 21 | ) |
19 | 22 | from test_framework.wallet import MiniWallet |
20 | 23 | from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE |
@@ -87,6 +90,9 @@ def run_test(self): |
87 | 90 | self.log.info("Running test full replace by fee...") |
88 | 91 | self.test_fullrbf() |
89 | 92 |
|
| 93 | + self.log.info("Running test incremental relay feerates...") |
| 94 | + self.test_incremental_relay_feerates() |
| 95 | + |
90 | 96 | self.log.info("Passed") |
91 | 97 |
|
92 | 98 | def make_utxo(self, node, amount, *, confirmed=True, scriptPubKey=None): |
@@ -701,6 +707,38 @@ def test_replacement_relay_fee(self): |
701 | 707 | tx.vout[0].nValue -= 1 |
702 | 708 | assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex()) |
703 | 709 |
|
| 710 | + def test_incremental_relay_feerates(self): |
| 711 | + self.log.info("Test that incremental relay fee is applied correctly in RBF for various settings...") |
| 712 | + node = self.nodes[0] |
| 713 | + for incremental_setting in (0, 5, 10, 50, 100, 234, 1000, 5000, 21000): |
| 714 | + incremental_setting_decimal = incremental_setting / Decimal(COIN) |
| 715 | + self.log.info(f"-> Test -incrementalrelayfee={incremental_setting_decimal:.8f}sat/kvB...") |
| 716 | + self.restart_node(0, extra_args=[f"-incrementalrelayfee={incremental_setting_decimal:.8f}", "-datacarriersize=5000", "-persistmempool=0"]) |
| 717 | + |
| 718 | + # When incremental relay feerate is higher than min relay feerate, min relay feerate is automatically increased. |
| 719 | + min_relay_feerate = node.getmempoolinfo()["minrelaytxfee"] |
| 720 | + assert_greater_than_or_equal(min_relay_feerate, incremental_setting_decimal) |
| 721 | + |
| 722 | + low_feerate = min_relay_feerate * 2 |
| 723 | + confirmed_utxo = self.wallet.get_utxo(confirmed_only=True) |
| 724 | + replacee_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee_rate=low_feerate, target_weight=20000) |
| 725 | + node.sendrawtransaction(replacee_tx['hex']) |
| 726 | + |
| 727 | + replacement_placeholder_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo) |
| 728 | + replacement_expected_size = replacement_placeholder_tx['tx'].get_vsize() |
| 729 | + replacement_required_fee = get_fee(replacement_expected_size, incremental_setting_decimal) + replacee_tx['fee'] |
| 730 | + |
| 731 | + # Should always be required to pay additional fees |
| 732 | + if incremental_setting > 0: |
| 733 | + assert_greater_than(replacement_required_fee, replacee_tx['fee']) |
| 734 | + |
| 735 | + # 1 satoshi shy of the required fee |
| 736 | + failed_replacement_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee=replacement_required_fee - Decimal("0.00000001")) |
| 737 | + assert_raises_rpc_error(-26, "insufficient fee", node.sendrawtransaction, failed_replacement_tx['hex']) |
| 738 | + |
| 739 | + replacement_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee=replacement_required_fee) |
| 740 | + node.sendrawtransaction(replacement_tx['hex']) |
| 741 | + |
704 | 742 | def test_fullrbf(self): |
705 | 743 |
|
706 | 744 | confirmed_utxo = self.make_utxo(self.nodes[0], int(2 * COIN)) |
|
0 commit comments