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