Skip to content

Commit 79e2af9

Browse files
authored
Merge pull request #1550 from tnull/2022-06-scorer-banlist
Allow nodes to be avoided during pathfinding
2 parents 3676a05 + 57d8257 commit 79e2af9

File tree

2 files changed

+72
-10
lines changed

2 files changed

+72
-10
lines changed

lightning/src/routing/router.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -1878,7 +1878,7 @@ mod tests {
18781878
use routing::router::{get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features,
18791879
PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees,
18801880
DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE};
1881-
use routing::scoring::{ChannelUsage, Score};
1881+
use routing::scoring::{ChannelUsage, Score, ProbabilisticScorer, ProbabilisticScoringParameters};
18821882
use chain::transaction::OutPoint;
18831883
use chain::keysinterface::KeysInterface;
18841884
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
@@ -5713,6 +5713,33 @@ mod tests {
57135713
}
57145714
}
57155715
}
5716+
5717+
#[test]
5718+
fn avoids_banned_nodes() {
5719+
let (secp_ctx, network_graph, _, _, logger) = build_line_graph();
5720+
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
5721+
5722+
let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
5723+
let random_seed_bytes = keys_manager.get_secure_random_bytes();
5724+
5725+
let scorer_params = ProbabilisticScoringParameters::default();
5726+
let mut scorer = ProbabilisticScorer::new(scorer_params, Arc::clone(&network_graph), Arc::clone(&logger));
5727+
5728+
// First check we can get a route.
5729+
let payment_params = PaymentParameters::from_node_id(nodes[10]);
5730+
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes);
5731+
assert!(route.is_ok());
5732+
5733+
// Then check that we can't get a route if we ban an intermediate node.
5734+
scorer.add_banned(&NodeId::from_pubkey(&nodes[3]));
5735+
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes);
5736+
assert!(route.is_err());
5737+
5738+
// Finally make sure we can route again, when we remove the ban.
5739+
scorer.remove_banned(&NodeId::from_pubkey(&nodes[3]));
5740+
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes);
5741+
assert!(route.is_ok());
5742+
}
57165743
}
57175744

57185745
#[cfg(all(test, not(feature = "no-std")))]

lightning/src/routing/scoring.rs

+44-9
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ where L::Target: Logger {
306306
///
307307
/// Used to configure base, liquidity, and amount penalties, the sum of which comprises the channel
308308
/// penalty (i.e., the amount in msats willing to be paid to avoid routing through the channel).
309-
#[derive(Clone, Copy)]
309+
#[derive(Clone)]
310310
pub struct ProbabilisticScoringParameters {
311311
/// A fixed penalty in msats to apply to each channel.
312312
///
@@ -361,6 +361,11 @@ pub struct ProbabilisticScoringParameters {
361361
///
362362
/// Default value: 256 msat
363363
pub amount_penalty_multiplier_msat: u64,
364+
365+
/// A list of nodes that won't be considered during path finding.
366+
///
367+
/// (C-not exported)
368+
pub banned_nodes: HashSet<NodeId>,
364369
}
365370

366371
/// Accounting for channel liquidity balance uncertainty.
@@ -451,6 +456,22 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
451456
}
452457
None
453458
}
459+
460+
/// Marks the node with the given `node_id` as banned, i.e.,
461+
/// it will be avoided during path finding.
462+
pub fn add_banned(&mut self, node_id: &NodeId) {
463+
self.params.banned_nodes.insert(*node_id);
464+
}
465+
466+
/// Removes the node with the given `node_id` from the list of nodes to avoid.
467+
pub fn remove_banned(&mut self, node_id: &NodeId) {
468+
self.params.banned_nodes.remove(node_id);
469+
}
470+
471+
/// Clears the list of nodes that are avoided during path finding.
472+
pub fn clear_banned(&mut self) {
473+
self.params.banned_nodes = HashSet::new();
474+
}
454475
}
455476

456477
impl ProbabilisticScoringParameters {
@@ -461,6 +482,15 @@ impl ProbabilisticScoringParameters {
461482
liquidity_penalty_multiplier_msat: 0,
462483
liquidity_offset_half_life: Duration::from_secs(3600),
463484
amount_penalty_multiplier_msat: 0,
485+
banned_nodes: HashSet::new(),
486+
}
487+
}
488+
489+
/// Marks all nodes in the given list as banned, i.e.,
490+
/// they will be avoided during path finding.
491+
pub fn add_banned_from_list(&mut self, node_ids: Vec<NodeId>) {
492+
for id in node_ids {
493+
self.banned_nodes.insert(id);
464494
}
465495
}
466496
}
@@ -472,6 +502,7 @@ impl Default for ProbabilisticScoringParameters {
472502
liquidity_penalty_multiplier_msat: 40_000,
473503
liquidity_offset_half_life: Duration::from_secs(3600),
474504
amount_penalty_multiplier_msat: 256,
505+
banned_nodes: HashSet::new(),
475506
}
476507
}
477508
}
@@ -543,7 +574,7 @@ const AMOUNT_PENALTY_DIVISOR: u64 = 1 << 20;
543574
impl<L: Deref<Target = u64>, T: Time, U: Deref<Target = T>> DirectedChannelLiquidity<L, T, U> {
544575
/// Returns a penalty for routing the given HTLC `amount_msat` through the channel in this
545576
/// direction.
546-
fn penalty_msat(&self, amount_msat: u64, params: ProbabilisticScoringParameters) -> u64 {
577+
fn penalty_msat(&self, amount_msat: u64, params: &ProbabilisticScoringParameters) -> u64 {
547578
let max_liquidity_msat = self.max_liquidity_msat();
548579
let min_liquidity_msat = core::cmp::min(self.min_liquidity_msat(), max_liquidity_msat);
549580
if amount_msat <= min_liquidity_msat {
@@ -580,7 +611,7 @@ impl<L: Deref<Target = u64>, T: Time, U: Deref<Target = T>> DirectedChannelLiqui
580611
#[inline(always)]
581612
fn combined_penalty_msat(
582613
&self, amount_msat: u64, negative_log10_times_2048: u64,
583-
params: ProbabilisticScoringParameters
614+
params: &ProbabilisticScoringParameters
584615
) -> u64 {
585616
let liquidity_penalty_msat = {
586617
// Upper bound the liquidity penalty to ensure some channel is selected.
@@ -672,6 +703,10 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
672703
fn channel_penalty_msat(
673704
&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage
674705
) -> u64 {
706+
if self.params.banned_nodes.contains(source) || self.params.banned_nodes.contains(target) {
707+
return u64::max_value();
708+
}
709+
675710
if let EffectiveCapacity::ExactLiquidity { liquidity_msat } = usage.effective_capacity {
676711
if usage.amount_msat > liquidity_msat {
677712
return u64::max_value();
@@ -688,7 +723,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
688723
.get(&short_channel_id)
689724
.unwrap_or(&ChannelLiquidity::new())
690725
.as_directed(source, target, capacity_msat, liquidity_offset_half_life)
691-
.penalty_msat(amount_msat, self.params)
726+
.penalty_msat(amount_msat, &self.params)
692727
}
693728

694729
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
@@ -1072,7 +1107,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Writeable for Probab
10721107
#[inline]
10731108
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
10741109
write_tlv_fields!(w, {
1075-
(0, self.channel_liquidities, required)
1110+
(0, self.channel_liquidities, required),
10761111
});
10771112
Ok(())
10781113
}
@@ -1087,7 +1122,7 @@ ReadableArgs<(ProbabilisticScoringParameters, G, L)> for ProbabilisticScorerUsin
10871122
let (params, network_graph, logger) = args;
10881123
let mut channel_liquidities = HashMap::new();
10891124
read_tlv_fields!(r, {
1090-
(0, channel_liquidities, required)
1125+
(0, channel_liquidities, required),
10911126
});
10921127
Ok(Self {
10931128
params,
@@ -1862,7 +1897,7 @@ mod tests {
18621897
liquidity_offset_half_life: Duration::from_secs(10),
18631898
..ProbabilisticScoringParameters::zero_penalty()
18641899
};
1865-
let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
1900+
let mut scorer = ProbabilisticScorer::new(params.clone(), &network_graph, &logger);
18661901
let source = source_node_id();
18671902
let target = target_node_id();
18681903
let usage = ChannelUsage {
@@ -1898,7 +1933,7 @@ mod tests {
18981933
liquidity_offset_half_life: Duration::from_secs(10),
18991934
..ProbabilisticScoringParameters::zero_penalty()
19001935
};
1901-
let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
1936+
let mut scorer = ProbabilisticScorer::new(params.clone(), &network_graph, &logger);
19021937
let source = source_node_id();
19031938
let target = target_node_id();
19041939
let usage = ChannelUsage {
@@ -2086,7 +2121,7 @@ mod tests {
20862121
let logger = TestLogger::new();
20872122
let network_graph = network_graph(&logger);
20882123
let params = ProbabilisticScoringParameters::default();
2089-
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
2124+
let scorer = ProbabilisticScorer::new(params.clone(), &network_graph, &logger);
20902125
let source = source_node_id();
20912126
let target = target_node_id();
20922127

0 commit comments

Comments
 (0)