use crate::blinded_path::{BlindedHop, BlindedPath};
use crate::ln::PaymentHash;
use crate::ln::channelmanager::{ChannelDetails, PaymentId};
-use crate::ln::features::{Bolt12InvoiceFeatures, ChannelFeatures, InvoiceFeatures, NodeFeatures};
+use crate::ln::features::{Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
payer: &PublicKey,
params: &RouteParameters,
first_hops: Option<&[&ChannelDetails]>,
- inflight_htlcs: &InFlightHtlcs
+ inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError> {
let random_seed_bytes = {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
};
find_route(
payer, params, &self.network_graph, first_hops, &*self.logger,
- &ScorerAccountingForInFlightHtlcs::new(self.scorer.lock().deref_mut(), inflight_htlcs),
+ &ScorerAccountingForInFlightHtlcs::new(self.scorer.lock().deref_mut(), &inflight_htlcs),
&self.score_params,
&random_seed_bytes
)
/// A trait defining behavior for routing a payment.
pub trait Router {
- /// Finds a [`Route`] between `payer` and `payee` for a payment with the given values.
+ /// Finds a [`Route`] for a payment between the given `payer` and a payee.
+ ///
+ /// The `payee` and the payment's value are given in [`RouteParameters::payment_params`]
+ /// and [`RouteParameters::final_value_msat`], respectively.
fn find_route(
&self, payer: &PublicKey, route_params: &RouteParameters,
- first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: &InFlightHtlcs
+ first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError>;
- /// Finds a [`Route`] between `payer` and `payee` for a payment with the given values. Includes
- /// `PaymentHash` and `PaymentId` to be able to correlate the request with a specific payment.
+ /// Finds a [`Route`] for a payment between the given `payer` and a payee.
+ ///
+ /// The `payee` and the payment's value are given in [`RouteParameters::payment_params`]
+ /// and [`RouteParameters::final_value_msat`], respectively.
+ ///
+ /// Includes a [`PaymentHash`] and a [`PaymentId`] to be able to correlate the request with a specific
+ /// payment.
fn find_route_with_id(
&self, payer: &PublicKey, route_params: &RouteParameters,
- first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: &InFlightHtlcs,
+ first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs,
_payment_hash: PaymentHash, _payment_id: PaymentId
) -> Result<Route, LightningError> {
self.find_route(payer, route_params, first_hops, inflight_htlcs)
impl<'a, S: Score<ScoreParams = SP>, SP: Sized> ScorerAccountingForInFlightHtlcs<'a, S, SP> {
/// Initialize a new `ScorerAccountingForInFlightHtlcs`.
- pub fn new(scorer: &'a mut S, inflight_htlcs: &'a InFlightHtlcs) -> Self {
+ pub fn new(scorer: &'a mut S, inflight_htlcs: &'a InFlightHtlcs) -> Self {
ScorerAccountingForInFlightHtlcs {
scorer,
inflight_htlcs
/// [`BlindedTail`]s are present, then the pubkey of the last [`RouteHop`] in each path must be
/// the same.
pub paths: Vec<Path>,
- /// The `payment_params` parameter passed to [`find_route`].
- /// This is used by `ChannelManager` to track information which may be required for retries,
- /// provided back to you via [`Event::PaymentPathFailed`].
+ /// The `payment_params` parameter passed via [`RouteParameters`] to [`find_route`].
///
- /// [`Event::PaymentPathFailed`]: crate::events::Event::PaymentPathFailed
+ /// This is used by `ChannelManager` to track information which may be required for retries.
pub payment_params: Option<PaymentParameters>,
}
/// 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 `params.final_value_msat`.
+ /// the amount passed to [`find_route`]'s `route_params.final_value_msat`.
pub fn get_total_fees(&self) -> u64 {
self.paths.iter().map(|path| path.fee_msat()).sum()
}
/// Parameters needed to find a [`Route`].
///
-/// Passed to [`find_route`] and [`build_route_from_hops`], but also provided in
-/// [`Event::PaymentPathFailed`].
-///
-/// [`Event::PaymentPathFailed`]: crate::events::Event::PaymentPathFailed
+/// Passed to [`find_route`] and [`build_route_from_hops`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RouteParameters {
/// The parameters of the failed payment path.
/// [`RecipientOnionFields::secret_only`]: crate::ln::channelmanager::RecipientOnionFields::secret_only
pub fn for_keysend(payee_pubkey: PublicKey, final_cltv_expiry_delta: u32, allow_mpp: bool) -> Self {
Self::from_node_id(payee_pubkey, final_cltv_expiry_delta)
- .with_bolt11_features(InvoiceFeatures::for_keysend(allow_mpp))
+ .with_bolt11_features(Bolt11InvoiceFeatures::for_keysend(allow_mpp))
.expect("PaymentParameters::from_node_id should always initialize the payee as unblinded")
}
.with_expiry_time(invoice.created_at().as_secs().saturating_add(invoice.relative_expiry().as_secs()))
}
- fn blinded(blinded_route_hints: Vec<(BlindedPayInfo, BlindedPath)>) -> Self {
+ /// Creates parameters for paying to a blinded payee from the provided blinded route hints.
+ pub fn blinded(blinded_route_hints: Vec<(BlindedPayInfo, BlindedPath)>) -> Self {
Self {
payee: Payee::Blinded { route_hints: blinded_route_hints, features: None },
expiry_time: None,
/// [`PaymentParameters::from_bolt12_invoice`].
///
/// This is not exported to bindings users since bindings don't support move semantics
- pub fn with_bolt11_features(self, features: InvoiceFeatures) -> Result<Self, ()> {
+ pub fn with_bolt11_features(self, features: Bolt11InvoiceFeatures) -> Result<Self, ()> {
match self.payee {
Payee::Blinded { .. } => Err(()),
Payee::Clear { route_hints, node_id, final_cltv_expiry_delta, .. } =>
/// does not contain any features.
///
/// [`for_keysend`]: PaymentParameters::for_keysend
- features: Option<InvoiceFeatures>,
+ features: Option<Bolt11InvoiceFeatures>,
/// The minimum CLTV delta at the end of the route. This value must not be zero.
final_cltv_expiry_delta: u32,
},
}
enum FeaturesRef<'a> {
- Bolt11(&'a InvoiceFeatures),
+ Bolt11(&'a Bolt11InvoiceFeatures),
Bolt12(&'a Bolt12InvoiceFeatures),
}
enum Features {
- Bolt11(InvoiceFeatures),
+ Bolt11(Bolt11InvoiceFeatures),
Bolt12(Bolt12InvoiceFeatures),
}
_ => None,
}
}
- fn bolt11(self) -> Option<InvoiceFeatures> {
+ fn bolt11(self) -> Option<Bolt11InvoiceFeatures> {
match self {
Self::Bolt11(f) => Some(f),
_ => None,
" and blinding point ".fmt(f)?;
hint.1.blinding_point.fmt(f)
},
+ CandidateRouteHop::FirstHop { .. } => {
+ "first hop with SCID ".fmt(f)?;
+ self.0.short_channel_id().unwrap().fmt(f)
+ },
+ CandidateRouteHop::PrivateHop { .. } => {
+ "route hint with SCID ".fmt(f)?;
+ self.0.short_channel_id().unwrap().fmt(f)
+ },
_ => {
"SCID ".fmt(f)?;
self.0.short_channel_id().unwrap().fmt(f)
/// Finds a route from us (payer) to the given target node (payee).
///
-/// If the payee provided features in their invoice, they should be provided via `params.payee`.
+/// If the payee provided features in their invoice, they should be provided via the `payee` field
+/// in the given [`RouteParameters::payment_params`].
/// Without this, MPP will only be used if the payee's features are available in the network graph.
///
-/// Private routing paths between a public node and the target may be included in `params.payee`.
+/// Private routing paths between a public node and the target may be included in the `payee` field
+/// of [`RouteParameters::payment_params`].
///
/// If some channels aren't announced, it may be useful to fill in `first_hops` with the results
/// from [`ChannelManager::list_usable_channels`]. If it is filled in, the view of these channels
/// However, the enabled/disabled bit on such channels as well as the `htlc_minimum_msat` /
/// `htlc_maximum_msat` *are* checked as they may change based on the receiving node.
///
-/// # Note
-///
-/// May be used to re-compute a [`Route`] when handling a [`Event::PaymentPathFailed`]. Any
-/// adjustments to the [`NetworkGraph`] and channel scores should be made prior to calling this
-/// function.
-///
/// # Panics
///
-/// Panics if first_hops contains channels without short_channel_ids;
+/// Panics if first_hops contains channels without `short_channel_id`s;
/// [`ChannelManager::list_usable_channels`] will never include such channels.
///
/// [`ChannelManager::list_usable_channels`]: crate::ln::channelmanager::ChannelManager::list_usable_channels
log_trace!(logger, "Building path from {} to payer {} for value {} msat.",
LoggedPayeePubkey(payment_params.payee.node_id()), our_node_pubkey, final_value_msat);
+ // Remember how many candidates we ignored to allow for some logging afterwards.
+ let mut num_ignored_value_contribution = 0;
+ let mut num_ignored_path_length_limit = 0;
+ let mut num_ignored_cltv_delta_limit = 0;
+ let mut num_ignored_previously_failed = 0;
+
macro_rules! add_entry {
// Adds entry which goes from $src_node_id to $dest_node_id over the $candidate hop.
// $next_hops_fee_msat represents the fees paid for using all the channels *after* this one,
let payment_failed_on_this_channel = scid_opt.map_or(false,
|scid| payment_params.previously_failed_channels.contains(&scid));
+ let should_log_candidate = match $candidate {
+ CandidateRouteHop::FirstHop { .. } => true,
+ CandidateRouteHop::PrivateHop { .. } => true,
+ CandidateRouteHop::Blinded { .. } => true,
+ _ => false,
+ };
+
// If HTLC minimum is larger than the amount we're going to transfer, we shouldn't
// bother considering this channel. If retrying with recommended_value_msat may
// allow us to hit the HTLC minimum limit, set htlc_minimum_limit so that we go
// around again with a higher amount.
- if !contributes_sufficient_value || exceeds_max_path_length ||
- exceeds_cltv_delta_limit || payment_failed_on_this_channel {
- // Path isn't useful, ignore it and move on.
+ if !contributes_sufficient_value {
+ if should_log_candidate {
+ log_trace!(logger, "Ignoring {} due to insufficient value contribution.", LoggedCandidateHop(&$candidate));
+ }
+ num_ignored_value_contribution += 1;
+ } else if exceeds_max_path_length {
+ if should_log_candidate {
+ log_trace!(logger, "Ignoring {} due to exceeding maximum path length limit.", LoggedCandidateHop(&$candidate));
+ }
+ num_ignored_path_length_limit += 1;
+ } else if exceeds_cltv_delta_limit {
+ if should_log_candidate {
+ log_trace!(logger, "Ignoring {} due to exceeding CLTV delta limit.", LoggedCandidateHop(&$candidate));
+ }
+ num_ignored_cltv_delta_limit += 1;
+ } else if payment_failed_on_this_channel {
+ if should_log_candidate {
+ log_trace!(logger, "Ignoring {} due to a failed previous payment attempt.", LoggedCandidateHop(&$candidate));
+ }
+ num_ignored_previously_failed += 1;
} else if may_overpay_to_meet_path_minimum_msat {
hit_minimum_limit = true;
} else if over_path_minimum_msat {
}
}
+ let num_ignored_total = num_ignored_value_contribution + num_ignored_path_length_limit +
+ num_ignored_cltv_delta_limit + num_ignored_previously_failed;
+ if num_ignored_total > 0 {
+ log_trace!(logger, "Ignored {} candidate hops due to insufficient value contribution, {} due to path length limit, {} due to CLTV delta limit, {} due to previous payment failure. Total: {} ignored candidates.", num_ignored_value_contribution, num_ignored_path_length_limit, num_ignored_cltv_delta_limit, num_ignored_previously_failed, num_ignored_total);
+ }
+
// Step (5).
if payment_paths.len() == 0 {
return Err(LightningError{err: "Failed to find a path to the given destination".to_owned(), action: ErrorAction::IgnoreError});
inbound_scid_alias: None,
channel_value_satoshis: 0,
user_channel_id: 0,
- balance_msat: 0,
outbound_capacity_msat,
next_outbound_htlc_limit_msat: outbound_capacity_msat,
next_outbound_htlc_minimum_msat: 0,
let params = ProbabilisticScoringFeeParameters::default();
let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &graph, &logger);
- let features = super::InvoiceFeatures::empty();
+ let features = super::Bolt11InvoiceFeatures::empty();
super::bench_utils::generate_test_routes(&graph, &mut scorer, ¶ms, features, random_init_seed(), 0, 2);
}
use crate::chain::transaction::OutPoint;
use crate::sign::{EntropySource, KeysManager};
use crate::ln::channelmanager::{self, ChannelCounterparty, ChannelDetails};
- use crate::ln::features::InvoiceFeatures;
+ use crate::ln::features::Bolt11InvoiceFeatures;
use crate::routing::gossip::NetworkGraph;
use crate::util::config::UserConfig;
use crate::util::ser::ReadableArgs;
outbound_scid_alias: None,
channel_value_satoshis: 10_000_000_000,
user_channel_id: 0,
- balance_msat: 10_000_000_000,
outbound_capacity_msat: 10_000_000_000,
next_outbound_htlc_minimum_msat: 0,
next_outbound_htlc_limit_msat: 10_000_000_000,
}
pub(crate) fn generate_test_routes<S: Score>(graph: &NetworkGraph<&TestLogger>, scorer: &mut S,
- score_params: &S::ScoreParams, features: InvoiceFeatures, mut seed: u64,
+ score_params: &S::ScoreParams, features: Bolt11InvoiceFeatures, mut seed: u64,
starting_amount: u64, route_count: usize,
) -> Vec<(ChannelDetails, PaymentParameters, u64)> {
let payer = payer_pubkey();
use super::*;
use crate::sign::{EntropySource, KeysManager};
use crate::ln::channelmanager;
- use crate::ln::features::InvoiceFeatures;
+ use crate::ln::features::Bolt11InvoiceFeatures;
use crate::routing::gossip::NetworkGraph;
use crate::routing::scoring::{FixedPenaltyScorer, ProbabilisticScorer, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters};
use crate::util::config::UserConfig;
let logger = TestLogger::new();
let network_graph = bench_utils::read_network_graph(&logger).unwrap();
let scorer = FixedPenaltyScorer::with_penalty(0);
- generate_routes(bench, &network_graph, scorer, &(), InvoiceFeatures::empty(), 0,
+ generate_routes(bench, &network_graph, scorer, &(), Bolt11InvoiceFeatures::empty(), 0,
"generate_routes_with_zero_penalty_scorer");
}
let network_graph = bench_utils::read_network_graph(&logger).unwrap();
let params = ProbabilisticScoringFeeParameters::default();
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
- generate_routes(bench, &network_graph, scorer, ¶ms, InvoiceFeatures::empty(), 0,
+ generate_routes(bench, &network_graph, scorer, ¶ms, Bolt11InvoiceFeatures::empty(), 0,
"generate_routes_with_probabilistic_scorer");
}
fn generate_routes<S: Score>(
bench: &mut Criterion, graph: &NetworkGraph<&TestLogger>, mut scorer: S,
- score_params: &S::ScoreParams, features: InvoiceFeatures, starting_amount: u64,
+ score_params: &S::ScoreParams, features: Bolt11InvoiceFeatures, starting_amount: u64,
bench_name: &'static str,
) {
let payer = bench_utils::payer_pubkey();