@@ -36,8 +36,7 @@ const MIN_ACCEPTED_HTLCS: u16 = 12;
3636const MIN_MAX_IN_FLIGHT_MSAT : u64 = 1_000_000 ;
3737
3838/// Resolution time in seconds that is considered "good". HTLCs resolved within this period are
39- /// considered normal and are rewarded in the reputation score. HTLCs resolved slower than this
40- /// will incur an opportunity cost to penalize slow resolving payments.
39+ /// considered normal and are rewarded in the reputation score.
4140const ACCEPTABLE_RESOLUTION_PERIOD_SECS : u8 = 90 ;
4241
4342/// The maximum time (in seconds) that a HTLC can be held. Corresponds to the largest cltv delta
@@ -556,13 +555,10 @@ impl Channel {
556555 Ok ( congestion_resources_available && !misused_congestion && below_liquidity_limit)
557556 }
558557
559- fn pending_htlcs_in_congestion ( & self , channel_id : u64 ) -> bool {
558+ fn pending_htlcs_in_congestion ( & self ) -> bool {
560559 self . pending_htlcs
561560 . iter ( )
562- . find ( |( htlc_ref, pending_htlc) | {
563- htlc_ref. incoming_channel_id == channel_id
564- && pending_htlc. bucket == BucketAssigned :: Congestion
565- } )
561+ . find ( |( _, pending_htlc) | pending_htlc. bucket == BucketAssigned :: Congestion )
566562 . is_some ( )
567563 }
568564
@@ -610,7 +606,10 @@ impl DefaultResourceManager {
610606 fn opportunity_cost ( & self , resolution_time : Duration , fee_msat : u64 ) -> u64 {
611607 // Computed using f64 as a linear function of how far resolution_time exceeds the
612608 // acceptable resolution_period, scaled by the fee. This is done instead of stepwise at
613- // each resolution_period multiple.
609+ // each resolution_period multiple because a stepwise function could possibly allow an
610+ // attacker to hold HTLCs just under the acceptable resolution_period without incurring
611+ // any cost but causing the next hop slower's resolution to get fully penalized by the
612+ // fee.
614613 let resolution_period = self . config . resolution_period . as_secs_f64 ( ) ;
615614 let opportunity_cost = 0_f64
616615 . max ( ( resolution_time. as_secs_f64 ( ) - resolution_period) / resolution_period)
@@ -697,6 +696,7 @@ impl DefaultResourceManager {
697696 // Release bucket resources on each incoming channel for its pending HTLCs.
698697 if let Some ( removed_channel) = channels_lock. remove ( & channel_id) {
699698 for ( htlc_ref, pending_htlc) in & removed_channel. pending_htlcs {
699+ debug_assert ! ( channels_lock. get( & htlc_ref. incoming_channel_id) . is_some( ) ) ;
700700 if let Some ( incoming_channel) = channels_lock. get_mut ( & htlc_ref. incoming_channel_id )
701701 {
702702 let _ = match pending_htlc. bucket {
@@ -755,8 +755,7 @@ impl DefaultResourceManager {
755755 let outgoing_in_flight_risk: u64 = outgoing_channel. outgoing_in_flight_risk ( ) ;
756756 let fee = incoming_amount_msat - outgoing_amount_msat;
757757 let in_flight_htlc_risk = self . htlc_in_flight_risk ( fee, incoming_cltv_expiry, height_added) ;
758- let pending_htlcs_in_congestion =
759- outgoing_channel. pending_htlcs_in_congestion ( incoming_channel_id) ;
758+ let pending_htlcs_in_congestion = outgoing_channel. pending_htlcs_in_congestion ( ) ;
760759
761760 let incoming_channel = channels_lock. get_mut ( & incoming_channel_id) . ok_or ( ( ) ) ?;
762761
@@ -854,10 +853,16 @@ impl DefaultResourceManager {
854853 resolved_at : u64 ,
855854 ) -> Result < ( ) , ( ) > {
856855 let mut channels_lock = self . channels . lock ( ) . unwrap ( ) ;
856+ if channels_lock. get ( & incoming_channel_id) . is_none ( )
857+ || channels_lock. get ( & outgoing_channel_id) . is_none ( )
858+ {
859+ return Err ( ( ) ) ;
860+ }
861+
857862 let outgoing_channel = channels_lock. get_mut ( & outgoing_channel_id) . ok_or ( ( ) ) ?;
858863
859864 let htlc_ref = HtlcRef { incoming_channel_id, htlc_id } ;
860- let pending_htlc = outgoing_channel. pending_htlcs . get ( & htlc_ref) . ok_or ( ( ) ) ?. clone ( ) ;
865+ let pending_htlc = outgoing_channel. pending_htlcs . remove ( & htlc_ref) . ok_or ( ( ) ) ?;
861866
862867 if resolved_at < pending_htlc. added_at_unix_seconds {
863868 return Err ( ( ) ) ;
@@ -869,8 +874,11 @@ impl DefaultResourceManager {
869874 pending_htlc. outgoing_accountable ,
870875 settled,
871876 ) ;
877+ // Note that the decaying averages for reputation and revenue clamp `resolved_at` to
878+ // `last_updated_unix_secs` if it falls behind. This could happen because
879+ // `last_updated_unix_secs` is frequently mutated. We accept the clamping rather
880+ // than erroring, as rejecting out-of-order timestamps would leave resources stuck.
872881 outgoing_channel. outgoing_reputation . add_value ( effective_fee, resolved_at) ;
873- outgoing_channel. pending_htlcs . remove ( & htlc_ref) . ok_or ( ( ) ) ?;
874882
875883 let incoming_channel = channels_lock. get_mut ( & incoming_channel_id) . ok_or ( ( ) ) ?;
876884 match pending_htlc. bucket {
@@ -1461,8 +1469,7 @@ mod tests {
14611469 fn test_congestion_eligible ( rm : & DefaultResourceManager , incoming_htlc_amount : u64 ) -> bool {
14621470 let mut channels_lock = rm. channels . lock ( ) . unwrap ( ) ;
14631471 let outgoing_channel = channels_lock. get_mut ( & OUTGOING_SCID ) . unwrap ( ) ;
1464- let pending_htlcs_in_congestion =
1465- outgoing_channel. pending_htlcs_in_congestion ( INCOMING_SCID ) ;
1472+ let pending_htlcs_in_congestion = outgoing_channel. pending_htlcs_in_congestion ( ) ;
14661473
14671474 let incoming_channel = channels_lock. get_mut ( & INCOMING_SCID ) . unwrap ( ) ;
14681475 incoming_channel
@@ -1877,7 +1884,7 @@ mod tests {
18771884 . get( & INCOMING_SCID )
18781885 . unwrap( )
18791886 . incoming_revenue
1880- . aggregated_revenue_decaying
1887+ . aggregated_decaying_average
18811888 . value,
18821889 case. expected_revenue,
18831890 ) ;
@@ -2120,20 +2127,20 @@ mod tests {
21202127 let conflicting_slots = {
21212128 let mut channels = rm. channels . lock ( ) . unwrap ( ) ;
21222129 let incoming = channels. get_mut ( & INCOMING_SCID ) . unwrap ( ) ;
2123- let entry = assign_slots_for_channel (
2130+ let salt = entropy_source. get_secure_random_bytes ( ) ;
2131+ let slots = assign_slots_for_channel (
21242132 incoming. general_bucket . scid ,
21252133 OUTGOING_SCID_2 ,
2126- None ,
2127- & entropy_source,
2134+ salt,
21282135 incoming. general_bucket . per_channel_slots ,
21292136 incoming. general_bucket . total_slots ,
21302137 )
21312138 . unwrap ( ) ;
21322139
21332140 let slots_1 = & incoming. general_bucket . channels_slots . get ( & OUTGOING_SCID ) . unwrap ( ) . 0 ;
2134- let ret = entry . 0 . iter ( ) . filter ( |s| slots_1. contains ( s) ) . count ( ) ;
2141+ let ret = slots . iter ( ) . filter ( |s| slots_1. contains ( s) ) . count ( ) ;
21352142
2136- incoming. general_bucket . channels_slots . insert ( OUTGOING_SCID_2 , entry ) ;
2143+ incoming. general_bucket . channels_slots . insert ( OUTGOING_SCID_2 , ( slots , salt ) ) ;
21372144 ret
21382145 } ;
21392146
0 commit comments