@@ -30,6 +30,7 @@ use crate::ln::chan_utils::{
3030 HTLC_TIMEOUT_INPUT_KEYED_ANCHOR_WITNESS_WEIGHT , HTLC_TIMEOUT_INPUT_P2A_ANCHOR_WITNESS_WEIGHT ,
3131 P2WSH_TXOUT_WEIGHT , SEGWIT_MARKER_FLAG_WEIGHT , TRUC_CHILD_MAX_WEIGHT , TRUC_MAX_WEIGHT ,
3232} ;
33+ use crate :: ln:: funding:: FundingTxInput ;
3334use crate :: ln:: types:: ChannelId ;
3435use crate :: prelude:: * ;
3536use crate :: sign:: ecdsa:: EcdsaChannelSigner ;
@@ -354,20 +355,33 @@ impl Utxo {
354355 }
355356}
356357
358+ /// An unspent transaction output with at least one confirmation.
359+ pub type ConfirmedUtxo = FundingTxInput ;
360+
357361/// The result of a successful coin selection attempt for a transaction requiring additional UTXOs
358362/// to cover its fees.
359363#[ derive( Clone , Debug ) ]
360364pub struct CoinSelection {
361365 /// The set of UTXOs (with at least 1 confirmation) to spend and use within a transaction
362366 /// requiring additional fees.
363- pub confirmed_utxos : Vec < Utxo > ,
367+ pub confirmed_utxos : Vec < ConfirmedUtxo > ,
364368 /// An additional output tracking whether any change remained after coin selection. This output
365369 /// should always have a value above dust for its given `script_pubkey`. It should not be
366370 /// spent until the transaction it belongs to confirms to ensure mempool descendant limits are
367371 /// not met. This implies no other party should be able to spend it except us.
368372 pub change_output : Option < TxOut > ,
369373}
370374
375+ impl CoinSelection {
376+ fn satisfaction_weight ( & self ) -> u64 {
377+ self . confirmed_utxos . iter ( ) . map ( |ConfirmedUtxo { utxo, .. } | utxo. satisfaction_weight ) . sum ( )
378+ }
379+
380+ fn input_amount ( & self ) -> Amount {
381+ self . confirmed_utxos . iter ( ) . map ( |ConfirmedUtxo { utxo, .. } | utxo. output . value ) . sum ( )
382+ }
383+ }
384+
371385/// An abstraction over a bitcoin wallet that can perform coin selection over a set of UTXOs and can
372386/// sign for them. The coin selection method aims to mimic Bitcoin Core's `fundrawtransaction` RPC,
373387/// which most wallets should be able to satisfy. Otherwise, consider implementing [`WalletSource`],
@@ -438,11 +452,18 @@ pub trait WalletSource {
438452 fn list_confirmed_utxos < ' a > (
439453 & ' a self ,
440454 ) -> impl Future < Output = Result < Vec < Utxo > , ( ) > > + MaybeSend + ' a ;
455+
456+ /// Returns the previous transaction containing the UTXO referenced by the outpoint.
457+ fn get_prevtx < ' a > (
458+ & ' a self , outpoint : OutPoint ,
459+ ) -> impl Future < Output = Result < Transaction , ( ) > > + MaybeSend + ' a ;
460+
441461 /// Returns a script to use for change above dust resulting from a successful coin selection
442462 /// attempt.
443463 fn get_change_script < ' a > (
444464 & ' a self ,
445465 ) -> impl Future < Output = Result < ScriptBuf , ( ) > > + MaybeSend + ' a ;
466+
446467 /// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
447468 /// the transaction known to the wallet (i.e., any provided via
448469 /// [`WalletSource::list_confirmed_utxos`]).
@@ -628,10 +649,26 @@ where
628649 Some ( TxOut { script_pubkey : change_script, value : change_output_amount } )
629650 } ;
630651
631- Ok ( CoinSelection {
632- confirmed_utxos : selected_utxos. into_iter ( ) . map ( |( utxo, _) | utxo) . collect ( ) ,
633- change_output,
634- } )
652+ let mut confirmed_utxos = Vec :: with_capacity ( selected_utxos. len ( ) ) ;
653+ for ( utxo, _) in selected_utxos {
654+ let prevtx = self . source . get_prevtx ( utxo. outpoint ) . await ?;
655+ let prevtx_id = prevtx. compute_txid ( ) ;
656+ if prevtx_id != utxo. outpoint . txid
657+ || prevtx. output . get ( utxo. outpoint . vout as usize ) . is_none ( )
658+ {
659+ log_error ! (
660+ self . logger,
661+ "Tx {} from wallet source doesn't contain output referenced by outpoint: {}" ,
662+ prevtx_id,
663+ utxo. outpoint,
664+ ) ;
665+ return Err ( ( ) ) ;
666+ }
667+
668+ confirmed_utxos. push ( ConfirmedUtxo { utxo, prevtx } ) ;
669+ }
670+
671+ Ok ( CoinSelection { confirmed_utxos, change_output } )
635672 }
636673}
637674
@@ -740,7 +777,7 @@ where
740777
741778 /// Updates a transaction with the result of a successful coin selection attempt.
742779 fn process_coin_selection ( & self , tx : & mut Transaction , coin_selection : & CoinSelection ) {
743- for utxo in coin_selection. confirmed_utxos . iter ( ) {
780+ for ConfirmedUtxo { utxo, .. } in coin_selection. confirmed_utxos . iter ( ) {
744781 tx. input . push ( TxIn {
745782 previous_output : utxo. outpoint ,
746783 script_sig : ScriptBuf :: new ( ) ,
@@ -865,12 +902,10 @@ where
865902 output : vec ! [ ] ,
866903 } ;
867904
868- let input_satisfaction_weight: u64 =
869- coin_selection. confirmed_utxos . iter ( ) . map ( |utxo| utxo. satisfaction_weight ) . sum ( ) ;
905+ let input_satisfaction_weight = coin_selection. satisfaction_weight ( ) ;
870906 let total_satisfaction_weight =
871907 anchor_input_witness_weight + EMPTY_SCRIPT_SIG_WEIGHT + input_satisfaction_weight;
872- let total_input_amount = must_spend_amount
873- + coin_selection. confirmed_utxos . iter ( ) . map ( |utxo| utxo. output . value ) . sum ( ) ;
908+ let total_input_amount = must_spend_amount + coin_selection. input_amount ( ) ;
874909
875910 self . process_coin_selection ( & mut anchor_tx, & coin_selection) ;
876911 let anchor_txid = anchor_tx. compute_txid ( ) ;
@@ -885,10 +920,10 @@ where
885920 let index = idx + 1 ;
886921 debug_assert_eq ! (
887922 anchor_psbt. unsigned_tx. input[ index] . previous_output,
888- utxo. outpoint
923+ utxo. outpoint( )
889924 ) ;
890- if utxo. output . script_pubkey . is_witness_program ( ) {
891- anchor_psbt. inputs [ index] . witness_utxo = Some ( utxo. output ) ;
925+ if utxo. output ( ) . script_pubkey . is_witness_program ( ) {
926+ anchor_psbt. inputs [ index] . witness_utxo = Some ( utxo. into_output ( ) ) ;
892927 }
893928 }
894929
@@ -1127,13 +1162,11 @@ where
11271162 utxo_id = claim_id. step_with_bytes ( & broadcasted_htlcs. to_be_bytes ( ) ) ;
11281163
11291164 #[ cfg( debug_assertions) ]
1130- let input_satisfaction_weight: u64 =
1131- coin_selection. confirmed_utxos . iter ( ) . map ( |utxo| utxo. satisfaction_weight ) . sum ( ) ;
1165+ let input_satisfaction_weight = coin_selection. satisfaction_weight ( ) ;
11321166 #[ cfg( debug_assertions) ]
11331167 let total_satisfaction_weight = must_spend_satisfaction_weight + input_satisfaction_weight;
11341168 #[ cfg( debug_assertions) ]
1135- let input_value: u64 =
1136- coin_selection. confirmed_utxos . iter ( ) . map ( |utxo| utxo. output . value . to_sat ( ) ) . sum ( ) ;
1169+ let input_value = coin_selection. input_amount ( ) . to_sat ( ) ;
11371170 #[ cfg( debug_assertions) ]
11381171 let total_input_amount = must_spend_amount + input_value;
11391172
@@ -1154,9 +1187,12 @@ where
11541187 for ( idx, utxo) in coin_selection. confirmed_utxos . into_iter ( ) . enumerate ( ) {
11551188 // offset to skip the htlc inputs
11561189 let index = idx + selected_htlcs. len ( ) ;
1157- debug_assert_eq ! ( htlc_psbt. unsigned_tx. input[ index] . previous_output, utxo. outpoint) ;
1158- if utxo. output . script_pubkey . is_witness_program ( ) {
1159- htlc_psbt. inputs [ index] . witness_utxo = Some ( utxo. output ) ;
1190+ debug_assert_eq ! (
1191+ htlc_psbt. unsigned_tx. input[ index] . previous_output,
1192+ utxo. outpoint( )
1193+ ) ;
1194+ if utxo. output ( ) . script_pubkey . is_witness_program ( ) {
1195+ htlc_psbt. inputs [ index] . witness_utxo = Some ( utxo. into_output ( ) ) ;
11601196 }
11611197 }
11621198
@@ -1311,10 +1347,9 @@ mod tests {
13111347 use crate :: util:: ser:: Readable ;
13121348 use crate :: util:: test_utils:: { TestBroadcaster , TestLogger } ;
13131349
1314- use bitcoin:: hashes:: Hash ;
13151350 use bitcoin:: hex:: FromHex ;
13161351 use bitcoin:: {
1317- Network , ScriptBuf , Transaction , Txid , WitnessProgram , WitnessVersion , XOnlyPublicKey ,
1352+ Network , ScriptBuf , Transaction , WitnessProgram , WitnessVersion , XOnlyPublicKey ,
13181353 } ;
13191354
13201355 struct TestCoinSelectionSource {
@@ -1335,9 +1370,17 @@ mod tests {
13351370 Ok ( res)
13361371 }
13371372 fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
1373+ let prevtx_ids: Vec < _ > = self
1374+ . expected_selects
1375+ . lock ( )
1376+ . unwrap ( )
1377+ . iter ( )
1378+ . flat_map ( |selection| selection. 3 . confirmed_utxos . iter ( ) )
1379+ . map ( |utxo| utxo. prevtx . compute_txid ( ) )
1380+ . collect ( ) ;
13381381 let mut tx = psbt. unsigned_tx ;
13391382 for input in tx. input . iter_mut ( ) {
1340- if input. previous_output . txid != Txid :: from_byte_array ( [ 44 ; 32 ] ) {
1383+ if prevtx_ids . contains ( & input. previous_output . txid ) {
13411384 // Channel output, add a realistic size witness to make the assertions happy
13421385 input. witness = Witness :: from_slice ( & [ vec ! [ 42 ; 162 ] ] ) ;
13431386 }
@@ -1378,6 +1421,13 @@ mod tests {
13781421 . weight ( )
13791422 . to_wu ( ) ;
13801423
1424+ let prevtx = Transaction {
1425+ version : Version :: TWO ,
1426+ lock_time : LockTime :: ZERO ,
1427+ input : vec ! [ ] ,
1428+ output : vec ! [ TxOut { value: Amount :: from_sat( 200 ) , script_pubkey: ScriptBuf :: new( ) } ] ,
1429+ } ;
1430+
13811431 let broadcaster = TestBroadcaster :: new ( Network :: Testnet ) ;
13821432 let source = TestCoinSelectionSource {
13831433 expected_selects : Mutex :: new ( vec ! [
@@ -1392,14 +1442,14 @@ mod tests {
13921442 commitment_and_anchor_fee,
13931443 868 ,
13941444 CoinSelection {
1395- confirmed_utxos: vec![ Utxo {
1396- outpoint: OutPoint { txid: Txid :: from_byte_array( [ 44 ; 32 ] ) , vout: 0 } ,
1397- output: TxOut {
1398- value: Amount :: from_sat( 200 ) ,
1399- script_pubkey: ScriptBuf :: new( ) ,
1445+ confirmed_utxos: vec![ ConfirmedUtxo {
1446+ utxo: Utxo {
1447+ outpoint: OutPoint { txid: prevtx. compute_txid( ) , vout: 0 } ,
1448+ output: prevtx. output[ 0 ] . clone( ) ,
1449+ satisfaction_weight: 5 , // Just the script_sig and witness lengths
1450+ sequence: Sequence :: ENABLE_RBF_NO_LOCKTIME ,
14001451 } ,
1401- satisfaction_weight: 5 , // Just the script_sig and witness lengths
1402- sequence: Sequence :: ENABLE_RBF_NO_LOCKTIME ,
1452+ prevtx,
14031453 } ] ,
14041454 change_output: None ,
14051455 } ,
0 commit comments