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