Skip to content

Commit 92ca7ff

Browse files
Merge pull request #1555 from tnull/2022-06-prefer-small-htlc-max
Add anti-probing penalty to `ProbabilisticScorer`
2 parents 79e2af9 + 800ccec commit 92ca7ff

File tree

2 files changed

+104
-36
lines changed

2 files changed

+104
-36
lines changed

lightning/src/routing/gossip.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -745,13 +745,13 @@ impl<'a> DirectedChannelInfo<'a> {
745745
let (htlc_maximum_msat, effective_capacity) = match (htlc_maximum_msat, capacity_msat) {
746746
(Some(amount_msat), Some(capacity_msat)) => {
747747
let htlc_maximum_msat = cmp::min(amount_msat, capacity_msat);
748-
(htlc_maximum_msat, EffectiveCapacity::Total { capacity_msat })
748+
(htlc_maximum_msat, EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: Some(htlc_maximum_msat) })
749749
},
750750
(Some(amount_msat), None) => {
751751
(amount_msat, EffectiveCapacity::MaximumHTLC { amount_msat })
752752
},
753753
(None, Some(capacity_msat)) => {
754-
(capacity_msat, EffectiveCapacity::Total { capacity_msat })
754+
(capacity_msat, EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: None })
755755
},
756756
(None, None) => (EffectiveCapacity::Unknown.as_msat(), EffectiveCapacity::Unknown),
757757
};
@@ -850,6 +850,8 @@ pub enum EffectiveCapacity {
850850
Total {
851851
/// The funding amount denominated in millisatoshi.
852852
capacity_msat: u64,
853+
/// The maximum HTLC amount denominated in millisatoshi.
854+
htlc_maximum_msat: Option<u64>
853855
},
854856
/// A capacity sufficient to route any payment, typically used for private channels provided by
855857
/// an invoice.
@@ -869,7 +871,7 @@ impl EffectiveCapacity {
869871
match self {
870872
EffectiveCapacity::ExactLiquidity { liquidity_msat } => *liquidity_msat,
871873
EffectiveCapacity::MaximumHTLC { amount_msat } => *amount_msat,
872-
EffectiveCapacity::Total { capacity_msat } => *capacity_msat,
874+
EffectiveCapacity::Total { capacity_msat, .. } => *capacity_msat,
873875
EffectiveCapacity::Infinite => u64::max_value(),
874876
EffectiveCapacity::Unknown => UNKNOWN_CHANNEL_CAPACITY_MSAT,
875877
}

lightning/src/routing/scoring.rs

+99-33
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,14 @@ pub struct ProbabilisticScoringParameters {
366366
///
367367
/// (C-not exported)
368368
pub banned_nodes: HashSet<NodeId>,
369+
370+
/// This penalty is applied when `htlc_maximum_msat` is equal to or larger than half of the
371+
/// channel's capacity, which makes us prefer nodes with a smaller `htlc_maximum_msat`. We
372+
/// treat such nodes preferentially as this makes balance discovery attacks harder to execute,
373+
/// thereby creating an incentive to restrict `htlc_maximum_msat` and improve privacy.
374+
///
375+
/// Default value: 250 msat
376+
pub anti_probing_penalty_msat: u64,
369377
}
370378

371379
/// Accounting for channel liquidity balance uncertainty.
@@ -483,6 +491,7 @@ impl ProbabilisticScoringParameters {
483491
liquidity_offset_half_life: Duration::from_secs(3600),
484492
amount_penalty_multiplier_msat: 0,
485493
banned_nodes: HashSet::new(),
494+
anti_probing_penalty_msat: 0,
486495
}
487496
}
488497

@@ -503,6 +512,7 @@ impl Default for ProbabilisticScoringParameters {
503512
liquidity_offset_half_life: Duration::from_secs(3600),
504513
amount_penalty_multiplier_msat: 256,
505514
banned_nodes: HashSet::new(),
515+
anti_probing_penalty_msat: 250,
506516
}
507517
}
508518
}
@@ -707,12 +717,21 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
707717
return u64::max_value();
708718
}
709719

710-
if let EffectiveCapacity::ExactLiquidity { liquidity_msat } = usage.effective_capacity {
711-
if usage.amount_msat > liquidity_msat {
712-
return u64::max_value();
713-
} else {
714-
return self.params.base_penalty_msat;
715-
};
720+
let mut anti_probing_penalty_msat = 0;
721+
match usage.effective_capacity {
722+
EffectiveCapacity::ExactLiquidity { liquidity_msat } => {
723+
if usage.amount_msat > liquidity_msat {
724+
return u64::max_value();
725+
} else {
726+
return self.params.base_penalty_msat;
727+
}
728+
},
729+
EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: Some(htlc_maximum_msat) } => {
730+
if htlc_maximum_msat >= capacity_msat/2 {
731+
anti_probing_penalty_msat = self.params.anti_probing_penalty_msat;
732+
}
733+
},
734+
_ => {},
716735
}
717736

718737
let liquidity_offset_half_life = self.params.liquidity_offset_half_life;
@@ -724,6 +743,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
724743
.unwrap_or(&ChannelLiquidity::new())
725744
.as_directed(source, target, capacity_msat, liquidity_offset_half_life)
726745
.penalty_msat(amount_msat, &self.params)
746+
.saturating_add(anti_probing_penalty_msat)
727747
}
728748

729749
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
@@ -1547,7 +1567,7 @@ mod tests {
15471567
let usage = ChannelUsage {
15481568
amount_msat: 1_024,
15491569
inflight_htlc_msat: 0,
1550-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000 },
1570+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(1_000) },
15511571
};
15521572
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
15531573
let usage = ChannelUsage { amount_msat: 10_240, ..usage };
@@ -1560,7 +1580,7 @@ mod tests {
15601580
let usage = ChannelUsage {
15611581
amount_msat: 128,
15621582
inflight_htlc_msat: 0,
1563-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
1583+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: Some(1_000) },
15641584
};
15651585
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58);
15661586
let usage = ChannelUsage { amount_msat: 256, ..usage };
@@ -1597,7 +1617,7 @@ mod tests {
15971617
let usage = ChannelUsage {
15981618
amount_msat: 39,
15991619
inflight_htlc_msat: 0,
1600-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 100 },
1620+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 100, htlc_maximum_msat: Some(1_000) },
16011621
};
16021622
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
16031623
let usage = ChannelUsage { amount_msat: 50, ..usage };
@@ -1621,7 +1641,7 @@ mod tests {
16211641
let usage = ChannelUsage {
16221642
amount_msat: 500,
16231643
inflight_htlc_msat: 0,
1624-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
1644+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
16251645
};
16261646
let failed_path = payment_path_for_amount(500);
16271647
let successful_path = payment_path_for_amount(200);
@@ -1651,7 +1671,7 @@ mod tests {
16511671
let usage = ChannelUsage {
16521672
amount_msat: 250,
16531673
inflight_htlc_msat: 0,
1654-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
1674+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
16551675
};
16561676
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
16571677
let usage = ChannelUsage { amount_msat: 500, ..usage };
@@ -1685,7 +1705,7 @@ mod tests {
16851705
let usage = ChannelUsage {
16861706
amount_msat: 250,
16871707
inflight_htlc_msat: 0,
1688-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
1708+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
16891709
};
16901710
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
16911711
let usage = ChannelUsage { amount_msat: 500, ..usage };
@@ -1719,7 +1739,7 @@ mod tests {
17191739
let usage = ChannelUsage {
17201740
amount_msat: 250,
17211741
inflight_htlc_msat: 0,
1722-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
1742+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
17231743
};
17241744
let path = payment_path_for_amount(500);
17251745

@@ -1750,7 +1770,7 @@ mod tests {
17501770
let usage = ChannelUsage {
17511771
amount_msat: 0,
17521772
inflight_htlc_msat: 0,
1753-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
1773+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: Some(1_000) },
17541774
};
17551775
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
17561776
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
@@ -1828,7 +1848,7 @@ mod tests {
18281848
let usage = ChannelUsage {
18291849
amount_msat: 256,
18301850
inflight_htlc_msat: 0,
1831-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
1851+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: Some(1_000) },
18321852
};
18331853
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
18341854

@@ -1859,7 +1879,7 @@ mod tests {
18591879
let usage = ChannelUsage {
18601880
amount_msat: 512,
18611881
inflight_htlc_msat: 0,
1862-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
1882+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: Some(1_000) },
18631883
};
18641884

18651885
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
@@ -1903,7 +1923,7 @@ mod tests {
19031923
let usage = ChannelUsage {
19041924
amount_msat: 500,
19051925
inflight_htlc_msat: 0,
1906-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
1926+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
19071927
};
19081928

19091929
scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::<Vec<_>>(), 42);
@@ -1939,7 +1959,7 @@ mod tests {
19391959
let usage = ChannelUsage {
19401960
amount_msat: 500,
19411961
inflight_htlc_msat: 0,
1942-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
1962+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
19431963
};
19441964

19451965
scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::<Vec<_>>(), 42);
@@ -1976,47 +1996,47 @@ mod tests {
19761996
let usage = ChannelUsage {
19771997
amount_msat: 100_000_000,
19781998
inflight_htlc_msat: 0,
1979-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 950_000_000 },
1999+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 950_000_000, htlc_maximum_msat: Some(1_000) },
19802000
};
19812001
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 3613);
19822002
let usage = ChannelUsage {
1983-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_950_000_000 }, ..usage
2003+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
19842004
};
19852005
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1977);
19862006
let usage = ChannelUsage {
1987-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 2_950_000_000 }, ..usage
2007+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 2_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
19882008
};
19892009
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1474);
19902010
let usage = ChannelUsage {
1991-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 3_950_000_000 }, ..usage
2011+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 3_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
19922012
};
19932013
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1223);
19942014
let usage = ChannelUsage {
1995-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 4_950_000_000 }, ..usage
2015+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 4_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
19962016
};
19972017
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 877);
19982018
let usage = ChannelUsage {
1999-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 5_950_000_000 }, ..usage
2019+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 5_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
20002020
};
20012021
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 845);
20022022
let usage = ChannelUsage {
2003-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 6_950_000_000 }, ..usage
2023+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 6_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
20042024
};
20052025
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
20062026
let usage = ChannelUsage {
2007-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_450_000_000 }, ..usage
2027+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_450_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
20082028
};
20092029
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
20102030
let usage = ChannelUsage {
2011-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_950_000_000 }, ..usage
2031+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
20122032
};
20132033
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
20142034
let usage = ChannelUsage {
2015-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 8_950_000_000 }, ..usage
2035+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 8_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
20162036
};
20172037
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
20182038
let usage = ChannelUsage {
2019-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 9_950_000_000 }, ..usage
2039+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 9_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
20202040
};
20212041
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
20222042
}
@@ -2030,7 +2050,7 @@ mod tests {
20302050
let usage = ChannelUsage {
20312051
amount_msat: 128,
20322052
inflight_htlc_msat: 0,
2033-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
2053+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: Some(1_000) },
20342054
};
20352055

20362056
let params = ProbabilisticScoringParameters {
@@ -2041,7 +2061,8 @@ mod tests {
20412061
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58);
20422062

20432063
let params = ProbabilisticScoringParameters {
2044-
base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
2064+
base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
2065+
anti_probing_penalty_msat: 0, ..Default::default()
20452066
};
20462067
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
20472068
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 558);
@@ -2056,7 +2077,7 @@ mod tests {
20562077
let usage = ChannelUsage {
20572078
amount_msat: 512_000,
20582079
inflight_htlc_msat: 0,
2059-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000 },
2080+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(1_000) },
20602081
};
20612082

20622083
let params = ProbabilisticScoringParameters {
@@ -2108,7 +2129,7 @@ mod tests {
21082129
let usage = ChannelUsage {
21092130
amount_msat: 750,
21102131
inflight_htlc_msat: 0,
2111-
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
2132+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
21122133
};
21132134
assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
21142135

@@ -2139,4 +2160,49 @@ mod tests {
21392160
let usage = ChannelUsage { amount_msat: 1_001, ..usage };
21402161
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
21412162
}
2163+
2164+
#[test]
2165+
fn adds_anti_probing_penalty() {
2166+
let logger = TestLogger::new();
2167+
let network_graph = network_graph(&logger);
2168+
let source = source_node_id();
2169+
let target = target_node_id();
2170+
let params = ProbabilisticScoringParameters {
2171+
anti_probing_penalty_msat: 500,
2172+
..ProbabilisticScoringParameters::zero_penalty()
2173+
};
2174+
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
2175+
2176+
// Check we receive no penalty for a low htlc_maximum_msat.
2177+
let usage = ChannelUsage {
2178+
amount_msat: 512_000,
2179+
inflight_htlc_msat: 0,
2180+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(1_000) },
2181+
};
2182+
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
2183+
2184+
// Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity.
2185+
let usage = ChannelUsage {
2186+
amount_msat: 512_000,
2187+
inflight_htlc_msat: 0,
2188+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(1_024_000) },
2189+
};
2190+
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
2191+
2192+
// Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity/2.
2193+
let usage = ChannelUsage {
2194+
amount_msat: 512_000,
2195+
inflight_htlc_msat: 0,
2196+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(512_000) },
2197+
};
2198+
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
2199+
2200+
// Check we receive no anti-probing penalty for htlc_maximum_msat == channel_capacity/2 - 1.
2201+
let usage = ChannelUsage {
2202+
amount_msat: 512_000,
2203+
inflight_htlc_msat: 0,
2204+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(511_999) },
2205+
};
2206+
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
2207+
}
21422208
}

0 commit comments

Comments
 (0)