@@ -273,13 +273,16 @@ 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
- optional_utxos. sort_unstable_by_key ( |wu| match & wu. utxo {
281
- Utxo :: Local ( local) => Some ( local. chain_position ) ,
282
- Utxo :: Foreign { .. } => None ,
281
+ optional_utxos. sort_unstable_by_key ( |wu| {
282
+ match & wu. utxo {
283
+ Utxo :: Local ( local) => ( false , Some ( local. chain_position ) ) ,
284
+ Utxo :: Foreign { .. } => ( true , None ) ,
285
+ }
283
286
} ) ;
284
287
285
288
required_utxos
@@ -726,10 +729,11 @@ fn calculate_cs_result(
726
729
mod test {
727
730
use assert_matches:: assert_matches;
728
731
use bitcoin:: hashes:: Hash ;
729
- use bitcoin:: OutPoint ;
732
+ use bitcoin:: { psbt , OutPoint , Sequence } ;
730
733
use chain:: { ChainPosition , ConfirmationBlockTime } ;
731
734
use core:: str:: FromStr ;
732
735
use rand:: rngs:: StdRng ;
736
+ use std:: boxed:: Box ;
733
737
734
738
use bitcoin:: { Amount , BlockHash , ScriptBuf , TxIn , TxOut } ;
735
739
@@ -778,6 +782,30 @@ mod test {
778
782
)
779
783
}
780
784
785
+ fn foreign_utxo ( value : Amount , index : u32 ) -> WeightedUtxo {
786
+ assert ! ( index < 10 ) ;
787
+ let outpoint = OutPoint :: from_str ( & format ! (
788
+ "000000000000000000000000000000000000000000000000000000000000000{}:0" ,
789
+ index
790
+ ) )
791
+ . unwrap ( ) ;
792
+ WeightedUtxo {
793
+ utxo : Utxo :: Foreign {
794
+ outpoint,
795
+ sequence : Sequence ( 0xFFFFFFFD ) ,
796
+ psbt_input : Box :: new ( psbt:: Input {
797
+ witness_utxo : Some ( TxOut {
798
+ value,
799
+ script_pubkey : ScriptBuf :: new ( ) ,
800
+ } ) ,
801
+ non_witness_utxo : None ,
802
+ ..Default :: default ( )
803
+ } ) ,
804
+ } ,
805
+ satisfaction_weight : Weight :: from_wu_usize ( P2WPKH_SATISFACTION_SIZE ) ,
806
+ }
807
+ }
808
+
781
809
fn utxo (
782
810
value : Amount ,
783
811
index : u32 ,
@@ -1051,6 +1079,56 @@ mod test {
1051
1079
assert_eq ! ( result. fee_amount, Amount :: from_sat( 204 ) ) ;
1052
1080
}
1053
1081
1082
+ #[ test]
1083
+ fn test_oldest_first_coin_selection_uses_all_optional_with_foreign_utxo_locals_sorted_first ( ) {
1084
+ let utxos = get_oldest_first_test_utxos ( ) ;
1085
+ let mut all_utxos = vec ! [ foreign_utxo( Amount :: from_sat( 120_000 ) , 1 ) ] ;
1086
+ all_utxos. extend_from_slice ( & utxos) ;
1087
+ let drain_script = ScriptBuf :: default ( ) ;
1088
+ let target_amount = Amount :: from_sat ( 619_000 ) + FEE_AMOUNT ;
1089
+
1090
+ let result = OldestFirstCoinSelection
1091
+ . coin_select (
1092
+ vec ! [ ] ,
1093
+ all_utxos,
1094
+ FeeRate :: from_sat_per_vb_unchecked ( 1 ) ,
1095
+ target_amount,
1096
+ & drain_script,
1097
+ & mut thread_rng ( ) ,
1098
+ )
1099
+ . unwrap ( ) ;
1100
+
1101
+ assert_eq ! ( result. selected. len( ) , 4 ) ;
1102
+ assert_eq ! ( result. selected_amount( ) , Amount :: from_sat( 620_000 ) ) ;
1103
+ assert_eq ! ( result. fee_amount, Amount :: from_sat( 272 ) ) ;
1104
+ assert ! ( matches!( result. selected[ 3 ] , Utxo :: Foreign { .. } ) ) ;
1105
+ }
1106
+
1107
+ #[ test]
1108
+ fn test_oldest_first_coin_selection_uses_only_all_optional_local_utxos_not_a_single_foreign ( ) {
1109
+ let utxos = get_oldest_first_test_utxos ( ) ;
1110
+ let mut all_utxos = vec ! [ foreign_utxo( Amount :: from_sat( 120_000 ) , 1 ) ] ;
1111
+ all_utxos. extend_from_slice ( & utxos) ;
1112
+ let drain_script = ScriptBuf :: default ( ) ;
1113
+ let target_amount = Amount :: from_sat ( 499_000 ) + FEE_AMOUNT ;
1114
+
1115
+ let result = OldestFirstCoinSelection
1116
+ . coin_select (
1117
+ vec ! [ ] ,
1118
+ all_utxos,
1119
+ FeeRate :: from_sat_per_vb_unchecked ( 1 ) ,
1120
+ target_amount,
1121
+ & drain_script,
1122
+ & mut thread_rng ( ) ,
1123
+ )
1124
+ . unwrap ( ) ;
1125
+
1126
+ assert_eq ! ( result. selected. len( ) , 3 ) ;
1127
+ assert_eq ! ( result. selected_amount( ) , Amount :: from_sat( 500_000 ) ) ;
1128
+ assert_eq ! ( result. fee_amount, Amount :: from_sat( 204 ) ) ;
1129
+ assert ! ( matches!( result. selected[ 2 ] , Utxo :: Local ( ..) ) ) ;
1130
+ }
1131
+
1054
1132
#[ test]
1055
1133
fn test_oldest_first_coin_selection_use_only_necessary ( ) {
1056
1134
let utxos = get_oldest_first_test_utxos ( ) ;
0 commit comments