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