@@ -273,13 +273,14 @@ impl CoinSelectionAlgorithm for OldestFirstCoinSelection {
273
273
drain_script : & Script ,
274
274
_: & mut R ,
275
275
) -> Result < CoinSelectionResult , InsufficientFunds > {
276
- // We put the "required UTXOs " first and make sure the optional UTXOs are sorted from
276
+ // We put the "required UTxOs " first and make sure the optional UTxOs are sorted from
277
277
// 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
279
280
let utxos = {
280
281
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 ) ,
283
284
} ) ;
284
285
285
286
required_utxos
@@ -726,10 +727,11 @@ fn calculate_cs_result(
726
727
mod test {
727
728
use assert_matches:: assert_matches;
728
729
use bitcoin:: hashes:: Hash ;
729
- use bitcoin:: OutPoint ;
730
+ use bitcoin:: { psbt , OutPoint , Sequence } ;
730
731
use chain:: { ChainPosition , ConfirmationBlockTime } ;
731
732
use core:: str:: FromStr ;
732
733
use rand:: rngs:: StdRng ;
734
+ use std:: boxed:: Box ;
733
735
734
736
use bitcoin:: { Amount , BlockHash , ScriptBuf , TxIn , TxOut } ;
735
737
@@ -778,6 +780,30 @@ mod test {
778
780
)
779
781
}
780
782
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
+
781
807
fn utxo (
782
808
value : Amount ,
783
809
index : u32 ,
@@ -1051,6 +1077,56 @@ mod test {
1051
1077
assert_eq ! ( result. fee_amount, Amount :: from_sat( 204 ) ) ;
1052
1078
}
1053
1079
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 ! ( matches!( result. selected[ 2 ] , Utxo :: Local ( ..) ) ) ;
1128
+ }
1129
+
1054
1130
#[ test]
1055
1131
fn test_oldest_first_coin_selection_use_only_necessary ( ) {
1056
1132
let utxos = get_oldest_first_test_utxos ( ) ;
0 commit comments