@@ -606,11 +606,15 @@ def test_psbt_input_keys(psbt_input, keys):
606606
607607 assert_raises_rpc_error (- 25 , 'Inputs missing or spent' , self .nodes [0 ].walletprocesspsbt , 'cHNidP8BAJoCAAAAAkvEW8NnDtdNtDpsmze+Ht2LH35IJcKv00jKAlUs21RrAwAAAAD/////S8Rbw2cO1020OmybN74e3Ysffkglwq/TSMoCVSzbVGsBAAAAAP7///8CwLYClQAAAAAWABSNJKzjaUb3uOxixsvh1GGE3fW7zQD5ApUAAAAAFgAUKNw0x8HRctAgmvoevm4u1SbN7XIAAAAAAAEAnQIAAAACczMa321tVHuN4GKWKRncycI22aX3uXgwSFUKM2orjRsBAAAAAP7///9zMxrfbW1Ue43gYpYpGdzJwjbZpfe5eDBIVQozaiuNGwAAAAAA/v///wIA+QKVAAAAABl2qRT9zXUVA8Ls5iVqynLHe5/vSe1XyYisQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAAAAAQEfQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAA==' )
608608
609- # Test that we can fund psbts with external inputs specified
609+ self .log .info ("Test that we can fund psbts with external inputs specified" )
610+
610611 eckey = ECKey ()
611612 eckey .generate ()
612613 privkey = bytes_to_wif (eckey .get_bytes ())
613614
615+ self .nodes [1 ].createwallet ("extfund" )
616+ wallet = self .nodes [1 ].get_wallet_rpc ("extfund" )
617+
614618 # Make a weird but signable script. sh(pkh()) descriptor accomplishes this
615619 desc = descsum_create ("sh(pkh({}))" .format (privkey ))
616620 if self .options .descriptors :
@@ -622,26 +626,97 @@ def test_psbt_input_keys(psbt_input, keys):
622626 addr_info = self .nodes [0 ].getaddressinfo (addr )
623627
624628 self .nodes [0 ].sendtoaddress (addr , 10 )
629+ self .nodes [0 ].sendtoaddress (wallet .getnewaddress (), 10 )
625630 self .generate (self .nodes [0 ], 6 )
626631 ext_utxo = self .nodes [0 ].listunspent (addresses = [addr ])[0 ]
627632
628633 # An external input without solving data should result in an error
629- assert_raises_rpc_error (- 4 , "Insufficient funds" , self . nodes [ 1 ]. walletcreatefundedpsbt , [ext_utxo ], {self .nodes [0 ].getnewaddress (): 10 + ext_utxo [ 'amount' ]}, 0 , { 'add_inputs' : True })
634+ assert_raises_rpc_error (- 4 , "Insufficient funds" , wallet . walletcreatefundedpsbt , [ext_utxo ], {self .nodes [0 ].getnewaddress (): 15 })
630635
631636 # But funding should work when the solving data is provided
632- psbt = self . nodes [ 1 ]. walletcreatefundedpsbt ([ext_utxo ], {self .nodes [0 ].getnewaddress (): 15 }, 0 , {' add_inputs' : True , "solving_data" : {"pubkeys" : [addr_info ['pubkey' ]], "scripts" : [addr_info ["embedded" ]["scriptPubKey" ]]}})
633- signed = self . nodes [ 1 ] .walletprocesspsbt (psbt ['psbt' ])
637+ psbt = wallet . walletcreatefundedpsbt ([ext_utxo ], {self .nodes [0 ].getnewaddress (): 15 }, 0 , {" add_inputs" : True , "solving_data" : {"pubkeys" : [addr_info ['pubkey' ]], "scripts" : [addr_info ["embedded" ]["scriptPubKey" ]]}})
638+ signed = wallet .walletprocesspsbt (psbt ['psbt' ])
634639 assert not signed ['complete' ]
635640 signed = self .nodes [0 ].walletprocesspsbt (signed ['psbt' ])
636641 assert signed ['complete' ]
637642 self .nodes [0 ].finalizepsbt (signed ['psbt' ])
638643
639- psbt = self . nodes [ 1 ]. walletcreatefundedpsbt ([ext_utxo ], {self .nodes [0 ].getnewaddress (): 15 }, 0 , {' add_inputs' : True , "solving_data" :{"descriptors" : [desc ]}})
640- signed = self . nodes [ 1 ] .walletprocesspsbt (psbt ['psbt' ])
644+ psbt = wallet . walletcreatefundedpsbt ([ext_utxo ], {self .nodes [0 ].getnewaddress (): 15 }, 0 , {" add_inputs" : True , "solving_data" :{"descriptors" : [desc ]}})
645+ signed = wallet .walletprocesspsbt (psbt ['psbt' ])
641646 assert not signed ['complete' ]
642647 signed = self .nodes [0 ].walletprocesspsbt (signed ['psbt' ])
643648 assert signed ['complete' ]
644- self .nodes [0 ].finalizepsbt (signed ['psbt' ])
649+ final = self .nodes [0 ].finalizepsbt (signed ['psbt' ], False )
650+
651+ dec = self .nodes [0 ].decodepsbt (signed ["psbt" ])
652+ for i , txin in enumerate (dec ["tx" ]["vin" ]):
653+ if txin ["txid" ] == ext_utxo ["txid" ] and txin ["vout" ] == ext_utxo ["vout" ]:
654+ input_idx = i
655+ break
656+ psbt_in = dec ["inputs" ][input_idx ]
657+ # Calculate the input weight
658+ # (prevout + sequence + length of scriptSig + 2 bytes buffer) * 4 + len of scriptwitness
659+ len_scriptsig = len (psbt_in ["final_scriptSig" ]["hex" ]) // 2 if "final_scriptSig" in psbt_in else 0
660+ len_scriptwitness = len (psbt_in ["final_scriptwitness" ]["hex" ]) // 2 if "final_scriptwitness" in psbt_in else 0
661+ input_weight = ((41 + len_scriptsig + 2 ) * 4 ) + len_scriptwitness
662+ low_input_weight = input_weight // 2
663+ high_input_weight = input_weight * 2
664+
665+ # Input weight error conditions
666+ assert_raises_rpc_error (
667+ - 8 ,
668+ "Input weights should be specified in inputs rather than in options." ,
669+ wallet .walletcreatefundedpsbt ,
670+ inputs = [ext_utxo ],
671+ outputs = {self .nodes [0 ].getnewaddress (): 15 },
672+ options = {"input_weights" : [{"txid" : ext_utxo ["txid" ], "vout" : ext_utxo ["vout" ], "weight" : 1000 }]}
673+ )
674+
675+ # Funding should also work if the input weight is provided
676+ psbt = wallet .walletcreatefundedpsbt (
677+ inputs = [{"txid" : ext_utxo ["txid" ], "vout" : ext_utxo ["vout" ], "weight" : input_weight }],
678+ outputs = {self .nodes [0 ].getnewaddress (): 15 },
679+ options = {"add_inputs" : True }
680+ )
681+ signed = wallet .walletprocesspsbt (psbt ["psbt" ])
682+ signed = self .nodes [0 ].walletprocesspsbt (signed ["psbt" ])
683+ final = self .nodes [0 ].finalizepsbt (signed ["psbt" ])
684+ assert self .nodes [0 ].testmempoolaccept ([final ["hex" ]])[0 ]["allowed" ]
685+ # Reducing the weight should have a lower fee
686+ psbt2 = wallet .walletcreatefundedpsbt (
687+ inputs = [{"txid" : ext_utxo ["txid" ], "vout" : ext_utxo ["vout" ], "weight" : low_input_weight }],
688+ outputs = {self .nodes [0 ].getnewaddress (): 15 },
689+ options = {"add_inputs" : True }
690+ )
691+ assert_greater_than (psbt ["fee" ], psbt2 ["fee" ])
692+ # Increasing the weight should have a higher fee
693+ psbt2 = wallet .walletcreatefundedpsbt (
694+ inputs = [{"txid" : ext_utxo ["txid" ], "vout" : ext_utxo ["vout" ], "weight" : high_input_weight }],
695+ outputs = {self .nodes [0 ].getnewaddress (): 15 },
696+ options = {"add_inputs" : True }
697+ )
698+ assert_greater_than (psbt2 ["fee" ], psbt ["fee" ])
699+ # The provided weight should override the calculated weight when solving data is provided
700+ psbt3 = wallet .walletcreatefundedpsbt (
701+ inputs = [{"txid" : ext_utxo ["txid" ], "vout" : ext_utxo ["vout" ], "weight" : high_input_weight }],
702+ outputs = {self .nodes [0 ].getnewaddress (): 15 },
703+ options = {'add_inputs' : True , "solving_data" :{"descriptors" : [desc ]}}
704+ )
705+ assert_equal (psbt2 ["fee" ], psbt3 ["fee" ])
706+
707+ # Import the external utxo descriptor so that we can sign for it from the test wallet
708+ if self .options .descriptors :
709+ res = wallet .importdescriptors ([{"desc" : desc , "timestamp" : "now" }])
710+ else :
711+ res = wallet .importmulti ([{"desc" : desc , "timestamp" : "now" }])
712+ assert res [0 ]["success" ]
713+ # The provided weight should override the calculated weight for a wallet input
714+ psbt3 = wallet .walletcreatefundedpsbt (
715+ inputs = [{"txid" : ext_utxo ["txid" ], "vout" : ext_utxo ["vout" ], "weight" : high_input_weight }],
716+ outputs = {self .nodes [0 ].getnewaddress (): 15 },
717+ options = {"add_inputs" : True }
718+ )
719+ assert_equal (psbt2 ["fee" ], psbt3 ["fee" ])
645720
646721if __name__ == '__main__' :
647722 PSBTTest ().main ()
0 commit comments