1#!/usr/bin/env python3 2# Copyright (c) 2014-2016 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 6# 7# Test replace by fee code 8# 9 10from test_framework.test_framework import BitcoinTestFramework 11from test_framework.util import * 12from test_framework.script import * 13from test_framework.mininode import * 14 15MAX_REPLACEMENT_LIMIT = 100 16 17def txToHex(tx): 18 return bytes_to_hex_str(tx.serialize()) 19 20def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): 21 """Create a txout with a given amount and scriptPubKey 22 23 Mines coins as needed. 24 25 confirmed - txouts created will be confirmed in the blockchain; 26 unconfirmed otherwise. 27 """ 28 fee = 1*COIN 29 while node.getbalance() < satoshi_round((amount + fee)/COIN): 30 node.generate(100) 31 #print (node.getbalance(), amount, fee) 32 33 new_addr = node.getnewaddress() 34 #print new_addr 35 txid = node.sendtoaddress(new_addr, satoshi_round((amount+fee)/COIN)) 36 tx1 = node.getrawtransaction(txid, 1) 37 txid = int(txid, 16) 38 i = None 39 40 for i, txout in enumerate(tx1['vout']): 41 #print i, txout['scriptPubKey']['addresses'] 42 if txout['scriptPubKey']['addresses'] == [new_addr]: 43 #print i 44 break 45 assert i is not None 46 47 tx2 = CTransaction() 48 tx2.vin = [CTxIn(COutPoint(txid, i))] 49 tx2.vout = [CTxOut(amount, scriptPubKey)] 50 tx2.rehash() 51 52 signed_tx = node.signrawtransaction(txToHex(tx2)) 53 54 txid = node.sendrawtransaction(signed_tx['hex'], True) 55 56 # If requested, ensure txouts are confirmed. 57 if confirmed: 58 mempool_size = len(node.getrawmempool()) 59 while mempool_size > 0: 60 node.generate(1) 61 new_size = len(node.getrawmempool()) 62 # Error out if we have something stuck in the mempool, as this 63 # would likely be a bug. 64 assert(new_size < mempool_size) 65 mempool_size = new_size 66 67 return COutPoint(int(txid, 16), 0) 68 69class ReplaceByFeeTest(BitcoinTestFramework): 70 71 def __init__(self): 72 super().__init__() 73 self.num_nodes = 1 74 self.setup_clean_chain = False 75 76 def setup_network(self): 77 self.nodes = [] 78 self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-debug", 79 "-relaypriority=0", "-whitelist=127.0.0.1", 80 "-limitancestorcount=50", 81 "-limitancestorsize=101", 82 "-limitdescendantcount=200", 83 "-limitdescendantsize=101" 84 ])) 85 self.is_network_split = False 86 87 def run_test(self): 88 make_utxo(self.nodes[0], 1*COIN) 89 90 print("Running test simple doublespend...") 91 self.test_simple_doublespend() 92 93 print("Running test doublespend chain...") 94 self.test_doublespend_chain() 95 96 print("Running test doublespend tree...") 97 self.test_doublespend_tree() 98 99 print("Running test replacement feeperkb...") 100 self.test_replacement_feeperkb() 101 102 print("Running test spends of conflicting outputs...") 103 self.test_spends_of_conflicting_outputs() 104 105 print("Running test new unconfirmed inputs...") 106 self.test_new_unconfirmed_inputs() 107 108 print("Running test too many replacements...") 109 self.test_too_many_replacements() 110 111 print("Running test opt-in...") 112 self.test_opt_in() 113 114 print("Running test prioritised transactions...") 115 self.test_prioritised_transactions() 116 117 print("Passed\n") 118 119 def test_simple_doublespend(self): 120 """Simple doublespend""" 121 tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) 122 123 tx1a = CTransaction() 124 tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] 125 tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] 126 tx1a_hex = txToHex(tx1a) 127 tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) 128 129 # Should fail because we haven't changed the fee 130 tx1b = CTransaction() 131 tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] 132 tx1b.vout = [CTxOut(1*COIN, CScript([b'b']))] 133 tx1b_hex = txToHex(tx1b) 134 135 try: 136 tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) 137 except JSONRPCException as exp: 138 assert_equal(exp.error['code'], -26) # insufficient fee 139 else: 140 assert(False) 141 142 # Extra 0.1 BTC fee 143 tx1b = CTransaction() 144 tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] 145 tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] 146 tx1b_hex = txToHex(tx1b) 147 tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) 148 149 mempool = self.nodes[0].getrawmempool() 150 151 assert (tx1a_txid not in mempool) 152 assert (tx1b_txid in mempool) 153 154 assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid)) 155 156 def test_doublespend_chain(self): 157 """Doublespend of a long chain""" 158 159 initial_nValue = 50*COIN 160 tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) 161 162 prevout = tx0_outpoint 163 remaining_value = initial_nValue 164 chain_txids = [] 165 while remaining_value > 10*COIN: 166 remaining_value -= 1*COIN 167 tx = CTransaction() 168 tx.vin = [CTxIn(prevout, nSequence=0)] 169 tx.vout = [CTxOut(remaining_value, CScript([1]))] 170 tx_hex = txToHex(tx) 171 txid = self.nodes[0].sendrawtransaction(tx_hex, True) 172 chain_txids.append(txid) 173 prevout = COutPoint(int(txid, 16), 0) 174 175 # Whether the double-spend is allowed is evaluated by including all 176 # child fees - 40 BTC - so this attempt is rejected. 177 dbl_tx = CTransaction() 178 dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] 179 dbl_tx.vout = [CTxOut(initial_nValue - 30*COIN, CScript([1]))] 180 dbl_tx_hex = txToHex(dbl_tx) 181 182 try: 183 self.nodes[0].sendrawtransaction(dbl_tx_hex, True) 184 except JSONRPCException as exp: 185 assert_equal(exp.error['code'], -26) # insufficient fee 186 else: 187 assert(False) # transaction mistakenly accepted! 188 189 # Accepted with sufficient fee 190 dbl_tx = CTransaction() 191 dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] 192 dbl_tx.vout = [CTxOut(1*COIN, CScript([1]))] 193 dbl_tx_hex = txToHex(dbl_tx) 194 self.nodes[0].sendrawtransaction(dbl_tx_hex, True) 195 196 mempool = self.nodes[0].getrawmempool() 197 for doublespent_txid in chain_txids: 198 assert(doublespent_txid not in mempool) 199 200 def test_doublespend_tree(self): 201 """Doublespend of a big tree of transactions""" 202 203 initial_nValue = 50*COIN 204 tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) 205 206 def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001*COIN, _total_txs=None): 207 if _total_txs is None: 208 _total_txs = [0] 209 if _total_txs[0] >= max_txs: 210 return 211 212 txout_value = (initial_value - fee) // tree_width 213 if txout_value < fee: 214 return 215 216 vout = [CTxOut(txout_value, CScript([i+1])) 217 for i in range(tree_width)] 218 tx = CTransaction() 219 tx.vin = [CTxIn(prevout, nSequence=0)] 220 tx.vout = vout 221 tx_hex = txToHex(tx) 222 223 assert(len(tx.serialize()) < 100000) 224 txid = self.nodes[0].sendrawtransaction(tx_hex, True) 225 yield tx 226 _total_txs[0] += 1 227 228 txid = int(txid, 16) 229 230 for i, txout in enumerate(tx.vout): 231 for x in branch(COutPoint(txid, i), txout_value, 232 max_txs, 233 tree_width=tree_width, fee=fee, 234 _total_txs=_total_txs): 235 yield x 236 237 fee = int(0.0001*COIN) 238 n = MAX_REPLACEMENT_LIMIT 239 tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee)) 240 assert_equal(len(tree_txs), n) 241 242 # Attempt double-spend, will fail because too little fee paid 243 dbl_tx = CTransaction() 244 dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] 245 dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1]))] 246 dbl_tx_hex = txToHex(dbl_tx) 247 try: 248 self.nodes[0].sendrawtransaction(dbl_tx_hex, True) 249 except JSONRPCException as exp: 250 assert_equal(exp.error['code'], -26) # insufficient fee 251 else: 252 assert(False) 253 254 # 1 BTC fee is enough 255 dbl_tx = CTransaction() 256 dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] 257 dbl_tx.vout = [CTxOut(initial_nValue - fee*n - 1*COIN, CScript([1]))] 258 dbl_tx_hex = txToHex(dbl_tx) 259 self.nodes[0].sendrawtransaction(dbl_tx_hex, True) 260 261 mempool = self.nodes[0].getrawmempool() 262 263 for tx in tree_txs: 264 tx.rehash() 265 assert (tx.hash not in mempool) 266 267 # Try again, but with more total transactions than the "max txs 268 # double-spent at once" anti-DoS limit. 269 for n in (MAX_REPLACEMENT_LIMIT+1, MAX_REPLACEMENT_LIMIT*2): 270 fee = int(0.0001*COIN) 271 tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) 272 tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee)) 273 assert_equal(len(tree_txs), n) 274 275 dbl_tx = CTransaction() 276 dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] 277 dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1]))] 278 dbl_tx_hex = txToHex(dbl_tx) 279 try: 280 self.nodes[0].sendrawtransaction(dbl_tx_hex, True) 281 except JSONRPCException as exp: 282 assert_equal(exp.error['code'], -26) 283 assert_equal("too many potential replacements" in exp.error['message'], True) 284 else: 285 assert(False) 286 287 for tx in tree_txs: 288 tx.rehash() 289 self.nodes[0].getrawtransaction(tx.hash) 290 291 def test_replacement_feeperkb(self): 292 """Replacement requires fee-per-KB to be higher""" 293 tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) 294 295 tx1a = CTransaction() 296 tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] 297 tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] 298 tx1a_hex = txToHex(tx1a) 299 tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) 300 301 # Higher fee, but the fee per KB is much lower, so the replacement is 302 # rejected. 303 tx1b = CTransaction() 304 tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] 305 tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*999000]))] 306 tx1b_hex = txToHex(tx1b) 307 308 try: 309 tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) 310 except JSONRPCException as exp: 311 assert_equal(exp.error['code'], -26) # insufficient fee 312 else: 313 assert(False) 314 315 def test_spends_of_conflicting_outputs(self): 316 """Replacements that spend conflicting tx outputs are rejected""" 317 utxo1 = make_utxo(self.nodes[0], int(1.2*COIN)) 318 utxo2 = make_utxo(self.nodes[0], 3*COIN) 319 320 tx1a = CTransaction() 321 tx1a.vin = [CTxIn(utxo1, nSequence=0)] 322 tx1a.vout = [CTxOut(int(1.1*COIN), CScript([b'a']))] 323 tx1a_hex = txToHex(tx1a) 324 tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) 325 326 tx1a_txid = int(tx1a_txid, 16) 327 328 # Direct spend an output of the transaction we're replacing. 329 tx2 = CTransaction() 330 tx2.vin = [CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0)] 331 tx2.vin.append(CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)) 332 tx2.vout = tx1a.vout 333 tx2_hex = txToHex(tx2) 334 335 try: 336 tx2_txid = self.nodes[0].sendrawtransaction(tx2_hex, True) 337 except JSONRPCException as exp: 338 assert_equal(exp.error['code'], -26) 339 else: 340 assert(False) 341 342 # Spend tx1a's output to test the indirect case. 343 tx1b = CTransaction() 344 tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] 345 tx1b.vout = [CTxOut(1*COIN, CScript([b'a']))] 346 tx1b_hex = txToHex(tx1b) 347 tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) 348 tx1b_txid = int(tx1b_txid, 16) 349 350 tx2 = CTransaction() 351 tx2.vin = [CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0), 352 CTxIn(COutPoint(tx1b_txid, 0))] 353 tx2.vout = tx1a.vout 354 tx2_hex = txToHex(tx2) 355 356 try: 357 tx2_txid = self.nodes[0].sendrawtransaction(tx2_hex, True) 358 except JSONRPCException as exp: 359 assert_equal(exp.error['code'], -26) 360 else: 361 assert(False) 362 363 def test_new_unconfirmed_inputs(self): 364 """Replacements that add new unconfirmed inputs are rejected""" 365 confirmed_utxo = make_utxo(self.nodes[0], int(1.1*COIN)) 366 unconfirmed_utxo = make_utxo(self.nodes[0], int(0.1*COIN), False) 367 368 tx1 = CTransaction() 369 tx1.vin = [CTxIn(confirmed_utxo)] 370 tx1.vout = [CTxOut(1*COIN, CScript([b'a']))] 371 tx1_hex = txToHex(tx1) 372 tx1_txid = self.nodes[0].sendrawtransaction(tx1_hex, True) 373 374 tx2 = CTransaction() 375 tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)] 376 tx2.vout = tx1.vout 377 tx2_hex = txToHex(tx2) 378 379 try: 380 tx2_txid = self.nodes[0].sendrawtransaction(tx2_hex, True) 381 except JSONRPCException as exp: 382 assert_equal(exp.error['code'], -26) 383 else: 384 assert(False) 385 386 def test_too_many_replacements(self): 387 """Replacements that evict too many transactions are rejected""" 388 # Try directly replacing more than MAX_REPLACEMENT_LIMIT 389 # transactions 390 391 # Start by creating a single transaction with many outputs 392 initial_nValue = 10*COIN 393 utxo = make_utxo(self.nodes[0], initial_nValue) 394 fee = int(0.0001*COIN) 395 split_value = int((initial_nValue-fee)/(MAX_REPLACEMENT_LIMIT+1)) 396 actual_fee = initial_nValue - split_value*(MAX_REPLACEMENT_LIMIT+1) 397 398 outputs = [] 399 for i in range(MAX_REPLACEMENT_LIMIT+1): 400 outputs.append(CTxOut(split_value, CScript([1]))) 401 402 splitting_tx = CTransaction() 403 splitting_tx.vin = [CTxIn(utxo, nSequence=0)] 404 splitting_tx.vout = outputs 405 splitting_tx_hex = txToHex(splitting_tx) 406 407 txid = self.nodes[0].sendrawtransaction(splitting_tx_hex, True) 408 txid = int(txid, 16) 409 410 # Now spend each of those outputs individually 411 for i in range(MAX_REPLACEMENT_LIMIT+1): 412 tx_i = CTransaction() 413 tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)] 414 tx_i.vout = [CTxOut(split_value-fee, CScript([b'a']))] 415 tx_i_hex = txToHex(tx_i) 416 self.nodes[0].sendrawtransaction(tx_i_hex, True) 417 418 # Now create doublespend of the whole lot; should fail. 419 # Need a big enough fee to cover all spending transactions and have 420 # a higher fee rate 421 double_spend_value = (split_value-100*fee)*(MAX_REPLACEMENT_LIMIT+1) 422 inputs = [] 423 for i in range(MAX_REPLACEMENT_LIMIT+1): 424 inputs.append(CTxIn(COutPoint(txid, i), nSequence=0)) 425 double_tx = CTransaction() 426 double_tx.vin = inputs 427 double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))] 428 double_tx_hex = txToHex(double_tx) 429 430 try: 431 self.nodes[0].sendrawtransaction(double_tx_hex, True) 432 except JSONRPCException as exp: 433 assert_equal(exp.error['code'], -26) 434 assert_equal("too many potential replacements" in exp.error['message'], True) 435 else: 436 assert(False) 437 438 # If we remove an input, it should pass 439 double_tx = CTransaction() 440 double_tx.vin = inputs[0:-1] 441 double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))] 442 double_tx_hex = txToHex(double_tx) 443 self.nodes[0].sendrawtransaction(double_tx_hex, True) 444 445 def test_opt_in(self): 446 """ Replacing should only work if orig tx opted in """ 447 tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) 448 449 # Create a non-opting in transaction 450 tx1a = CTransaction() 451 tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)] 452 tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] 453 tx1a_hex = txToHex(tx1a) 454 tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) 455 456 # Shouldn't be able to double-spend 457 tx1b = CTransaction() 458 tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] 459 tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] 460 tx1b_hex = txToHex(tx1b) 461 462 try: 463 tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) 464 except JSONRPCException as exp: 465 assert_equal(exp.error['code'], -26) 466 else: 467 print(tx1b_txid) 468 assert(False) 469 470 tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) 471 472 # Create a different non-opting in transaction 473 tx2a = CTransaction() 474 tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)] 475 tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] 476 tx2a_hex = txToHex(tx2a) 477 tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) 478 479 # Still shouldn't be able to double-spend 480 tx2b = CTransaction() 481 tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] 482 tx2b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] 483 tx2b_hex = txToHex(tx2b) 484 485 try: 486 tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True) 487 except JSONRPCException as exp: 488 assert_equal(exp.error['code'], -26) 489 else: 490 assert(False) 491 492 # Now create a new transaction that spends from tx1a and tx2a 493 # opt-in on one of the inputs 494 # Transaction should be replaceable on either input 495 496 tx1a_txid = int(tx1a_txid, 16) 497 tx2a_txid = int(tx2a_txid, 16) 498 499 tx3a = CTransaction() 500 tx3a.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0xffffffff), 501 CTxIn(COutPoint(tx2a_txid, 0), nSequence=0xfffffffd)] 502 tx3a.vout = [CTxOut(int(0.9*COIN), CScript([b'c'])), CTxOut(int(0.9*COIN), CScript([b'd']))] 503 tx3a_hex = txToHex(tx3a) 504 505 self.nodes[0].sendrawtransaction(tx3a_hex, True) 506 507 tx3b = CTransaction() 508 tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] 509 tx3b.vout = [CTxOut(int(0.5*COIN), CScript([b'e']))] 510 tx3b_hex = txToHex(tx3b) 511 512 tx3c = CTransaction() 513 tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), nSequence=0)] 514 tx3c.vout = [CTxOut(int(0.5*COIN), CScript([b'f']))] 515 tx3c_hex = txToHex(tx3c) 516 517 self.nodes[0].sendrawtransaction(tx3b_hex, True) 518 # If tx3b was accepted, tx3c won't look like a replacement, 519 # but make sure it is accepted anyway 520 self.nodes[0].sendrawtransaction(tx3c_hex, True) 521 522 def test_prioritised_transactions(self): 523 # Ensure that fee deltas used via prioritisetransaction are 524 # correctly used by replacement logic 525 526 # 1. Check that feeperkb uses modified fees 527 tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) 528 529 tx1a = CTransaction() 530 tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] 531 tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] 532 tx1a_hex = txToHex(tx1a) 533 tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) 534 535 # Higher fee, but the actual fee per KB is much lower. 536 tx1b = CTransaction() 537 tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] 538 tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*740000]))] 539 tx1b_hex = txToHex(tx1b) 540 541 # Verify tx1b cannot replace tx1a. 542 try: 543 tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) 544 except JSONRPCException as exp: 545 assert_equal(exp.error['code'], -26) 546 else: 547 assert(False) 548 549 # Use prioritisetransaction to set tx1a's fee to 0. 550 self.nodes[0].prioritisetransaction(tx1a_txid, 0, int(-0.1*COIN)) 551 552 # Now tx1b should be able to replace tx1a 553 tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) 554 555 assert(tx1b_txid in self.nodes[0].getrawmempool()) 556 557 # 2. Check that absolute fee checks use modified fee. 558 tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) 559 560 tx2a = CTransaction() 561 tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)] 562 tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] 563 tx2a_hex = txToHex(tx2a) 564 tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) 565 566 # Lower fee, but we'll prioritise it 567 tx2b = CTransaction() 568 tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] 569 tx2b.vout = [CTxOut(int(1.01*COIN), CScript([b'a']))] 570 tx2b.rehash() 571 tx2b_hex = txToHex(tx2b) 572 573 # Verify tx2b cannot replace tx2a. 574 try: 575 tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True) 576 except JSONRPCException as exp: 577 assert_equal(exp.error['code'], -26) 578 else: 579 assert(False) 580 581 # Now prioritise tx2b to have a higher modified fee 582 self.nodes[0].prioritisetransaction(tx2b.hash, 0, int(0.1*COIN)) 583 584 # tx2b should now be accepted 585 tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True) 586 587 assert(tx2b_txid in self.nodes[0].getrawmempool()) 588 589if __name__ == '__main__': 590 ReplaceByFeeTest().main() 591