@@ -275,11 +275,12 @@ impl CoinSelectionAlgorithm for OldestFirstCoinSelection {
275275 ) -> Result < CoinSelectionResult , InsufficientFunds > {
276276 // We put the "required UTXOs" first and make sure the optional UTXOs are sorted from
277277 // oldest to newest according to blocktime
278- // For utxo that doesn't exist in DB, they will have lowest priority to be selected
278+ // For UTXOs that doesn't exist in DB (Utxo::Foreign), they will have lowest priority to be
279+ // selected
279280 let utxos = {
280281 optional_utxos. sort_unstable_by_key ( |wu| match & wu. utxo {
281- Utxo :: Local ( local) => Some ( local. chain_position ) ,
282- Utxo :: Foreign { .. } => None ,
282+ Utxo :: Local ( local) => ( false , Some ( local. chain_position ) ) ,
283+ Utxo :: Foreign { .. } => ( true , None ) ,
283284 } ) ;
284285
285286 required_utxos
@@ -726,10 +727,11 @@ fn calculate_cs_result(
726727mod test {
727728 use assert_matches:: assert_matches;
728729 use bitcoin:: hashes:: Hash ;
729- use bitcoin:: OutPoint ;
730+ use bitcoin:: { psbt , OutPoint , Sequence } ;
730731 use chain:: { ChainPosition , ConfirmationBlockTime } ;
731732 use core:: str:: FromStr ;
732733 use rand:: rngs:: StdRng ;
734+ use std:: boxed:: Box ;
733735
734736 use bitcoin:: { Amount , BlockHash , ScriptBuf , TxIn , TxOut } ;
735737
@@ -778,6 +780,30 @@ mod test {
778780 )
779781 }
780782
783+ fn foreign_utxo ( value : Amount , index : u32 ) -> WeightedUtxo {
784+ assert ! ( index < 10 ) ;
785+ let outpoint = OutPoint :: from_str ( & format ! (
786+ "000000000000000000000000000000000000000000000000000000000000000{}:0" ,
787+ index
788+ ) )
789+ . unwrap ( ) ;
790+ WeightedUtxo {
791+ utxo : Utxo :: Foreign {
792+ outpoint,
793+ sequence : Sequence ( 0xFFFFFFFD ) ,
794+ psbt_input : Box :: new ( psbt:: Input {
795+ witness_utxo : Some ( TxOut {
796+ value,
797+ script_pubkey : ScriptBuf :: new ( ) ,
798+ } ) ,
799+ non_witness_utxo : None ,
800+ ..Default :: default ( )
801+ } ) ,
802+ } ,
803+ satisfaction_weight : Weight :: from_wu_usize ( P2WPKH_SATISFACTION_SIZE ) ,
804+ }
805+ }
806+
781807 fn utxo (
782808 value : Amount ,
783809 index : u32 ,
@@ -1051,6 +1077,59 @@ mod test {
10511077 assert_eq ! ( result. fee_amount, Amount :: from_sat( 204 ) ) ;
10521078 }
10531079
1080+ #[ test]
1081+ fn test_oldest_first_coin_selection_uses_all_optional_with_foreign_utxo_locals_sorted_first ( ) {
1082+ let utxos = get_oldest_first_test_utxos ( ) ;
1083+ let mut all_utxos = vec ! [ foreign_utxo( Amount :: from_sat( 120_000 ) , 1 ) ] ;
1084+ all_utxos. extend_from_slice ( & utxos) ;
1085+ let drain_script = ScriptBuf :: default ( ) ;
1086+ let target_amount = Amount :: from_sat ( 619_000 ) + FEE_AMOUNT ;
1087+
1088+ let result = OldestFirstCoinSelection
1089+ . coin_select (
1090+ vec ! [ ] ,
1091+ all_utxos,
1092+ FeeRate :: from_sat_per_vb_unchecked ( 1 ) ,
1093+ target_amount,
1094+ & drain_script,
1095+ & mut thread_rng ( ) ,
1096+ )
1097+ . unwrap ( ) ;
1098+
1099+ assert_eq ! ( result. selected. len( ) , 4 ) ;
1100+ assert_eq ! ( result. selected_amount( ) , Amount :: from_sat( 620_000 ) ) ;
1101+ assert_eq ! ( result. fee_amount, Amount :: from_sat( 272 ) ) ;
1102+ assert ! ( matches!( result. selected[ 3 ] , Utxo :: Foreign { .. } ) ) ;
1103+ }
1104+
1105+ #[ test]
1106+ fn test_oldest_first_coin_selection_uses_only_all_optional_local_utxos_not_a_single_foreign ( ) {
1107+ let utxos = get_oldest_first_test_utxos ( ) ;
1108+ let mut all_utxos = vec ! [ foreign_utxo( Amount :: from_sat( 120_000 ) , 1 ) ] ;
1109+ all_utxos. extend_from_slice ( & utxos) ;
1110+ let drain_script = ScriptBuf :: default ( ) ;
1111+ let target_amount = Amount :: from_sat ( 499_000 ) + FEE_AMOUNT ;
1112+
1113+ let result = OldestFirstCoinSelection
1114+ . coin_select (
1115+ vec ! [ ] ,
1116+ all_utxos,
1117+ FeeRate :: from_sat_per_vb_unchecked ( 1 ) ,
1118+ target_amount,
1119+ & drain_script,
1120+ & mut thread_rng ( ) ,
1121+ )
1122+ . unwrap ( ) ;
1123+
1124+ assert_eq ! ( result. selected. len( ) , 3 ) ;
1125+ assert_eq ! ( result. selected_amount( ) , Amount :: from_sat( 500_000 ) ) ;
1126+ assert_eq ! ( result. fee_amount, Amount :: from_sat( 204 ) ) ;
1127+ assert ! ( result
1128+ . selected
1129+ . iter( )
1130+ . all( |utxo| matches!( utxo, Utxo :: Local ( ..) ) ) ) ;
1131+ }
1132+
10541133 #[ test]
10551134 fn test_oldest_first_coin_selection_use_only_necessary ( ) {
10561135 let utxos = get_oldest_first_test_utxos ( ) ;
0 commit comments