wallet_spend_unconfirmed.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2022-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
6 from decimal import Decimal, getcontext
7
8 from test_framework.test_framework import BitcoinTestFramework
9 from test_framework.util import (
10 assert_greater_than_or_equal,
11 assert_equal,
12 find_vout_for_address,
13 )
14
15 class UnconfirmedInputTest(BitcoinTestFramework):
16 def set_test_params(self):
17 getcontext().prec=9
18 self.setup_clean_chain = True
19 self.num_nodes = 1
20
21 def setup_and_fund_wallet(self, walletname):
22 self.nodes[0].createwallet(walletname)
23 wallet = self.nodes[0].get_wallet_rpc(walletname)
24
25 self.def_wallet.sendtoaddress(address=wallet.getnewaddress(), amount=2)
26 self.generate(self.nodes[0], 1) # confirm funding tx
27 return wallet
28
29 def skip_test_if_missing_module(self):
30 self.skip_if_no_wallet()
31
32 def calc_fee_rate(self, tx):
33 fee = Decimal(-1e8) * tx["fee"]
34 vsize = tx["decoded"]["vsize"]
35 return fee / vsize
36
37 def calc_set_fee_rate(self, txs):
38 fees = Decimal(-1e8) * sum([tx["fee"] for tx in txs]) # fee is negative!
39 vsizes = sum([tx["decoded"]["vsize"] for tx in txs])
40 return fees / vsizes
41
42 def assert_spends_only_parents(self, tx, parent_txids):
43 parent_checklist = parent_txids.copy()
44 number_inputs = len(tx["decoded"]["vin"])
45 assert_equal(number_inputs, len(parent_txids))
46 for i in range(number_inputs):
47 txid_of_input = tx["decoded"]["vin"][i]["txid"]
48 assert txid_of_input in parent_checklist
49 parent_checklist.remove(txid_of_input)
50
51 def assert_undershoots_target(self, tx):
52 resulting_fee_rate = self.calc_fee_rate(tx)
53 assert_greater_than_or_equal(self.target_fee_rate, resulting_fee_rate)
54
55 def assert_beats_target(self, tx):
56 resulting_fee_rate = self.calc_fee_rate(tx)
57 assert_greater_than_or_equal(resulting_fee_rate, self.target_fee_rate)
58
59 # Meta-Test: try feerate testing function on confirmed UTXO
60 def test_target_feerate_confirmed(self):
61 self.log.info("Start test feerate with confirmed input")
62 wallet = self.setup_and_fund_wallet("confirmed_wallet")
63
64 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=0.5, fee_rate=self.target_fee_rate)
65 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
66 self.assert_beats_target(ancestor_aware_tx)
67
68 wallet.unloadwallet()
69
70 # Spend unconfirmed UTXO from high-feerate parent
71 def test_target_feerate_unconfirmed_high(self):
72 self.log.info("Start test feerate with high feerate unconfirmed input")
73 wallet = self.setup_and_fund_wallet("unconfirmed_high_wallet")
74
75 # Send unconfirmed transaction with high feerate to testing wallet
76 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1, fee_rate=3*self.target_fee_rate)
77 parent_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
78 self.assert_beats_target(parent_tx)
79
80 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=0.5, fee_rate=self.target_fee_rate)
81 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
82
83 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
84
85 self.assert_beats_target(ancestor_aware_tx)
86
87 wallet.unloadwallet()
88
89 # Spend unconfirmed UTXO from low-feerate parent. Expect that parent gets
90 # bumped to target feerate.
91 def test_target_feerate_unconfirmed_low(self):
92 self.log.info("Start test feerate with low feerate unconfirmed input")
93 wallet = self.setup_and_fund_wallet("unconfirmed_low_wallet")
94
95 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1, fee_rate=1)
96 parent_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
97
98 self.assert_undershoots_target(parent_tx)
99
100 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=0.5, fee_rate=self.target_fee_rate)
101 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
102
103 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
104
105 self.assert_beats_target(ancestor_aware_tx)
106 resulting_ancestry_fee_rate = self.calc_set_fee_rate([parent_tx, ancestor_aware_tx])
107 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
108 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
109
110 wallet.unloadwallet()
111
112 # Spend UTXO with unconfirmed low feerate parent and grandparent
113 # txs. Expect that both ancestors get bumped to target feerate.
114 def test_chain_of_unconfirmed_low(self):
115 self.log.info("Start test with parent and grandparent tx")
116 wallet = self.setup_and_fund_wallet("unconfirmed_low_chain_wallet")
117
118 grandparent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1.8, fee_rate=1)
119 gp_tx = wallet.gettransaction(txid=grandparent_txid, verbose=True)
120
121 self.assert_undershoots_target(gp_tx)
122
123 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1.5, fee_rate=2)
124 p_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
125
126 self.assert_undershoots_target(p_tx)
127
128 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=1.3, fee_rate=self.target_fee_rate)
129 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
130 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
131
132 self.assert_beats_target(ancestor_aware_tx)
133 resulting_ancestry_fee_rate = self.calc_set_fee_rate([gp_tx, p_tx, ancestor_aware_tx])
134 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
135 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
136
137 wallet.unloadwallet()
138
139 # Spend unconfirmed UTXOs from two low feerate parent txs.
140 def test_two_low_feerate_unconfirmed_parents(self):
141 self.log.info("Start test with two unconfirmed parent txs")
142 wallet = self.setup_and_fund_wallet("two_parents_wallet")
143
144 # Add second UTXO to tested wallet
145 self.def_wallet.sendtoaddress(address=wallet.getnewaddress(), amount=2)
146 self.generate(self.nodes[0], 1) # confirm funding tx
147
148 parent_one_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1.5, fee_rate=2)
149 p_one_tx = wallet.gettransaction(txid=parent_one_txid, verbose=True)
150 self.assert_undershoots_target(p_one_tx)
151
152 parent_two_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1.5, fee_rate=1)
153 p_two_tx = wallet.gettransaction(txid=parent_two_txid, verbose=True)
154 self.assert_undershoots_target(p_two_tx)
155
156 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=2.8, fee_rate=self.target_fee_rate)
157 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
158 self.assert_spends_only_parents(ancestor_aware_tx, [parent_one_txid, parent_two_txid])
159
160 self.assert_beats_target(ancestor_aware_tx)
161 resulting_ancestry_fee_rate = self.calc_set_fee_rate([p_one_tx, p_two_tx, ancestor_aware_tx])
162 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
163 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
164
165 wallet.unloadwallet()
166
167 # Spend two unconfirmed inputs, one each from low and high feerate parents
168 def test_mixed_feerate_unconfirmed_parents(self):
169 self.log.info("Start test with two unconfirmed parent txs one of which has a higher feerate")
170 wallet = self.setup_and_fund_wallet("two_mixed_parents_wallet")
171
172 # Add second UTXO to tested wallet
173 self.def_wallet.sendtoaddress(address=wallet.getnewaddress(), amount=2)
174 self.generate(self.nodes[0], 1) # confirm funding tx
175
176 high_parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1.5, fee_rate=self.target_fee_rate*2)
177 p_high_tx = wallet.gettransaction(txid=high_parent_txid, verbose=True)
178 # This time the parent is greater than the child
179 self.assert_beats_target(p_high_tx)
180
181 parent_low_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1.5, fee_rate=1)
182 p_low_tx = wallet.gettransaction(txid=parent_low_txid, verbose=True)
183 # Other parent needs bump
184 self.assert_undershoots_target(p_low_tx)
185
186 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=2.8, fee_rate=self.target_fee_rate)
187 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
188 self.assert_spends_only_parents(ancestor_aware_tx, [parent_low_txid, high_parent_txid])
189
190 self.assert_beats_target(ancestor_aware_tx)
191 resulting_ancestry_fee_rate = self.calc_set_fee_rate([p_high_tx, p_low_tx, ancestor_aware_tx])
192 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
193
194 resulting_bumped_ancestry_fee_rate = self.calc_set_fee_rate([p_low_tx, ancestor_aware_tx])
195 assert_greater_than_or_equal(resulting_bumped_ancestry_fee_rate, self.target_fee_rate)
196 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_bumped_ancestry_fee_rate)
197
198 wallet.unloadwallet()
199
200 # Spend from chain with high feerate grandparent and low feerate parent
201 def test_chain_of_high_low(self):
202 self.log.info("Start test with low parent and high grandparent tx")
203 wallet = self.setup_and_fund_wallet("high_low_chain_wallet")
204
205 grandparent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1.8, fee_rate=self.target_fee_rate * 10)
206 gp_tx = wallet.gettransaction(txid=grandparent_txid, verbose=True)
207 # grandparent has higher feerate
208 self.assert_beats_target(gp_tx)
209
210 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1.5, fee_rate=1)
211 # parent is low feerate
212 p_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
213 self.assert_undershoots_target(p_tx)
214
215 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=1.3, fee_rate=self.target_fee_rate)
216 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
217 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
218
219 self.assert_beats_target(ancestor_aware_tx)
220 resulting_ancestry_fee_rate = self.calc_set_fee_rate([p_tx, ancestor_aware_tx])
221 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
222 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
223 resulting_ancestry_fee_rate_with_high_feerate_gp = self.calc_set_fee_rate([gp_tx, p_tx, ancestor_aware_tx])
224 # Check that we bumped the parent without relying on the grandparent
225 assert_greater_than_or_equal(resulting_ancestry_fee_rate_with_high_feerate_gp, self.target_fee_rate*1.1)
226
227 wallet.unloadwallet()
228
229 # Spend UTXO from chain of unconfirmed transactions with low feerate
230 # grandparent and even lower feerate parent
231 def test_chain_of_high_low_below_target_feerate(self):
232 self.log.info("Start test with low parent and higher low grandparent tx")
233 wallet = self.setup_and_fund_wallet("low_and_lower_chain_wallet")
234
235 grandparent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1.8, fee_rate=5)
236 gp_tx = wallet.gettransaction(txid=grandparent_txid, verbose=True)
237
238 # grandparent has higher feerate, but below target
239 self.assert_undershoots_target(gp_tx)
240
241 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1.5, fee_rate=1)
242 p_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
243 # parent even lower
244 self.assert_undershoots_target(p_tx)
245
246 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=1.3, fee_rate=self.target_fee_rate)
247 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
248 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
249
250 self.assert_beats_target(ancestor_aware_tx)
251 resulting_ancestry_fee_rate = self.calc_set_fee_rate([gp_tx, p_tx, ancestor_aware_tx])
252 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
253 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
254
255 wallet.unloadwallet()
256
257 # Test fee calculation when bumping while using subtract fee from output (SFFO)
258 def test_target_feerate_unconfirmed_low_sffo(self):
259 self.log.info("Start test feerate with low feerate unconfirmed input, while subtracting from output")
260 wallet = self.setup_and_fund_wallet("unconfirmed_low_wallet_sffo")
261
262 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1, fee_rate=1)
263 parent_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
264
265 self.assert_undershoots_target(parent_tx)
266
267 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=0.5, fee_rate=self.target_fee_rate, subtractfeefromamount=True)
268 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
269
270 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
271
272 self.assert_beats_target(ancestor_aware_tx)
273 resulting_ancestry_fee_rate = self.calc_set_fee_rate([parent_tx, ancestor_aware_tx])
274 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
275 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
276
277 wallet.unloadwallet()
278
279 # Test that parents of preset unconfirmed inputs get cpfp'ed
280 def test_preset_input_cpfp(self):
281 self.log.info("Start test with preset input from low feerate unconfirmed transaction")
282 wallet = self.setup_and_fund_wallet("preset_input")
283
284 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1, fee_rate=1)
285 parent_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
286
287 self.assert_undershoots_target(parent_tx)
288
289 number_outputs = len(parent_tx["decoded"]["vout"])
290 assert_equal(number_outputs, 2)
291
292 # we don't care which of the two outputs we spent, they're both ours
293 ancestor_aware_txid = wallet.send(outputs=[{self.def_wallet.getnewaddress(): 0.5}], fee_rate=self.target_fee_rate, options={"add_inputs": True, "inputs": [{"txid": parent_txid, "vout": 0}]})["txid"]
294 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
295
296 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
297
298 self.assert_beats_target(ancestor_aware_tx)
299 resulting_ancestry_fee_rate = self.calc_set_fee_rate([parent_tx, ancestor_aware_tx])
300 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
301 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
302
303 wallet.unloadwallet()
304
305 # Test that RBFing a transaction with unconfirmed input gets the right feerate
306 def test_rbf_bumping(self):
307 self.log.info("Start test to rbf a transaction unconfirmed input to bump it")
308 wallet = self.setup_and_fund_wallet("bump")
309
310 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1, fee_rate=1)
311 parent_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
312
313 self.assert_undershoots_target(parent_tx)
314
315 to_be_rbfed_ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=0.5, fee_rate=self.target_fee_rate)
316 ancestor_aware_tx = wallet.gettransaction(txid=to_be_rbfed_ancestor_aware_txid, verbose=True)
317
318 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
319
320 self.assert_beats_target(ancestor_aware_tx)
321 resulting_ancestry_fee_rate = self.calc_set_fee_rate([parent_tx, ancestor_aware_tx])
322 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
323 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
324
325 bumped_ancestor_aware_txid = wallet.bumpfee(txid=to_be_rbfed_ancestor_aware_txid, options={"fee_rate": self.target_fee_rate * 2} )["txid"]
326 bumped_ancestor_aware_tx = wallet.gettransaction(txid=bumped_ancestor_aware_txid, verbose=True)
327 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
328
329 resulting_bumped_fee_rate = self.calc_fee_rate(bumped_ancestor_aware_tx)
330 assert_greater_than_or_equal(resulting_bumped_fee_rate, 2*self.target_fee_rate)
331 resulting_bumped_ancestry_fee_rate = self.calc_set_fee_rate([parent_tx, bumped_ancestor_aware_tx])
332 assert_greater_than_or_equal(resulting_bumped_ancestry_fee_rate, 2*self.target_fee_rate)
333 assert_greater_than_or_equal(2*self.target_fee_rate*1.01, resulting_bumped_ancestry_fee_rate)
334
335 wallet.unloadwallet()
336
337 # Test that transaction spending two UTXOs with overlapping ancestry does not bump shared ancestors twice
338 def test_target_feerate_unconfirmed_low_overlapping_ancestry(self):
339 self.log.info("Start test where two UTXOs have overlapping ancestry")
340 wallet = self.setup_and_fund_wallet("overlapping_ancestry_wallet")
341
342 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1, fee_rate=1)
343 two_output_parent_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
344
345 self.assert_undershoots_target(two_output_parent_tx)
346
347 # spend both outputs from parent transaction
348 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=1.5, fee_rate=self.target_fee_rate)
349 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
350 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid, parent_txid])
351
352 self.assert_beats_target(ancestor_aware_tx)
353 resulting_ancestry_fee_rate = self.calc_set_fee_rate([two_output_parent_tx, ancestor_aware_tx])
354 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
355 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
356
357 wallet.unloadwallet()
358
359 # Test that new transaction ignores sibling transaction with low feerate
360 def test_sibling_tx_gets_ignored(self):
361 self.log.info("Start test where a low-fee sibling tx gets created and check that bumping ignores it")
362 wallet = self.setup_and_fund_wallet("ignore-sibling")
363
364 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1, fee_rate=2)
365 parent_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
366
367 self.assert_undershoots_target(parent_tx)
368
369 # create sibling tx
370 sibling_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=0.9, fee_rate=1)
371 sibling_tx = wallet.gettransaction(txid=sibling_txid, verbose=True)
372 self.assert_undershoots_target(sibling_tx)
373
374 # spend both outputs from parent transaction
375 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=0.5, fee_rate=self.target_fee_rate)
376 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
377
378 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
379
380 self.assert_beats_target(ancestor_aware_tx)
381 resulting_ancestry_fee_rate = self.calc_set_fee_rate([parent_tx, ancestor_aware_tx])
382 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
383 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
384
385 wallet.unloadwallet()
386
387 # Test that new transaction only pays for itself when high feerate sibling pays for parent
388 def test_sibling_tx_bumps_parent(self):
389 self.log.info("Start test where a high-fee sibling tx bumps the parent")
390 wallet = self.setup_and_fund_wallet("generous-sibling")
391
392 parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1, fee_rate=1)
393 parent_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
394 self.assert_undershoots_target(parent_tx)
395
396 # create sibling tx
397 sibling_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=0.9, fee_rate=3*self.target_fee_rate)
398 sibling_tx = wallet.gettransaction(txid=sibling_txid, verbose=True)
399 self.assert_beats_target(sibling_tx)
400
401 # spend both outputs from parent transaction
402 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=0.5, fee_rate=self.target_fee_rate)
403 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
404
405 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
406
407 self.assert_beats_target(ancestor_aware_tx)
408 # Child is only paying for itself…
409 resulting_fee_rate = self.calc_fee_rate(ancestor_aware_tx)
410 assert_greater_than_or_equal(1.05 * self.target_fee_rate, resulting_fee_rate)
411 # …because sibling bumped to parent to ~50 s/vB, while our target is 30 s/vB
412 resulting_ancestry_fee_rate_sibling = self.calc_set_fee_rate([parent_tx, sibling_tx])
413 assert_greater_than_or_equal(resulting_ancestry_fee_rate_sibling, self.target_fee_rate)
414 # and our resulting "ancestry feerate" is therefore BELOW target feerate
415 resulting_ancestry_fee_rate = self.calc_set_fee_rate([parent_tx, ancestor_aware_tx])
416 assert_greater_than_or_equal(self.target_fee_rate, resulting_ancestry_fee_rate)
417
418 wallet.unloadwallet()
419
420 # Spend a confirmed and an unconfirmed input at the same time
421 def test_confirmed_and_unconfirmed_parent(self):
422 self.log.info("Start test with one unconfirmed and one confirmed input")
423 wallet = self.setup_and_fund_wallet("confirmed_and_unconfirmed_wallet")
424 confirmed_parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1, fee_rate=self.target_fee_rate)
425 self.generate(self.nodes[0], 1) # Wallet has two confirmed UTXOs of ~1BTC each
426 unconfirmed_parent_txid = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=0.5, fee_rate=0.5*self.target_fee_rate)
427
428 # wallet has one confirmed UTXO of 1BTC and two unconfirmed UTXOs of ~0.5BTC each
429 ancestor_aware_txid = wallet.sendtoaddress(address=self.def_wallet.getnewaddress(), amount=1.4, fee_rate=self.target_fee_rate)
430 ancestor_aware_tx = wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
431 self.assert_spends_only_parents(ancestor_aware_tx, [confirmed_parent_txid, unconfirmed_parent_txid])
432 resulting_fee_rate = self.calc_fee_rate(ancestor_aware_tx)
433 assert_greater_than_or_equal(resulting_fee_rate, self.target_fee_rate)
434
435 wallet.unloadwallet()
436
437 def test_external_input_unconfirmed_low(self):
438 self.log.info("Send funds to an external wallet then build tx that bumps parent by spending external input")
439 wallet = self.setup_and_fund_wallet("test_external_wallet")
440
441 external_address = self.def_wallet.getnewaddress()
442 address_info = self.def_wallet.getaddressinfo(external_address)
443 external_descriptor = address_info["desc"]
444 parent_txid = wallet.sendtoaddress(address=external_address, amount=1, fee_rate=1)
445 parent_tx = wallet.gettransaction(txid=parent_txid, verbose=True)
446
447 self.assert_undershoots_target(parent_tx)
448
449 spend_res = wallet.send(outputs=[{self.def_wallet.getnewaddress(): 0.5}], fee_rate=self.target_fee_rate, options={"inputs":[{"txid":parent_txid, "vout":find_vout_for_address(self.nodes[0], parent_txid, external_address)}], "solving_data":{"descriptors":[external_descriptor]}})
450 signed_psbt = self.def_wallet.walletprocesspsbt(spend_res["psbt"])
451 external_tx = self.def_wallet.finalizepsbt(signed_psbt["psbt"])
452 ancestor_aware_txid = self.def_wallet.sendrawtransaction(external_tx["hex"])
453
454 ancestor_aware_tx = self.def_wallet.gettransaction(txid=ancestor_aware_txid, verbose=True)
455
456 self.assert_spends_only_parents(ancestor_aware_tx, [parent_txid])
457
458 self.assert_beats_target(ancestor_aware_tx)
459 resulting_ancestry_fee_rate = self.calc_set_fee_rate([parent_tx, ancestor_aware_tx])
460 assert_greater_than_or_equal(resulting_ancestry_fee_rate, self.target_fee_rate)
461 assert_greater_than_or_equal(self.target_fee_rate*1.01, resulting_ancestry_fee_rate)
462
463 wallet.unloadwallet()
464
465
466 def run_test(self):
467 self.log.info("Starting UnconfirmedInputTest!")
468 self.target_fee_rate = 30
469 self.def_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
470 self.generate(self.nodes[0], 110)
471
472 self.test_target_feerate_confirmed()
473
474 self.test_target_feerate_unconfirmed_high()
475
476 self.test_target_feerate_unconfirmed_low()
477
478 self.test_chain_of_unconfirmed_low()
479
480 self.test_two_low_feerate_unconfirmed_parents()
481
482 self.test_mixed_feerate_unconfirmed_parents()
483
484 self.test_chain_of_high_low()
485
486 self.test_chain_of_high_low_below_target_feerate()
487
488 self.test_target_feerate_unconfirmed_low_sffo()
489
490 self.test_preset_input_cpfp()
491
492 self.test_rbf_bumping()
493
494 self.test_target_feerate_unconfirmed_low_overlapping_ancestry()
495
496 self.test_sibling_tx_gets_ignored()
497
498 self.test_sibling_tx_bumps_parent()
499
500 self.test_confirmed_and_unconfirmed_parent()
501
502 self.test_external_input_unconfirmed_low()
503
504 if __name__ == '__main__':
505 UnconfirmedInputTest(__file__).main()
506