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

Code before:
    def test_rpc(self):
        us0 = self.wallet.get_utxo()
        ins = [us0]
        outs = {ADDRESS_BCRT1_UNSPENDABLE: Decimal(1.0000000)}
        rawtx0 = self.nodes[0].createrawtransaction(ins, outs, 0, True)
        rawtx1 = self.nodes[0].createrawtransaction(ins, outs, 0, False)
        json0 = self.nodes[0].decoderawtransaction(rawtx0)
        json1 = self.nodes[0].decoderawtransaction(rawtx1)
        assert_equal(json0["vin"][0]["sequence"], 4294967293)
        assert_equal(json1["vin"][0]["sequence"], 4294967295)

        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_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']

Review comment (suggestion):
nit:

```suggestion
        # fee conforming to node's `incrementalrelayfee` policy of 100 sat per kvB.
```

Code after:
        us0 = self.wallet.get_utxo()
        ins = [us0]
        outs = {ADDRESS_BCRT1_UNSPENDABLE: Decimal(1.0000000)}
        rawtx0 = self.nodes[0].createrawtransaction(ins, outs, 0, True)
        rawtx1 = self.nodes[0].createrawtransaction(ins, outs, 0, False)
        json0 = self.nodes[0].decoderawtransaction(rawtx0)
        json1 = self.nodes[0].decoderawtransaction(rawtx1)
        assert_equal(json0["vin"][0]["sequence"], 4294967293)
        assert_equal(json1["vin"][0]["sequence"], 4294967295)

        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()