@@ -287,6 +287,19 @@ def test_txprepare_multi(node_factory, bitcoind):
287
287
l1 .rpc .txdiscard (prep ['txid' ])
288
288
289
289
290
+ def feerate_from_psbt (bitcoind , node , psbt ):
291
+ # signpsbt insists they are reserved!
292
+ node .rpc .reserveinputs (psbt , exclusive = False )
293
+ final = node .rpc .dev_finalizepsbt (node .rpc .signpsbt (psbt )['signed_psbt' ])
294
+ node .rpc .unreserveinputs (psbt )
295
+ psbt = node .rpc .setpsbtversion (final ['psbt' ], 0 )['psbt' ]
296
+ # analyzepsbt gives a vsize, but not a weight!
297
+ # e.g. 'estimated_vsize': 356, 'estimated_feerate': Decimal('0.00030042'), 'fee': Decimal('0.00010695')
298
+ fee = int (bitcoind .rpc .analyzepsbt (psbt )['fee' ] * 100_000_000 )
299
+ weight = bitcoind .rpc .decoderawtransaction (final ['tx' ])['weight' ]
300
+ return fee / weight * 1000
301
+
302
+
290
303
def test_txprepare (node_factory , bitcoind , chainparams ):
291
304
amount = 1000000
292
305
l1 = node_factory .get_node (random_hsm = True )
@@ -299,13 +312,18 @@ def test_txprepare(node_factory, bitcoind, chainparams):
299
312
300
313
bitcoind .generate_block (1 )
301
314
wait_for (lambda : len (l1 .rpc .listfunds ()['outputs' ]) == 10 )
315
+ for est in l1 .rpc .feerates ('perkw' )['perkw' ]['estimates' ]:
316
+ if est ['blockcount' ] == 12 :
317
+ normal_feerate_perkw = est ['feerate' ]
302
318
303
319
prep = l1 .rpc .txprepare (outputs = [{addr : Millisatoshi (amount * 3 * 1000 )}])
304
320
decode = bitcoind .rpc .decoderawtransaction (prep ['unsigned_tx' ])
305
321
assert decode ['txid' ] == prep ['txid' ]
306
322
# 4 inputs, 2 outputs (3 if we have a fee output).
307
323
assert len (decode ['vin' ]) == 4
308
324
assert len (decode ['vout' ]) == 2 if not chainparams ['feeoutput' ] else 3
325
+ # Feerate should be ~ as we asked for
326
+ assert normal_feerate_perkw - 2 < feerate_from_psbt (bitcoind , l1 , prep ['psbt' ]) < normal_feerate_perkw + 2
309
327
310
328
# One output will be correct.
311
329
outnum = [i for i , o in enumerate (decode ['vout' ]) if o ['value' ] == Decimal (amount * 3 ) / 10 ** 8 ][0 ]
@@ -333,6 +351,8 @@ def test_txprepare(node_factory, bitcoind, chainparams):
333
351
assert decode ['vout' ][0 ]['value' ] > Decimal (amount * 6 ) / 10 ** 8 - Decimal (0.0002 )
334
352
assert decode ['vout' ][0 ]['scriptPubKey' ]['type' ] == 'witness_v0_keyhash'
335
353
assert scriptpubkey_addr (decode ['vout' ][0 ]['scriptPubKey' ]) == addr
354
+ # Feerate should be ~ as we asked for
355
+ assert normal_feerate_perkw - 2 < feerate_from_psbt (bitcoind , l1 , prep2 ['psbt' ]) < normal_feerate_perkw + 2
336
356
337
357
# If I cancel the first one, I can get those first 4 outputs.
338
358
discard = l1 .rpc .txdiscard (prep ['txid' ])
@@ -351,6 +371,8 @@ def test_txprepare(node_factory, bitcoind, chainparams):
351
371
assert decode ['vout' ][0 ]['value' ] > Decimal (amount * 4 ) / 10 ** 8 - Decimal (0.0002 )
352
372
assert decode ['vout' ][0 ]['scriptPubKey' ]['type' ] == 'witness_v0_keyhash'
353
373
assert scriptpubkey_addr (decode ['vout' ][0 ]['scriptPubKey' ]) == addr
374
+ # Feerate should be ~ as we asked for
375
+ assert normal_feerate_perkw - 1 < feerate_from_psbt (bitcoind , l1 , prep3 ['psbt' ]) < normal_feerate_perkw + 1
354
376
355
377
# Cannot discard twice.
356
378
with pytest .raises (RpcError , match = r'not an unreleased txid' ):
@@ -371,20 +393,28 @@ def test_txprepare(node_factory, bitcoind, chainparams):
371
393
assert decode ['vout' ][0 ]['value' ] > Decimal (amount * 10 ) / 10 ** 8 - Decimal (0.0003 )
372
394
assert decode ['vout' ][0 ]['scriptPubKey' ]['type' ] == 'witness_v0_keyhash'
373
395
assert scriptpubkey_addr (decode ['vout' ][0 ]['scriptPubKey' ]) == addr
396
+ # Feerate should be ~ as we asked for
397
+ assert normal_feerate_perkw - 2 < feerate_from_psbt (bitcoind , l1 , prep4 ['psbt' ]) < normal_feerate_perkw + 2
374
398
l1 .rpc .txdiscard (prep4 ['txid' ])
375
399
376
400
# Try passing in a utxo set
377
401
utxos = [utxo ["txid" ] + ":" + str (utxo ["output" ])
378
402
for utxo in l1 .rpc .listfunds ()["outputs" ]][:4 ]
379
403
prep5 = l1 .rpc .txprepare ([{addr :
380
404
Millisatoshi (amount * 3.5 * 1000 )}], utxos = utxos )
405
+ # Feerate should be ~ as we asked for
406
+ assert normal_feerate_perkw - 2 < feerate_from_psbt (bitcoind , l1 , prep3 ['psbt' ]) < normal_feerate_perkw + 2
381
407
382
408
# Try passing unconfirmed utxos
383
409
unconfirmed_utxo = l1 .rpc .withdraw (l1 .rpc .newaddr ()["bech32" ], 10 ** 5 )
384
410
uutxos = [unconfirmed_utxo ["txid" ] + ":0" ]
385
411
with pytest .raises (RpcError , match = r"Could not afford" ):
386
412
l1 .rpc .txprepare ([{addr : Millisatoshi (amount * 3.5 * 1000 )}],
387
413
utxos = uutxos )
414
+ # Feerate should be ~ as we asked for
415
+ unconfirmed_tx = bitcoind .rpc .getrawmempool (True )[unconfirmed_utxo ["txid" ]]
416
+ feerate_perkw = int (unconfirmed_tx ['fees' ]['base' ] * 100_000_000 ) * 1000 / unconfirmed_tx ['weight' ]
417
+ assert normal_feerate_perkw - 1 < feerate_perkw < normal_feerate_perkw + 1
388
418
389
419
decode = bitcoind .rpc .decoderawtransaction (prep5 ['unsigned_tx' ])
390
420
assert decode ['txid' ] == prep5 ['txid' ]
@@ -413,12 +443,16 @@ def test_txprepare(node_factory, bitcoind, chainparams):
413
443
# You can have one which is all, but not two.
414
444
prep5 = l1 .rpc .txprepare ([{addr : Millisatoshi (amount * 3 * 1000 )},
415
445
{addr : 'all' }])
446
+ # Feerate should be ~ as we asked for
447
+ assert normal_feerate_perkw - 2 < feerate_from_psbt (bitcoind , l1 , prep5 ['psbt' ]) < normal_feerate_perkw + 2
416
448
l1 .rpc .txdiscard (prep5 ['txid' ])
417
449
with pytest .raises (RpcError , match = r"'all'" ):
418
450
prep5 = l1 .rpc .txprepare ([{addr : 'all' }, {addr : 'all' }])
419
451
420
452
prep5 = l1 .rpc .txprepare ([{addr : Millisatoshi (amount * 3 * 500 + 100000 )},
421
453
{addr : Millisatoshi (amount * 3 * 500 - 100000 )}])
454
+ # Feerate should be ~ as we asked for
455
+ assert normal_feerate_perkw - 2 < feerate_from_psbt (bitcoind , l1 , prep5 ['psbt' ]) < normal_feerate_perkw + 2
422
456
decode = bitcoind .rpc .decoderawtransaction (prep5 ['unsigned_tx' ])
423
457
assert decode ['txid' ] == prep5 ['txid' ]
424
458
# 4 inputs, 3 outputs(include change).
@@ -445,6 +479,91 @@ def test_txprepare(node_factory, bitcoind, chainparams):
445
479
else :
446
480
assert decode ['vout' ][changenum ]['scriptPubKey' ]['type' ] == 'witness_v1_taproot'
447
481
482
+ l1 .rpc .txdiscard (prep5 ['txid' ])
483
+
484
+
485
+ def test_txprepare_feerate (node_factory , bitcoind , chainparams ):
486
+ # Make sure it works at different feerates!
487
+ l1 , l2 = node_factory .get_nodes (2 )
488
+
489
+ # Add some funds to withdraw later
490
+ for i in range (20 ):
491
+ bitcoind .rpc .sendtoaddress (l1 .rpc .newaddr ()['bech32' ],
492
+ 1000 / 10 ** 8 )
493
+
494
+ bitcoind .generate_block (1 )
495
+ out_addrs = l2 .rpc .newaddr ('all' )
496
+ wait_for (lambda : len (l1 .rpc .listfunds ()['outputs' ]) == 20 )
497
+
498
+ for addrtype in ('bech32' , 'p2tr' ):
499
+ for feerate in range (255 , 1000 , 250 ):
500
+ prep = l1 .rpc .txprepare ([{out_addrs [addrtype ]: Millisatoshi (9000 )}], f"{ feerate } perkw" )
501
+ actual_feerate = feerate_from_psbt (bitcoind , l1 , prep ['psbt' ])
502
+ assert feerate - 2 < actual_feerate
503
+ # Feerate can be larger, if it chose not to give change output.
504
+ if chainparams ['elements' ]:
505
+ fee_output = 1
506
+ else :
507
+ fee_output = 0
508
+ if len (bitcoind .rpc .decoderawtransaction (prep ['unsigned_tx' ])['vout' ]) == 1 + 1 + fee_output :
509
+ assert actual_feerate < feerate + 2
510
+ l1 .rpc .txdiscard (prep ['txid' ])
511
+
512
+
513
+ @pytest .mark .parametrize ("addrtype" , ["bech32" , "p2tr" ])
514
+ def test_fundpsbt_feerates (node_factory , bitcoind , chainparams , addrtype ):
515
+ if chainparams ['elements' ] and addrtype == 'p2tr' :
516
+ pytest .skip ('No p2tr for elements' )
517
+
518
+ l1 = node_factory .get_node ()
519
+
520
+ # Add some funds to withdraw later
521
+ for i in range (20 ):
522
+ bitcoind .rpc .sendtoaddress (l1 .rpc .newaddr (addrtype )[addrtype ],
523
+ 5000 / 10 ** 8 )
524
+
525
+ # See utxo_spend_weight()
526
+ if addrtype == 'bech32' :
527
+ witness_weight = 1 + 71 + 1 + 33
528
+ elif addrtype == 'p2tr' :
529
+ witness_weight = 1 + 64
530
+ else :
531
+ assert False
532
+
533
+ input_weight = 1 + witness_weight + (32 + 4 + 4 + 1 ) * 4
534
+ if chainparams ['elements' ]:
535
+ input_weight += 6
536
+
537
+ bitcoind .generate_block (1 )
538
+ wait_for (lambda : len (l1 .rpc .listfunds ()['outputs' ]) == 20 )
539
+
540
+ # version, input count, output count, locktime, segwit marker, flag
541
+ base_weight = (4 + 1 + 1 + 4 ) * 4 + 1 + 1
542
+ if chainparams ['elements' ]:
543
+ # Elements has empty surjection and rangeproof, and fee output
544
+ base_weight += 2 * 4 + (32 + 1 + 1 + 1 ) * 4
545
+ # Bech32 change output
546
+ change_weight = (8 + 1 + (1 + 1 + 20 ) + (32 + 1 + 1 + 1 )) * 4
547
+ else :
548
+ # P2TR output
549
+ change_weight = (8 + 1 + (1 + 1 + 32 )) * 4
550
+
551
+ # Both minimal and higher feerate
552
+ for feerate in (253 , 1000 ):
553
+ # Try with both 1 and 2 inputs
554
+ for amount , num_inputs in ((260 , 1 ), (5000 , 2 )):
555
+ prep = l1 .rpc .fundpsbt (amount , f"{ feerate } perkw" , base_weight , excess_as_change = True )
556
+ assert prep ['estimated_final_weight' ] == base_weight + change_weight + input_weight * num_inputs
557
+ signed = l1 .rpc .signpsbt (prep ['psbt' ])['signed_psbt' ]
558
+ sent = l1 .rpc .sendpsbt (signed )
559
+ txinfo = bitcoind .rpc .getmempoolentry (sent ['txid' ])
560
+ assert txinfo ['weight' ] == prep ['estimated_final_weight' ]
561
+ # We never actually added that `amount` output to PSBT, so that appears as "fee"
562
+ fee = int (txinfo ['fees' ]['base' ] * 100_000_000 ) - amount
563
+ actual_feerate = fee / (txinfo ['weight' ] / 1000 )
564
+ # Out by one errors (due to rounding) change feerate by 2.
565
+ assert feerate - 2 < actual_feerate < feerate + 2
566
+
448
567
449
568
def test_reserveinputs (node_factory , bitcoind , chainparams ):
450
569
amount = 1000000
0 commit comments