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