@@ -36,7 +36,7 @@ use bdk_chain::{
36
36
use bitcoin:: {
37
37
absolute,
38
38
consensus:: encode:: serialize,
39
- constants:: { genesis_block, COINBASE_MATURITY } ,
39
+ constants:: genesis_block,
40
40
psbt,
41
41
secp256k1:: Secp256k1 ,
42
42
sighash:: { EcdsaSighashType , TapSighashType } ,
@@ -1417,8 +1417,19 @@ impl Wallet {
1417
1417
1418
1418
fee_amount += fee_rate * tx. weight ( ) ;
1419
1419
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
+ } ;
1422
1433
1423
1434
// get drain script
1424
1435
let mut drain_index = Option :: < ( KeychainKind , u32 ) > :: None ;
@@ -1447,9 +1458,6 @@ impl Wallet {
1447
1458
}
1448
1459
} ;
1449
1460
1450
- let ( required_utxos, optional_utxos) =
1451
- coin_selection:: filter_duplicates ( required_utxos, optional_utxos) ;
1452
-
1453
1461
let coin_selection = coin_selection
1454
1462
. coin_select (
1455
1463
required_utxos,
@@ -1618,60 +1626,71 @@ impl Wallet {
1618
1626
. map_err ( |_| BuildFeeBumpError :: FeeRateUnavailable ) ?;
1619
1627
1620
1628
// 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 ( .. )
1624
1632
. map ( |txin| -> Result < _ , BuildFeeBumpError > {
1625
- let prev_tx = graph
1633
+ graph
1634
+ // Get previous transaction
1626
1635
. 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
+ }
1668
1690
}
1669
- }
1670
- } ;
1671
-
1672
- Ok ( weighted_utxo)
1691
+ } )
1673
1692
} )
1674
- . collect :: < Result < Vec < _ > , _ > > ( ) ?;
1693
+ . collect :: < Result < HashMap < OutPoint , WeightedUtxo > , BuildFeeBumpError > > ( ) ?;
1675
1694
1676
1695
if tx. output . len ( ) > 1 {
1677
1696
let mut change_index = None ;
@@ -1698,7 +1717,7 @@ impl Wallet {
1698
1717
. into_iter ( )
1699
1718
. map ( |txout| ( txout. script_pubkey , txout. value ) )
1700
1719
. collect ( ) ,
1701
- utxos : original_utxos ,
1720
+ utxos,
1702
1721
bumping_fee : Some ( tx_builder:: PreviousFee {
1703
1722
absolute : fee,
1704
1723
rate : fee_rate,
@@ -1976,117 +1995,52 @@ impl Wallet {
1976
1995
descriptor. at_derivation_index ( child) . ok ( )
1977
1996
}
1978
1997
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
-
1992
1998
/// Given the options returns the list of utxos that must be used to form the
1993
1999
/// 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 ( )
2087
2043
}
2088
-
2089
- ( must_spend, may_spend)
2090
2044
}
2091
2045
2092
2046
fn complete_transaction (
0 commit comments