@@ -9,11 +9,35 @@ pub struct LowestFee<'c, C> {
9
9
pub change_policy : & ' c C ,
10
10
}
11
11
12
+ impl < ' c , C > Clone for LowestFee < ' c , C > {
13
+ fn clone ( & self ) -> Self {
14
+ Self {
15
+ target : self . target . clone ( ) ,
16
+ long_term_feerate : self . long_term_feerate . clone ( ) ,
17
+ change_policy : self . change_policy . clone ( ) ,
18
+ }
19
+ }
20
+ }
21
+
22
+ impl < ' c , C > Copy for LowestFee < ' c , C > { }
23
+
12
24
impl < ' c , C > LowestFee < ' c , C >
13
25
where
14
26
for < ' a , ' b > C : Fn ( & ' b CoinSelector < ' a > , Target ) -> Drain ,
15
27
{
16
- fn calculate_metric ( & self , cs : & CoinSelector < ' _ > , drain_weights : Option < DrainWeights > ) -> f32 {
28
+ fn calc_metric ( & self , cs : & CoinSelector < ' _ > , drain_weights : Option < DrainWeights > ) -> f32 {
29
+ self . calc_metric_lb ( cs, drain_weights)
30
+ + match drain_weights {
31
+ Some ( _) => {
32
+ let selected_value = cs. selected_value ( ) ;
33
+ assert ! ( selected_value >= self . target. value) ;
34
+ ( cs. selected_value ( ) - self . target . value ) as f32
35
+ }
36
+ None => 0.0 ,
37
+ }
38
+ }
39
+
40
+ fn calc_metric_lb ( & self , cs : & CoinSelector < ' _ > , drain_weights : Option < DrainWeights > ) -> f32 {
17
41
match drain_weights {
18
42
// with change
19
43
Some ( drain_weights) => {
22
46
+ drain_weights. spend_weight as f32 * self . long_term_feerate . spwu ( )
23
47
}
24
48
// changeless
25
- None => {
26
- cs. input_weight ( ) as f32 * self . target . feerate . spwu ( )
27
- + ( cs. selected_value ( ) - self . target . value ) as f32
28
- }
49
+ None => cs. input_weight ( ) as f32 * self . target . feerate . spwu ( ) ,
29
50
}
30
51
}
31
52
}
48
69
None
49
70
} ;
50
71
51
- Some ( Ordf32 ( self . calculate_metric ( cs, drain_weights) ) )
72
+ Some ( Ordf32 ( self . calc_metric ( cs, drain_weights) ) )
52
73
}
53
74
54
75
fn bound ( & mut self , cs : & CoinSelector < ' _ > ) -> Option < Self :: Score > {
@@ -66,11 +87,12 @@ where
66
87
// Target is met, is it possible to add further inputs to remove drain output?
67
88
// If we do, can we get a better score?
68
89
69
- // First lower bound candidate is just the selection itself.
70
- let mut lower_bound = self . calculate_metric ( cs, change_lb_weights) ;
90
+ // First lower bound candidate is just the selection itself (include excess) .
91
+ let mut lower_bound = self . calc_metric ( cs, change_lb_weights) ;
71
92
72
- // Since a changeless solution may exist, we should try reduce the excess
73
93
if change_lb. is_none ( ) {
94
+ // Since a changeless solution may exist, we should try minimize the excess with by
95
+ // adding as much -ev candidates as possible
74
96
let selection_with_as_much_negative_ev_as_possible = cs
75
97
. clone ( )
76
98
. select_iter ( )
@@ -95,18 +117,19 @@ where
95
117
} ) ;
96
118
let lower_bound_changeless = match can_do_better_by_slurping {
97
119
Some ( finishing_input) => {
98
- let excess = cs. rate_excess ( self . target , Drain :: none ( ) ) ;
120
+ let excess = dbg ! ( cs. rate_excess( self . target, Drain :: none( ) ) ) ;
99
121
100
122
// change the input's weight to make it's effective value match the excess
101
123
let perfect_input_weight =
102
- slurp_candidate ( finishing_input , excess , self . target . feerate ) ;
124
+ slurp ( & cs , self . target , excess , finishing_input ) ;
103
125
104
- ( cs. input_weight ( ) as f32 + perfect_input_weight)
126
+ ( cs. input_weight ( ) as f32 + dbg ! ( perfect_input_weight) )
105
127
* self . target . feerate . spwu ( )
106
128
}
107
- None => self . calculate_metric ( & cs, None ) ,
129
+ None => self . calc_metric ( & cs, None ) ,
108
130
} ;
109
131
132
+ // assert!(lower_bound_changeless > lower_bound);
110
133
lower_bound = lower_bound. min ( lower_bound_changeless)
111
134
}
112
135
}
@@ -122,54 +145,46 @@ where
122
145
. find ( |( cs, _, _) | cs. is_target_met ( self . target , change_lb) ) ?;
123
146
cs. deselect ( slurp_index) ;
124
147
125
- let perfect_excess = i64:: min (
126
- cs. rate_excess ( self . target , Drain :: none ( ) ) ,
127
- cs. absolute_excess ( self . target , Drain :: none ( ) ) ,
128
- ) ;
148
+ let mut lower_bound = self . calc_metric_lb ( & cs, change_lb_weights) ;
129
149
130
- match change_lb_weights {
131
- // must have change!
132
- Some ( change_weights) => {
133
- // [todo] This will not be perfect, just a placeholder for now
134
- let lowest_fee = ( cs. input_weight ( ) + change_weights. output_weight ) as f32
135
- * self . target . feerate . spwu ( )
136
- + change_weights. spend_weight as f32 * self . long_term_feerate . spwu ( ) ;
150
+ if change_lb_weights. is_none ( ) {
151
+ // changeless solution is possible, find the max excess we need to rid of
152
+ let perfect_excess = i64:: max (
153
+ cs. rate_excess ( self . target , Drain :: none ( ) ) ,
154
+ cs. absolute_excess ( self . target , Drain :: none ( ) ) ,
155
+ ) ;
137
156
138
- Some ( Ordf32 ( lowest_fee) )
139
- }
140
- // can be changeless!
141
- None => {
142
- // use the lowest excess to find "perfect candidate weight"
143
- let perfect_input_weight =
144
- slurp_candidate ( candidate_to_slurp, perfect_excess, self . target . feerate ) ;
157
+ // use the lowest excess to find "perfect candidate weight"
158
+ let perfect_input_weight = slurp ( & cs, self . target , perfect_excess, candidate_to_slurp) ;
145
159
146
- // the perfect input weight canned the excess and we assume no change
147
- let lowest_fee =
148
- ( cs. input_weight ( ) as f32 + perfect_input_weight) * self . target . feerate . spwu ( ) ;
149
-
150
- Some ( Ordf32 ( lowest_fee) )
151
- }
160
+ lower_bound += perfect_input_weight * self . target . feerate . spwu ( ) ;
152
161
}
162
+
163
+ Some ( Ordf32 ( lower_bound) )
153
164
}
154
165
155
166
fn requires_ordering_by_descending_value_pwu ( & self ) -> bool {
156
167
true
157
168
}
158
169
}
159
170
160
- fn slurp_candidate ( candidate : Candidate , excess : i64 , feerate : FeeRate ) -> f32 {
161
- let candidate_weight = candidate. weight as f32 ;
162
-
163
- // this equation is dervied from:
164
- // * `input_effective_value = input_value - input_weight * feerate`
165
- // * `input_value * new_input_weight = new_input_value * input_weight`
166
- // (ensure we have the same value:weight ratio)
167
- // where we want `input_effective_value` to match `-excess`.
168
- let perfect_weight = -( candidate_weight * excess as f32 )
169
- / ( candidate. value as f32 - candidate_weight * feerate. spwu ( ) ) ;
170
-
171
- debug_assert ! ( perfect_weight <= candidate_weight) ;
172
-
173
- // we can't allow the weight to go negative
174
- perfect_weight. min ( 0.0 )
171
+ fn slurp ( _cs : & CoinSelector , target : Target , excess : i64 , candidate : Candidate ) -> f32 {
172
+ // let input_eff_sum = cs.effective_value(target.feerate) as f32;
173
+ // let eff_target = cs.base_weight as f32 * target.feerate.spwu() + target.value as f32;
174
+ let vpw = candidate. value_pwu ( ) . 0 ;
175
+
176
+ // let perfect_weight =
177
+ // (-old_excess as f32 + eff_target - input_eff_sum) / (vpw - target.feerate.spwu());
178
+ let perfect_weight = -excess as f32 / ( vpw - target. feerate . spwu ( ) ) ;
179
+
180
+ debug_assert ! (
181
+ {
182
+ let perfect_value = ( candidate. value as f32 * perfect_weight) / candidate. weight as f32 ;
183
+ let perfect_vpw = perfect_value / perfect_weight;
184
+ ( vpw - perfect_vpw) . abs( ) < 0.01
185
+ } ,
186
+ "value:weight ratio must stay the same"
187
+ ) ;
188
+
189
+ perfect_weight. max ( 0.0 )
175
190
}
0 commit comments