Merge pull request #2562 from TheBlueMatt/2023-08-no-perm-fail
[rust-lightning] / lightning / src / routing / router.rs
index 8b3a96eb42ed21c04fe915d6c9674ced3d3da46a..258665c23cd82ca5766ea75393f8bdd13e9bbcc8 100644 (file)
@@ -250,10 +250,20 @@ pub struct RouteHop {
        ///
        /// [`BlindedPath`]: crate::blinded_path::BlindedPath
        pub cltv_expiry_delta: u32,
+       /// Indicates whether this hop is possibly announced in the public network graph.
+       ///
+       /// Will be `true` if there is a possibility that the channel is publicly known, i.e., if we
+       /// either know for sure it's announced in the public graph, or if any public channels exist
+       /// for which the given `short_channel_id` could be an alias for. Will be `false` if we believe
+       /// the channel to be unannounced.
+       ///
+       /// Will be `true` for objects serialized with LDK version 0.0.116 and before.
+       pub maybe_announced_channel: bool,
 }
 
 impl_writeable_tlv_based!(RouteHop, {
        (0, pubkey, required),
+       (1, maybe_announced_channel, (default_value, true)),
        (2, node_features, required),
        (4, short_channel_id, required),
        (6, channel_features, required),
@@ -348,14 +358,23 @@ pub struct Route {
 impl Route {
        /// Returns the total amount of fees paid on this [`Route`].
        ///
-       /// This doesn't include any extra payment made to the recipient, which can happen in excess of
-       /// the amount passed to [`find_route`]'s `route_params.final_value_msat`.
+       /// For objects serialized with LDK 0.0.117 and after, this includes any extra payment made to
+       /// the recipient, which can happen in excess of the amount passed to [`find_route`] via
+       /// [`RouteParameters::final_value_msat`], if we had to reach the [`htlc_minimum_msat`] limits.
+       ///
+       /// [`htlc_minimum_msat`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-channel_update-message
        pub fn get_total_fees(&self) -> u64 {
-               self.paths.iter().map(|path| path.fee_msat()).sum()
+               let overpaid_value_msat = self.route_params.as_ref()
+                       .map_or(0, |p| self.get_total_amount().saturating_sub(p.final_value_msat));
+               overpaid_value_msat + self.paths.iter().map(|path| path.fee_msat()).sum::<u64>()
        }
 
-       /// Returns the total amount paid on this [`Route`], excluding the fees. Might be more than
-       /// requested if we had to reach htlc_minimum_msat.
+       /// Returns the total amount paid on this [`Route`], excluding the fees.
+       ///
+       /// Might be more than requested as part of the given [`RouteParameters::final_value_msat`] if
+       /// we had to reach the [`htlc_minimum_msat`] limits.
+       ///
+       /// [`htlc_minimum_msat`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-channel_update-message
        pub fn get_total_amount(&self) -> u64 {
                self.paths.iter().map(|path| path.final_value_msat()).sum()
        }
@@ -2306,8 +2325,9 @@ where L::Target: Logger {
                                        // Decrease the available liquidity of a hop in the middle of the path.
                                        let victim_candidate = &payment_path.hops[(payment_path.hops.len()) / 2].0.candidate;
                                        let exhausted = u64::max_value();
-                                       log_trace!(logger, "Disabling route candidate {} for future path building iterations to
-                                               avoid duplicates.", LoggedCandidateHop(victim_candidate));
+                                       log_trace!(logger,
+                                               "Disabling route candidate {} for future path building iterations to avoid duplicates.",
+                                               LoggedCandidateHop(victim_candidate));
                                        *used_liquidities.entry(victim_candidate.id(false)).or_default() = exhausted;
                                        *used_liquidities.entry(victim_candidate.id(true)).or_default() = exhausted;
                                }
@@ -2463,9 +2483,27 @@ where L::Target: Logger {
        let mut paths = Vec::new();
        for payment_path in selected_route {
                let mut hops = Vec::with_capacity(payment_path.hops.len());
+               let mut prev_hop_node_id = our_node_id;
                for (hop, node_features) in payment_path.hops.iter()
                        .filter(|(h, _)| h.candidate.short_channel_id().is_some())
                {
+                       let maybe_announced_channel = if let CandidateRouteHop::PublicHop { .. } = hop.candidate {
+                               // If we sourced the hop from the graph we're sure the target node is announced.
+                               true
+                       } else if let CandidateRouteHop::FirstHop { details } = hop.candidate {
+                               // If this is a first hop we also know if it's announced.
+                               details.is_public
+                       } else {
+                               // If we sourced it any other way, we double-check the network graph to see if
+                               // there are announced channels between the endpoints. If so, the hop might be
+                               // referring to any of the announced channels, as its `short_channel_id` might be
+                               // an alias, in which case we don't take any chances here.
+                               network_graph.node(&hop.node_id).map_or(false, |hop_node|
+                                       hop_node.channels.iter().any(|scid| network_graph.channel(*scid)
+                                                       .map_or(false, |c| c.as_directed_from(&prev_hop_node_id).is_some()))
+                               )
+                       };
+
                        hops.push(RouteHop {
                                pubkey: PublicKey::from_slice(hop.node_id.as_slice()).map_err(|_| LightningError{err: format!("Public key {:?} is invalid", &hop.node_id), action: ErrorAction::IgnoreAndLog(Level::Trace)})?,
                                node_features: node_features.clone(),
@@ -2473,7 +2511,10 @@ where L::Target: Logger {
                                channel_features: hop.candidate.features(),
                                fee_msat: hop.fee_msat,
                                cltv_expiry_delta: hop.candidate.cltv_expiry_delta(),
+                               maybe_announced_channel,
                        });
+
+                       prev_hop_node_id = hop.node_id;
                }
                let mut final_cltv_delta = final_cltv_expiry_delta;
                let blinded_tail = payment_path.hops.last().and_then(|(h, _)| {
@@ -5955,17 +5996,17 @@ mod tests {
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
+                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0, maybe_announced_channel: true,
                                },
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
+                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0, maybe_announced_channel: true,
                                },
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0
+                                       short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0, maybe_announced_channel: true,
                                },
                        ], blinded_tail: None }],
                        route_params: None,
@@ -5982,23 +6023,23 @@ mod tests {
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
+                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0, maybe_announced_channel: true,
                                },
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
+                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0, maybe_announced_channel: true,
                                },
                        ], blinded_tail: None }, Path { hops: vec![
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
+                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0, maybe_announced_channel: true,
                                },
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
+                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0, maybe_announced_channel: true,
                                },
                        ], blinded_tail: None }],
                        route_params: None,
@@ -6597,6 +6638,7 @@ mod tests {
                                channel_features: ChannelFeatures::empty(),
                                fee_msat: 100,
                                cltv_expiry_delta: 0,
+                               maybe_announced_channel: true,
                        }],
                        blinded_tail: Some(BlindedTail {
                                hops: blinded_path_1.blinded_hops,
@@ -6611,6 +6653,7 @@ mod tests {
                                channel_features: ChannelFeatures::empty(),
                                fee_msat: 100,
                                cltv_expiry_delta: 0,
+                               maybe_announced_channel: true,
                        }], blinded_tail: None }],
                        route_params: None,
                };
@@ -6650,6 +6693,7 @@ mod tests {
                                channel_features: ChannelFeatures::empty(),
                                fee_msat: 100,
                                cltv_expiry_delta: 0,
+                               maybe_announced_channel: false,
                        },
                        RouteHop {
                                pubkey: blinded_path.introduction_node_id,
@@ -6658,6 +6702,7 @@ mod tests {
                                channel_features: ChannelFeatures::empty(),
                                fee_msat: 1,
                                cltv_expiry_delta: 0,
+                               maybe_announced_channel: false,
                        }],
                        blinded_tail: Some(BlindedTail {
                                hops: blinded_path.blinded_hops,
@@ -6690,6 +6735,7 @@ mod tests {
                                channel_features: ChannelFeatures::empty(),
                                fee_msat: 100,
                                cltv_expiry_delta: 0,
+                               maybe_announced_channel: false,
                        },
                        RouteHop {
                                pubkey: blinded_path.introduction_node_id,
@@ -6698,6 +6744,7 @@ mod tests {
                                channel_features: ChannelFeatures::empty(),
                                fee_msat: 1,
                                cltv_expiry_delta: 0,
+                               maybe_announced_channel: false,
                        }
                        ],
                        blinded_tail: Some(BlindedTail {
@@ -7315,6 +7362,42 @@ pub mod benches {
                        "generate_large_mpp_routes_with_probabilistic_scorer");
        }
 
+       pub fn generate_routes_with_nonlinear_probabilistic_scorer(bench: &mut Criterion) {
+               let logger = TestLogger::new();
+               let network_graph = bench_utils::read_network_graph(&logger).unwrap();
+               let mut params = ProbabilisticScoringFeeParameters::default();
+               params.linear_success_probability = false;
+               let scorer = ProbabilisticScorer::new(
+                       ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+               generate_routes(bench, &network_graph, scorer, &params,
+                       channelmanager::provided_invoice_features(&UserConfig::default()), 0,
+                       "generate_routes_with_nonlinear_probabilistic_scorer");
+       }
+
+       pub fn generate_mpp_routes_with_nonlinear_probabilistic_scorer(bench: &mut Criterion) {
+               let logger = TestLogger::new();
+               let network_graph = bench_utils::read_network_graph(&logger).unwrap();
+               let mut params = ProbabilisticScoringFeeParameters::default();
+               params.linear_success_probability = false;
+               let scorer = ProbabilisticScorer::new(
+                       ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+               generate_routes(bench, &network_graph, scorer, &params,
+                       channelmanager::provided_invoice_features(&UserConfig::default()), 0,
+                       "generate_mpp_routes_with_nonlinear_probabilistic_scorer");
+       }
+
+       pub fn generate_large_mpp_routes_with_nonlinear_probabilistic_scorer(bench: &mut Criterion) {
+               let logger = TestLogger::new();
+               let network_graph = bench_utils::read_network_graph(&logger).unwrap();
+               let mut params = ProbabilisticScoringFeeParameters::default();
+               params.linear_success_probability = false;
+               let scorer = ProbabilisticScorer::new(
+                       ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+               generate_routes(bench, &network_graph, scorer, &params,
+                       channelmanager::provided_invoice_features(&UserConfig::default()), 100_000_000,
+                       "generate_large_mpp_routes_with_nonlinear_probabilistic_scorer");
+       }
+
        fn generate_routes<S: ScoreLookUp + ScoreUpdate>(
                bench: &mut Criterion, graph: &NetworkGraph<&TestLogger>, mut scorer: S,
                score_params: &S::ScoreParams, features: Bolt11InvoiceFeatures, starting_amount: u64,