use ln::channelmanager::ChannelDetails;
use ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures};
use ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
-use routing::gossip::{DirectedChannelInfoWithUpdate, EffectiveCapacity, ReadOnlyNetworkGraph, NodeId, RoutingFees};
+use routing::gossip::{DirectedChannelInfoWithUpdate, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
use routing::scoring::{ChannelUsage, Score};
use util::ser::{Writeable, Readable, Writer};
use util::logger::{Level, Logger};
/// Maximum total CTLV difference we allow for a full payment path.
pub const DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA: u32 = 1008;
-/// Maximum number of paths we allow an MPP payment to have.
+/// Maximum number of paths we allow an (MPP) payment to have.
// The default limit is currently set rather arbitrary - there aren't any real fundamental path-count
// limits, but for now more than 10 paths likely carries too much one-path failure.
-pub const DEFAULT_MAX_MPP_PATH_COUNT: u8 = 10;
+pub const DEFAULT_MAX_PATH_COUNT: u8 = 10;
// The median hop CLTV expiry delta currently seen in the network.
const MEDIAN_HOP_CLTV_EXPIRY_DELTA: u32 = 40;
/// Defaults to [`DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA`].
pub max_total_cltv_expiry_delta: u32,
- /// The maximum number of paths that may be used by MPP payments.
- /// Defaults to [`DEFAULT_MAX_MPP_PATH_COUNT`].
- pub max_mpp_path_count: u8,
+ /// The maximum number of paths that may be used by (MPP) payments.
+ /// Defaults to [`DEFAULT_MAX_PATH_COUNT`].
+ pub max_path_count: u8,
}
impl_writeable_tlv_based!(PaymentParameters, {
(0, payee_pubkey, required),
(1, max_total_cltv_expiry_delta, (default_value, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA)),
(2, features, option),
- (3, max_mpp_path_count, (default_value, DEFAULT_MAX_MPP_PATH_COUNT)),
+ (3, max_path_count, (default_value, DEFAULT_MAX_PATH_COUNT)),
(4, route_hints, vec_type),
(6, expiry_time, option),
});
route_hints: vec![],
expiry_time: None,
max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
- max_mpp_path_count: DEFAULT_MAX_MPP_PATH_COUNT,
+ max_path_count: DEFAULT_MAX_PATH_COUNT,
}
}
Self { max_total_cltv_expiry_delta, ..self }
}
- /// Includes a limit for the maximum number of payment paths that may be used by MPP.
+ /// Includes a limit for the maximum number of payment paths that may be used.
///
/// (C-not exported) since bindings don't support move semantics
- pub fn with_max_mpp_path_count(self, max_mpp_path_count: u8) -> Self {
- Self { max_mpp_path_count, ..self }
+ pub fn with_max_path_count(self, max_path_count: u8) -> Self {
+ Self { max_path_count, ..self }
}
}
/// [`ChannelManager::list_usable_channels`]: crate::ln::channelmanager::ChannelManager::list_usable_channels
/// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
/// [`NetworkGraph`]: crate::routing::gossip::NetworkGraph
-pub fn find_route<L: Deref, S: Score>(
+pub fn find_route<L: Deref, GL: Deref, S: Score>(
our_node_pubkey: &PublicKey, route_params: &RouteParameters,
- network_graph: &ReadOnlyNetworkGraph, first_hops: Option<&[&ChannelDetails]>, logger: L,
+ network_graph: &NetworkGraph<GL>, first_hops: Option<&[&ChannelDetails]>, logger: L,
scorer: &S, random_seed_bytes: &[u8; 32]
) -> Result<Route, LightningError>
-where L::Target: Logger {
- let mut route = get_route(our_node_pubkey, &route_params.payment_params, network_graph, first_hops,
+where L::Target: Logger, GL::Target: Logger {
+ let graph_lock = network_graph.read_only();
+ let mut route = get_route(our_node_pubkey, &route_params.payment_params, &graph_lock, first_hops,
route_params.final_value_msat, route_params.final_cltv_expiry_delta, logger, scorer,
random_seed_bytes)?;
- add_random_cltv_offset(&mut route, &route_params.payment_params, network_graph, random_seed_bytes);
+ add_random_cltv_offset(&mut route, &route_params.payment_params, &graph_lock, random_seed_bytes);
Ok(route)
}
let network_channels = network_graph.channels();
let network_nodes = network_graph.nodes();
+ if payment_params.max_path_count == 0 {
+ return Err(LightningError{err: "Can't find a route with no paths allowed.".to_owned(), action: ErrorAction::IgnoreError});
+ }
+
// Allow MPP only if we have a features set from somewhere that indicates the payee supports
// it. If the payee supports it they're supposed to include it in the invoice, so that should
// work reliably.
- let allow_mpp = if let Some(features) = &payment_params.features {
+ let allow_mpp = if payment_params.max_path_count == 1 {
+ false
+ } else if let Some(features) = &payment_params.features {
features.supports_basic_mpp()
} else if let Some(node) = network_nodes.get(&payee_node_id) {
if let Some(node_info) = node.announcement_info.as_ref() {
} else { false }
} else { false };
- if allow_mpp && payment_params.max_mpp_path_count == 0 {
- return Err(LightningError{err: "Can't find an MPP route with no paths allowed.".to_owned(), action: ErrorAction::IgnoreError});
- }
-
log_trace!(logger, "Searching for a route from payer {} to payee {} {} MPP and {} first hops {}overriding the network graph", our_node_pubkey,
payment_params.payee_pubkey, if allow_mpp { "with" } else { "without" },
first_hops.map(|hops| hops.len()).unwrap_or(0), if first_hops.is_some() { "" } else { "not " });
// Taking too many smaller paths also increases the chance of payment failure.
// Thus to avoid this effect, we require from our collected links to provide
// at least a minimal contribution to the recommended value yet-to-be-fulfilled.
- // This requirement is currently set to be 1/max_mpp_path_count of the payment
+ // This requirement is currently set to be 1/max_path_count of the payment
// value to ensure we only ever return routes that do not violate this limit.
let minimal_value_contribution_msat: u64 = if allow_mpp {
- (final_value_msat + (payment_params.max_mpp_path_count as u64 - 1)) / payment_params.max_mpp_path_count as u64
+ (final_value_msat + (payment_params.max_path_count as u64 - 1)) / payment_params.max_path_count as u64
} else {
final_value_msat
};
selected_paths.push(path);
}
// Make sure we would never create a route with more paths than we allow.
- debug_assert!(selected_paths.len() <= payment_params.max_mpp_path_count.into());
+ debug_assert!(selected_paths.len() <= payment_params.max_path_count.into());
if let Some(features) = &payment_params.features {
for path in selected_paths.iter_mut() {
/// exclude the payer, but include the payee). This may be useful, e.g., for probing the chosen path.
///
/// Re-uses logic from `find_route`, so the restrictions described there also apply here.
-pub fn build_route_from_hops<L: Deref>(
+pub fn build_route_from_hops<L: Deref, GL: Deref>(
our_node_pubkey: &PublicKey, hops: &[PublicKey], route_params: &RouteParameters,
- network_graph: &ReadOnlyNetworkGraph, logger: L, random_seed_bytes: &[u8; 32]
+ network_graph: &NetworkGraph<GL>, logger: L, random_seed_bytes: &[u8; 32]
) -> Result<Route, LightningError>
-where L::Target: Logger {
+where L::Target: Logger, GL::Target: Logger {
+ let graph_lock = network_graph.read_only();
let mut route = build_route_from_hops_internal(
- our_node_pubkey, hops, &route_params.payment_params, &network_graph,
+ our_node_pubkey, hops, &route_params.payment_params, &graph_lock,
route_params.final_value_msat, route_params.final_cltv_expiry_delta, logger, random_seed_bytes)?;
- add_random_cltv_offset(&mut route, &route_params.payment_params, &network_graph, random_seed_bytes);
+ add_random_cltv_offset(&mut route, &route_params.payment_params, &graph_lock, random_seed_bytes);
Ok(route)
}
fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
+
+ fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
+
+ fn probe_successful(&mut self, _path: &[&RouteHop]) {}
}
impl<'a> Writeable for HopScorer {
#[cfg(test)]
mod tests {
- use routing::gossip::{NetworkGraph, P2PGossipSync, NodeId};
+ use routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, EffectiveCapacity};
use routing::router::{get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features,
PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees,
DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE};
}
{
- // Attempt to route while setting max_mpp_path_count to 0 results in a failure.
- let zero_payment_params = payment_params.clone().with_max_mpp_path_count(0);
+ // Attempt to route while setting max_path_count to 0 results in a failure.
+ let zero_payment_params = payment_params.clone().with_max_path_count(0);
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
&our_id, &zero_payment_params, &network_graph.read_only(), None, 100, 42,
Arc::clone(&logger), &scorer, &random_seed_bytes) {
- assert_eq!(err, "Can't find an MPP route with no paths allowed.");
+ assert_eq!(err, "Can't find a route with no paths allowed.");
} else { panic!(); }
}
{
- // Attempt to route while setting max_mpp_path_count to 3 results in a failure.
+ // Attempt to route while setting max_path_count to 3 results in a failure.
// This is the case because the minimal_value_contribution_msat would require each path
// to account for 1/3 of the total value, which is violated by 2 out of 3 paths.
- let fail_payment_params = payment_params.clone().with_max_mpp_path_count(3);
+ let fail_payment_params = payment_params.clone().with_max_path_count(3);
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
&our_id, &fail_payment_params, &network_graph.read_only(), None, 250_000, 42,
Arc::clone(&logger), &scorer, &random_seed_bytes) {
fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
+ fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
+ fn probe_successful(&mut self, _path: &[&RouteHop]) {}
}
struct BadNodeScorer {
fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
+ fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
+ fn probe_successful(&mut self, _path: &[&RouteHop]) {}
}
#[test]
}
#[test]
- fn avoids_banned_nodes() {
+ fn honors_manual_penalties() {
let (secp_ctx, network_graph, _, _, logger) = build_line_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
let scorer_params = ProbabilisticScoringParameters::default();
let mut scorer = ProbabilisticScorer::new(scorer_params, Arc::clone(&network_graph), Arc::clone(&logger));
- // First check we can get a route.
+ // First check set manual penalties are returned by the scorer.
+ let usage = ChannelUsage {
+ amount_msat: 0,
+ inflight_htlc_msat: 0,
+ effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(1_000) },
+ };
+ scorer.set_manual_penalty(&NodeId::from_pubkey(&nodes[3]), 123);
+ scorer.set_manual_penalty(&NodeId::from_pubkey(&nodes[4]), 456);
+ assert_eq!(scorer.channel_penalty_msat(42, &NodeId::from_pubkey(&nodes[3]), &NodeId::from_pubkey(&nodes[4]), usage), 456);
+
+ // Then check we can get a normal route
let payment_params = PaymentParameters::from_node_id(nodes[10]);
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes);
assert!(route.is_ok());