@@ -36,7 +36,7 @@ use bdk_chain::{
3636use bitcoin:: {
3737 absolute,
3838 consensus:: encode:: serialize,
39- constants:: { genesis_block, COINBASE_MATURITY } ,
39+ constants:: genesis_block,
4040 psbt,
4141 secp256k1:: Secp256k1 ,
4242 sighash:: { EcdsaSighashType , TapSighashType } ,
@@ -1417,8 +1417,19 @@ impl Wallet {
14171417
14181418 fee_amount += fee_rate * tx. weight ( ) ;
14191419
1420- let ( required_utxos, optional_utxos) =
1421- self . preselect_utxos ( & params, Some ( current_height. to_consensus_u32 ( ) ) ) ;
1420+ let ( required_utxos, optional_utxos) = {
1421+ // NOTE: manual selection overrides unspendable
1422+ let mut required: Vec < WeightedUtxo > = params. utxos . values ( ) . cloned ( ) . collect ( ) ;
1423+ let optional = self . filter_utxos ( & params, current_height. to_consensus_u32 ( ) ) ;
1424+
1425+ // if drain_wallet is true, all UTxOs are required
1426+ if params. drain_wallet {
1427+ required. extend ( optional) ;
1428+ ( required, vec ! [ ] )
1429+ } else {
1430+ ( required, optional)
1431+ }
1432+ } ;
14221433
14231434 // get drain script
14241435 let mut drain_index = Option :: < ( KeychainKind , u32 ) > :: None ;
@@ -1447,9 +1458,6 @@ impl Wallet {
14471458 }
14481459 } ;
14491460
1450- let ( required_utxos, optional_utxos) =
1451- coin_selection:: filter_duplicates ( required_utxos, optional_utxos) ;
1452-
14531461 let coin_selection = coin_selection
14541462 . coin_select (
14551463 required_utxos,
@@ -1618,60 +1626,71 @@ impl Wallet {
16181626 . map_err ( |_| BuildFeeBumpError :: FeeRateUnavailable ) ?;
16191627
16201628 // remove the inputs from the tx and process them
1621- let original_txin = tx. input . drain ( .. ) . collect :: < Vec < _ > > ( ) ;
1622- let original_utxos = original_txin
1623- . iter ( )
1629+ let utxos = tx
1630+ . input
1631+ . drain ( .. )
16241632 . map ( |txin| -> Result < _ , BuildFeeBumpError > {
1625- let prev_tx = graph
1633+ graph
1634+ // Get previous transaction
16261635 . get_tx ( txin. previous_output . txid )
1627- . ok_or ( BuildFeeBumpError :: UnknownUtxo ( txin. previous_output ) ) ?;
1628- let txout = & prev_tx. output [ txin. previous_output . vout as usize ] ;
1629-
1630- let chain_position = chain_positions
1631- . get ( & txin. previous_output . txid )
1632- . cloned ( )
1633- . ok_or ( BuildFeeBumpError :: UnknownUtxo ( txin. previous_output ) ) ?;
1634-
1635- let weighted_utxo = match txout_index. index_of_spk ( txout. script_pubkey . clone ( ) ) {
1636- Some ( & ( keychain, derivation_index) ) => {
1637- let satisfaction_weight = self
1638- . public_descriptor ( keychain)
1639- . max_weight_to_satisfy ( )
1640- . unwrap ( ) ;
1641- WeightedUtxo {
1642- utxo : Utxo :: Local ( LocalOutput {
1643- outpoint : txin. previous_output ,
1644- txout : txout. clone ( ) ,
1645- keychain,
1646- is_spent : true ,
1647- derivation_index,
1648- chain_position,
1649- } ) ,
1650- satisfaction_weight,
1651- }
1652- }
1653- None => {
1654- let satisfaction_weight = Weight :: from_wu_usize (
1655- serialize ( & txin. script_sig ) . len ( ) * 4 + serialize ( & txin. witness ) . len ( ) ,
1656- ) ;
1657- WeightedUtxo {
1658- utxo : Utxo :: Foreign {
1659- outpoint : txin. previous_output ,
1660- sequence : txin. sequence ,
1661- psbt_input : Box :: new ( psbt:: Input {
1662- witness_utxo : Some ( txout. clone ( ) ) ,
1663- non_witness_utxo : Some ( prev_tx. as_ref ( ) . clone ( ) ) ,
1664- ..Default :: default ( )
1665- } ) ,
1666- } ,
1667- satisfaction_weight,
1636+ . ok_or ( BuildFeeBumpError :: UnknownUtxo ( txin. previous_output ) )
1637+ // Get chain position
1638+ . and_then ( |prev_tx| {
1639+ chain_positions
1640+ . get ( & txin. previous_output . txid )
1641+ . cloned ( )
1642+ . ok_or ( BuildFeeBumpError :: UnknownUtxo ( txin. previous_output ) )
1643+ . map ( |chain_position| ( prev_tx, chain_position) )
1644+ } )
1645+ . map ( |( prev_tx, chain_position) | {
1646+ let txout = prev_tx. output [ txin. previous_output . vout as usize ] . clone ( ) ;
1647+ match txout_index. index_of_spk ( txout. script_pubkey . clone ( ) ) {
1648+ Some ( & ( keychain, derivation_index) ) => (
1649+ txin. previous_output ,
1650+ WeightedUtxo {
1651+ satisfaction_weight : self
1652+ . public_descriptor ( keychain)
1653+ . max_weight_to_satisfy ( )
1654+ . unwrap ( ) ,
1655+ utxo : Utxo :: Local ( LocalOutput {
1656+ outpoint : txin. previous_output ,
1657+ txout : txout. clone ( ) ,
1658+ keychain,
1659+ is_spent : true ,
1660+ derivation_index,
1661+ chain_position,
1662+ } ) ,
1663+ } ,
1664+ ) ,
1665+ None => {
1666+ let satisfaction_weight = Weight :: from_wu_usize (
1667+ serialize ( & txin. script_sig ) . len ( ) * 4
1668+ + serialize ( & txin. witness ) . len ( ) ,
1669+ ) ;
1670+
1671+ (
1672+ txin. previous_output ,
1673+ WeightedUtxo {
1674+ utxo : Utxo :: Foreign {
1675+ outpoint : txin. previous_output ,
1676+ sequence : txin. sequence ,
1677+ psbt_input : Box :: new ( psbt:: Input {
1678+ witness_utxo : txout
1679+ . script_pubkey
1680+ . witness_version ( )
1681+ . map ( |_| txout. clone ( ) ) ,
1682+ non_witness_utxo : Some ( prev_tx. as_ref ( ) . clone ( ) ) ,
1683+ ..Default :: default ( )
1684+ } ) ,
1685+ } ,
1686+ satisfaction_weight,
1687+ } ,
1688+ )
1689+ }
16681690 }
1669- }
1670- } ;
1671-
1672- Ok ( weighted_utxo)
1691+ } )
16731692 } )
1674- . collect :: < Result < Vec < _ > , _ > > ( ) ?;
1693+ . collect :: < Result < HashMap < OutPoint , WeightedUtxo > , BuildFeeBumpError > > ( ) ?;
16751694
16761695 if tx. output . len ( ) > 1 {
16771696 let mut change_index = None ;
@@ -1698,7 +1717,7 @@ impl Wallet {
16981717 . into_iter ( )
16991718 . map ( |txout| ( txout. script_pubkey , txout. value ) )
17001719 . collect ( ) ,
1701- utxos : original_utxos ,
1720+ utxos,
17021721 bumping_fee : Some ( tx_builder:: PreviousFee {
17031722 absolute : fee,
17041723 rate : fee_rate,
@@ -1976,117 +1995,52 @@ impl Wallet {
19761995 descriptor. at_derivation_index ( child) . ok ( )
19771996 }
19781997
1979- fn get_available_utxos ( & self ) -> Vec < ( LocalOutput , Weight ) > {
1980- self . list_unspent ( )
1981- . map ( |utxo| {
1982- let keychain = utxo. keychain ;
1983- ( utxo, {
1984- self . public_descriptor ( keychain)
1985- . max_weight_to_satisfy ( )
1986- . unwrap ( )
1987- } )
1988- } )
1989- . collect ( )
1990- }
1991-
19921998 /// Given the options returns the list of utxos that must be used to form the
19931999 /// transaction and any further that may be used if needed.
1994- fn preselect_utxos (
1995- & self ,
1996- params : & TxParams ,
1997- current_height : Option < u32 > ,
1998- ) -> ( Vec < WeightedUtxo > , Vec < WeightedUtxo > ) {
1999- let TxParams {
2000- change_policy,
2001- unspendable,
2002- utxos,
2003- drain_wallet,
2004- manually_selected_only,
2005- bumping_fee,
2006- ..
2007- } = params;
2008-
2009- let manually_selected = utxos. clone ( ) ;
2010- // we mandate confirmed transactions if we're bumping the fee
2011- let must_only_use_confirmed_tx = bumping_fee. is_some ( ) ;
2012- let must_use_all_available = * drain_wallet;
2013-
2014- // must_spend <- manually selected utxos
2015- // may_spend <- all other available utxos
2016- let mut may_spend = self . get_available_utxos ( ) ;
2017-
2018- may_spend. retain ( |may_spend| {
2019- !manually_selected
2020- . iter ( )
2021- . any ( |manually_selected| manually_selected. utxo . outpoint ( ) == may_spend. 0 . outpoint )
2022- } ) ;
2023- let mut must_spend = manually_selected;
2024-
2025- // NOTE: we are intentionally ignoring `unspendable` here. i.e manual
2026- // selection overrides unspendable.
2027- if * manually_selected_only {
2028- return ( must_spend, vec ! [ ] ) ;
2029- }
2030-
2031- let satisfies_confirmed = may_spend
2032- . iter ( )
2033- . map ( |u| -> bool {
2034- let txid = u. 0 . outpoint . txid ;
2035- let tx = match self . indexed_graph . graph ( ) . get_tx ( txid) {
2036- Some ( tx) => tx,
2037- None => return false ,
2038- } ;
2039-
2040- // Whether the UTXO is mature and, if needed, confirmed
2041- let mut spendable = true ;
2042- let chain_position = u. 0 . chain_position ;
2043- if must_only_use_confirmed_tx && !chain_position. is_confirmed ( ) {
2044- return false ;
2045- }
2046- if tx. is_coinbase ( ) {
2047- debug_assert ! (
2048- chain_position. is_confirmed( ) ,
2049- "coinbase must always be confirmed"
2050- ) ;
2051- if let Some ( current_height) = current_height {
2052- match chain_position {
2053- ChainPosition :: Confirmed { anchor, .. } => {
2054- // https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
2055- let spend_height = current_height + 1 ;
2056- let coin_age_at_spend_height =
2057- spend_height. saturating_sub ( anchor. block_id . height ) ;
2058- spendable &= coin_age_at_spend_height >= COINBASE_MATURITY ;
2059- }
2060- ChainPosition :: Unconfirmed { .. } => spendable = false ,
2061- }
2062- }
2063- }
2064- spendable
2065- } )
2066- . collect :: < Vec < _ > > ( ) ;
2067-
2068- let mut i = 0 ;
2069- may_spend. retain ( |u| {
2070- let retain = ( self . keychains ( ) . count ( ) == 1 || change_policy. is_satisfied_by ( & u. 0 ) )
2071- && !unspendable. contains ( & u. 0 . outpoint )
2072- && satisfies_confirmed[ i] ;
2073- i += 1 ;
2074- retain
2075- } ) ;
2076-
2077- let mut may_spend = may_spend
2078- . into_iter ( )
2079- . map ( |( local_utxo, satisfaction_weight) | WeightedUtxo {
2080- satisfaction_weight,
2081- utxo : Utxo :: Local ( local_utxo) ,
2082- } )
2083- . collect ( ) ;
2084-
2085- if must_use_all_available {
2086- must_spend. append ( & mut may_spend) ;
2000+ fn filter_utxos ( & self , params : & TxParams , current_height : u32 ) -> Vec < WeightedUtxo > {
2001+ if params. manually_selected_only {
2002+ vec ! [ ]
2003+ // only process optional UTxOs if manually_selected_only is false
2004+ } else {
2005+ self . indexed_graph
2006+ . graph ( )
2007+ // get all unspent UTxOs from wallet
2008+ // NOTE: the UTxOs returned by the following method already belong to wallet as the
2009+ // call chain uses get_tx_node infallibly
2010+ . filter_chain_unspents (
2011+ & self . chain ,
2012+ self . chain . tip ( ) . block_id ( ) ,
2013+ self . indexed_graph . index . outpoints ( ) . iter ( ) . cloned ( ) ,
2014+ )
2015+ // only create LocalOutput if UTxO is mature
2016+ . filter_map ( move |( ( k, i) , full_txo) | {
2017+ full_txo
2018+ . is_mature ( current_height)
2019+ . then ( || new_local_utxo ( k, i, full_txo) )
2020+ } )
2021+ // only process UTxOs not selected manually, they will be considered later in the chain
2022+ // NOTE: this avoid UTxOs in both required and optional list
2023+ . filter ( |may_spend| !params. utxos . contains_key ( & may_spend. outpoint ) )
2024+ // only add to optional UTxOs those which satisfy the change policy if we reuse change
2025+ . filter ( |local_output| {
2026+ self . keychains ( ) . count ( ) == 1
2027+ || params. change_policy . is_satisfied_by ( local_output)
2028+ } )
2029+ // only add to optional UTxOs those marked as spendable
2030+ . filter ( |local_output| !params. unspendable . contains ( & local_output. outpoint ) )
2031+ // if bumping fees only add to optional UTxOs those confirmed
2032+ . filter ( |local_output| {
2033+ params. bumping_fee . is_none ( ) || local_output. chain_position . is_confirmed ( )
2034+ } )
2035+ . map ( |utxo| WeightedUtxo {
2036+ satisfaction_weight : self
2037+ . public_descriptor ( utxo. keychain )
2038+ . max_weight_to_satisfy ( )
2039+ . unwrap ( ) ,
2040+ utxo : Utxo :: Local ( utxo) ,
2041+ } )
2042+ . collect ( )
20872043 }
2088-
2089- ( must_spend, may_spend)
20902044 }
20912045
20922046 fn complete_transaction (
0 commit comments