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

Code before:

        if self.is_wallet_compiled():
            self.init_wallet(node=0)
            rawtx2 = self.nodes[0].createrawtransaction([], outs)
            frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
            frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})

            json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
            json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
            assert_equal(json0["vin"][0]["sequence"], 4294967293)
            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:.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)

Review comment (suggestion):
In 636fa219d37f86067d996c86fada286cedc0d78e "test fixups"

Can drop the `.8f`, otherwise these are printed with 8 decimal places of 0's that are not necessary.

Code after:
        if self.is_wallet_compiled():
            self.init_wallet(node=0)
            rawtx2 = self.nodes[0].createrawtransaction([], outs)
            frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
            frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})

            json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
            json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
            assert_equal(json0["vin"][0]["sequence"], 4294967293)
            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 100 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:.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)
            # Use different versions to avoid creating an identical transaction when failed_replacement_tx is created.
            # Use a target vsize that is small, but something larger than the minimum so that we can create a transaction that is 1vB smaller later.
            replacee_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee_rate=low_feerate, version=3, target_vsize=200)
            node.sendrawtransaction(replacee_tx['hex'])

            replacement_placeholder_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, target_vsize=200)
            replacement_expected_size = replacement_placeholder_tx['tx'].get_vsize()
            replacement_required_fee = get_fee(replacement_expected_size, incremental_setting_decimal) + replacee_tx['fee']

            # Show that replacement fails when paying 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"), version=2, target_vsize=200)
            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, version=2, target_vsize=200)

            if incremental_setting == 0:
                # When incremental relay feerate is 0, additional fees are not required, but higher feerate is still required.
                assert_raises_rpc_error(-26, "insufficient fee", node.sendrawtransaction, replacement_tx['hex'])