feature_rbf.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2014-present The Bitcoin Core developers
   3  # Distributed under the MIT software license, see the accompanying
   4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  """Test the RBF code."""
   6  
   7  from decimal import Decimal
   8  
   9  from test_framework.messages import (
  10      MAX_BIP125_RBF_SEQUENCE,
  11      COIN,
  12  )
  13  from test_framework.test_framework import BitcoinTestFramework
  14  from test_framework.util import (
  15      assert_equal,
  16      assert_greater_than,
  17      assert_greater_than_or_equal,
  18      assert_raises_rpc_error,
  19      get_fee,
  20  )
  21  from test_framework.wallet import MiniWallet
  22  from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
  23  from test_framework.mempool_util import DEFAULT_CLUSTER_LIMIT
  24  
  25  MAX_REPLACEMENT_LIMIT = 100
  26  
  27  class ReplaceByFeeTest(BitcoinTestFramework):
  28      def set_test_params(self):
  29          self.num_nodes = 2
  30          self.uses_wallet = None
  31          self.extra_args = [["-deprecatedrpc=fullrbf", "-deprecatedrpc=bip125"], []]
  32  
  33      def run_test(self):
  34          self.wallet = MiniWallet(self.nodes[0])
  35          self.generate(self.nodes[0], 100)
  36  
  37          self.log.info("Running test simple doublespend...")
  38          self.test_simple_doublespend()
  39  
  40          self.log.info("Running test doublespend chain...")
  41          self.test_doublespend_chain()
  42  
  43          self.log.info("Running test doublespend tree...")
  44          self.test_doublespend_tree()
  45  
  46          self.log.info("Running test replacement feeperkb...")
  47          self.test_replacement_feeperkb()
  48  
  49          self.log.info("Running test spends of conflicting outputs...")
  50          self.test_spends_of_conflicting_outputs()
  51  
  52          self.log.info("Running test new unconfirmed inputs...")
  53          self.test_new_unconfirmed_inputs()
  54  
  55          self.log.info("Running test new unconfirmed input from low feerate tx...")
  56          self.test_new_unconfirmed_input_with_low_feerate()
  57  
  58          self.log.info("Running test too many replacements...")
  59          self.test_too_many_replacements()
  60  
  61          self.log.info("Running test RPC...")
  62          self.test_rpc()
  63  
  64          self.log.info("Running test prioritised transactions...")
  65          self.test_prioritised_transactions()
  66  
  67          self.log.info("Running test replacement relay fee...")
  68          self.test_replacement_relay_fee()
  69  
  70          self.log.info("Running test full replace by fee...")
  71          self.test_fullrbf()
  72  
  73          self.log.info("Running test incremental relay feerates...")
  74          self.test_incremental_relay_feerates()
  75  
  76          self.log.info("Passed")
  77  
  78      def make_utxo(self, node, amount, *, confirmed=True, scriptPubKey=None):
  79          """Create a txout with a given amount and scriptPubKey
  80  
  81          confirmed - txout created will be confirmed in the blockchain;
  82                      unconfirmed otherwise.
  83          """
  84          tx = self.wallet.send_to(from_node=node, scriptPubKey=scriptPubKey or self.wallet.get_output_script(), amount=amount)
  85  
  86          if confirmed:
  87              mempool_size = len(node.getrawmempool())
  88              while mempool_size > 0:
  89                  self.generate(node, 1)
  90                  new_size = len(node.getrawmempool())
  91                  # Error out if we have something stuck in the mempool, as this
  92                  # would likely be a bug.
  93                  assert new_size < mempool_size
  94                  mempool_size = new_size
  95  
  96          return self.wallet.get_utxo(txid=tx["txid"], vout=tx["sent_vout"])
  97  
  98      def test_simple_doublespend(self):
  99          """Simple doublespend"""
 100          # we use MiniWallet to create a transaction template with inputs correctly set,
 101          # and modify the output (amount, scriptPubKey) according to our needs
 102          tx = self.wallet.create_self_transfer(fee_rate=Decimal("0.003"))["tx"]
 103          tx1a_txid = self.nodes[0].sendrawtransaction(tx.serialize().hex())
 104  
 105          # Should fail because we haven't changed the fee
 106          tx.vout[0].scriptPubKey[-1] ^= 1
 107          tx_hex = tx.serialize().hex()
 108  
 109          # This will raise an exception due to insufficient fee
 110          reject_reason = "insufficient fee"
 111          reject_details = f"{reject_reason}, rejecting replacement {tx.txid_hex}, not enough additional fees to relay; 0.00 < 0.00000011"
 112          res = self.nodes[0].testmempoolaccept(rawtxs=[tx_hex])[0]
 113          assert_equal(res["reject-reason"], reject_reason)
 114          assert_equal(res["reject-details"], reject_details)
 115          assert_raises_rpc_error(-26, f"{reject_details}", self.nodes[0].sendrawtransaction, tx_hex, 0)
 116  
 117  
 118          # Extra 0.1 BTC fee
 119          tx.vout[0].nValue -= int(0.1 * COIN)
 120          tx1b_hex = tx.serialize().hex()
 121          # Works when enabled
 122          tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
 123  
 124          mempool = self.nodes[0].getrawmempool()
 125  
 126          assert tx1a_txid not in mempool
 127          assert tx1b_txid in mempool
 128  
 129          assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid))
 130  
 131      def test_doublespend_chain(self):
 132          """Doublespend of a long chain"""
 133  
 134          initial_nValue = 5 * COIN
 135          tx0_outpoint = self.make_utxo(self.nodes[0], initial_nValue)
 136  
 137          prevout = tx0_outpoint
 138          remaining_value = initial_nValue
 139          chain_txids = []
 140          for _ in range(DEFAULT_CLUSTER_LIMIT):
 141              if remaining_value <= 1 * COIN:
 142                  break
 143              remaining_value -= int(0.1 * COIN)
 144              prevout = self.wallet.send_self_transfer(
 145                  from_node=self.nodes[0],
 146                  utxo_to_spend=prevout,
 147                  sequence=0,
 148                  fee=Decimal("0.1"),
 149              )["new_utxo"]
 150              chain_txids.append(prevout["txid"])
 151  
 152          # Whether the double-spend is allowed is evaluated by including all
 153          # child fees - 4 BTC - so this attempt is rejected.
 154          dbl_tx = self.wallet.create_self_transfer(
 155              utxo_to_spend=tx0_outpoint,
 156              sequence=0,
 157              fee=Decimal("3"),
 158          )["tx"]
 159          dbl_tx_hex = dbl_tx.serialize().hex()
 160  
 161          # This will raise an exception due to insufficient fee
 162          reject_reason = "insufficient fee"
 163          reject_details = f"{reject_reason}, rejecting replacement {dbl_tx.txid_hex}, less fees than conflicting txs; 3.00 < 4.00"
 164          res = self.nodes[0].testmempoolaccept(rawtxs=[dbl_tx_hex])[0]
 165          assert_equal(res["reject-reason"], reject_reason)
 166          assert_equal(res["reject-details"], reject_details)
 167          assert_raises_rpc_error(-26, f"{reject_details}", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
 168  
 169  
 170  
 171          # Accepted with sufficient fee
 172          dbl_tx.vout[0].nValue = int(0.1 * COIN)
 173          dbl_tx_hex = dbl_tx.serialize().hex()
 174          self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
 175  
 176          mempool = self.nodes[0].getrawmempool()
 177          for doublespent_txid in chain_txids:
 178              assert doublespent_txid not in mempool
 179  
 180      def test_doublespend_tree(self):
 181          """Doublespend of a big tree of transactions"""
 182  
 183          initial_nValue = 5 * COIN
 184          tx0_outpoint = self.make_utxo(self.nodes[0], initial_nValue)
 185  
 186          def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.00001 * COIN, _total_txs=None):
 187              if _total_txs is None:
 188                  _total_txs = [0]
 189              if _total_txs[0] >= max_txs:
 190                  return
 191  
 192              txout_value = (initial_value - fee) // tree_width
 193              if txout_value < fee:
 194                  return
 195  
 196              tx = self.wallet.send_self_transfer_multi(
 197                  utxos_to_spend=[prevout],
 198                  from_node=self.nodes[0],
 199                  sequence=0,
 200                  num_outputs=tree_width,
 201                  amount_per_output=txout_value,
 202              )
 203  
 204              yield tx["txid"]
 205              _total_txs[0] += 1
 206  
 207              for utxo in tx["new_utxos"]:
 208                  for x in branch(utxo, txout_value,
 209                                    max_txs,
 210                                    tree_width=tree_width, fee=fee,
 211                                    _total_txs=_total_txs):
 212                      yield x
 213  
 214          fee = int(0.00001 * COIN)
 215          n = DEFAULT_CLUSTER_LIMIT
 216          tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
 217          assert_equal(len(tree_txs), n)
 218  
 219          # Attempt double-spend, will fail because too little fee paid
 220          dbl_tx_hex = self.wallet.create_self_transfer(
 221              utxo_to_spend=tx0_outpoint,
 222              sequence=0,
 223              fee=(Decimal(fee) / COIN) * n,
 224          )["hex"]
 225          # This will raise an exception due to insufficient fee
 226          assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
 227  
 228          # 0.1 BTC fee is enough
 229          dbl_tx_hex = self.wallet.create_self_transfer(
 230              utxo_to_spend=tx0_outpoint,
 231              sequence=0,
 232              fee=(Decimal(fee) / COIN) * n + Decimal("0.1"),
 233          )["hex"]
 234          self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
 235  
 236          mempool = self.nodes[0].getrawmempool()
 237  
 238          for txid in tree_txs:
 239              assert txid not in mempool
 240  
 241      def test_replacement_feeperkb(self):
 242          """Replacement requires fee-per-KB to be higher"""
 243          tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
 244  
 245          self.wallet.send_self_transfer(
 246              from_node=self.nodes[0],
 247              utxo_to_spend=tx0_outpoint,
 248              sequence=0,
 249              fee=Decimal("0.1"),
 250          )
 251  
 252          # Higher fee, but the fee per KB is much lower, so the replacement is
 253          # rejected.
 254          tx1b_hex = self.wallet.create_self_transfer_multi(
 255              utxos_to_spend=[tx0_outpoint],
 256              sequence=0,
 257              num_outputs=100,
 258              amount_per_output=1000,
 259          )["hex"]
 260  
 261          # This will raise an exception due to insufficient fee
 262          assert_raises_rpc_error(-26, "does not improve feerate diagram", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
 263  
 264      def test_spends_of_conflicting_outputs(self):
 265          """Replacements that spend conflicting tx outputs are rejected"""
 266          utxo1 = self.make_utxo(self.nodes[0], int(1.2 * COIN))
 267          utxo2 = self.make_utxo(self.nodes[0], 3 * COIN)
 268  
 269          tx1a = self.wallet.send_self_transfer(
 270              from_node=self.nodes[0],
 271              utxo_to_spend=utxo1,
 272              sequence=0,
 273              fee=Decimal("0.1"),
 274          )
 275          tx1a_utxo = tx1a["new_utxo"]
 276  
 277          # Direct spend an output of the transaction we're replacing.
 278          tx2 = self.wallet.create_self_transfer_multi(
 279              utxos_to_spend=[utxo1, utxo2, tx1a_utxo],
 280              sequence=0,
 281              amount_per_output=int(COIN * tx1a_utxo["value"]),
 282          )["tx"]
 283          tx2_hex = tx2.serialize().hex()
 284  
 285          # This will raise an exception
 286          reject_reason = "bad-txns-spends-conflicting-tx"
 287          reject_details = f"{reject_reason}, {tx2.txid_hex} spends conflicting transaction {tx1a['tx'].txid_hex}"
 288          res = self.nodes[0].testmempoolaccept(rawtxs=[tx2_hex])[0]
 289          assert_equal(res["reject-reason"], reject_reason)
 290          assert_equal(res["reject-details"], reject_details)
 291          assert_raises_rpc_error(-26, f"{reject_details}", self.nodes[0].sendrawtransaction, tx2_hex, 0)
 292  
 293  
 294          # Spend tx1a's output to test the indirect case.
 295          tx1b_utxo = self.wallet.send_self_transfer(
 296              from_node=self.nodes[0],
 297              utxo_to_spend=tx1a_utxo,
 298              sequence=0,
 299              fee=Decimal("0.1"),
 300          )["new_utxo"]
 301  
 302          tx2_hex = self.wallet.create_self_transfer_multi(
 303              utxos_to_spend=[utxo1, utxo2, tx1b_utxo],
 304              sequence=0,
 305              amount_per_output=int(COIN * tx1a_utxo["value"]),
 306          )["hex"]
 307  
 308          # This will raise an exception
 309          assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, 0)
 310  
 311      def test_new_unconfirmed_inputs(self):
 312          """Replacements that add new unconfirmed inputs may be accepted"""
 313          confirmed_utxo = self.make_utxo(self.nodes[0], int(1.1 * COIN))
 314          unconfirmed_utxo = self.make_utxo(self.nodes[0], int(0.2 * COIN), confirmed=False)
 315  
 316          self.wallet.send_self_transfer(
 317              from_node=self.nodes[0],
 318              utxo_to_spend=confirmed_utxo,
 319              sequence=0,
 320              fee=Decimal("0.1"),
 321          )
 322  
 323          tx2 = self.wallet.create_self_transfer_multi(
 324              utxos_to_spend=[confirmed_utxo, unconfirmed_utxo],
 325              sequence=0,
 326              amount_per_output=1 * COIN,
 327          )["tx"]
 328          tx2_hex = tx2.serialize().hex()
 329  
 330          # This will not raise an exception
 331          tx2_id = self.nodes[0].sendrawtransaction(tx2_hex, 0)
 332          assert tx2_id in self.nodes[0].getrawmempool()
 333  
 334      def test_new_unconfirmed_input_with_low_feerate(self):
 335          """Replacements that add new unconfirmed inputs are allowed, but must pass the feerate diagram check"""
 336          confirmed_utxos = [self.make_utxo(self.nodes[0], int(1.1 * COIN)) for _ in range(3)]
 337          large_low_feerate = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxos[0], target_vsize=10000, fee=Decimal("0.00001000"))
 338          self.nodes[0].sendrawtransaction(large_low_feerate['hex'])
 339          unconfirmed_utxo = large_low_feerate['new_utxo']
 340  
 341          # These two transactions are approximately the same size. The replacement tx pays twice the fee.
 342          tx_to_replace = self.wallet.create_self_transfer_multi(utxos_to_spend=[confirmed_utxos[1], confirmed_utxos[2]], fee_per_output=2000)
 343          tx_replacement = self.wallet.create_self_transfer_multi(utxos_to_spend=[confirmed_utxos[1], unconfirmed_utxo], fee_per_output=4000)
 344          assert_greater_than(tx_replacement['fee']*tx_to_replace['tx'].get_vsize(), tx_to_replace['fee']*tx_replacement['tx'].get_vsize())
 345  
 346          self.nodes[0].sendrawtransaction(tx_to_replace['hex'])
 347          assert_raises_rpc_error(-26, "insufficient feerate: does not improve feerate diagram", self.nodes[0].sendrawtransaction, tx_replacement['hex'])
 348  
 349  
 350      def test_too_many_replacements(self):
 351          """Replacements that conflict with too many clusters are rejected"""
 352          # Try directly replacing transactions in more than MAX_REPLACEMENT_LIMIT
 353          # distinct clusters
 354  
 355          # Start by creating a single transaction with many outputs
 356          initial_nValue = 10 * COIN
 357          utxo = self.make_utxo(self.nodes[0], initial_nValue)
 358          fee = int(0.0001 * COIN)
 359          split_value = int((initial_nValue - fee) / (MAX_REPLACEMENT_LIMIT + 1))
 360  
 361          splitting_tx_utxos = self.wallet.send_self_transfer_multi(
 362              from_node=self.nodes[0],
 363              utxos_to_spend=[utxo],
 364              sequence=0,
 365              num_outputs=MAX_REPLACEMENT_LIMIT + 1,
 366              amount_per_output=split_value,
 367          )["new_utxos"]
 368  
 369          self.generate(self.nodes[0], 1)
 370  
 371          # Now spend each of those outputs individually
 372          for utxo in splitting_tx_utxos:
 373              self.wallet.send_self_transfer(
 374                  from_node=self.nodes[0],
 375                  utxo_to_spend=utxo,
 376                  sequence=0,
 377                  fee=Decimal(fee) / COIN,
 378              )
 379  
 380          # Now create doublespend of the whole lot; should fail.
 381          # Need a big enough fee to cover all spending transactions and have
 382          # a higher fee rate
 383          double_spend_value = (split_value - 100 * fee) * (MAX_REPLACEMENT_LIMIT + 1)
 384          double_tx = self.wallet.create_self_transfer_multi(
 385              utxos_to_spend=splitting_tx_utxos,
 386              sequence=0,
 387              amount_per_output=double_spend_value,
 388          )["tx"]
 389          double_tx_hex = double_tx.serialize().hex()
 390  
 391          # This will raise an exception
 392          reject_reason = "too many potential replacements"
 393          reject_details = f"{reject_reason}, rejecting replacement {double_tx.txid_hex}; too many conflicting clusters ({MAX_REPLACEMENT_LIMIT + 1} > {MAX_REPLACEMENT_LIMIT})"
 394          res = self.nodes[0].testmempoolaccept(rawtxs=[double_tx_hex])[0]
 395          assert_equal(res["reject-reason"], reject_reason)
 396          assert_equal(res["reject-details"], reject_details)
 397          assert_raises_rpc_error(-26, f"{reject_details}", self.nodes[0].sendrawtransaction, double_tx_hex, 0)
 398  
 399  
 400          # If we remove an input, it should pass
 401          double_tx.vin.pop()
 402          double_tx_hex = double_tx.serialize().hex()
 403          self.nodes[0].sendrawtransaction(double_tx_hex, 0)
 404  
 405      def test_prioritised_transactions(self):
 406          # Ensure that fee deltas used via prioritisetransaction are
 407          # correctly used by replacement logic
 408  
 409          # 1. Check that feeperkb uses modified fees
 410          tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
 411  
 412          tx1a_txid = self.wallet.send_self_transfer(
 413              from_node=self.nodes[0],
 414              utxo_to_spend=tx0_outpoint,
 415              sequence=0,
 416              fee=Decimal("0.1"),
 417          )["txid"]
 418  
 419          # Higher fee, but the actual fee per KB is much lower.
 420          tx1b_hex = self.wallet.create_self_transfer_multi(
 421              utxos_to_spend=[tx0_outpoint],
 422              sequence=0,
 423              num_outputs=100,
 424              amount_per_output=int(0.00001 * COIN),
 425          )["hex"]
 426  
 427          # Verify tx1b cannot replace tx1a.
 428          assert_raises_rpc_error(-26, "does not improve feerate diagram", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
 429  
 430          # Use prioritisetransaction to set tx1a's fee to 0.
 431          self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1 * COIN))
 432  
 433          # Now tx1b should be able to replace tx1a
 434          tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
 435  
 436          assert tx1b_txid in self.nodes[0].getrawmempool()
 437  
 438          # 2. Check that absolute fee checks use modified fee.
 439          tx1_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
 440  
 441          # tx2a
 442          self.wallet.send_self_transfer(
 443              from_node=self.nodes[0],
 444              utxo_to_spend=tx1_outpoint,
 445              sequence=0,
 446              fee=Decimal("0.1"),
 447          )
 448  
 449          # Lower fee, but we'll prioritise it
 450          tx2b = self.wallet.create_self_transfer(
 451              utxo_to_spend=tx1_outpoint,
 452              sequence=0,
 453              fee=Decimal("0.09"),
 454          )
 455  
 456          # Verify tx2b cannot replace tx2a.
 457          assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b["hex"], 0)
 458  
 459          # Now prioritise tx2b to have a higher modified fee
 460          self.nodes[0].prioritisetransaction(txid=tx2b["txid"], fee_delta=int(0.1 * COIN))
 461  
 462          # tx2b should now be accepted
 463          tx2b_txid = self.nodes[0].sendrawtransaction(tx2b["hex"], 0)
 464  
 465          assert tx2b_txid in self.nodes[0].getrawmempool()
 466  
 467      def test_rpc(self):
 468          us0 = self.wallet.get_utxo()
 469          ins = [us0]
 470          outs = {ADDRESS_BCRT1_UNSPENDABLE: Decimal(1.0000000)}
 471          rawtx0 = self.nodes[0].createrawtransaction(ins, outs, 0, True)
 472          rawtx1 = self.nodes[0].createrawtransaction(ins, outs, 0, False)
 473          json0 = self.nodes[0].decoderawtransaction(rawtx0)
 474          json1 = self.nodes[0].decoderawtransaction(rawtx1)
 475          assert_equal(json0["vin"][0]["sequence"], 4294967293)
 476          assert_equal(json1["vin"][0]["sequence"], 4294967295)
 477  
 478          if self.is_wallet_compiled():
 479              self.init_wallet(node=0)
 480              rawtx2 = self.nodes[0].createrawtransaction([], outs)
 481              frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
 482              frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})
 483  
 484              json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
 485              json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
 486              assert_equal(json0["vin"][0]["sequence"], 4294967293)
 487              assert_equal(json1["vin"][0]["sequence"], 4294967294)
 488  
 489      def test_replacement_relay_fee(self):
 490          tx = self.wallet.send_self_transfer(from_node=self.nodes[0])['tx']
 491  
 492          # Higher fee, higher feerate, different txid, but the replacement does not provide a relay
 493          # fee conforming to node's `incrementalrelayfee` policy of 100 sat per KB.
 494          assert_equal(self.nodes[0].getmempoolinfo()["incrementalrelayfee"], Decimal("0.000001"))
 495          tx.vout[0].nValue -= 1
 496          assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex())
 497  
 498      def test_incremental_relay_feerates(self):
 499          self.log.info("Test that incremental relay fee is applied correctly in RBF for various settings...")
 500          node = self.nodes[0]
 501          for incremental_setting in (0, 5, 10, 50, 100, 234, 1000, 5000, 21000):
 502              incremental_setting_decimal = incremental_setting / Decimal(COIN)
 503              self.log.info(f"-> Test -incrementalrelayfee={incremental_setting:.8f}sat/kvB...")
 504              self.restart_node(0, extra_args=[f"-incrementalrelayfee={incremental_setting_decimal:.8f}", "-persistmempool=0"])
 505  
 506              # When incremental relay feerate is higher than min relay feerate, min relay feerate is automatically increased.
 507              min_relay_feerate = node.getmempoolinfo()["minrelaytxfee"]
 508              assert_greater_than_or_equal(min_relay_feerate, incremental_setting_decimal)
 509  
 510              low_feerate = min_relay_feerate * 2
 511              confirmed_utxo = self.wallet.get_utxo(confirmed_only=True)
 512              # Use different versions to avoid creating an identical transaction when failed_replacement_tx is created.
 513              # 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.
 514              replacee_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee_rate=low_feerate, version=3, target_vsize=200)
 515              node.sendrawtransaction(replacee_tx['hex'])
 516  
 517              replacement_placeholder_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, target_vsize=200)
 518              replacement_expected_size = replacement_placeholder_tx['tx'].get_vsize()
 519              replacement_required_fee = get_fee(replacement_expected_size, incremental_setting_decimal) + replacee_tx['fee']
 520  
 521              # Show that replacement fails when paying 1 satoshi shy of the required fee
 522              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)
 523              assert_raises_rpc_error(-26, "insufficient fee", node.sendrawtransaction, failed_replacement_tx['hex'])
 524              replacement_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee=replacement_required_fee, version=2, target_vsize=200)
 525  
 526              if incremental_setting == 0:
 527                  # When incremental relay feerate is 0, additional fees are not required, but higher feerate is still required.
 528                  assert_raises_rpc_error(-26, "insufficient fee", node.sendrawtransaction, replacement_tx['hex'])
 529                  replacement_tx_smaller = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee=replacement_required_fee, version=2, target_vsize=199)
 530                  node.sendrawtransaction(replacement_tx_smaller['hex'])
 531              else:
 532                  node.sendrawtransaction(replacement_tx['hex'])
 533  
 534      def test_fullrbf(self):
 535          # BIP125 signaling is not respected
 536  
 537          confirmed_utxo = self.make_utxo(self.nodes[0], int(2 * COIN))
 538          assert self.nodes[0].getmempoolinfo()["fullrbf"]
 539  
 540          # Create an explicitly opt-out BIP125 transaction, which will be ignored
 541          optout_tx = self.wallet.send_self_transfer(
 542              from_node=self.nodes[0],
 543              utxo_to_spend=confirmed_utxo,
 544              sequence=MAX_BIP125_RBF_SEQUENCE + 1,
 545              fee_rate=Decimal('0.01'),
 546          )
 547          assert_equal(False, self.nodes[0].getmempoolentry(optout_tx['txid'])['bip125-replaceable'])
 548  
 549          conflicting_tx = self.wallet.create_self_transfer(
 550                  utxo_to_spend=confirmed_utxo,
 551                  fee_rate=Decimal('0.02'),
 552          )
 553  
 554          # Send the replacement transaction, conflicting with the optout_tx.
 555          self.nodes[0].sendrawtransaction(conflicting_tx['hex'], 0)
 556  
 557          # Optout_tx is not anymore in the mempool.
 558          assert optout_tx['txid'] not in self.nodes[0].getrawmempool()
 559          assert conflicting_tx['txid'] in self.nodes[0].getrawmempool()
 560  
 561  if __name__ == '__main__':
 562      ReplaceByFeeTest(__file__).main()
 563