Repository: bitcoin/bitcoin
File: test/functional/feature_rbf.py (Python)

Code before:
            assert_equal(json1["vin"][0]["sequence"], 4294967294)

    def test_replacement_relay_fee(self):
        tx = self.wallet.send_self_transfer(from_node=self.nodes[0])['tx']

        # 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"))
        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

        confirmed_utxo = self.make_utxo(self.nodes[0], int(2 * COIN))
        assert self.nodes[0].getmempoolinfo()["fullrbf"]

        # Create an explicitly opt-out BIP125 transaction, which will be ignored
        optout_tx = self.wallet.send_self_transfer(

Review comment (bug):
Feel free to disregard, I am confused about what issue this avoids by setting the target_vsize of the replacee to 5000, if you remove the `target_vsize=`,  the 5sat/kvB case's `failed_replacement_tx` succeeds when it should fail and same is true for any test case with fees greater than 0 sat/kvB and less than 10 sat/kvB, and a `target_vsize=114` is sufficient to avoid the issue.

I am not sure I understand why that happens, it appears that in these cases the failed replacement tx has the exact same fee as the replacement tx (`0.00000208`), so why is it not rejected?

Code after:
            assert_equal(json1["vin"][0]["sequence"], 4294967294)

    def test_replacement_relay_fee(self):
        tx = self.wallet.send_self_transfer(from_node=self.nodes[0])['tx']

        # 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.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

        confirmed_utxo = self.make_utxo(self.nodes[0], int(2 * COIN))
        assert self.nodes[0].getmempoolinfo()["fullrbf"]

        # Create an explicitly opt-out BIP125 transaction, which will be ignored
        optout_tx = self.wallet.send_self_transfer(