Skip to content

Commit 0f049dc

Browse files
committed
f - fix comment and add extra error check
1 parent 3ac95b7 commit 0f049dc

1 file changed

Lines changed: 27 additions & 20 deletions

File tree

lightning/src/ln/resource_manager.rs

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ const MIN_ACCEPTED_HTLCS: u16 = 12;
3636
const 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.
4140
const 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

Comments
 (0)