@@ -171,7 +171,16 @@ def sign_and_broadcast(psbt):
171
171
psbt = PSBT .from_base64 (res ["psbt" ])
172
172
sign_and_broadcast (psbt )
173
173
assert len (psbt .o ) == 4
174
- assert len (res ["warnings" ]) == 0
174
+ assert bitcoind .rpc .getmempoolentry (deposit_d )["ancestorsize" ] == 165
175
+ assert bitcoind .rpc .getmempoolentry (deposit_d )["fees" ]["ancestor" ] * COIN == 165
176
+ # ancestor vsize at feerate 2 sat/vb = ancestor_fee / 2 = 165 / 2 = 82
177
+ # extra_weight <= (extra vsize * witness factor) = (165 - 82) * 4 = 332
178
+ # additional fee at 2 sat/vb (0.5 sat/wu) = 332 * 0.5 = 166
179
+ assert len (res ["warnings" ]) == 1
180
+ assert (
181
+ res ["warnings" ][0 ]
182
+ == "An additional fee of 166 sats has been added to pay for ancestors at the target feerate."
183
+ )
175
184
176
185
# All the spent coins must have been detected as such
177
186
all_deposits = (deposit_a , deposit_b , deposit_c , deposit_d )
@@ -263,6 +272,7 @@ def test_coin_selection(lianad, bitcoind):
263
272
# Coin selection now succeeds.
264
273
spend_res_1 = lianad .rpc .createspend ({dest_addr_1 : 100_000 }, [], 2 )
265
274
assert "psbt" in spend_res_1
275
+ assert len (spend_res_1 ["warnings" ]) == 0
266
276
# Increase spend amount and we have insufficient funds again even though we
267
277
# now have confirmed coins.
268
278
assert "missing" in lianad .rpc .createspend ({dest_addr_1 : 200_000 }, [], 2 )
@@ -280,17 +290,61 @@ def test_coin_selection(lianad, bitcoind):
280
290
assert lianad .rpc .listcoins (["unconfirmed" ])["coins" ][0 ]["is_change" ] is True
281
291
assert len (lianad .rpc .listcoins (["spending" ])["coins" ]) == 1
282
292
# We can use unconfirmed change as candidate.
293
+ # Depending on the feerate, we'll get a warning about paying extra for the ancestor.
283
294
dest_addr_2 = bitcoind .rpc .getnewaddress ()
295
+ # If feerate is higher than ancestor, we'll need to pay extra.
296
+
297
+ # Try 10 sat/vb:
298
+ spend_res_2 = lianad .rpc .createspend ({dest_addr_2 : 10_000 }, [], 10 )
299
+ assert "psbt" in spend_res_2
300
+ spend_psbt_2 = PSBT .from_base64 (spend_res_2 ["psbt" ])
301
+ # The spend is using the unconfirmed change.
302
+ assert spend_psbt_2 .tx .vin [0 ].prevout .hash == uint256_from_str (
303
+ bytes .fromhex (spend_txid_1 )[::- 1 ]
304
+ )
305
+ assert bitcoind .rpc .getmempoolentry (spend_txid_1 )["ancestorsize" ] == 161
306
+ assert bitcoind .rpc .getmempoolentry (spend_txid_1 )["fees" ]["ancestor" ] * COIN == 339
307
+ # ancestor vsize at feerate 10 sat/vb = ancestor_fee / 10 = 339 / 10 = 33
308
+ # extra_weight <= (extra vsize * witness factor) = (161 - 33) * 4 = 512
309
+ # additional fee at 10 sat/vb (2.5 sat/wu) = 512 * 2.5 = 1280
310
+ assert len (spend_res_2 ["warnings" ]) == 1
311
+ assert (
312
+ spend_res_2 ["warnings" ][0 ]
313
+ == "An additional fee of 1280 sats has been added to pay for ancestors at the target feerate."
314
+ )
315
+
316
+ # Try 3 sat/vb:
317
+ spend_res_2 = lianad .rpc .createspend ({dest_addr_2 : 10_000 }, [], 3 )
318
+ assert "psbt" in spend_res_2
319
+ spend_psbt_2 = PSBT .from_base64 (spend_res_2 ["psbt" ])
320
+ # The spend is using the unconfirmed change.
321
+ assert spend_psbt_2 .tx .vin [0 ].prevout .hash == uint256_from_str (
322
+ bytes .fromhex (spend_txid_1 )[::- 1 ]
323
+ )
324
+ assert bitcoind .rpc .getmempoolentry (spend_txid_1 )["ancestorsize" ] == 161
325
+ assert bitcoind .rpc .getmempoolentry (spend_txid_1 )["fees" ]["ancestor" ] * COIN == 339
326
+ # ancestor vsize at feerate 3 sat/vb = ancestor_fee / 3 = 339 / 3 = 113
327
+ # extra_weight <= (extra vsize * witness factor) = (161 - 113) * 4 = 192
328
+ # additional fee at 3 sat/vb (0.75 sat/wu) = 192 * 0.75 = 144
329
+ assert len (spend_res_2 ["warnings" ]) == 1
330
+ assert (
331
+ spend_res_2 ["warnings" ][0 ]
332
+ == "An additional fee of 144 sats has been added to pay for ancestors at the target feerate."
333
+ )
334
+
335
+ # 2 sat/vb is same feerate as ancestor and we have no warnings:
284
336
spend_res_2 = lianad .rpc .createspend ({dest_addr_2 : 10_000 }, [], 2 )
285
337
assert "psbt" in spend_res_2
338
+ assert len (spend_res_2 ["warnings" ]) == 0
286
339
spend_psbt_2 = PSBT .from_base64 (spend_res_2 ["psbt" ])
287
340
# The spend is using the unconfirmed change.
288
341
assert spend_psbt_2 .tx .vin [0 ].prevout .hash == uint256_from_str (
289
342
bytes .fromhex (spend_txid_1 )[::- 1 ]
290
343
)
344
+
291
345
# Get another coin to check coin selection with more than one candidate.
292
346
recv_addr_2 = lianad .rpc .getnewaddress ()["address" ]
293
- deposit_2 = bitcoind .rpc .sendtoaddress (recv_addr_2 , 0.0002 ) # 20_000 sats
347
+ deposit_2 = bitcoind .rpc .sendtoaddress (recv_addr_2 , 30_000 / COIN )
294
348
wait_for (lambda : len (lianad .rpc .listcoins (["unconfirmed" ])["coins" ]) == 2 )
295
349
assert (
296
350
len (
@@ -302,33 +356,77 @@ def test_coin_selection(lianad, bitcoind):
302
356
)
303
357
== 1
304
358
)
305
- # As only one unconfirmed coin is change, we have insufficient funds.
306
359
dest_addr_3 = bitcoind .rpc .getnewaddress ()
307
- assert "missing" in lianad .rpc .createspend ({dest_addr_3 : 30_000 }, [], 2 )
308
- # Now confirm both coins.
309
- bitcoind .generate_block (1 , wait_for_mempool = deposit_2 )
310
- wait_for (lambda : len (lianad .rpc .listcoins (["confirmed" ])["coins" ]) == 2 )
311
- spend_res_3 = lianad .rpc .createspend ({dest_addr_3 : 30_000 }, [], 2 )
360
+ # As only one unconfirmed coin is change, we have insufficient funds.
361
+ assert "missing" in lianad .rpc .createspend ({dest_addr_3 : 20_000 }, [], 10 )
362
+
363
+ # If we include both unconfirmed coins manually, it will succeed.
364
+ # We'll need to pay extra for each unconfirmed coin's ancestors.
365
+ outpoints = [c ["outpoint" ] for c in lianad .rpc .listcoins (["unconfirmed" ])["coins" ]]
366
+
367
+ spend_res_3 = lianad .rpc .createspend ({dest_addr_3 : 20_000 }, outpoints , 10 )
312
368
assert "psbt" in spend_res_3
369
+ assert bitcoind .rpc .getmempoolentry (deposit_2 )["ancestorsize" ] == 165
370
+ assert bitcoind .rpc .getmempoolentry (deposit_2 )["fees" ]["ancestor" ] * COIN == 165
371
+ # From above, extra fee for unconfirmed change at 10 sat/vb = 1280.
372
+ # For unconfirmed non-change:
373
+ # ancestor vsize at feerate 10 sat/vb = ancestor_fee / 10 = 165 / 10 = 16
374
+ # extra_weight <= (extra vsize * witness factor) = (165 - 16) * 4 = 596
375
+ # additional fee at 10 sat/vb (2.5 sat/wu) = 596 * 2.5 = 1490
376
+ # Sum of extra ancestor fees = 1280 + 1490 = 2770.
377
+ assert len (spend_res_3 ["warnings" ]) == 1
378
+ assert (
379
+ spend_res_3 ["warnings" ][0 ]
380
+ == "An additional fee of 2770 sats has been added to pay for ancestors at the target feerate."
381
+ )
382
+ spend_psbt_3 = PSBT .from_base64 (spend_res_3 ["psbt" ])
383
+ spend_txid_3 = sign_and_broadcast_psbt (lianad , spend_psbt_3 )
384
+ mempool_txid_3 = bitcoind .rpc .getmempoolentry (spend_txid_3 )
385
+ # The effective feerate of new transaction plus ancestor matches the target.
386
+ # Note that in the mempool entry, "ancestor" includes spend_txid_3 itself.
387
+ assert (
388
+ mempool_txid_3 ["fees" ]["ancestor" ] * COIN // mempool_txid_3 ["ancestorsize" ]
389
+ == 10
390
+ )
391
+ # The spend_txid_3 transaction itself has a higher feerate.
392
+ assert (mempool_txid_3 ["fees" ]["base" ] * COIN ) // mempool_txid_3 ["vsize" ] > 10
393
+ # If we subtract the extra that pays for the ancestor, the feerate is at the target value.
394
+ assert ((mempool_txid_3 ["fees" ]["base" ] * COIN ) - 2770 ) // mempool_txid_3 [
395
+ "vsize"
396
+ ] == 10
397
+
398
+ # Now confirm the spend.
399
+ bitcoind .generate_block (1 , wait_for_mempool = spend_txid_3 )
400
+ wait_for (lambda : len (lianad .rpc .listcoins (["confirmed" ])["coins" ]) == 1 )
401
+
402
+ # Now create the same spend with auto and manual selection:
403
+ dest_addr_4 = bitcoind .rpc .getnewaddress ()
404
+ spend_res_4 = lianad .rpc .createspend ({dest_addr_4 : 15_000 }, [], 2 )
405
+ assert "psbt" in spend_res_4
406
+ assert len (spend_res_4 ["warnings" ]) == 0
313
407
314
408
# The transaction contains a change output.
315
- spend_psbt_3 = PSBT .from_base64 (spend_res_3 ["psbt" ])
316
- assert len (spend_psbt_3 .i ) == 2
317
- assert len (spend_psbt_3 .o ) == 2
318
- assert len (spend_psbt_3 .tx .vout ) == 2
409
+ spend_psbt_4 = PSBT .from_base64 (spend_res_4 ["psbt" ])
410
+ assert len (spend_psbt_4 .i ) == 1
411
+ assert len (spend_psbt_4 .o ) == 2
412
+ assert len (spend_psbt_4 .tx .vout ) == 2
319
413
320
414
# Now create a transaction with manual coin selection using the same outpoints.
321
415
outpoints = [
322
- f"{ txin .prevout .hash :064x} :{ txin .prevout .n } " for txin in spend_psbt_3 .tx .vin
416
+ f"{ txin .prevout .hash :064x} :{ txin .prevout .n } " for txin in spend_psbt_4 .tx .vin
323
417
]
324
- res_manual = lianad .rpc .createspend ({dest_addr_3 : 30_000 }, outpoints , 2 )
418
+ assert len (outpoints ) > 0
419
+ res_manual = lianad .rpc .createspend ({dest_addr_4 : 15_000 }, outpoints , 2 )
420
+ assert len (res_manual ["warnings" ]) == 0
325
421
psbt_manual = PSBT .from_base64 (res_manual ["psbt" ])
326
422
327
423
# Recipient details are the same for both.
328
- assert spend_psbt_3 .tx .vout [0 ].nValue == psbt_manual .tx .vout [0 ].nValue
329
- assert spend_psbt_3 .tx .vout [0 ].scriptPubKey == psbt_manual .tx .vout [0 ].scriptPubKey
330
- # Change amount is the same (change address will be different).
331
- assert spend_psbt_3 .tx .vout [1 ].nValue == psbt_manual .tx .vout [1 ].nValue
424
+ assert spend_psbt_4 .tx .vout [0 ].nValue == psbt_manual .tx .vout [0 ].nValue
425
+ assert spend_psbt_4 .tx .vout [0 ].scriptPubKey == psbt_manual .tx .vout [0 ].scriptPubKey
426
+ # Change details are also the same
427
+ # (change address is same as neither transaction has been broadcast)
428
+ assert spend_psbt_4 .tx .vout [1 ].nValue == psbt_manual .tx .vout [1 ].nValue
429
+ assert spend_psbt_4 .tx .vout [1 ].scriptPubKey == psbt_manual .tx .vout [1 ].scriptPubKey
332
430
333
431
334
432
def test_coin_selection_changeless (lianad , bitcoind ):
0 commit comments