wallet_bumpfee.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2016-2022 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 bumpfee RPC. 6 7 Verifies that the bumpfee RPC creates replacement transactions successfully when 8 its preconditions are met, and returns appropriate errors in other cases. 9 10 This module consists of around a dozen individual test cases implemented in the 11 top-level functions named as test_<test_case_description>. The test functions 12 can be disabled or reordered if needed for debugging. If new test cases are 13 added in the future, they should try to follow the same convention and not 14 make assumptions about execution order. 15 """ 16 from decimal import Decimal 17 18 from test_framework.blocktools import ( 19 COINBASE_MATURITY, 20 ) 21 from test_framework.messages import ( 22 MAX_BIP125_RBF_SEQUENCE, 23 MAX_SEQUENCE_NONFINAL, 24 ) 25 from test_framework.test_framework import BitcoinTestFramework 26 from test_framework.util import ( 27 assert_equal, 28 assert_fee_amount, 29 assert_greater_than, 30 assert_raises_rpc_error, 31 get_fee, 32 find_vout_for_address, 33 ) 34 from test_framework.wallet import MiniWallet 35 36 37 WALLET_PASSPHRASE = "test" 38 WALLET_PASSPHRASE_TIMEOUT = 3600 39 40 # Fee rates (sat/vB) 41 INSUFFICIENT = 1 42 ECONOMICAL = 50 43 NORMAL = 100 44 HIGH = 500 45 TOO_HIGH = 100000 46 47 def get_change_address(tx, node): 48 tx_details = node.getrawtransaction(tx, 1) 49 txout_addresses = [txout['scriptPubKey']['address'] for txout in tx_details["vout"]] 50 return [address for address in txout_addresses if node.getaddressinfo(address)["ischange"]] 51 52 class BumpFeeTest(BitcoinTestFramework): 53 def set_test_params(self): 54 self.num_nodes = 2 55 self.setup_clean_chain = True 56 # whitelist peers to speed up tx relay / mempool sync 57 self.noban_tx_relay = True 58 self.extra_args = [[ 59 "-walletrbf={}".format(i), 60 "-mintxfee=0.00002", 61 "-addresstype=bech32", 62 "-deprecatedrpc=settxfee" 63 ] for i in range(self.num_nodes)] 64 65 def skip_test_if_missing_module(self): 66 self.skip_if_no_wallet() 67 68 def clear_mempool(self): 69 # Clear mempool between subtests. The subtests may only depend on chainstate (utxos) 70 self.generate(self.nodes[1], 1) 71 72 def run_test(self): 73 # Encrypt wallet for test_locked_wallet_fails test 74 self.nodes[1].encryptwallet(WALLET_PASSPHRASE) 75 self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) 76 77 peer_node, rbf_node = self.nodes 78 rbf_node_address = rbf_node.getnewaddress() 79 80 # fund rbf node with 10 coins of 0.001 btc (100,000 satoshis) 81 self.log.info("Mining blocks...") 82 self.generate(peer_node, 110) 83 for _ in range(25): 84 peer_node.sendtoaddress(rbf_node_address, 0.001) 85 self.sync_all() 86 self.generate(peer_node, 1) 87 assert_equal(rbf_node.getbalance(), Decimal("0.025")) 88 89 self.log.info("Running tests") 90 dest_address = peer_node.getnewaddress() 91 for mode in ["default", "fee_rate", "new_outputs"]: 92 test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address) 93 self.test_invalid_parameters(rbf_node, peer_node, dest_address) 94 test_segwit_bumpfee_succeeds(self, rbf_node, dest_address) 95 test_nonrbf_bumpfee_succeeds(self, peer_node, dest_address) 96 test_notmine_bumpfee(self, rbf_node, peer_node, dest_address) 97 test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address) 98 test_bumpfee_with_abandoned_descendant_succeeds(self, rbf_node, rbf_node_address, dest_address) 99 test_dust_to_fee(self, rbf_node, dest_address) 100 test_watchonly_psbt(self, peer_node, rbf_node, dest_address) 101 test_rebumping(self, rbf_node, dest_address) 102 test_rebumping_not_replaceable(self, rbf_node, dest_address) 103 test_bumpfee_already_spent(self, rbf_node, dest_address) 104 test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address) 105 test_bumpfee_metadata(self, rbf_node, dest_address) 106 test_locked_wallet_fails(self, rbf_node, dest_address) 107 test_change_script_match(self, rbf_node, dest_address) 108 test_settxfee(self, rbf_node, dest_address) 109 test_maxtxfee_fails(self, rbf_node, dest_address) 110 # These tests wipe out a number of utxos that are expected in other tests 111 test_small_output_with_feerate_succeeds(self, rbf_node, dest_address) 112 test_no_more_inputs_fails(self, rbf_node, dest_address) 113 self.test_bump_back_to_yourself() 114 self.test_provided_change_pos(rbf_node) 115 self.test_single_output() 116 117 # Context independent tests 118 test_feerate_checks_replaced_outputs(self, rbf_node, peer_node) 119 test_bumpfee_with_feerate_ignores_walletincrementalrelayfee(self, rbf_node, peer_node) 120 121 def test_invalid_parameters(self, rbf_node, peer_node, dest_address): 122 self.log.info('Test invalid parameters') 123 rbfid = spend_one_input(rbf_node, dest_address) 124 self.sync_mempools((rbf_node, peer_node)) 125 assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool() 126 127 for key in ["totalFee", "feeRate"]: 128 assert_raises_rpc_error(-3, "Unexpected key {}".format(key), rbf_node.bumpfee, rbfid, {key: NORMAL}) 129 130 # Bumping to just above minrelay should fail to increase the total fee enough. 131 assert_raises_rpc_error(-8, "Insufficient total fee 0.00000141", rbf_node.bumpfee, rbfid, fee_rate=INSUFFICIENT) 132 133 self.log.info("Test invalid fee rate settings") 134 assert_raises_rpc_error(-4, "Specified or calculated fee 0.141 is too high (cannot be higher than -maxtxfee 0.10", 135 rbf_node.bumpfee, rbfid, fee_rate=TOO_HIGH) 136 # Test fee_rate with zero values. 137 msg = "Insufficient total fee 0.00" 138 for zero_value in [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]: 139 assert_raises_rpc_error(-8, msg, rbf_node.bumpfee, rbfid, fee_rate=zero_value) 140 msg = "Invalid amount" 141 # Test fee_rate values that don't pass fixed-point parsing checks. 142 for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]: 143 assert_raises_rpc_error(-3, msg, rbf_node.bumpfee, rbfid, fee_rate=invalid_value) 144 # Test fee_rate values that cannot be represented in sat/vB. 145 for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]: 146 assert_raises_rpc_error(-3, msg, rbf_node.bumpfee, rbfid, fee_rate=invalid_value) 147 # Test fee_rate out of range (negative number). 148 assert_raises_rpc_error(-3, "Amount out of range", rbf_node.bumpfee, rbfid, fee_rate=-1) 149 # Test type error. 150 for value in [{"foo": "bar"}, True]: 151 assert_raises_rpc_error(-3, "Amount is not a number or string", rbf_node.bumpfee, rbfid, fee_rate=value) 152 153 self.log.info("Test explicit fee rate raises RPC error if both fee_rate and conf_target are passed") 154 assert_raises_rpc_error(-8, "Cannot specify both conf_target and fee_rate. Please provide either a confirmation " 155 "target in blocks for automatic fee estimation, or an explicit fee rate.", 156 rbf_node.bumpfee, rbfid, conf_target=NORMAL, fee_rate=NORMAL) 157 158 self.log.info("Test explicit fee rate raises RPC error if both fee_rate and estimate_mode are passed") 159 assert_raises_rpc_error(-8, "Cannot specify both estimate_mode and fee_rate", 160 rbf_node.bumpfee, rbfid, estimate_mode="economical", fee_rate=NORMAL) 161 162 self.log.info("Test invalid conf_target settings") 163 assert_raises_rpc_error(-8, "confTarget and conf_target options should not both be set", 164 rbf_node.bumpfee, rbfid, {"confTarget": 123, "conf_target": 456}) 165 166 self.log.info("Test invalid estimate_mode settings") 167 for k, v in {"number": 42, "object": {"foo": "bar"}}.items(): 168 assert_raises_rpc_error(-3, f"JSON value of type {k} for field estimate_mode is not of expected type string", 169 rbf_node.bumpfee, rbfid, estimate_mode=v) 170 for mode in ["foo", Decimal("3.1415"), "sat/B", "BTC/kB"]: 171 assert_raises_rpc_error(-8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"', 172 rbf_node.bumpfee, rbfid, estimate_mode=mode) 173 174 self.log.info("Test invalid outputs values") 175 assert_raises_rpc_error(-8, "Invalid parameter, output argument cannot be an empty array", 176 rbf_node.bumpfee, rbfid, {"outputs": []}) 177 assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: " + dest_address, 178 rbf_node.bumpfee, rbfid, {"outputs": [{dest_address: 0.1}, {dest_address: 0.2}]}) 179 assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", 180 rbf_node.bumpfee, rbfid, {"outputs": [{"data": "deadbeef"}, {"data": "deadbeef"}]}) 181 182 self.log.info("Test original_change_index option") 183 assert_raises_rpc_error(-1, "JSON integer out of range", rbf_node.bumpfee, rbfid, {"original_change_index": -1}) 184 assert_raises_rpc_error(-8, "Change position is out of range", rbf_node.bumpfee, rbfid, {"original_change_index": 2}) 185 186 self.log.info("Test outputs and original_change_index cannot both be provided") 187 assert_raises_rpc_error(-8, "The options 'outputs' and 'original_change_index' are incompatible. You can only either specify a new set of outputs, or designate a change output to be recycled.", rbf_node.bumpfee, rbfid, {"original_change_index": 2, "outputs": [{dest_address: 0.1}]}) 188 189 self.clear_mempool() 190 191 def test_bump_back_to_yourself(self): 192 self.log.info("Test that bumpfee can send coins back to yourself") 193 node = self.nodes[1] 194 195 node.createwallet("back_to_yourself") 196 wallet = node.get_wallet_rpc("back_to_yourself") 197 198 # Make 3 UTXOs 199 addr = wallet.getnewaddress() 200 for _ in range(3): 201 self.nodes[0].sendtoaddress(addr, 5) 202 self.generate(self.nodes[0], 1) 203 204 # Create a tx with two outputs. recipient and change. 205 tx = wallet.send(outputs={wallet.getnewaddress(): 9}, fee_rate=2) 206 tx_info = wallet.gettransaction(txid=tx["txid"], verbose=True) 207 assert_equal(len(tx_info["decoded"]["vout"]), 2) 208 assert_equal(len(tx_info["decoded"]["vin"]), 2) 209 210 # Bump tx, send coins back to change address. 211 change_addr = get_change_address(tx["txid"], wallet)[0] 212 out_amount = 10 213 bumped = wallet.bumpfee(txid=tx["txid"], options={"fee_rate": 20, "outputs": [{change_addr: out_amount}]}) 214 bumped_tx = wallet.gettransaction(txid=bumped["txid"], verbose=True) 215 assert_equal(len(bumped_tx["decoded"]["vout"]), 1) 216 assert_equal(len(bumped_tx["decoded"]["vin"]), 2) 217 assert_equal(bumped_tx["decoded"]["vout"][0]["value"] + bumped["fee"], out_amount) 218 219 # Bump tx again, now test send fewer coins back to change address. 220 out_amount = 6 221 bumped = wallet.bumpfee(txid=bumped["txid"], options={"fee_rate": 40, "outputs": [{change_addr: out_amount}]}) 222 bumped_tx = wallet.gettransaction(txid=bumped["txid"], verbose=True) 223 assert_equal(len(bumped_tx["decoded"]["vout"]), 2) 224 assert_equal(len(bumped_tx["decoded"]["vin"]), 2) 225 assert any(txout['value'] == out_amount - bumped["fee"] and txout['scriptPubKey']['address'] == change_addr for txout in bumped_tx['decoded']['vout']) 226 # Check that total out amount is still equal to the previously bumped tx 227 assert_equal(bumped_tx["decoded"]["vout"][0]["value"] + bumped_tx["decoded"]["vout"][1]["value"] + bumped["fee"], 10) 228 229 # Bump tx again, send more coins back to change address. The process will add another input to cover the target. 230 out_amount = 12 231 bumped = wallet.bumpfee(txid=bumped["txid"], options={"fee_rate": 80, "outputs": [{change_addr: out_amount}]}) 232 bumped_tx = wallet.gettransaction(txid=bumped["txid"], verbose=True) 233 assert_equal(len(bumped_tx["decoded"]["vout"]), 2) 234 assert_equal(len(bumped_tx["decoded"]["vin"]), 3) 235 assert any(txout['value'] == out_amount - bumped["fee"] and txout['scriptPubKey']['address'] == change_addr for txout in bumped_tx['decoded']['vout']) 236 assert_equal(bumped_tx["decoded"]["vout"][0]["value"] + bumped_tx["decoded"]["vout"][1]["value"] + bumped["fee"], 15) 237 238 node.unloadwallet("back_to_yourself") 239 240 def test_provided_change_pos(self, rbf_node): 241 self.log.info("Test the original_change_index option") 242 243 change_addr = rbf_node.getnewaddress() 244 dest_addr = rbf_node.getnewaddress() 245 assert_equal(rbf_node.getaddressinfo(change_addr)["ischange"], False) 246 assert_equal(rbf_node.getaddressinfo(dest_addr)["ischange"], False) 247 248 send_res = rbf_node.send(outputs=[{dest_addr: 1}], options={"change_address": change_addr}) 249 assert send_res["complete"] 250 txid = send_res["txid"] 251 252 tx = rbf_node.gettransaction(txid=txid, verbose=True) 253 assert_equal(len(tx["decoded"]["vout"]), 2) 254 255 change_pos = find_vout_for_address(rbf_node, txid, change_addr) 256 change_value = tx["decoded"]["vout"][change_pos]["value"] 257 258 bumped = rbf_node.bumpfee(txid, {"original_change_index": change_pos}) 259 new_txid = bumped["txid"] 260 261 new_tx = rbf_node.gettransaction(txid=new_txid, verbose=True) 262 assert_equal(len(new_tx["decoded"]["vout"]), 2) 263 new_change_pos = find_vout_for_address(rbf_node, new_txid, change_addr) 264 new_change_value = new_tx["decoded"]["vout"][new_change_pos]["value"] 265 266 assert_greater_than(change_value, new_change_value) 267 268 269 def test_single_output(self): 270 self.log.info("Test that single output txs can be bumped") 271 node = self.nodes[1] 272 273 node.createwallet("single_out_rbf") 274 wallet = node.get_wallet_rpc("single_out_rbf") 275 276 addr = wallet.getnewaddress() 277 amount = Decimal("0.001") 278 # Make 2 UTXOs 279 self.nodes[0].sendtoaddress(addr, amount) 280 self.nodes[0].sendtoaddress(addr, amount) 281 self.generate(self.nodes[0], 1) 282 utxos = wallet.listunspent() 283 284 tx = wallet.sendall(recipients=[wallet.getnewaddress()], fee_rate=2, options={"inputs": [utxos[0]]}) 285 286 # Set the only output with a crazy high feerate as change, should fail as the output would be dust 287 assert_raises_rpc_error(-4, "The transaction amount is too small to pay the fee", wallet.bumpfee, txid=tx["txid"], options={"fee_rate": 1100, "original_change_index": 0}) 288 289 # Specify single output as change successfully 290 bumped = wallet.bumpfee(txid=tx["txid"], options={"fee_rate": 10, "original_change_index": 0}) 291 bumped_tx = wallet.gettransaction(txid=bumped["txid"], verbose=True) 292 assert_equal(len(bumped_tx["decoded"]["vout"]), 1) 293 assert_equal(len(bumped_tx["decoded"]["vin"]), 1) 294 assert_equal(bumped_tx["decoded"]["vout"][0]["value"] + bumped["fee"], amount) 295 assert_fee_amount(bumped["fee"], bumped_tx["decoded"]["vsize"], Decimal(10) / Decimal(1e8) * 1000) 296 297 # Bumping without specifying change adds a new input and output 298 bumped = wallet.bumpfee(txid=bumped["txid"], options={"fee_rate": 20}) 299 bumped_tx = wallet.gettransaction(txid=bumped["txid"], verbose=True) 300 assert_equal(len(bumped_tx["decoded"]["vout"]), 2) 301 assert_equal(len(bumped_tx["decoded"]["vin"]), 2) 302 assert_fee_amount(bumped["fee"], bumped_tx["decoded"]["vsize"], Decimal(20) / Decimal(1e8) * 1000) 303 304 wallet.unloadwallet() 305 306 def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address): 307 self.log.info('Test simple bumpfee: {}'.format(mode)) 308 rbfid = spend_one_input(rbf_node, dest_address) 309 rbftx = rbf_node.gettransaction(rbfid) 310 self.sync_mempools((rbf_node, peer_node)) 311 assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool() 312 if mode == "fee_rate": 313 bumped_psbt = rbf_node.psbtbumpfee(rbfid, fee_rate=str(NORMAL)) 314 bumped_tx = rbf_node.bumpfee(rbfid, fee_rate=NORMAL) 315 elif mode == "new_outputs": 316 new_address = peer_node.getnewaddress() 317 bumped_psbt = rbf_node.psbtbumpfee(rbfid, outputs={new_address: 0.0003}) 318 bumped_tx = rbf_node.bumpfee(rbfid, outputs={new_address: 0.0003}) 319 else: 320 bumped_psbt = rbf_node.psbtbumpfee(rbfid) 321 bumped_tx = rbf_node.bumpfee(rbfid) 322 assert_equal(bumped_tx["errors"], []) 323 assert bumped_tx["fee"] > -rbftx["fee"] 324 assert_equal(bumped_tx["origfee"], -rbftx["fee"]) 325 assert "psbt" not in bumped_tx 326 assert_equal(bumped_psbt["errors"], []) 327 assert bumped_psbt["fee"] > -rbftx["fee"] 328 assert_equal(bumped_psbt["origfee"], -rbftx["fee"]) 329 assert "psbt" in bumped_psbt 330 # check that bumped_tx propagates, original tx was evicted and has a wallet conflict 331 self.sync_mempools((rbf_node, peer_node)) 332 assert bumped_tx["txid"] in rbf_node.getrawmempool() 333 assert bumped_tx["txid"] in peer_node.getrawmempool() 334 assert rbfid not in rbf_node.getrawmempool() 335 assert rbfid not in peer_node.getrawmempool() 336 oldwtx = rbf_node.gettransaction(rbfid) 337 assert len(oldwtx["walletconflicts"]) > 0 338 # check wallet transaction replaces and replaced_by values 339 bumpedwtx = rbf_node.gettransaction(bumped_tx["txid"]) 340 assert_equal(oldwtx["replaced_by_txid"], bumped_tx["txid"]) 341 assert_equal(bumpedwtx["replaces_txid"], rbfid) 342 # if this is a new_outputs test, check that outputs were indeed replaced 343 if mode == "new_outputs": 344 assert len(bumpedwtx["details"]) == 1 345 assert bumpedwtx["details"][0]["address"] == new_address 346 self.clear_mempool() 347 348 349 def test_segwit_bumpfee_succeeds(self, rbf_node, dest_address): 350 self.log.info('Test that segwit-sourcing bumpfee works') 351 # Create a transaction with segwit output, then create an RBF transaction 352 # which spends it, and make sure bumpfee can be called on it. 353 354 segwit_out = rbf_node.getnewaddress(address_type='bech32') 355 segwitid = rbf_node.send({segwit_out: "0.0009"}, options={"change_position": 1})["txid"] 356 357 rbfraw = rbf_node.createrawtransaction([{ 358 'txid': segwitid, 359 'vout': 0, 360 "sequence": MAX_BIP125_RBF_SEQUENCE 361 }], {dest_address: Decimal("0.0005"), 362 rbf_node.getrawchangeaddress(): Decimal("0.0003")}) 363 rbfsigned = rbf_node.signrawtransactionwithwallet(rbfraw) 364 rbfid = rbf_node.sendrawtransaction(rbfsigned["hex"]) 365 assert rbfid in rbf_node.getrawmempool() 366 367 bumped_tx = rbf_node.bumpfee(rbfid) 368 assert bumped_tx["txid"] in rbf_node.getrawmempool() 369 assert rbfid not in rbf_node.getrawmempool() 370 self.clear_mempool() 371 372 373 def test_nonrbf_bumpfee_succeeds(self, peer_node, dest_address): 374 self.log.info("Test that we can replace a non RBF transaction") 375 not_rbfid = peer_node.sendtoaddress(dest_address, Decimal("0.00090000")) 376 peer_node.bumpfee(not_rbfid) 377 self.clear_mempool() 378 379 380 def test_notmine_bumpfee(self, rbf_node, peer_node, dest_address): 381 self.log.info('Test that it cannot bump fee if non-owned inputs are included') 382 # here, the rbftx has a peer_node coin and then adds a rbf_node input 383 # Note that this test depends upon the RPC code checking input ownership prior to change outputs 384 # (since it can't use fundrawtransaction, it lacks a proper change output) 385 fee = Decimal("0.001") 386 utxos = [node.listunspent(minimumAmount=fee)[-1] for node in (rbf_node, peer_node)] 387 inputs = [{ 388 "txid": utxo["txid"], 389 "vout": utxo["vout"], 390 "address": utxo["address"], 391 "sequence": MAX_BIP125_RBF_SEQUENCE 392 } for utxo in utxos] 393 output_val = sum(utxo["amount"] for utxo in utxos) - fee 394 rawtx = rbf_node.createrawtransaction(inputs, {dest_address: output_val}) 395 signedtx = rbf_node.signrawtransactionwithwallet(rawtx) 396 signedtx = peer_node.signrawtransactionwithwallet(signedtx["hex"]) 397 rbfid = rbf_node.sendrawtransaction(signedtx["hex"]) 398 entry = rbf_node.getmempoolentry(rbfid) 399 old_fee = entry["fees"]["base"] 400 old_feerate = int(old_fee / entry["vsize"] * Decimal(1e8)) 401 assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet", 402 rbf_node.bumpfee, rbfid) 403 404 def finish_psbtbumpfee(psbt): 405 psbt = rbf_node.walletprocesspsbt(psbt) 406 psbt = peer_node.walletprocesspsbt(psbt["psbt"]) 407 res = rbf_node.testmempoolaccept([psbt["hex"]]) 408 assert res[0]["allowed"] 409 assert_greater_than(res[0]["fees"]["base"], old_fee) 410 411 self.log.info("Test that psbtbumpfee works for non-owned inputs") 412 psbt = rbf_node.psbtbumpfee(txid=rbfid) 413 finish_psbtbumpfee(psbt["psbt"]) 414 415 psbt = rbf_node.psbtbumpfee(txid=rbfid, fee_rate=old_feerate + 10) 416 finish_psbtbumpfee(psbt["psbt"]) 417 418 self.clear_mempool() 419 420 421 def test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address): 422 self.log.info('Test that fee cannot be bumped when it has descendant') 423 # parent is send-to-self, so we don't have to check which output is change when creating the child tx 424 parent_id = spend_one_input(rbf_node, rbf_node_address) 425 tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000}) 426 tx = rbf_node.signrawtransactionwithwallet(tx) 427 rbf_node.sendrawtransaction(tx["hex"]) 428 assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) 429 430 # create tx with descendant in the mempool by using MiniWallet 431 miniwallet = MiniWallet(rbf_node) 432 parent_id = spend_one_input(rbf_node, miniwallet.get_address()) 433 tx = rbf_node.gettransaction(txid=parent_id, verbose=True)['decoded'] 434 miniwallet.scan_tx(tx) 435 miniwallet.send_self_transfer(from_node=rbf_node) 436 assert_raises_rpc_error(-8, "Transaction has descendants in the mempool", rbf_node.bumpfee, parent_id) 437 self.clear_mempool() 438 439 440 def test_bumpfee_with_abandoned_descendant_succeeds(self, rbf_node, rbf_node_address, dest_address): 441 self.log.info('Test that fee can be bumped when it has abandoned descendant') 442 # parent is send-to-self, so we don't have to check which output is change when creating the child tx 443 parent_id = spend_one_input(rbf_node, rbf_node_address) 444 # Submit child transaction with low fee 445 child_id = rbf_node.send(outputs={dest_address: 0.00020000}, 446 options={"inputs": [{"txid": parent_id, "vout": 0}], "fee_rate": 2})["txid"] 447 assert child_id in rbf_node.getrawmempool() 448 449 # Restart the node with higher min relay fee so the descendant tx is no longer in mempool so that we can abandon it 450 self.restart_node(1, ['-minrelaytxfee=0.00005'] + self.extra_args[1]) 451 rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) 452 self.connect_nodes(1, 0) 453 assert parent_id in rbf_node.getrawmempool() 454 assert child_id not in rbf_node.getrawmempool() 455 # Should still raise an error even if not in mempool 456 assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) 457 # Now abandon the child transaction and bump the original 458 rbf_node.abandontransaction(child_id) 459 bumped_result = rbf_node.bumpfee(parent_id, {"fee_rate": HIGH}) 460 assert bumped_result['txid'] in rbf_node.getrawmempool() 461 assert parent_id not in rbf_node.getrawmempool() 462 # Cleanup 463 self.restart_node(1, self.extra_args[1]) 464 rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) 465 self.connect_nodes(1, 0) 466 self.clear_mempool() 467 468 469 def test_small_output_with_feerate_succeeds(self, rbf_node, dest_address): 470 self.log.info('Testing small output with feerate bump succeeds') 471 472 # Make sure additional inputs exist 473 self.generatetoaddress(rbf_node, COINBASE_MATURITY + 1, rbf_node.getnewaddress()) 474 rbfid = spend_one_input(rbf_node, dest_address) 475 input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"] 476 assert_equal(len(input_list), 1) 477 original_txin = input_list[0] 478 self.log.info('Keep bumping until transaction fee out-spends non-destination value') 479 tx_fee = 0 480 while True: 481 input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"] 482 new_item = list(input_list)[0] 483 assert_equal(len(input_list), 1) 484 assert_equal(original_txin["txid"], new_item["txid"]) 485 assert_equal(original_txin["vout"], new_item["vout"]) 486 rbfid_new_details = rbf_node.bumpfee(rbfid) 487 rbfid_new = rbfid_new_details["txid"] 488 raw_pool = rbf_node.getrawmempool() 489 assert rbfid not in raw_pool 490 assert rbfid_new in raw_pool 491 rbfid = rbfid_new 492 tx_fee = rbfid_new_details["fee"] 493 494 # Total value from input not going to destination 495 if tx_fee > Decimal('0.00050000'): 496 break 497 498 # input(s) have been added 499 final_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"] 500 assert_greater_than(len(final_input_list), 1) 501 # Original input is in final set 502 assert [txin for txin in final_input_list 503 if txin["txid"] == original_txin["txid"] 504 and txin["vout"] == original_txin["vout"]] 505 506 self.generatetoaddress(rbf_node, 1, rbf_node.getnewaddress()) 507 assert_equal(rbf_node.gettransaction(rbfid)["confirmations"], 1) 508 self.clear_mempool() 509 510 511 def test_dust_to_fee(self, rbf_node, dest_address): 512 self.log.info('Test that bumped output that is dust is dropped to fee') 513 rbfid = spend_one_input(rbf_node, dest_address) 514 fulltx = rbf_node.getrawtransaction(rbfid, 1) 515 # The DER formatting used by Bitcoin to serialize ECDSA signatures means that signatures can have a 516 # variable size of 70-72 bytes (or possibly even less), with most being 71 or 72 bytes. The signature 517 # in the witness is divided by 4 for the vsize, so this variance can take the weight across a 4-byte 518 # boundary. Thus expected transaction size (p2wpkh, 1 input, 2 outputs) is 140-141 vbytes, usually 141. 519 if not 140 <= fulltx["vsize"] <= 141: 520 raise AssertionError("Invalid tx vsize of {} (140-141 expected), full tx: {}".format(fulltx["vsize"], fulltx)) 521 # Bump with fee_rate of 350.25 sat/vB vbytes to create dust. 522 # Expected fee is 141 vbytes * fee_rate 0.00350250 BTC / 1000 vbytes = 0.00049385 BTC. 523 # or occasionally 140 vbytes * fee_rate 0.00350250 BTC / 1000 vbytes = 0.00049035 BTC. 524 # Dust should be dropped to the fee, so actual bump fee is 0.00050000 BTC. 525 bumped_tx = rbf_node.bumpfee(rbfid, fee_rate=350.25) 526 full_bumped_tx = rbf_node.getrawtransaction(bumped_tx["txid"], 1) 527 assert_equal(bumped_tx["fee"], Decimal("0.00050000")) 528 assert_equal(len(fulltx["vout"]), 2) 529 assert_equal(len(full_bumped_tx["vout"]), 1) # change output is eliminated 530 assert_equal(full_bumped_tx["vout"][0]['value'], Decimal("0.00050000")) 531 self.clear_mempool() 532 533 534 def test_settxfee(self, rbf_node, dest_address): 535 self.log.info('Test settxfee') 536 assert_raises_rpc_error(-8, "txfee cannot be less than min relay tx fee", rbf_node.settxfee, Decimal('0.000005')) 537 assert_raises_rpc_error(-8, "txfee cannot be less than wallet min fee", rbf_node.settxfee, Decimal('0.000015')) 538 # check that bumpfee reacts correctly to the use of settxfee (paytxfee) 539 rbfid = spend_one_input(rbf_node, dest_address) 540 requested_feerate = Decimal("0.00025000") 541 rbf_node.settxfee(requested_feerate) 542 bumped_tx = rbf_node.bumpfee(rbfid) 543 actual_feerate = bumped_tx["fee"] * 1000 / rbf_node.getrawtransaction(bumped_tx["txid"], True)["vsize"] 544 # Assert that the difference between the requested feerate and the actual 545 # feerate of the bumped transaction is small. 546 assert_greater_than(Decimal("0.00001000"), abs(requested_feerate - actual_feerate)) 547 rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee 548 549 # check that settxfee respects -maxtxfee 550 self.restart_node(1, ['-maxtxfee=0.000025'] + self.extra_args[1]) 551 assert_raises_rpc_error(-8, "txfee cannot be more than wallet max tx fee", rbf_node.settxfee, Decimal('0.00003')) 552 self.restart_node(1, self.extra_args[1]) 553 rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) 554 self.connect_nodes(1, 0) 555 self.clear_mempool() 556 557 558 def test_maxtxfee_fails(self, rbf_node, dest_address): 559 self.log.info('Test that bumpfee fails when it hits -maxtxfee') 560 # size of bumped transaction (p2wpkh, 1 input, 2 outputs): 141 vbytes 561 # expected bump fee of 141 vbytes * 0.00200000 BTC / 1000 vbytes = 0.00002820 BTC 562 # which exceeds maxtxfee and is expected to raise 563 self.restart_node(1, ['-maxtxfee=0.000025'] + self.extra_args[1]) 564 rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) 565 rbfid = spend_one_input(rbf_node, dest_address) 566 assert_raises_rpc_error(-4, "Unable to create transaction. Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", rbf_node.bumpfee, rbfid) 567 self.restart_node(1, self.extra_args[1]) 568 rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) 569 self.connect_nodes(1, 0) 570 self.clear_mempool() 571 572 573 def test_watchonly_psbt(self, peer_node, rbf_node, dest_address): 574 self.log.info('Test that PSBT is returned for bumpfee in watchonly wallets') 575 priv_rec_desc = "wpkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0/*)#rweraev0" 576 pub_rec_desc = rbf_node.getdescriptorinfo(priv_rec_desc)["descriptor"] 577 priv_change_desc = "wpkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/*)#j6uzqvuh" 578 pub_change_desc = rbf_node.getdescriptorinfo(priv_change_desc)["descriptor"] 579 # Create a wallet with private keys that can sign PSBTs 580 rbf_node.createwallet(wallet_name="signer", disable_private_keys=False, blank=True) 581 signer = rbf_node.get_wallet_rpc("signer") 582 assert signer.getwalletinfo()['private_keys_enabled'] 583 reqs = [{ 584 "desc": priv_rec_desc, 585 "timestamp": 0, 586 "range": [0,1], 587 "internal": False, 588 "keypool": False # Keys can only be imported to the keypool when private keys are disabled 589 }, 590 { 591 "desc": priv_change_desc, 592 "timestamp": 0, 593 "range": [0, 0], 594 "internal": True, 595 "keypool": False 596 }] 597 result = signer.importdescriptors(reqs) 598 assert_equal(result, [{'success': True}, {'success': True}]) 599 600 # Create another wallet with just the public keys, which creates PSBTs 601 rbf_node.createwallet(wallet_name="watcher", disable_private_keys=True, blank=True) 602 watcher = rbf_node.get_wallet_rpc("watcher") 603 assert not watcher.getwalletinfo()['private_keys_enabled'] 604 605 reqs = [{ 606 "desc": pub_rec_desc, 607 "timestamp": 0, 608 "range": [0, 10], 609 "internal": False, 610 "keypool": True, 611 "watchonly": True, 612 "active": True, 613 }, { 614 "desc": pub_change_desc, 615 "timestamp": 0, 616 "range": [0, 10], 617 "internal": True, 618 "keypool": True, 619 "watchonly": True, 620 "active": True, 621 }] 622 result = watcher.importdescriptors(reqs) 623 assert_equal(result, [{'success': True}, {'success': True}]) 624 625 funding_address1 = watcher.getnewaddress(address_type='bech32') 626 funding_address2 = watcher.getnewaddress(address_type='bech32') 627 peer_node.sendmany("", {funding_address1: 0.001, funding_address2: 0.001}) 628 self.generate(peer_node, 1) 629 630 # Create single-input PSBT for transaction to be bumped 631 # Ensure the payment amount + change can be fully funded using one of the 0.001BTC inputs. 632 psbt = watcher.walletcreatefundedpsbt([watcher.listunspent()[0]], {dest_address: 0.0005}, 0, 633 {"fee_rate": 1, "add_inputs": False}, True)['psbt'] 634 psbt_signed = signer.walletprocesspsbt(psbt=psbt, sign=True, sighashtype="ALL", bip32derivs=True) 635 original_txid = watcher.sendrawtransaction(psbt_signed["hex"]) 636 assert_equal(len(watcher.decodepsbt(psbt)["tx"]["vin"]), 1) 637 638 # bumpfee can't be used on watchonly wallets 639 assert_raises_rpc_error(-4, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead.", watcher.bumpfee, original_txid) 640 641 # Bump fee, obnoxiously high to add additional watchonly input 642 bumped_psbt = watcher.psbtbumpfee(original_txid, fee_rate=HIGH) 643 assert_greater_than(len(watcher.decodepsbt(bumped_psbt['psbt'])["tx"]["vin"]), 1) 644 assert "txid" not in bumped_psbt 645 assert_equal(bumped_psbt["origfee"], -watcher.gettransaction(original_txid)["fee"]) 646 assert not watcher.finalizepsbt(bumped_psbt["psbt"])["complete"] 647 648 # Sign bumped transaction 649 bumped_psbt_signed = signer.walletprocesspsbt(psbt=bumped_psbt["psbt"], sign=True, sighashtype="ALL", bip32derivs=True) 650 assert bumped_psbt_signed["complete"] 651 652 # Broadcast bumped transaction 653 bumped_txid = watcher.sendrawtransaction(bumped_psbt_signed["hex"]) 654 assert bumped_txid in rbf_node.getrawmempool() 655 assert original_txid not in rbf_node.getrawmempool() 656 657 rbf_node.unloadwallet("watcher") 658 rbf_node.unloadwallet("signer") 659 self.clear_mempool() 660 661 662 def test_rebumping(self, rbf_node, dest_address): 663 self.log.info('Test that re-bumping the original tx fails, but bumping successor works') 664 rbfid = spend_one_input(rbf_node, dest_address) 665 bumped = rbf_node.bumpfee(rbfid, fee_rate=ECONOMICAL) 666 assert_raises_rpc_error(-4, f"Cannot bump transaction {rbfid} which was already bumped by transaction {bumped['txid']}", 667 rbf_node.bumpfee, rbfid, fee_rate=NORMAL) 668 rbf_node.bumpfee(bumped["txid"], fee_rate=NORMAL) 669 self.clear_mempool() 670 671 672 def test_rebumping_not_replaceable(self, rbf_node, dest_address): 673 self.log.info("Test that re-bumping non-replaceable passes") 674 rbfid = spend_one_input(rbf_node, dest_address) 675 676 def check_sequence(tx, seq_in): 677 tx = rbf_node.getrawtransaction(tx["txid"]) 678 tx = rbf_node.decoderawtransaction(tx) 679 seq = [i["sequence"] for i in tx["vin"]] 680 assert_equal(seq, [seq_in]) 681 682 bumped = rbf_node.bumpfee(rbfid, fee_rate=ECONOMICAL, replaceable=False) 683 check_sequence(bumped, MAX_SEQUENCE_NONFINAL) 684 bumped = rbf_node.bumpfee(bumped["txid"], {"fee_rate": NORMAL}) 685 check_sequence(bumped, MAX_BIP125_RBF_SEQUENCE) 686 687 self.clear_mempool() 688 689 690 def test_bumpfee_already_spent(self, rbf_node, dest_address): 691 self.log.info('Test that bumping tx with already spent coin fails') 692 txid = spend_one_input(rbf_node, dest_address) 693 self.generate(rbf_node, 1) # spend coin simply by mining block with tx 694 spent_input = rbf_node.gettransaction(txid=txid, verbose=True)['decoded']['vin'][0] 695 assert_raises_rpc_error(-1, f"{spent_input['txid']}:{spent_input['vout']} is already spent", 696 rbf_node.bumpfee, txid, fee_rate=NORMAL) 697 698 699 def test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address): 700 self.log.info('Test that unconfirmed outputs from bumped txns are not spendable') 701 rbfid = spend_one_input(rbf_node, rbf_node_address) 702 rbftx = rbf_node.gettransaction(rbfid)["hex"] 703 assert rbfid in rbf_node.getrawmempool() 704 bumpid = rbf_node.bumpfee(rbfid)["txid"] 705 assert bumpid in rbf_node.getrawmempool() 706 assert rbfid not in rbf_node.getrawmempool() 707 708 # check that outputs from the bump transaction are not spendable 709 # due to the replaces_txid check in CWallet::AvailableCoins 710 assert_equal([t for t in rbf_node.listunspent(minconf=0, include_unsafe=False) if t["txid"] == bumpid], []) 711 712 # submit a block with the rbf tx to clear the bump tx out of the mempool, 713 # then invalidate the block so the rbf tx will be put back in the mempool. 714 # This makes it possible to check whether the rbf tx outputs are 715 # spendable before the rbf tx is confirmed. 716 block = self.generateblock(rbf_node, output="raw(51)", transactions=[rbftx]) 717 # Can not abandon conflicted tx 718 assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: rbf_node.abandontransaction(txid=bumpid)) 719 rbf_node.invalidateblock(block["hash"]) 720 # Call abandon to make sure the wallet doesn't attempt to resubmit 721 # the bump tx and hope the wallet does not rebroadcast before we call. 722 rbf_node.abandontransaction(bumpid) 723 724 tx_bump_abandoned = rbf_node.gettransaction(bumpid) 725 for tx in tx_bump_abandoned['details']: 726 assert_equal(tx['abandoned'], True) 727 728 assert bumpid not in rbf_node.getrawmempool() 729 assert rbfid in rbf_node.getrawmempool() 730 731 # check that outputs from the rbf tx are not spendable before the 732 # transaction is confirmed, due to the replaced_by_txid check in 733 # CWallet::AvailableCoins 734 assert_equal([t for t in rbf_node.listunspent(minconf=0, include_unsafe=False) if t["txid"] == rbfid], []) 735 736 # check that the main output from the rbf tx is spendable after confirmed 737 self.generate(rbf_node, 1, sync_fun=self.no_op) 738 assert_equal( 739 sum(1 for t in rbf_node.listunspent(minconf=0, include_unsafe=False) 740 if t["txid"] == rbfid and t["address"] == rbf_node_address and t["spendable"]), 1) 741 self.clear_mempool() 742 743 744 def test_bumpfee_metadata(self, rbf_node, dest_address): 745 self.log.info('Test that bumped txn metadata persists to new txn record') 746 assert rbf_node.getbalance() < 49 747 self.generatetoaddress(rbf_node, 101, rbf_node.getnewaddress()) 748 rbfid = rbf_node.sendtoaddress(dest_address, 49, "comment value", "to value") 749 bumped_tx = rbf_node.bumpfee(rbfid) 750 bumped_wtx = rbf_node.gettransaction(bumped_tx["txid"]) 751 assert_equal(bumped_wtx["comment"], "comment value") 752 assert_equal(bumped_wtx["to"], "to value") 753 self.clear_mempool() 754 755 756 def test_locked_wallet_fails(self, rbf_node, dest_address): 757 self.log.info('Test that locked wallet cannot bump txn') 758 rbfid = spend_one_input(rbf_node, dest_address) 759 rbf_node.walletlock() 760 assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.", 761 rbf_node.bumpfee, rbfid) 762 rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) 763 self.clear_mempool() 764 765 766 def test_change_script_match(self, rbf_node, dest_address): 767 self.log.info('Test that the same change addresses is used for the replacement transaction when possible') 768 769 # Check that there is only one change output 770 rbfid = spend_one_input(rbf_node, dest_address) 771 change_addresses = get_change_address(rbfid, rbf_node) 772 assert_equal(len(change_addresses), 1) 773 774 # Now find that address in each subsequent tx, and no other change 775 bumped_total_tx = rbf_node.bumpfee(rbfid, fee_rate=ECONOMICAL) 776 assert_equal(change_addresses, get_change_address(bumped_total_tx['txid'], rbf_node)) 777 bumped_rate_tx = rbf_node.bumpfee(bumped_total_tx["txid"]) 778 assert_equal(change_addresses, get_change_address(bumped_rate_tx['txid'], rbf_node)) 779 self.clear_mempool() 780 781 782 def spend_one_input(node, dest_address, change_size=Decimal("0.00049000"), data=None): 783 tx_input = dict( 784 sequence=MAX_BIP125_RBF_SEQUENCE, **next(u for u in node.listunspent() if u["amount"] == Decimal("0.00100000"))) 785 destinations = {dest_address: Decimal("0.00050000")} 786 if change_size > 0: 787 destinations[node.getrawchangeaddress()] = change_size 788 if data: 789 destinations['data'] = data 790 rawtx = node.createrawtransaction([tx_input], destinations) 791 signedtx = node.signrawtransactionwithwallet(rawtx) 792 txid = node.sendrawtransaction(signedtx["hex"]) 793 return txid 794 795 796 def test_no_more_inputs_fails(self, rbf_node, dest_address): 797 self.log.info('Test that bumpfee fails when there are no available confirmed outputs') 798 # feerate rbf requires confirmed outputs when change output doesn't exist or is insufficient 799 self.generatetoaddress(rbf_node, 1, dest_address) 800 # spend all funds, no change output 801 rbfid = rbf_node.sendall(recipients=[rbf_node.getnewaddress()])['txid'] 802 assert_raises_rpc_error(-4, "Unable to create transaction. Insufficient funds", rbf_node.bumpfee, rbfid) 803 self.clear_mempool() 804 805 806 def test_feerate_checks_replaced_outputs(self, rbf_node, peer_node): 807 # Make sure there is enough balance 808 peer_node.sendtoaddress(rbf_node.getnewaddress(), 60) 809 self.generate(peer_node, 1) 810 811 self.log.info("Test that feerate checks use replaced outputs") 812 outputs = [] 813 for i in range(50): 814 outputs.append({rbf_node.getnewaddress(address_type="bech32"): 1}) 815 tx_res = rbf_node.send(outputs=outputs, fee_rate=5) 816 tx_details = rbf_node.gettransaction(txid=tx_res["txid"], verbose=True) 817 818 # Calculate the minimum feerate required for the bump to work. 819 # Since the bumped tx will replace all of the outputs with a single output, we can estimate that its size will 31 * (len(outputs) - 1) bytes smaller 820 tx_size = tx_details["decoded"]["vsize"] 821 est_bumped_size = tx_size - (len(tx_details["decoded"]["vout"]) - 1) * 31 822 inc_fee_rate = rbf_node.getmempoolinfo()["incrementalrelayfee"] 823 # RPC gives us fee as negative 824 min_fee = (-tx_details["fee"] + get_fee(est_bumped_size, inc_fee_rate)) * Decimal(1e8) 825 min_fee_rate = (min_fee / est_bumped_size).quantize(Decimal("1.000")) 826 827 # Attempt to bumpfee and replace all outputs with a single one using a feerate slightly less than the minimum 828 new_outputs = [{rbf_node.getnewaddress(address_type="bech32"): 49}] 829 assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx_res["txid"], {"fee_rate": min_fee_rate - 1, "outputs": new_outputs}) 830 831 # Bumpfee and replace all outputs with a single one using the minimum feerate 832 rbf_node.bumpfee(tx_res["txid"], {"fee_rate": min_fee_rate, "outputs": new_outputs}) 833 self.clear_mempool() 834 835 836 def test_bumpfee_with_feerate_ignores_walletincrementalrelayfee(self, rbf_node, peer_node): 837 self.log.info('Test that bumpfee with fee_rate ignores walletincrementalrelayfee') 838 # Make sure there is enough balance 839 peer_node.sendtoaddress(rbf_node.getnewaddress(), 2) 840 self.generate(peer_node, 1) 841 842 dest_address = peer_node.getnewaddress(address_type="bech32") 843 tx = rbf_node.send(outputs=[{dest_address: 1}], fee_rate=2) 844 845 # Ensure you can not fee bump with a fee_rate below or equal to the original fee_rate 846 assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 1}) 847 assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 2}) 848 849 # Ensure you can not fee bump if the fee_rate is more than original fee_rate but the total fee from new fee_rate is 850 # less than (original fee + incrementalrelayfee) 851 assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 2.8}) 852 853 # You can fee bump as long as the new fee set from fee_rate is at least (original fee + incrementalrelayfee) 854 rbf_node.bumpfee(tx["txid"], {"fee_rate": 3}) 855 self.clear_mempool() 856 857 858 if __name__ == "__main__": 859 BumpFeeTest(__file__).main()