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