Skip to content

Commit

Permalink
Merge pull request #1555 from tnull/2022-06-prefer-small-htlc-max
Browse files Browse the repository at this point in the history
Add anti-probing penalty to `ProbabilisticScorer`
  • Loading branch information
valentinewallace authored Jun 27, 2022
2 parents 79e2af9 + 800ccec commit 92ca7ff
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 36 deletions.
8 changes: 5 additions & 3 deletions lightning/src/routing/gossip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,13 +745,13 @@ impl<'a> DirectedChannelInfo<'a> {
let (htlc_maximum_msat, effective_capacity) = match (htlc_maximum_msat, capacity_msat) {
(Some(amount_msat), Some(capacity_msat)) => {
let htlc_maximum_msat = cmp::min(amount_msat, capacity_msat);
(htlc_maximum_msat, EffectiveCapacity::Total { capacity_msat })
(htlc_maximum_msat, EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: Some(htlc_maximum_msat) })
},
(Some(amount_msat), None) => {
(amount_msat, EffectiveCapacity::MaximumHTLC { amount_msat })
},
(None, Some(capacity_msat)) => {
(capacity_msat, EffectiveCapacity::Total { capacity_msat })
(capacity_msat, EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: None })
},
(None, None) => (EffectiveCapacity::Unknown.as_msat(), EffectiveCapacity::Unknown),
};
Expand Down Expand Up @@ -850,6 +850,8 @@ pub enum EffectiveCapacity {
Total {
/// The funding amount denominated in millisatoshi.
capacity_msat: u64,
/// The maximum HTLC amount denominated in millisatoshi.
htlc_maximum_msat: Option<u64>
},
/// A capacity sufficient to route any payment, typically used for private channels provided by
/// an invoice.
Expand All @@ -869,7 +871,7 @@ impl EffectiveCapacity {
match self {
EffectiveCapacity::ExactLiquidity { liquidity_msat } => *liquidity_msat,
EffectiveCapacity::MaximumHTLC { amount_msat } => *amount_msat,
EffectiveCapacity::Total { capacity_msat } => *capacity_msat,
EffectiveCapacity::Total { capacity_msat, .. } => *capacity_msat,
EffectiveCapacity::Infinite => u64::max_value(),
EffectiveCapacity::Unknown => UNKNOWN_CHANNEL_CAPACITY_MSAT,
}
Expand Down
132 changes: 99 additions & 33 deletions lightning/src/routing/scoring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,14 @@ pub struct ProbabilisticScoringParameters {
///
/// (C-not exported)
pub banned_nodes: HashSet<NodeId>,

/// This penalty is applied when `htlc_maximum_msat` is equal to or larger than half of the
/// channel's capacity, which makes us prefer nodes with a smaller `htlc_maximum_msat`. We
/// treat such nodes preferentially as this makes balance discovery attacks harder to execute,
/// thereby creating an incentive to restrict `htlc_maximum_msat` and improve privacy.
///
/// Default value: 250 msat
pub anti_probing_penalty_msat: u64,
}

/// Accounting for channel liquidity balance uncertainty.
Expand Down Expand Up @@ -483,6 +491,7 @@ impl ProbabilisticScoringParameters {
liquidity_offset_half_life: Duration::from_secs(3600),
amount_penalty_multiplier_msat: 0,
banned_nodes: HashSet::new(),
anti_probing_penalty_msat: 0,
}
}

Expand All @@ -503,6 +512,7 @@ impl Default for ProbabilisticScoringParameters {
liquidity_offset_half_life: Duration::from_secs(3600),
amount_penalty_multiplier_msat: 256,
banned_nodes: HashSet::new(),
anti_probing_penalty_msat: 250,
}
}
}
Expand Down Expand Up @@ -707,12 +717,21 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
return u64::max_value();
}

if let EffectiveCapacity::ExactLiquidity { liquidity_msat } = usage.effective_capacity {
if usage.amount_msat > liquidity_msat {
return u64::max_value();
} else {
return self.params.base_penalty_msat;
};
let mut anti_probing_penalty_msat = 0;
match usage.effective_capacity {
EffectiveCapacity::ExactLiquidity { liquidity_msat } => {
if usage.amount_msat > liquidity_msat {
return u64::max_value();
} else {
return self.params.base_penalty_msat;
}
},
EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: Some(htlc_maximum_msat) } => {
if htlc_maximum_msat >= capacity_msat/2 {
anti_probing_penalty_msat = self.params.anti_probing_penalty_msat;
}
},
_ => {},
}

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

fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
Expand Down Expand Up @@ -1547,7 +1567,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 1_024,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(1_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
let usage = ChannelUsage { amount_msat: 10_240, ..usage };
Expand All @@ -1560,7 +1580,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 128,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: Some(1_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58);
let usage = ChannelUsage { amount_msat: 256, ..usage };
Expand Down Expand Up @@ -1597,7 +1617,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 39,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 100 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 100, htlc_maximum_msat: Some(1_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
let usage = ChannelUsage { amount_msat: 50, ..usage };
Expand All @@ -1621,7 +1641,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 500,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
};
let failed_path = payment_path_for_amount(500);
let successful_path = payment_path_for_amount(200);
Expand Down Expand Up @@ -1651,7 +1671,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 250,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
let usage = ChannelUsage { amount_msat: 500, ..usage };
Expand Down Expand Up @@ -1685,7 +1705,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 250,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
let usage = ChannelUsage { amount_msat: 500, ..usage };
Expand Down Expand Up @@ -1719,7 +1739,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 250,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
};
let path = payment_path_for_amount(500);

Expand Down Expand Up @@ -1750,7 +1770,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 0,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: Some(1_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
Expand Down Expand Up @@ -1828,7 +1848,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 256,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: Some(1_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);

Expand Down Expand Up @@ -1859,7 +1879,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 512,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: Some(1_000) },
};

assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
Expand Down Expand Up @@ -1903,7 +1923,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 500,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
};

scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::<Vec<_>>(), 42);
Expand Down Expand Up @@ -1939,7 +1959,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 500,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
};

scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::<Vec<_>>(), 42);
Expand Down Expand Up @@ -1976,47 +1996,47 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 100_000_000,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 950_000_000 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 950_000_000, htlc_maximum_msat: Some(1_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 3613);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_950_000_000 }, ..usage
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1977);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 2_950_000_000 }, ..usage
effective_capacity: EffectiveCapacity::Total { capacity_msat: 2_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1474);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 3_950_000_000 }, ..usage
effective_capacity: EffectiveCapacity::Total { capacity_msat: 3_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1223);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 4_950_000_000 }, ..usage
effective_capacity: EffectiveCapacity::Total { capacity_msat: 4_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 877);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 5_950_000_000 }, ..usage
effective_capacity: EffectiveCapacity::Total { capacity_msat: 5_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 845);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 6_950_000_000 }, ..usage
effective_capacity: EffectiveCapacity::Total { capacity_msat: 6_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_450_000_000 }, ..usage
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_450_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_950_000_000 }, ..usage
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 8_950_000_000 }, ..usage
effective_capacity: EffectiveCapacity::Total { capacity_msat: 8_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 9_950_000_000 }, ..usage
effective_capacity: EffectiveCapacity::Total { capacity_msat: 9_950_000_000, htlc_maximum_msat: Some(1_000) }, ..usage
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
}
Expand All @@ -2030,7 +2050,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 128,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: Some(1_000) },
};

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

let params = ProbabilisticScoringParameters {
base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
anti_probing_penalty_msat: 0, ..Default::default()
};
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 558);
Expand All @@ -2056,7 +2077,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 512_000,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(1_000) },
};

let params = ProbabilisticScoringParameters {
Expand Down Expand Up @@ -2108,7 +2129,7 @@ mod tests {
let usage = ChannelUsage {
amount_msat: 750,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: Some(1_000) },
};
assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());

Expand Down Expand Up @@ -2139,4 +2160,49 @@ mod tests {
let usage = ChannelUsage { amount_msat: 1_001, ..usage };
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
}

#[test]
fn adds_anti_probing_penalty() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
let source = source_node_id();
let target = target_node_id();
let params = ProbabilisticScoringParameters {
anti_probing_penalty_msat: 500,
..ProbabilisticScoringParameters::zero_penalty()
};
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);

// Check we receive no penalty for a low htlc_maximum_msat.
let usage = ChannelUsage {
amount_msat: 512_000,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(1_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);

// Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity.
let usage = ChannelUsage {
amount_msat: 512_000,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(1_024_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);

// Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity/2.
let usage = ChannelUsage {
amount_msat: 512_000,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(512_000) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);

// Check we receive no anti-probing penalty for htlc_maximum_msat == channel_capacity/2 - 1.
let usage = ChannelUsage {
amount_msat: 512_000,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(511_999) },
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
}
}

0 comments on commit 92ca7ff

Please sign in to comment.