From 78cc9e9d080ede40c4b7c9d95a6f286ef71ace09 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Thu, 25 Jan 2024 14:29:37 -0600 Subject: [PATCH] Consider success probability in blinded path How certain a scorer is about a channel's success probability is useful in determining if the channel should be included in a blinded payment path. Channels with low success probability for a given amount should be avoided to facilitate successful payments. Expand ScoreLookUp with a channel_success_probability method and use it in DefaultRouter::create_blinded_payment_paths. --- lightning/src/routing/router.rs | 31 +++++++++++++++++++++++++++---- lightning/src/routing/scoring.rs | 12 ++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 836867570fc..57828dceece 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -106,6 +106,9 @@ impl> + Clone, L: Deref, ES: Deref, S: Deref, // The minimum channel balance certainty required for using a channel in a blinded path. const MIN_CHANNEL_CERTAINTY: f64 = 0.5; + // The minimum success probability required for using a channel in a blinded path. + const MIN_SUCCESS_PROBABILITY: f64 = 0.25; + let network_graph = self.network_graph.deref().read_only(); let counterparty_channels = first_hops.into_iter() .filter(|details| details.counterparty.features.supports_route_blinding()) @@ -181,6 +184,21 @@ impl> + Clone, L: Deref, ES: Deref, S: Deref, // // source --- info ---> counterparty --- counterparty_forward_node ---> recipient .filter_map(|(introduction_node_id, scid, info, counterparty_forward_node)| { + let amount_msat = amount_msats; + let effective_capacity = info.effective_capacity(); + let usage = ChannelUsage { amount_msat, inflight_htlc_msat: 0, effective_capacity }; + let success_probability = scorer.channel_success_probability( + scid, &info, usage, &self.score_params + ); + + if !success_probability.is_finite() { + return None; + } + + if success_probability < MIN_SUCCESS_PROBABILITY { + return None; + } + let htlc_minimum_msat = info.direction().htlc_minimum_msat; let htlc_maximum_msat = info.direction().htlc_maximum_msat; let payment_relay: PaymentRelay = match info.try_into() { @@ -202,12 +220,13 @@ impl> + Clone, L: Deref, ES: Deref, S: Deref, node_id: introduction_node_id.as_pubkey().unwrap(), htlc_maximum_msat, }; - Some(BlindedPath::new_for_payment( + let path = BlindedPath::new_for_payment( &[introduction_forward_node, counterparty_forward_node], recipient, tlvs.clone(), u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, entropy_source, secp_ctx - )) - }) - .take(MAX_PAYMENT_PATHS); + ); + + Some(path.map(|path| (path, success_probability))) + }); let two_hop_paths = counterparty_channels .map(|(forward_node, _)| { @@ -221,6 +240,10 @@ impl> + Clone, L: Deref, ES: Deref, S: Deref, three_hop_paths .collect::, _>>().ok() .and_then(|paths| (!paths.is_empty()).then(|| paths)) + .map(|mut paths| { + paths.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + paths.into_iter().map(|(path, _)| path).take(MAX_PAYMENT_PATHS).collect::>() + }) .or_else(|| two_hop_paths.collect::, _>>().ok()) .and_then(|paths| (!paths.is_empty()).then(|| paths)) .or_else(|| network_graph diff --git a/lightning/src/routing/scoring.rs b/lightning/src/routing/scoring.rs index e9494b8f323..ac58d47ec53 100644 --- a/lightning/src/routing/scoring.rs +++ b/lightning/src/routing/scoring.rs @@ -109,6 +109,18 @@ pub trait ScoreLookUp { &self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams ) -> u64; + /// Returns the success probability of sending an HTLC through a channel. + /// + /// Expected to return a value between `0.0` and `1.0`, inclusive, where `0.0` indicates + /// highly unlikely and `1.0` highly likely. + /// + /// This is useful to determine whether a channel should be included in a blinded path and the + /// preferred ordering of blinded paths. + fn channel_success_probability( + &self, _short_channel_id: u64, _info: &DirectedChannelInfo, _usage: ChannelUsage, + _score_params: &Self::ScoreParams + ) -> f64 { 0.5 } + /// Returns how certain any knowledge gained about the channel's liquidity balance is. /// /// Expected to return a value between `0.0` and `1.0`, inclusive, where `0.0` indicates