1- use serde_json:: json;
21use std:: cmp;
3- use std:: collections:: HashMap ;
42use std:: env;
53use std:: fmt;
64use std:: str:: FromStr ;
@@ -10,11 +8,10 @@ use anyhow::bail;
108use anyhow:: Context ;
119use bdk_chain:: bitcoin:: {
1210 absolute, address:: NetworkUnchecked , bip32, consensus, constants, hex:: DisplayHex , relative,
13- secp256k1:: Secp256k1 , transaction, Address , Amount , Network , NetworkKind , PrivateKey , Psbt ,
14- PublicKey , Sequence , Transaction , TxIn , TxOut ,
11+ secp256k1:: Secp256k1 , Address , Amount , Network , NetworkKind , Psbt , Transaction , TxOut , Txid ,
1512} ;
1613use bdk_chain:: miniscript:: {
17- descriptor:: { DescriptorSecretKey , SinglePubKey } ,
14+ descriptor:: DefiniteDescriptorKey ,
1815 plan:: { Assets , Plan } ,
1916 psbt:: PsbtExt ,
2017 Descriptor , DescriptorPublicKey , ForEachKey ,
@@ -27,12 +24,14 @@ use bdk_chain::{
2724 tx_graph, ChainOracle , DescriptorExt , FullTxOut , IndexedTxGraph , Merge ,
2825} ;
2926use bdk_coin_select:: {
30- metrics:: LowestFee , Candidate , ChangePolicy , CoinSelector , DrainWeights , FeeRate , Target ,
31- TargetFee , TargetOutputs ,
27+ metrics, Candidate , ChangePolicy , CoinSelector , DrainWeights , FeeRate , Target , TargetFee ,
28+ TargetOutputs ,
3229} ;
3330use bdk_file_store:: Store ;
31+ use bdk_tx:: { self , DataProvider , Signer } ;
3432use clap:: { Parser , Subcommand } ;
3533use rand:: prelude:: * ;
34+ use serde_json:: json;
3635
3736pub use anyhow;
3837pub use clap;
@@ -263,21 +262,19 @@ pub fn create_tx<O: ChainOracle>(
263262 graph : & mut KeychainTxGraph ,
264263 chain : & O ,
265264 assets : & Assets ,
266- cs_algorithm : CoinSelectionAlgo ,
265+ coin_selection : CoinSelectionAlgo ,
267266 address : Address ,
268267 value : u64 ,
269268 feerate : f32 ,
270269) -> anyhow:: Result < ( Psbt , Option < ChangeInfo > ) >
271270where
272271 O :: Error : std:: error:: Error + Send + Sync + ' static ,
273272{
274- let mut changeset = keychain_txout:: ChangeSet :: default ( ) ;
275-
276273 // get planned utxos
277274 let mut plan_utxos = planned_utxos ( graph, chain, assets) ?;
278275
279- // sort utxos if cs-algo requires it
280- match cs_algorithm {
276+ // sort utxos if coin selection requires it
277+ match coin_selection {
281278 CoinSelectionAlgo :: LargestFirst => {
282279 plan_utxos. sort_by_key ( |( _, utxo) | cmp:: Reverse ( utxo. txout . value ) )
283280 }
@@ -301,120 +298,109 @@ where
301298 } )
302299 . collect ( ) ;
303300
304- // create recipient output(s)
305- let mut outputs = vec ! [ TxOut {
306- value: Amount :: from_sat( value) ,
307- script_pubkey: address. script_pubkey( ) ,
308- } ] ;
301+ // if this is a segwit spend, don't bother setting the input `non_witness_utxo`
302+ let only_witness_utxo = candidates. iter ( ) . all ( |c| c. is_segwit ) ;
309303
310304 let ( change_keychain, _) = graph
311305 . index
312306 . keychains ( )
313307 . last ( )
314308 . expect ( "must have a keychain" ) ;
315309
316- let ( ( change_index, change_script) , index_changeset) = graph
310+ // get drain script
311+ let ( ( drain_index, drain_spk) , index_changeset) = graph
317312 . index
318313 . next_unused_spk ( change_keychain)
319314 . expect ( "Must exist" ) ;
320- changeset. merge ( index_changeset) ;
321-
322- let mut change_output = TxOut {
323- value : Amount :: ZERO ,
324- script_pubkey : change_script,
325- } ;
326315
327316 let change_desc = graph
328317 . index
329318 . keychains ( )
330319 . find ( |( k, _) | k == & change_keychain)
331320 . expect ( "must exist" )
332321 . 1 ;
322+ let min_value = 3 * change_desc. dust_value ( ) . to_sat ( ) ;
323+
324+ let change_policy = ChangePolicy {
325+ min_value,
326+ drain_weights : DrainWeights :: TR_KEYSPEND ,
327+ } ;
328+
329+ let mut builder = bdk_tx:: Builder :: new ( ) ;
333330
334- let min_drain_value = change_desc. dust_value ( ) . to_sat ( ) ;
331+ let locktime = assets
332+ . absolute_timelock
333+ . unwrap_or ( absolute:: LockTime :: from_consensus (
334+ chain. get_chain_tip ( ) ?. height ,
335+ ) ) ;
336+ builder. locktime ( locktime) ;
335337
338+ // fund outputs
339+ builder. add_outputs ( [ ( address. script_pubkey ( ) , Amount :: from_sat ( value) ) ] ) ;
336340 let target = Target {
337341 outputs : TargetOutputs :: fund_outputs (
338- outputs
339- . iter ( )
340- . map ( |output | ( output . weight ( ) . to_wu ( ) , output . value . to_sat ( ) ) ) ,
342+ builder
343+ . target_outputs ( )
344+ . map ( |( w , v ) | ( w . to_wu ( ) , v . to_sat ( ) ) ) ,
341345 ) ,
342346 fee : TargetFee {
343347 rate : FeeRate :: from_sat_per_vb ( feerate) ,
344348 ..Default :: default ( )
345349 } ,
346350 } ;
347351
348- let change_policy = ChangePolicy {
349- min_value : min_drain_value,
350- drain_weights : DrainWeights :: TR_KEYSPEND ,
351- } ;
352-
353- // run coin selection
352+ // select coins
354353 let mut selector = CoinSelector :: new ( & candidates) ;
355- match cs_algorithm {
356- CoinSelectionAlgo :: BranchAndBound => {
357- let metric = LowestFee {
358- target,
359- long_term_feerate : FeeRate :: from_sat_per_vb ( 10.0 ) ,
360- change_policy,
361- } ;
362- match selector. run_bnb ( metric, 10_000 ) {
363- Ok ( _) => { }
364- Err ( _) => selector
365- . select_until_target_met ( target)
366- . context ( "selecting coins" ) ?,
367- }
354+ if let CoinSelectionAlgo :: BranchAndBound = coin_selection {
355+ let metric = metrics:: Changeless {
356+ target,
357+ change_policy,
358+ } ;
359+ if selector. run_bnb ( metric, 10_000 ) . is_err ( ) {
360+ selector. select_until_target_met ( target) ?;
368361 }
369- _ => selector
370- . select_until_target_met ( target)
371- . context ( "selecting coins" ) ?,
362+ } else {
363+ selector. select_until_target_met ( target) ?;
372364 }
373365
374- // get the selected plan utxos
375- let selected: Vec < _ > = selector. apply_selection ( & plan_utxos) . collect ( ) ;
366+ // apply selection
367+ let selection = selector
368+ . apply_selection ( & plan_utxos)
369+ . cloned ( )
370+ . map ( |( plan, txo) | bdk_tx:: PlanUtxo {
371+ plan,
372+ outpoint : txo. outpoint ,
373+ txout : txo. txout ,
374+ } ) ;
375+ builder. add_inputs ( selection) ;
376376
377- // if the selection tells us to use change and the change value is sufficient, we add it as an output
378- let mut change_info = Option :: < ChangeInfo > :: None ;
377+ // if the selection tells us to use change, we add it as an output
378+ // and fill in the change_info.
379+ let mut change_info = None ;
379380 let drain = selector. drain ( target, change_policy) ;
380- if drain. value > min_drain_value {
381- change_output. value = Amount :: from_sat ( drain. value ) ;
382- outputs. push ( change_output) ;
381+ if drain. is_some ( ) {
382+ builder. add_change_output ( drain_spk, Amount :: from_sat ( drain. value ) ) ;
383383 change_info = Some ( ChangeInfo {
384384 change_keychain,
385- indexer : changeset ,
386- index : change_index ,
385+ indexer : index_changeset ,
386+ index : drain_index ,
387387 } ) ;
388- outputs. shuffle ( & mut thread_rng ( ) ) ;
389388 }
390389
391- let unsigned_tx = Transaction {
392- version : transaction:: Version :: TWO ,
393- lock_time : assets
394- . absolute_timelock
395- . unwrap_or ( absolute:: LockTime :: from_height (
396- chain. get_chain_tip ( ) ?. height ,
397- ) ?) ,
398- input : selected
399- . iter ( )
400- . map ( |( plan, utxo) | TxIn {
401- previous_output : utxo. outpoint ,
402- sequence : plan
403- . relative_timelock
404- . map_or ( Sequence :: ENABLE_RBF_NO_LOCKTIME , Sequence :: from) ,
405- ..Default :: default ( )
406- } )
407- . collect ( ) ,
408- output : outputs,
390+ // build psbt
391+ let mut provider = Provider {
392+ graph,
393+ rng : & mut thread_rng ( ) ,
409394 } ;
410395
411- // update psbt with plan
412- let mut psbt = Psbt :: from_unsigned_tx ( unsigned_tx) ?;
413- for ( i, ( plan, utxo) ) in selected. iter ( ) . enumerate ( ) {
414- let psbt_input = & mut psbt. inputs [ i] ;
415- plan. update_psbt_input ( psbt_input) ;
416- psbt_input. witness_utxo = Some ( utxo. txout . clone ( ) ) ;
417- }
396+ let mut updater = builder. build_psbt ( & mut provider) ?;
397+ let opt = bdk_tx:: UpdateOptions {
398+ only_witness_utxo,
399+ update_with_descriptor : true ,
400+ ..Default :: default ( )
401+ } ;
402+ updater. update_psbt ( & provider, opt) ?;
403+ let ( psbt, _) = updater. into_finalizer ( ) ;
418404
419405 Ok ( ( psbt, change_info) )
420406}
@@ -684,26 +670,11 @@ pub fn handle_commands<CS: clap::Subcommand, S: clap::Args>(
684670 let secp = Secp256k1 :: new ( ) ;
685671 let ( _, keymap) = Descriptor :: parse_descriptor ( & secp, & desc_str) ?;
686672 if keymap. is_empty ( ) {
687- bail ! ( "unable to sign" )
673+ bail ! ( "unable to sign" ) ;
688674 }
689-
690- // note: we're only looking at the first entry in the keymap
691- // the idea is to find something that impls `GetKey`
692- let sign_res = match keymap. iter ( ) . next ( ) . expect ( "not empty" ) {
693- ( DescriptorPublicKey :: Single ( single_pub) , DescriptorSecretKey :: Single ( prv) ) => {
694- let pk = match single_pub. key {
695- SinglePubKey :: FullKey ( pk) => pk,
696- SinglePubKey :: XOnly ( _) => unimplemented ! ( "single xonly pubkey" ) ,
697- } ;
698- let keys: HashMap < PublicKey , PrivateKey > = [ ( pk, prv. key ) ] . into ( ) ;
699- psbt. sign ( & keys, & secp)
700- }
701- ( _, DescriptorSecretKey :: XPrv ( k) ) => psbt. sign ( & k. xkey , & secp) ,
702- _ => unimplemented ! ( "multi xkey signer" ) ,
703- } ;
704-
705- let _ = sign_res
706- . map_err ( |errors| anyhow:: anyhow!( "failed to sign PSBT {:?}" , errors) ) ?;
675+ let _ = psbt
676+ . sign ( & Signer ( keymap) , & secp)
677+ . map_err ( |( _, errors) | anyhow:: anyhow!( "failed to sign PSBT {:?}" , errors) ) ?;
707678
708679 let mut obj = serde_json:: Map :: new ( ) ;
709680 obj. insert ( "psbt" . to_string ( ) , json ! ( psbt. to_string( ) ) ) ;
@@ -776,6 +747,36 @@ pub fn handle_commands<CS: clap::Subcommand, S: clap::Args>(
776747 }
777748}
778749
750+ /// Helper struct for providing transaction data to tx builder
751+ struct Provider < ' a > {
752+ graph : & ' a KeychainTxGraph ,
753+ rng : & ' a mut dyn RngCore ,
754+ }
755+
756+ impl DataProvider for Provider < ' _ > {
757+ fn get_tx ( & self , txid : Txid ) -> Option < Transaction > {
758+ self . graph
759+ . graph ( )
760+ . get_tx ( txid)
761+ . map ( |tx| tx. as_ref ( ) . clone ( ) )
762+ }
763+
764+ fn get_descriptor_for_txout ( & self , txout : & TxOut ) -> Option < Descriptor < DefiniteDescriptorKey > > {
765+ let & ( keychain, index) = self . graph . index . index_of_spk ( txout. script_pubkey . clone ( ) ) ?;
766+ let desc = self
767+ . graph
768+ . index
769+ . get_descriptor ( keychain)
770+ . expect ( "must have descriptor" ) ;
771+ desc. at_derivation_index ( index) . ok ( )
772+ }
773+
774+ fn sort_transaction ( & mut self , tx : & mut Transaction ) {
775+ tx. input . shuffle ( self . rng ) ;
776+ tx. output . shuffle ( self . rng ) ;
777+ }
778+ }
779+
779780/// The initial state returned by [`init_or_load`].
780781pub struct Init < CS : clap:: Subcommand , S : clap:: Args > {
781782 /// CLI args
0 commit comments