X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Frouting%2Frouter.rs;h=a97e1b6ffa45db704b89505cfce7d6c58cc1f16b;hb=f75b6cb9a8de91594fec9e37f0b2a4bae36b246a;hp=d85485fe82600b351e927db598889ea96a81ce1a;hpb=75ca50f5c0834c01d84826b880f048ca031f08ce;p=rust-lightning diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index d85485fe..a97e1b6f 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -9,7 +9,7 @@ //! The top-level routing/network map tracking logic lives here. //! -//! You probably want to create a NetGraphMsgHandler and use that as your RoutingMessageHandler and then +//! You probably want to create a P2PGossipSync and use that as your RoutingMessageHandler and then //! interrogate it to get routes for your own payments. use bitcoin::secp256k1::PublicKey; @@ -17,9 +17,9 @@ use bitcoin::secp256k1::PublicKey; 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, NetworkGraph, NodeId, RoutingFees}; use routing::scoring::{ChannelUsage, Score}; -use routing::network_graph::{DirectedChannelInfoWithUpdate, EffectiveCapacity, NetworkGraph, ReadOnlyNetworkGraph, NodeId, RoutingFees}; -use util::ser::{Writeable, Readable}; +use util::ser::{Writeable, Readable, Writer}; use util::logger::{Level, Logger}; use util::chacha20::ChaCha20; @@ -151,8 +151,8 @@ impl Readable for Route { /// Parameters needed to find a [`Route`]. /// -/// Passed to [`find_route`] and also provided in [`Event::PaymentPathFailed`] for retrying a failed -/// payment path. +/// Passed to [`find_route`] and [`build_route_from_hops`], but also provided in +/// [`Event::PaymentPathFailed`] for retrying a failed payment path. /// /// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed #[derive(Clone, Debug)] @@ -176,6 +176,11 @@ impl_writeable_tlv_based!(RouteParameters, { /// 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. +// 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_PATH_COUNT: u8 = 10; + // The median hop CLTV expiry delta currently seen in the network. const MEDIAN_HOP_CLTV_EXPIRY_DELTA: u32 = 40; @@ -214,15 +219,43 @@ pub struct PaymentParameters { pub expiry_time: Option, /// The maximum total CLTV delta we accept for the route. + /// 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_PATH_COUNT`]. + pub max_path_count: u8, + + /// Selects the maximum share of a channel's total capacity which will be sent over a channel, + /// as a power of 1/2. A higher value prefers to send the payment using more MPP parts whereas + /// a lower value prefers to send larger MPP parts, potentially saturating channels and + /// increasing failure probability for those paths. + /// + /// Note that this restriction will be relaxed during pathfinding after paths which meet this + /// restriction have been found. While paths which meet this criteria will be searched for, it + /// is ultimately up to the scorer to select them over other paths. + /// + /// A value of 0 will allow payments up to and including a channel's total announced usable + /// capacity, a value of one will only use up to half its capacity, two 1/4, etc. + /// + /// Default value: 1 + pub max_channel_saturation_power_of_half: u8, + + /// A list of SCIDs which this payment was previously attempted over and which caused the + /// payment to fail. Future attempts for the same payment shouldn't be relayed through any of + /// these SCIDs. + pub previously_failed_channels: Vec, } 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_path_count, (default_value, DEFAULT_MAX_PATH_COUNT)), (4, route_hints, vec_type), + (5, max_channel_saturation_power_of_half, (default_value, 1)), (6, expiry_time, option), + (7, previously_failed_channels, vec_type), }); impl PaymentParameters { @@ -234,6 +267,9 @@ impl PaymentParameters { route_hints: vec![], expiry_time: None, max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, + max_path_count: DEFAULT_MAX_PATH_COUNT, + max_channel_saturation_power_of_half: 1, + previously_failed_channels: Vec::new(), } } @@ -269,6 +305,20 @@ impl PaymentParameters { pub fn with_max_total_cltv_expiry_delta(self, max_total_cltv_expiry_delta: u32) -> Self { Self { max_total_cltv_expiry_delta, ..self } } + + /// 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_path_count(self, max_path_count: u8) -> Self { + Self { max_path_count, ..self } + } + + /// 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_channel_saturation_power_of_half(self, max_channel_saturation_power_of_half: u8) -> Self { + Self { max_channel_saturation_power_of_half, ..self } + } } /// A list of hops along a payment path terminating with a channel to the recipient. @@ -369,7 +419,7 @@ enum CandidateRouteHop<'a> { FirstHop { details: &'a ChannelDetails, }, - /// A hop found in the [`NetworkGraph`], where the channel capacity may or may not be known. + /// A hop found in the [`ReadOnlyNetworkGraph`], where the channel capacity may be unknown. PublicHop { info: DirectedChannelInfoWithUpdate<'a>, short_channel_id: u64, @@ -383,7 +433,7 @@ enum CandidateRouteHop<'a> { impl<'a> CandidateRouteHop<'a> { fn short_channel_id(&self) -> u64 { match self { - CandidateRouteHop::FirstHop { details } => details.short_channel_id.unwrap(), + CandidateRouteHop::FirstHop { details } => details.get_outbound_payment_scid().unwrap(), CandidateRouteHop::PublicHop { short_channel_id, .. } => *short_channel_id, CandidateRouteHop::PrivateHop { hint } => hint.short_channel_id, } @@ -414,16 +464,6 @@ impl<'a> CandidateRouteHop<'a> { } } - fn htlc_maximum_msat(&self) -> u64 { - match self { - CandidateRouteHop::FirstHop { details } => details.next_outbound_htlc_limit_msat, - CandidateRouteHop::PublicHop { info, .. } => info.htlc_maximum_msat(), - CandidateRouteHop::PrivateHop { hint } => { - hint.htlc_maximum_msat.unwrap_or(u64::max_value()) - }, - } - } - fn fees(&self) -> RoutingFees { match self { CandidateRouteHop::FirstHop { .. } => RoutingFees { @@ -445,6 +485,33 @@ impl<'a> CandidateRouteHop<'a> { } } +#[inline] +fn max_htlc_from_capacity(capacity: EffectiveCapacity, max_channel_saturation_power_of_half: u8) -> u64 { + let saturation_shift: u32 = max_channel_saturation_power_of_half as u32; + match capacity { + EffectiveCapacity::ExactLiquidity { liquidity_msat } => liquidity_msat, + EffectiveCapacity::Infinite => u64::max_value(), + EffectiveCapacity::Unknown => EffectiveCapacity::Unknown.as_msat(), + EffectiveCapacity::MaximumHTLC { amount_msat } => + amount_msat.checked_shr(saturation_shift).unwrap_or(0), + EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: None } => + capacity_msat.checked_shr(saturation_shift).unwrap_or(0), + EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: Some(htlc_max) } => + cmp::min(capacity_msat.checked_shr(saturation_shift).unwrap_or(0), htlc_max), + } +} + +fn iter_equal(mut iter_a: I1, mut iter_b: I2) +-> bool where I1::Item: PartialEq { + loop { + let a = iter_a.next(); + let b = iter_b.next(); + if a.is_none() && b.is_none() { return true; } + if a.is_none() || b.is_none() { return false; } + if a.unwrap().ne(&b.unwrap()) { return false; } + } +} + /// It's useful to keep track of the hops associated with the fees required to use them, /// so that we can choose cheaper paths (as per Dijkstra's algorithm). /// Fee values should be updated only in the context of the whole path, see update_value_and_recompute_fees. @@ -552,10 +619,9 @@ impl<'a> PaymentPath<'a> { // to the fees being paid not lining up with the actual limits. // // Note that this function is not aware of the available_liquidity limit, and thus does not - // support increasing the value being transferred. + // support increasing the value being transferred beyond what was selected during the initial + // routing passes. fn update_value_and_recompute_fees(&mut self, value_msat: u64) { - assert!(value_msat <= self.hops.last().unwrap().0.fee_msat); - let mut total_fee_paid_msat = 0 as u64; for i in (0..self.hops.len()).rev() { let last_hop = i == self.hops.len() - 1; @@ -650,8 +716,8 @@ fn default_node_features() -> NodeFeatures { /// Private routing paths between a public node and the target may be included in `params.payee`. /// /// 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 our local -/// channels from [`NetworkGraph`] will be ignored, and only those in `first_hops` will be used. +/// from [`ChannelManager::list_usable_channels`]. If it is filled in, the view of these channels +/// from `network_graph` will be ignored, and only those in `first_hops` will be used. /// /// The fees on channels from us to the next hop are ignored as they are assumed to all be equal. /// However, the enabled/disabled bit on such channels as well as the `htlc_minimum_msat` / @@ -670,22 +736,19 @@ fn default_node_features() -> NodeFeatures { /// /// [`ChannelManager::list_usable_channels`]: crate::ln::channelmanager::ChannelManager::list_usable_channels /// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed -pub fn find_route( - our_node_pubkey: &PublicKey, route_params: &RouteParameters, network: &NetworkGraph, - first_hops: Option<&[&ChannelDetails]>, logger: L, scorer: &S, random_seed_bytes: &[u8; 32] +/// [`NetworkGraph`]: crate::routing::gossip::NetworkGraph +pub fn find_route( + our_node_pubkey: &PublicKey, route_params: &RouteParameters, + network_graph: &NetworkGraph, first_hops: Option<&[&ChannelDetails]>, logger: L, + scorer: &S, random_seed_bytes: &[u8; 32] ) -> Result -where L::Target: Logger { - let network_graph = network.read_only(); - match get_route( - our_node_pubkey, &route_params.payment_params, &network_graph, first_hops, route_params.final_value_msat, - route_params.final_cltv_expiry_delta, logger, scorer, random_seed_bytes - ) { - Ok(mut route) => { - add_random_cltv_offset(&mut route, &route_params.payment_params, &network_graph, random_seed_bytes); - Ok(route) - }, - Err(err) => Err(err), - } +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, &graph_lock, random_seed_bytes); + Ok(route) } pub(crate) fn get_route( @@ -784,16 +847,23 @@ where L::Target: Logger { 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() { node_info.features.supports_basic_mpp() } else { false } } else { false }; + 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 " }); @@ -806,7 +876,7 @@ where L::Target: Logger { HashMap::with_capacity(if first_hops.is_some() { first_hops.as_ref().unwrap().len() } else { 0 }); if let Some(hops) = first_hops { for chan in hops { - if chan.short_channel_id.is_none() { + if chan.get_outbound_payment_scid().is_none() { panic!("first_hops should be filled in with usable channels, not pending ones"); } if chan.counterparty.node_id == *our_node_pubkey { @@ -844,6 +914,26 @@ where L::Target: Logger { let recommended_value_msat = final_value_msat * ROUTE_CAPACITY_PROVISION_FACTOR as u64; let mut path_value_msat = final_value_msat; + // Routing Fragmentation Mitigation heuristic: + // + // Routing fragmentation across many payment paths increases the overall routing + // fees as you have irreducible routing fees per-link used (`fee_base_msat`). + // 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_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_path_count as u64 - 1)) / payment_params.max_path_count as u64 + } else { + final_value_msat + }; + + // When we start collecting routes we enforce the max_channel_saturation_power_of_half + // requirement strictly. After we've collected enough (or if we fail to find new routes) we + // drop the requirement by setting this to 0. + let mut channel_saturation_pow_half = payment_params.max_channel_saturation_power_of_half; + // Keep track of how much liquidity has been used in selected channels. Used to determine // if the channel can be used by additional MPP paths or to inform path finding decisions. It is // aware of direction *only* to ensure that the correct htlc_maximum_msat value is used. Hence, @@ -851,11 +941,8 @@ where L::Target: Logger { let mut used_channel_liquidities: HashMap<(u64, bool), u64> = HashMap::with_capacity(network_nodes.len()); - // Keeping track of how much value we already collected across other paths. Helps to decide: - // - how much a new path should be transferring (upper bound); - // - whether a channel should be disregarded because - // it's available liquidity is too small comparing to how much more we need to collect; - // - when we want to stop looking for new paths. + // Keeping track of how much value we already collected across other paths. Helps to decide + // when we want to stop looking for new paths. let mut already_collected_value_msat = 0; for (_, channels) in first_hop_targets.iter_mut() { @@ -899,7 +986,8 @@ where L::Target: Logger { // - for first and last hops early in get_route if $src_node_id != $dest_node_id { let short_channel_id = $candidate.short_channel_id(); - let htlc_maximum_msat = $candidate.htlc_maximum_msat(); + let effective_capacity = $candidate.effective_capacity(); + let htlc_maximum_msat = max_htlc_from_capacity(effective_capacity, channel_saturation_pow_half); // It is tricky to subtract $next_hops_fee_msat from available liquidity here. // It may be misleading because we might later choose to reduce the value transferred @@ -917,31 +1005,11 @@ where L::Target: Logger { *used_liquidity_msat }); - // Routing Fragmentation Mitigation heuristic: - // - // Routing fragmentation across many payment paths increases the overall routing - // fees as you have irreducible routing fees per-link used (`fee_base_msat`). - // 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 5% of the remaining-to-be-collected value. - // This means as we successfully advance in our collection, - // the absolute liquidity contribution is lowered, - // thus increasing the number of potential channels to be selected. - - // Derive the minimal liquidity contribution with a ratio of 20 (5%, rounded up) - // or 100% if we're not allowed to do multipath payments. - let minimal_value_contribution_msat: u64 = if allow_mpp { - (recommended_value_msat - already_collected_value_msat + 19) / 20 - } else { - final_value_msat - }; // Verify the liquidity offered by this channel complies to the minimal contribution. let contributes_sufficient_value = available_value_contribution_msat >= minimal_value_contribution_msat; // Do not consider candidate hops that would exceed the maximum path length. let path_length_to_node = $next_hops_path_length + 1; - let doesnt_exceed_max_path_length = path_length_to_node <= MAX_PATH_LENGTH_ESTIMATE; + let exceeds_max_path_length = path_length_to_node > MAX_PATH_LENGTH_ESTIMATE; // Do not consider candidates that exceed the maximum total cltv expiry limit. // In order to already account for some of the privacy enhancing random CLTV @@ -952,7 +1020,7 @@ where L::Target: Logger { .unwrap_or(payment_params.max_total_cltv_expiry_delta - final_cltv_expiry_delta); let hop_total_cltv_delta = ($next_hops_cltv_delta as u32) .saturating_add($candidate.cltv_expiry_delta()); - let doesnt_exceed_cltv_delta_limit = hop_total_cltv_delta <= max_total_cltv_expiry_delta; + let exceeds_cltv_delta_limit = hop_total_cltv_delta > max_total_cltv_expiry_delta; let value_contribution_msat = cmp::min(available_value_contribution_msat, $next_hops_value_contribution); // Includes paying fees for the use of the following channels. @@ -972,15 +1040,19 @@ where L::Target: Logger { (amount_to_transfer_over_msat < $next_hops_path_htlc_minimum_msat && recommended_value_msat > $next_hops_path_htlc_minimum_msat)); + let payment_failed_on_this_channel = + payment_params.previously_failed_channels.contains(&short_channel_id); + // 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 && doesnt_exceed_max_path_length && - doesnt_exceed_cltv_delta_limit && may_overpay_to_meet_path_minimum_msat { + 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. + } else if may_overpay_to_meet_path_minimum_msat { hit_minimum_limit = true; - } else if contributes_sufficient_value && doesnt_exceed_max_path_length && - doesnt_exceed_cltv_delta_limit && over_path_minimum_msat { + } else if over_path_minimum_msat { // Note that low contribution here (limited by available_liquidity_msat) // might violate htlc_minimum_msat on the hops which are next along the // payment path (upstream to the payee). To avoid that, we recompute @@ -1069,7 +1141,7 @@ where L::Target: Logger { let channel_usage = ChannelUsage { amount_msat: amount_to_transfer_over_msat, inflight_htlc_msat: used_liquidity_msat, - effective_capacity: $candidate.effective_capacity(), + effective_capacity, }; let channel_penalty_msat = scorer.channel_penalty_msat( short_channel_id, &$src_node_id, &$dest_node_id, channel_usage @@ -1416,7 +1488,7 @@ where L::Target: Logger { let mut features_set = false; if let Some(first_channels) = first_hop_targets.get(&ordered_hops.last().unwrap().0.node_id) { for details in first_channels { - if details.short_channel_id.unwrap() == ordered_hops.last().unwrap().0.candidate.short_channel_id() { + if details.get_outbound_payment_scid().unwrap() == ordered_hops.last().unwrap().0.candidate.short_channel_id() { ordered_hops.last_mut().unwrap().1 = details.counterparty.features.to_context(); features_set = true; break; @@ -1490,12 +1562,14 @@ where L::Target: Logger { .entry((hop.candidate.short_channel_id(), *prev_hop < hop.node_id)) .and_modify(|used_liquidity_msat| *used_liquidity_msat += spent_on_hop_msat) .or_insert(spent_on_hop_msat); - if *used_liquidity_msat == hop.candidate.htlc_maximum_msat() { + let hop_capacity = hop.candidate.effective_capacity(); + let hop_max_msat = max_htlc_from_capacity(hop_capacity, channel_saturation_pow_half); + if *used_liquidity_msat == hop_max_msat { // If this path used all of this channel's available liquidity, we know // this path will not be selected again in the next loop iteration. prevented_redundant_path_selection = true; } - debug_assert!(*used_liquidity_msat <= hop.candidate.htlc_maximum_msat()); + debug_assert!(*used_liquidity_msat <= hop_max_msat); } if !prevented_redundant_path_selection { // If we weren't capped by hitting a liquidity limit on a channel in the path, @@ -1508,10 +1582,8 @@ where L::Target: Logger { *used_channel_liquidities.entry((victim_scid, true)).or_default() = exhausted; } - // Track the total amount all our collected paths allow to send so that we: - // - know when to stop looking for more paths - // - know which of the hops are useless considering how much more sats we need - // (contributes_sufficient_value) + // Track the total amount all our collected paths allow to send so that we know + // when to stop looking for more paths already_collected_value_msat += value_contribution_msat; payment_paths.push(payment_path); @@ -1538,6 +1610,10 @@ where L::Target: Logger { } if !allow_mpp { + if !found_new_path && channel_saturation_pow_half != 0 { + channel_saturation_pow_half = 0; + continue 'paths_collection; + } // If we don't support MPP, no use trying to gather more value ever. break 'paths_collection; } @@ -1547,7 +1623,9 @@ where L::Target: Logger { // iteration. // In the latter case, making another path finding attempt won't help, // because we deterministically terminated the search due to low liquidity. - if already_collected_value_msat >= recommended_value_msat || !found_new_path { + if !found_new_path && channel_saturation_pow_half != 0 { + channel_saturation_pow_half = 0; + } else if already_collected_value_msat >= recommended_value_msat || !found_new_path { log_trace!(logger, "Have now collected {} msat (seeking {} msat) in paths. Last path loop {} a new path.", already_collected_value_msat, recommended_value_msat, if found_new_path { "found" } else { "did not find" }); break 'paths_collection; @@ -1663,8 +1741,32 @@ where L::Target: Logger { // Step (9). // Select the best route by lowest total cost. drawn_routes.sort_unstable_by_key(|paths| paths.iter().map(|path| path.get_cost_msat()).sum::()); + let selected_route = drawn_routes.first_mut().unwrap(); + + // Sort by the path itself and combine redundant paths. + // Note that we sort by SCIDs alone as its simpler but when combining we have to ensure we + // compare both SCIDs and NodeIds as individual nodes may use random aliases causing collisions + // across nodes. + selected_route.sort_unstable_by_key(|path| { + let mut key = [0u64; MAX_PATH_LENGTH_ESTIMATE as usize]; + debug_assert!(path.hops.len() <= key.len()); + for (scid, key) in path.hops.iter().map(|h| h.0.candidate.short_channel_id()).zip(key.iter_mut()) { + *key = scid; + } + key + }); + for idx in 0..(selected_route.len() - 1) { + if idx + 1 >= selected_route.len() { break; } + if iter_equal(selected_route[idx ].hops.iter().map(|h| (h.0.candidate.short_channel_id(), h.0.node_id)), + selected_route[idx + 1].hops.iter().map(|h| (h.0.candidate.short_channel_id(), h.0.node_id))) { + let new_value = selected_route[idx].get_value_msat() + selected_route[idx + 1].get_value_msat(); + selected_route[idx].update_value_and_recompute_fees(new_value); + selected_route.remove(idx + 1); + } + } + let mut selected_paths = Vec::>>::new(); - for payment_path in drawn_routes.first().unwrap() { + for payment_path in selected_route { let mut path = payment_path.hops.iter().map(|(payment_hop, node_features)| { Ok(RouteHop { pubkey: PublicKey::from_slice(payment_hop.node_id.as_slice()).map_err(|_| LightningError{err: format!("Public key {:?} is invalid", &payment_hop.node_id), action: ErrorAction::IgnoreAndLog(Level::Trace)})?, @@ -1682,6 +1784,8 @@ where L::Target: Logger { }); 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_path_count.into()); if let Some(features) = &payment_params.features { for path in selected_paths.iter_mut() { @@ -1703,7 +1807,9 @@ where L::Target: Logger { // destination, if the remaining CLTV expiry delta exactly matches a feasible path in the network // graph. In order to improve privacy, this method obfuscates the CLTV expiry deltas along the // payment path by adding a randomized 'shadow route' offset to the final hop. -fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph, random_seed_bytes: &[u8; 32]) { +fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters, + network_graph: &ReadOnlyNetworkGraph, random_seed_bytes: &[u8; 32] +) { let network_channels = network_graph.channels(); let network_nodes = network_graph.nodes(); @@ -1785,13 +1891,91 @@ fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters, } } +/// Construct a route from us (payer) to the target node (payee) via the given hops (which should +/// 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( + our_node_pubkey: &PublicKey, hops: &[PublicKey], route_params: &RouteParameters, + network_graph: &NetworkGraph, logger: L, random_seed_bytes: &[u8; 32] +) -> Result +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, &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, &graph_lock, random_seed_bytes); + Ok(route) +} + +fn build_route_from_hops_internal( + our_node_pubkey: &PublicKey, hops: &[PublicKey], payment_params: &PaymentParameters, + network_graph: &ReadOnlyNetworkGraph, final_value_msat: u64, final_cltv_expiry_delta: u32, + logger: L, random_seed_bytes: &[u8; 32] +) -> Result where L::Target: Logger { + + struct HopScorer { + our_node_id: NodeId, + hop_ids: [Option; MAX_PATH_LENGTH_ESTIMATE as usize], + } + + impl Score for HopScorer { + fn channel_penalty_msat(&self, _short_channel_id: u64, source: &NodeId, target: &NodeId, + _usage: ChannelUsage) -> u64 + { + let mut cur_id = self.our_node_id; + for i in 0..self.hop_ids.len() { + if let Some(next_id) = self.hop_ids[i] { + if cur_id == *source && next_id == *target { + return 0; + } + cur_id = next_id; + } else { + break; + } + } + u64::max_value() + } + + 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 { + #[inline] + fn write(&self, _w: &mut W) -> Result<(), io::Error> { + unreachable!(); + } + } + + if hops.len() > MAX_PATH_LENGTH_ESTIMATE.into() { + return Err(LightningError{err: "Cannot build a route exceeding the maximum path length.".to_owned(), action: ErrorAction::IgnoreError}); + } + + let our_node_id = NodeId::from_pubkey(our_node_pubkey); + let mut hop_ids = [None; MAX_PATH_LENGTH_ESTIMATE as usize]; + for i in 0..hops.len() { + hop_ids[i] = Some(NodeId::from_pubkey(&hops[i])); + } + + let scorer = HopScorer { our_node_id, hop_ids }; + + get_route(our_node_pubkey, payment_params, network_graph, None, final_value_msat, + final_cltv_expiry_delta, logger, &scorer, random_seed_bytes) +} + #[cfg(test)] mod tests { - use routing::network_graph::{NetworkGraph, NetGraphMsgHandler, NodeId}; - use routing::router::{get_route, add_random_cltv_offset, default_node_features, + 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}; - use routing::scoring::{ChannelUsage, Score}; + use routing::scoring::{ChannelUsage, Score, ProbabilisticScorer, ProbabilisticScoringParameters}; use chain::transaction::OutPoint; use chain::keysinterface::KeysInterface; use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures}; @@ -1820,6 +2004,8 @@ mod tests { use prelude::*; use sync::{self, Arc}; + use core::convert::TryInto; + fn get_channel_details(short_channel_id: Option, node_id: PublicKey, features: InitFeatures, outbound_capacity_msat: u64) -> channelmanager::ChannelDetails { channelmanager::ChannelDetails { @@ -1835,6 +2021,7 @@ mod tests { funding_txo: Some(OutPoint { txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(), index: 0 }), channel_type: None, short_channel_id, + outbound_scid_alias: None, inbound_scid_alias: None, channel_value_satoshis: 0, user_channel_id: 0, @@ -1845,16 +2032,17 @@ mod tests { unspendable_punishment_reserve: None, confirmations_required: None, force_close_spend_delay: None, - is_outbound: true, is_funding_locked: true, + is_outbound: true, is_channel_ready: true, is_usable: true, is_public: true, inbound_htlc_minimum_msat: None, inbound_htlc_maximum_msat: None, + config: None, } } // Using the same keys for LN and BTC ids fn add_channel( - net_graph_msg_handler: &NetGraphMsgHandler, Arc, Arc>, + gossip_sync: &P2PGossipSync>>, Arc, Arc>, secp_ctx: &Secp256k1, node_1_privkey: &SecretKey, node_2_privkey: &SecretKey, features: ChannelFeatures, short_channel_id: u64 ) { let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey); @@ -1879,14 +2067,14 @@ mod tests { bitcoin_signature_2: secp_ctx.sign_ecdsa(&msghash, node_2_privkey), contents: unsigned_announcement.clone(), }; - match net_graph_msg_handler.handle_channel_announcement(&valid_announcement) { + match gossip_sync.handle_channel_announcement(&valid_announcement) { Ok(res) => assert!(res), _ => panic!() }; } fn update_channel( - net_graph_msg_handler: &NetGraphMsgHandler, Arc, Arc>, + gossip_sync: &P2PGossipSync>>, Arc, Arc>, secp_ctx: &Secp256k1, node_privkey: &SecretKey, update: UnsignedChannelUpdate ) { let msghash = hash_to_message!(&Sha256dHash::hash(&update.encode()[..])[..]); @@ -1895,14 +2083,14 @@ mod tests { contents: update.clone() }; - match net_graph_msg_handler.handle_channel_update(&valid_channel_update) { + match gossip_sync.handle_channel_update(&valid_channel_update) { Ok(res) => assert!(res), Err(_) => panic!() }; } fn add_or_update_node( - net_graph_msg_handler: &NetGraphMsgHandler, Arc, Arc>, + gossip_sync: &P2PGossipSync>>, Arc, Arc>, secp_ctx: &Secp256k1, node_privkey: &SecretKey, features: NodeFeatures, timestamp: u32 ) { let node_id = PublicKey::from_secret_key(&secp_ctx, node_privkey); @@ -1922,7 +2110,7 @@ mod tests { contents: unsigned_announcement.clone() }; - match net_graph_msg_handler.handle_node_announcement(&valid_announcement) { + match gossip_sync.handle_node_announcement(&valid_announcement) { Ok(_) => (), Err(_) => panic!() }; @@ -1957,15 +2145,16 @@ mod tests { } fn build_line_graph() -> ( - Secp256k1, sync::Arc, NetGraphMsgHandler, - sync::Arc, sync::Arc>, + Secp256k1, sync::Arc>>, + P2PGossipSync>>, sync::Arc, sync::Arc>, sync::Arc, sync::Arc, ) { let secp_ctx = Secp256k1::new(); let logger = Arc::new(test_utils::TestLogger::new()); let chain_monitor = Arc::new(test_utils::TestChainSource::new(Network::Testnet)); - let network_graph = Arc::new(NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash())); - let net_graph_msg_handler = NetGraphMsgHandler::new(Arc::clone(&network_graph), None, Arc::clone(&logger)); + let genesis_hash = genesis_block(Network::Testnet).header.block_hash(); + let network_graph = Arc::new(NetworkGraph::new(genesis_hash, Arc::clone(&logger))); + let gossip_sync = P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)); // Build network from our_id to node 19: // our_id -1(1)2- node0 -1(2)2- node1 - ... - node19 @@ -1974,9 +2163,9 @@ mod tests { for (idx, (cur_privkey, next_privkey)) in core::iter::once(&our_privkey) .chain(privkeys.iter()).zip(privkeys.iter()).enumerate() { let cur_short_channel_id = (idx as u64) + 1; - add_channel(&net_graph_msg_handler, &secp_ctx, &cur_privkey, &next_privkey, + add_channel(&gossip_sync, &secp_ctx, &cur_privkey, &next_privkey, ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), cur_short_channel_id); - update_channel(&net_graph_msg_handler, &secp_ctx, &cur_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &cur_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: cur_short_channel_id, timestamp: idx as u32, @@ -1988,7 +2177,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &next_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &next_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: cur_short_channel_id, timestamp: (idx as u32)+1, @@ -2000,25 +2189,26 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, next_privkey, + add_or_update_node(&gossip_sync, &secp_ctx, next_privkey, NodeFeatures::from_le_bytes(id_to_feature_flags(1)), 0); } - (secp_ctx, network_graph, net_graph_msg_handler, chain_monitor, logger) + (secp_ctx, network_graph, gossip_sync, chain_monitor, logger) } fn build_graph() -> ( Secp256k1, - sync::Arc, - NetGraphMsgHandler, sync::Arc, sync::Arc>, + sync::Arc>>, + P2PGossipSync>>, sync::Arc, sync::Arc>, sync::Arc, sync::Arc, ) { let secp_ctx = Secp256k1::new(); let logger = Arc::new(test_utils::TestLogger::new()); let chain_monitor = Arc::new(test_utils::TestChainSource::new(Network::Testnet)); - let network_graph = Arc::new(NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash())); - let net_graph_msg_handler = NetGraphMsgHandler::new(Arc::clone(&network_graph), None, Arc::clone(&logger)); + let genesis_hash = genesis_block(Network::Testnet).header.block_hash(); + let network_graph = Arc::new(NetworkGraph::new(genesis_hash, Arc::clone(&logger))); + let gossip_sync = P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)); // Build network from our_id to node6: // // -1(1)2- node0 -1(3)2- @@ -2080,8 +2270,8 @@ mod tests { let (our_privkey, _, privkeys, _) = get_nodes(&secp_ctx); - add_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, &privkeys[0], ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[0], ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1); + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 1, @@ -2094,10 +2284,10 @@ mod tests { excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[0], NodeFeatures::from_le_bytes(id_to_feature_flags(1)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[0], NodeFeatures::from_le_bytes(id_to_feature_flags(1)), 0); - add_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(2)), 2); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(2)), 2); + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 1, @@ -2109,7 +2299,7 @@ mod tests { fee_proportional_millionths: u32::max_value(), excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 1, @@ -2122,10 +2312,10 @@ mod tests { excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[1], NodeFeatures::from_le_bytes(id_to_feature_flags(2)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[1], NodeFeatures::from_le_bytes(id_to_feature_flags(2)), 0); - add_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, &privkeys[7], ChannelFeatures::from_le_bytes(id_to_feature_flags(12)), 12); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[7], ChannelFeatures::from_le_bytes(id_to_feature_flags(12)), 12); + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 1, @@ -2137,7 +2327,7 @@ mod tests { fee_proportional_millionths: u32::max_value(), excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 1, @@ -2150,10 +2340,10 @@ mod tests { excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[7], NodeFeatures::from_le_bytes(id_to_feature_flags(8)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[7], NodeFeatures::from_le_bytes(id_to_feature_flags(8)), 0); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 3); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 3); + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 1, @@ -2165,7 +2355,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 1, @@ -2178,8 +2368,8 @@ mod tests { excess_data: Vec::new() }); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(4)), 4); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[1], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(4)), 4); + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 1, @@ -2191,7 +2381,7 @@ mod tests { fee_proportional_millionths: 1000000, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 1, @@ -2204,8 +2394,8 @@ mod tests { excess_data: Vec::new() }); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(13)), 13); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[7], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(13)), 13); + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 1, @@ -2217,7 +2407,7 @@ mod tests { fee_proportional_millionths: 2000000, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 1, @@ -2230,10 +2420,10 @@ mod tests { excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[2], NodeFeatures::from_le_bytes(id_to_feature_flags(3)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[2], NodeFeatures::from_le_bytes(id_to_feature_flags(3)), 0); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6); + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 6, timestamp: 1, @@ -2245,7 +2435,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 6, timestamp: 1, @@ -2258,8 +2448,8 @@ mod tests { excess_data: Vec::new(), }); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(11)), 11); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[4], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(11)), 11); + update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 11, timestamp: 1, @@ -2271,7 +2461,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[3], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 11, timestamp: 1, @@ -2284,12 +2474,12 @@ mod tests { excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[4], NodeFeatures::from_le_bytes(id_to_feature_flags(5)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[4], NodeFeatures::from_le_bytes(id_to_feature_flags(5)), 0); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[3], NodeFeatures::from_le_bytes(id_to_feature_flags(4)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[3], NodeFeatures::from_le_bytes(id_to_feature_flags(4)), 0); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], &privkeys[5], ChannelFeatures::from_le_bytes(id_to_feature_flags(7)), 7); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[5], ChannelFeatures::from_le_bytes(id_to_feature_flags(7)), 7); + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 7, timestamp: 1, @@ -2301,7 +2491,7 @@ mod tests { fee_proportional_millionths: 1000000, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[5], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[5], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 7, timestamp: 1, @@ -2314,9 +2504,9 @@ mod tests { excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[5], NodeFeatures::from_le_bytes(id_to_feature_flags(6)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[5], NodeFeatures::from_le_bytes(id_to_feature_flags(6)), 0); - (secp_ctx, network_graph, net_graph_msg_handler, chain_monitor, logger) + (secp_ctx, network_graph, gossip_sync, chain_monitor, logger) } #[test] @@ -2376,7 +2566,7 @@ mod tests { #[test] fn htlc_minimum_test() { - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let payment_params = PaymentParameters::from_node_id(nodes[2]); let scorer = test_utils::TestScorer::with_penalty(0); @@ -2386,7 +2576,7 @@ mod tests { // Simple route to 2 via 1 // Disable other paths - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 2, @@ -2398,7 +2588,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 2, @@ -2410,7 +2600,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 2, @@ -2422,7 +2612,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 6, timestamp: 2, @@ -2434,7 +2624,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 7, timestamp: 2, @@ -2449,7 +2639,7 @@ mod tests { // Check against amount_to_transfer_over_msat. // Set minimal HTLC of 200_000_000 msat. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 3, @@ -2464,7 +2654,7 @@ mod tests { // Second hop only allows to forward 199_999_999 at most, thus not allowing the first hop to // be used. - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 3, @@ -2483,7 +2673,7 @@ mod tests { } else { panic!(); } // Lift the restriction on the first hop. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 4, @@ -2503,7 +2693,7 @@ mod tests { #[test] fn htlc_minimum_overpay_test() { - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known()); let scorer = test_utils::TestScorer::with_penalty(0); @@ -2513,7 +2703,7 @@ mod tests { // A route to node#2 via two paths. // One path allows transferring 35-40 sats, another one also allows 35-40 sats. // Thus, they can't send 60 without overpaying. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -2525,7 +2715,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 3, @@ -2539,7 +2729,7 @@ mod tests { }); // Make 0 fee. - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 2, @@ -2551,7 +2741,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 2, @@ -2565,7 +2755,7 @@ mod tests { }); // Disable other paths - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 3, @@ -2586,7 +2776,7 @@ mod tests { // Now, test that if there are 2 paths, a "cheaper" by fee path wouldn't be prioritized // while taking even more fee to match htlc_minimum_msat. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 4, @@ -2598,7 +2788,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 3, @@ -2610,7 +2800,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 4, @@ -2641,7 +2831,7 @@ mod tests { #[test] fn disable_channels_test() { - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let payment_params = PaymentParameters::from_node_id(nodes[2]); let scorer = test_utils::TestScorer::with_penalty(0); @@ -2649,7 +2839,7 @@ mod tests { let random_seed_bytes = keys_manager.get_secure_random_bytes(); // // Disable channels 4 and 12 by flags=2 - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 2, @@ -2661,7 +2851,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 2, @@ -2701,7 +2891,7 @@ mod tests { #[test] fn disable_node_test() { - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let payment_params = PaymentParameters::from_node_id(nodes[2]); let scorer = test_utils::TestScorer::with_penalty(0); @@ -2711,9 +2901,9 @@ mod tests { // Disable nodes 1, 2, and 8 by requiring unknown feature bits let mut unknown_features = NodeFeatures::known(); unknown_features.set_unknown_feature_required(); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[0], unknown_features.clone(), 1); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[1], unknown_features.clone(), 1); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[7], unknown_features.clone(), 1); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[0], unknown_features.clone(), 1); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[1], unknown_features.clone(), 1); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[7], unknown_features.clone(), 1); // If all nodes require some features we don't understand, route should fail if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) { @@ -3056,7 +3246,7 @@ mod tests { #[test] fn multi_hint_last_hops_test() { - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let last_hops = multi_hop_last_hops_hint([nodes[2], nodes[3]]); let payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops.clone()); @@ -3067,7 +3257,7 @@ mod tests { // Test shows that multiple hop hints are considered. // Disabling channels 6 & 7 by flags=2 - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 6, timestamp: 2, @@ -3079,7 +3269,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 7, timestamp: 2, @@ -3126,7 +3316,7 @@ mod tests { #[test] fn private_multi_hint_last_hops_test() { - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let non_announced_privkey = SecretKey::from_slice(&hex::decode(format!("{:02x}", 0xf0).repeat(32)).unwrap()[..]).unwrap(); @@ -3139,7 +3329,7 @@ mod tests { // Test shows that multiple hop hints are considered. // Disabling channels 6 & 7 by flags=2 - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 6, timestamp: 2, @@ -3151,7 +3341,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 7, timestamp: 2, @@ -3417,8 +3607,12 @@ mod tests { let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); - get_route(&source_node_id, &payment_params, &NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()).read_only(), - Some(&our_chans.iter().collect::>()), route_val, 42, &test_utils::TestLogger::new(), &scorer, &random_seed_bytes) + let genesis_hash = genesis_block(Network::Testnet).header.block_hash(); + let logger = test_utils::TestLogger::new(); + let network_graph = NetworkGraph::new(genesis_hash, &logger); + let route = get_route(&source_node_id, &payment_params, &network_graph.read_only(), + Some(&our_chans.iter().collect::>()), route_val, 42, &logger, &scorer, &random_seed_bytes); + route } #[test] @@ -3469,7 +3663,7 @@ mod tests { fn available_amount_while_routing_test() { // Tests whether we choose the correct available channel amount while routing. - let (secp_ctx, network_graph, mut net_graph_msg_handler, chain_monitor, logger) = build_graph(); + let (secp_ctx, network_graph, mut gossip_sync, chain_monitor, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); @@ -3480,7 +3674,7 @@ mod tests { // our node to node2 via node0: channels {1, 3}. // First disable all other paths. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -3492,7 +3686,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 2, @@ -3507,7 +3701,7 @@ mod tests { // Make the first channel (#1) very permissive, // and we will be testing all limits on the second channel. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 2, @@ -3522,7 +3716,7 @@ mod tests { // First, let's see if routing works if we have absolutely no idea about the available amount. // In this case, it should be set to 250_000 sats. - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 2, @@ -3555,7 +3749,7 @@ mod tests { // Check that setting next_outbound_htlc_limit_msat in first_hops limits the channels. // Disable channel #1 and use another first hop. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 3, @@ -3590,7 +3784,7 @@ mod tests { } // Enable channel #1 back. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 4, @@ -3605,7 +3799,7 @@ mod tests { // Now let's see if routing works if we know only htlc_maximum_msat. - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 3, @@ -3640,7 +3834,7 @@ mod tests { // We can't change UTXO capacity on the fly, so we'll disable // the existing channel and add another one with the capacity we need. - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 4, @@ -3660,10 +3854,10 @@ mod tests { .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh(); *chain_monitor.utxo_ret.lock().unwrap() = Ok(TxOut { value: 15, script_pubkey: good_script.clone() }); - net_graph_msg_handler.add_chain_access(Some(chain_monitor)); + gossip_sync.add_chain_access(Some(chain_monitor)); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 333); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 333); + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 333, timestamp: 1, @@ -3675,7 +3869,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 333, timestamp: 1, @@ -3707,7 +3901,7 @@ mod tests { } // Now let's see if routing chooses htlc_maximum_msat over UTXO capacity. - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 333, timestamp: 6, @@ -3743,7 +3937,7 @@ mod tests { fn available_liquidity_last_hop_test() { // Check that available liquidity properly limits the path even when only // one of the latter hops is limited. - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); @@ -3755,7 +3949,7 @@ mod tests { // Total capacity: 50 sats. // Disable other potential paths. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -3767,7 +3961,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 7, timestamp: 2, @@ -3782,7 +3976,7 @@ mod tests { // Limit capacities - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 2, @@ -3794,7 +3988,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 2, @@ -3807,7 +4001,7 @@ mod tests { excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 6, timestamp: 2, @@ -3819,7 +4013,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 11, timestamp: 2, @@ -3868,7 +4062,7 @@ mod tests { #[test] fn ignore_fee_first_hop_test() { - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); @@ -3876,7 +4070,7 @@ mod tests { let payment_params = PaymentParameters::from_node_id(nodes[2]); // Path via node0 is channels {1, 3}. Limit them to 100 and 50 sats (total limit 50). - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 2, @@ -3888,7 +4082,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 2, @@ -3916,12 +4110,13 @@ mod tests { #[test] fn simple_mpp_route_test() { - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); - let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known()); + let payment_params = PaymentParameters::from_node_id(nodes[2]) + .with_features(InvoiceFeatures::known()); // We need a route consisting of 3 paths: // From our node to node2 via node0, node7, node1 (three paths one hop each). @@ -3932,7 +4127,7 @@ mod tests { // Their aggregate capacity will be 50 + 60 + 180 = 290 sats. // Path via node0 is channels {1, 3}. Limit them to 100 and 50 sats (total limit 50). - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 2, @@ -3944,7 +4139,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 2, @@ -3959,7 +4154,7 @@ mod tests { // Path via node7 is channels {12, 13}. Limit them to 60 and 60 sats // (total limit 60). - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 2, @@ -3971,7 +4166,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 2, @@ -3986,7 +4181,7 @@ mod tests { // Path via node1 is channels {2, 4}. Limit them to 200 and 180 sats // (total capacity 180 sats). - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -3998,7 +4193,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 2, @@ -4014,15 +4209,39 @@ mod tests { { // Attempt to route more than available results in a failure. if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( - &our_id, &payment_params, &network_graph.read_only(), None, 300_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) { - assert_eq!(err, "Failed to find a sufficient route to the given destination"); + &our_id, &payment_params, &network_graph.read_only(), None, 300_000, 42, + Arc::clone(&logger), &scorer, &random_seed_bytes) { + assert_eq!(err, "Failed to find a sufficient route to the given destination"); + } else { panic!(); } + } + + { + // 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 a route with no paths allowed."); + } else { panic!(); } + } + + { + // 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_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) { + assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } { // Now, attempt to route 250 sats (just a bit below the capacity). // Our algorithm should provide us with these 3 paths. - let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 250_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); + let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, + 250_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 3); let mut total_amount_paid_msat = 0; for path in &route.paths { @@ -4035,7 +4254,8 @@ mod tests { { // Attempt to route an exact amount is also fine - let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 290_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); + let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, + 290_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 3); let mut total_amount_paid_msat = 0; for path in &route.paths { @@ -4049,7 +4269,7 @@ mod tests { #[test] fn long_mpp_route_test() { - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); @@ -4064,7 +4284,7 @@ mod tests { // are used twice will have 200 sats capacity. // Disable other potential paths. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -4076,7 +4296,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 7, timestamp: 2, @@ -4090,7 +4310,7 @@ mod tests { }); // Path via {node0, node2} is channels {1, 3, 5}. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 2, @@ -4102,7 +4322,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 2, @@ -4116,8 +4336,8 @@ mod tests { }); // Capacity of 200 sats because this channel will be used by 3rd path as well. - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5); + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 5, timestamp: 2, @@ -4133,7 +4353,7 @@ mod tests { // Path via {node7, node2, node4} is channels {12, 13, 6, 11}. // Add 100 sats to the capacities of {12, 13}, because these channels // are also used for 3rd path. 100 sats for the rest. Total capacity: 100 sats. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 2, @@ -4145,7 +4365,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 2, @@ -4158,7 +4378,7 @@ mod tests { excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 6, timestamp: 2, @@ -4170,7 +4390,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 11, timestamp: 2, @@ -4213,7 +4433,7 @@ mod tests { #[test] fn mpp_cheaper_route_test() { - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); @@ -4232,7 +4452,7 @@ mod tests { // are used twice will have 200 sats capacity. // Disable other potential paths. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -4244,7 +4464,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 7, timestamp: 2, @@ -4258,7 +4478,7 @@ mod tests { }); // Path via {node0, node2} is channels {1, 3, 5}. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 2, @@ -4270,7 +4490,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 2, @@ -4284,8 +4504,8 @@ mod tests { }); // Capacity of 200 sats because this channel will be used by 3rd path as well. - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5); + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 5, timestamp: 2, @@ -4301,7 +4521,7 @@ mod tests { // Path via {node7, node2, node4} is channels {12, 13, 6, 11}. // Add 100 sats to the capacities of {12, 13}, because these channels // are also used for 3rd path. 100 sats for the rest. Total capacity: 100 sats. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 2, @@ -4313,7 +4533,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 2, @@ -4326,7 +4546,7 @@ mod tests { excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 6, timestamp: 2, @@ -4338,7 +4558,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 11, timestamp: 2, @@ -4382,7 +4602,7 @@ mod tests { // This test makes sure that MPP algorithm properly takes into account // fees charged on the channels, by making the fees impactful: // if the fee is not properly accounted for, the behavior is different. - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); @@ -4399,7 +4619,7 @@ mod tests { // It's fine to ignore this concern for now. // Disable other potential paths. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -4412,7 +4632,7 @@ mod tests { excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 7, timestamp: 2, @@ -4426,7 +4646,7 @@ mod tests { }); // Path via {node0, node2} is channels {1, 3, 5}. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 2, @@ -4438,7 +4658,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 2, @@ -4451,8 +4671,8 @@ mod tests { excess_data: Vec::new() }); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5); + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 5, timestamp: 2, @@ -4475,7 +4695,7 @@ mod tests { // - channel 12 capacity is 250 sats // - fee for channel 6 is 150 sats // Let's test this by enforcing these 2 conditions and removing other limits. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 2, @@ -4487,7 +4707,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 2, @@ -4500,7 +4720,7 @@ mod tests { excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 6, timestamp: 2, @@ -4512,7 +4732,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 11, timestamp: 2, @@ -4563,7 +4783,7 @@ mod tests { // to only have the remaining to-collect amount in available liquidity. // // This bug appeared in production in some specific channel configurations. - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); @@ -4583,7 +4803,7 @@ mod tests { // would first use the no-fee route and then fail to find a path along the second route as // we think we can only send up to 1 additional sat over the last-hop but refuse to as its // under 5% of our payment amount. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 2, @@ -4595,7 +4815,7 @@ mod tests { fee_proportional_millionths: u32::max_value(), excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -4607,7 +4827,7 @@ mod tests { fee_proportional_millionths: u32::max_value(), excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 2, @@ -4619,7 +4839,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 2, @@ -4634,17 +4854,18 @@ mod tests { // Get a route for 100 sats and check that we found the MPP route no problem and didn't // overpay at all. - let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); + let mut route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 2); - // Paths are somewhat randomly ordered, but: - // * the first is channel 2 (1 msat fee) -> channel 4 -> channel 42 - // * the second is channel 1 (0 fee, but 99 sat maximum) -> channel 3 -> channel 42 - assert_eq!(route.paths[0][0].short_channel_id, 2); - assert_eq!(route.paths[0][0].fee_msat, 1); - assert_eq!(route.paths[0][2].fee_msat, 1_000); - assert_eq!(route.paths[1][0].short_channel_id, 1); - assert_eq!(route.paths[1][0].fee_msat, 0); - assert_eq!(route.paths[1][2].fee_msat, 99_000); + route.paths.sort_by_key(|path| path[0].short_channel_id); + // Paths are manually ordered ordered by SCID, so: + // * the first is channel 1 (0 fee, but 99 sat maximum) -> channel 3 -> channel 42 + // * the second is channel 2 (1 msat fee) -> channel 4 -> channel 42 + assert_eq!(route.paths[0][0].short_channel_id, 1); + assert_eq!(route.paths[0][0].fee_msat, 0); + assert_eq!(route.paths[0][2].fee_msat, 99_000); + assert_eq!(route.paths[1][0].short_channel_id, 2); + assert_eq!(route.paths[1][0].fee_msat, 1); + assert_eq!(route.paths[1][2].fee_msat, 1_000); assert_eq!(route.get_total_fees(), 1); assert_eq!(route.get_total_amount(), 100_000); } @@ -4653,12 +4874,13 @@ mod tests { fn drop_lowest_channel_mpp_route_test() { // This test checks that low-capacity channel is dropped when after // path finding we realize that we found more capacity than we need. - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); - let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known()); + let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known()) + .with_max_channel_saturation_power_of_half(0); // We need a route consisting of 3 paths: // From our node to node2 via node0, node7, node1 (three paths one hop each). @@ -4670,7 +4892,7 @@ mod tests { // Their aggregate capacity will be 50 + 60 + 20 = 130 sats. // Path via node0 is channels {1, 3}. Limit them to 100 and 50 sats (total limit 50); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 2, @@ -4682,7 +4904,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 2, @@ -4696,7 +4918,7 @@ mod tests { }); // Path via node7 is channels {12, 13}. Limit them to 60 and 60 sats (total limit 60); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 2, @@ -4708,7 +4930,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 13, timestamp: 2, @@ -4722,7 +4944,7 @@ mod tests { }); // Path via node1 is channels {2, 4}. Limit them to 20 and 20 sats (total capacity 20 sats). - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -4734,7 +4956,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 2, @@ -4810,17 +5032,18 @@ mod tests { // payment) htlc_minimum_msat. In the original algorithm, this resulted in node4's // "previous hop" being set to node 3, creating a loop in the path. let secp_ctx = Secp256k1::new(); + let genesis_hash = genesis_block(Network::Testnet).header.block_hash(); let logger = Arc::new(test_utils::TestLogger::new()); - let network = Arc::new(NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash())); - let net_graph_msg_handler = NetGraphMsgHandler::new(Arc::clone(&network), None, Arc::clone(&logger)); + let network = Arc::new(NetworkGraph::new(genesis_hash, Arc::clone(&logger))); + let gossip_sync = P2PGossipSync::new(Arc::clone(&network), None, Arc::clone(&logger)); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); let payment_params = PaymentParameters::from_node_id(nodes[6]); - add_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6); + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 6, timestamp: 1, @@ -4832,10 +5055,10 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[1], NodeFeatures::from_le_bytes(id_to_feature_flags(1)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[1], NodeFeatures::from_le_bytes(id_to_feature_flags(1)), 0); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[1], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5); + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 5, timestamp: 1, @@ -4847,10 +5070,10 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[4], NodeFeatures::from_le_bytes(id_to_feature_flags(4)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[4], NodeFeatures::from_le_bytes(id_to_feature_flags(4)), 0); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(4)), 4); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[4], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(4)), 4); + update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 1, @@ -4862,10 +5085,10 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[3], NodeFeatures::from_le_bytes(id_to_feature_flags(3)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[3], NodeFeatures::from_le_bytes(id_to_feature_flags(3)), 0); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[3], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 3); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[3], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[3], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 3); + update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 3, timestamp: 1, @@ -4877,10 +5100,10 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[2], NodeFeatures::from_le_bytes(id_to_feature_flags(2)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[2], NodeFeatures::from_le_bytes(id_to_feature_flags(2)), 0); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(2)), 2); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(2)), 2); + update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 1, @@ -4893,8 +5116,8 @@ mod tests { excess_data: Vec::new() }); - add_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], &privkeys[6], ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { + add_channel(&gossip_sync, &secp_ctx, &privkeys[4], &privkeys[6], ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1); + update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 1, timestamp: 1, @@ -4906,7 +5129,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[6], NodeFeatures::from_le_bytes(id_to_feature_flags(6)), 0); + add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[6], NodeFeatures::from_le_bytes(id_to_feature_flags(6)), 0); { // Now ensure the route flows simply over nodes 1 and 4 to 6. @@ -4943,7 +5166,7 @@ mod tests { // Test that if, while walking the graph, we find a hop that has exactly enough liquidity // for us, including later hop fees, we take it. In the first version of our MPP algorithm // we calculated fees on a higher value, resulting in us ignoring such paths. - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, _, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); @@ -4952,7 +5175,7 @@ mod tests { // We modify the graph to set the htlc_maximum of channel 2 to below the value we wish to // send. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -4965,7 +5188,7 @@ mod tests { excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 12, timestamp: 2, @@ -5007,7 +5230,7 @@ mod tests { // htlc_maximum_msat, we don't end up undershooting a later htlc_minimum_msat. In the // initial version of MPP we'd accept such routes but reject them while recalculating fees, // resulting in us thinking there is no possible path, even if other paths exist. - let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph(); + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = test_utils::TestScorer::with_penalty(0); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); @@ -5017,7 +5240,7 @@ mod tests { // We modify the graph to set the htlc_minimum of channel 2 and 4 as needed - channel 2 // gets an htlc_maximum_msat of 80_000 and channel 4 an htlc_minimum_msat of 90_000. We // then try to send 90_000. - update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 2, timestamp: 2, @@ -5029,7 +5252,7 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); - update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { chain_hash: genesis_block(Network::Testnet).header.block_hash(), short_channel_id: 4, timestamp: 2, @@ -5076,8 +5299,9 @@ mod tests { // route over multiple channels with the same first hop. let secp_ctx = Secp256k1::new(); let (_, our_id, _, nodes) = get_nodes(&secp_ctx); + let genesis_hash = genesis_block(Network::Testnet).header.block_hash(); let logger = Arc::new(test_utils::TestLogger::new()); - let network_graph = NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()); + let network_graph = NetworkGraph::new(genesis_hash, Arc::clone(&logger)); let scorer = test_utils::TestScorer::with_penalty(0); let payment_params = PaymentParameters::from_node_id(nodes[0]).with_features(InvoiceFeatures::known()); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); @@ -5104,12 +5328,13 @@ mod tests { assert_eq!(route.paths[0].len(), 1); assert_eq!(route.paths[1].len(), 1); + assert!((route.paths[0][0].short_channel_id == 3 && route.paths[1][0].short_channel_id == 2) || + (route.paths[0][0].short_channel_id == 2 && route.paths[1][0].short_channel_id == 3)); + assert_eq!(route.paths[0][0].pubkey, nodes[0]); - assert_eq!(route.paths[0][0].short_channel_id, 3); assert_eq!(route.paths[0][0].fee_msat, 50_000); assert_eq!(route.paths[1][0].pubkey, nodes[0]); - assert_eq!(route.paths[1][0].short_channel_id, 2); assert_eq!(route.paths[1][0].fee_msat, 50_000); } @@ -5189,6 +5414,8 @@ mod tests { 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 { @@ -5207,6 +5434,8 @@ mod tests { 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] @@ -5357,6 +5586,35 @@ mod tests { } } + #[test] + fn avoids_recently_failed_paths() { + // Ensure that the router always avoids all of the `previously_failed_channels` channels by + // randomly inserting channels into it until we can't find a route anymore. + let (secp_ctx, network, _, _, logger) = build_graph(); + let (_, our_id, _, nodes) = get_nodes(&secp_ctx); + let network_graph = network.read_only(); + + let scorer = test_utils::TestScorer::with_penalty(0); + let mut payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops(&nodes)) + .with_max_path_count(1); + let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); + let random_seed_bytes = keys_manager.get_secure_random_bytes(); + + // We should be able to find a route initially, and then after we fail a few random + // channels eventually we won't be able to any longer. + assert!(get_route(&our_id, &payment_params, &network_graph, None, 100, 0, Arc::clone(&logger), &scorer, &random_seed_bytes).is_ok()); + loop { + if let Ok(route) = get_route(&our_id, &payment_params, &network_graph, None, 100, 0, Arc::clone(&logger), &scorer, &random_seed_bytes) { + for chan in route.paths[0].iter() { + assert!(!payment_params.previously_failed_channels.contains(&chan.short_channel_id)); + } + let victim = (u64::from_ne_bytes(random_seed_bytes[0..8].try_into().unwrap()) as usize) + % route.paths[0].len(); + payment_params.previously_failed_channels.push(route.paths[0][victim].short_channel_id); + } else { break; } + } + } + #[test] fn limits_path_length() { let (secp_ctx, network, _, _, logger) = build_line_graph(); @@ -5486,6 +5744,70 @@ mod tests { assert!(path_plausibility.iter().all(|x| *x)); } + #[test] + fn builds_correct_path_from_hops() { + let (secp_ctx, network, _, _, logger) = build_graph(); + let (_, our_id, _, nodes) = get_nodes(&secp_ctx); + let network_graph = network.read_only(); + + let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); + let random_seed_bytes = keys_manager.get_secure_random_bytes(); + + let payment_params = PaymentParameters::from_node_id(nodes[3]); + let hops = [nodes[1], nodes[2], nodes[4], nodes[3]]; + let route = build_route_from_hops_internal(&our_id, &hops, &payment_params, + &network_graph, 100, 0, Arc::clone(&logger), &random_seed_bytes).unwrap(); + let route_hop_pubkeys = route.paths[0].iter().map(|hop| hop.pubkey).collect::>(); + assert_eq!(hops.len(), route.paths[0].len()); + for (idx, hop_pubkey) in hops.iter().enumerate() { + assert!(*hop_pubkey == route_hop_pubkeys[idx]); + } + } + + #[test] + fn avoids_saturating_channels() { + let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); + let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx); + + let scorer = ProbabilisticScorer::new(Default::default(), &*network_graph, Arc::clone(&logger)); + + // Set the fee on channel 13 to 100% to match channel 4 giving us two equivalent paths (us + // -> node 7 -> node2 and us -> node 1 -> node 2) which we should balance over. + update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { + chain_hash: genesis_block(Network::Testnet).header.block_hash(), + short_channel_id: 4, + timestamp: 2, + flags: 0, + cltv_expiry_delta: (4 << 4) | 1, + htlc_minimum_msat: 0, + htlc_maximum_msat: OptionalField::Present(200_000_000), + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); + update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate { + chain_hash: genesis_block(Network::Testnet).header.block_hash(), + short_channel_id: 13, + timestamp: 2, + flags: 0, + cltv_expiry_delta: (13 << 4) | 1, + htlc_minimum_msat: 0, + htlc_maximum_msat: OptionalField::Present(200_000_000), + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); + + let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known()); + let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); + let random_seed_bytes = keys_manager.get_secure_random_bytes(); + // 150,000 sat is less than the available liquidity on each channel, set above. + let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 150_000_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); + assert_eq!(route.paths.len(), 2); + assert!((route.paths[0][1].short_channel_id == 4 && route.paths[1][1].short_channel_id == 13) || + (route.paths[1][1].short_channel_id == 4 && route.paths[0][1].short_channel_id == 13)); + } + #[cfg(not(feature = "no-std"))] pub(super) fn random_init_seed() -> u64 { // Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG. @@ -5495,7 +5817,7 @@ mod tests { seed } #[cfg(not(feature = "no-std"))] - use util::ser::Readable; + use util::ser::ReadableArgs; #[test] #[cfg(not(feature = "no-std"))] @@ -5509,7 +5831,8 @@ mod tests { return; }, }; - let graph = NetworkGraph::read(&mut d).unwrap(); + let logger = test_utils::TestLogger::new(); + let graph = NetworkGraph::read(&mut d, &logger).unwrap(); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); @@ -5525,7 +5848,6 @@ mod tests { let payment_params = PaymentParameters::from_node_id(dst); let amt = seed as u64 % 200_000_000; let params = ProbabilisticScoringParameters::default(); - let logger = test_utils::TestLogger::new(); let scorer = ProbabilisticScorer::new(params, &graph, &logger); if get_route(src, &payment_params, &graph.read_only(), None, amt, 42, &logger, &scorer, &random_seed_bytes).is_ok() { continue 'load_endpoints; @@ -5546,7 +5868,8 @@ mod tests { return; }, }; - let graph = NetworkGraph::read(&mut d).unwrap(); + let logger = test_utils::TestLogger::new(); + let graph = NetworkGraph::read(&mut d, &logger).unwrap(); let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); @@ -5562,7 +5885,6 @@ mod tests { let payment_params = PaymentParameters::from_node_id(dst).with_features(InvoiceFeatures::known()); let amt = seed as u64 % 200_000_000; let params = ProbabilisticScoringParameters::default(); - let logger = test_utils::TestLogger::new(); let scorer = ProbabilisticScorer::new(params, &graph, &logger); if get_route(src, &payment_params, &graph.read_only(), None, amt, 42, &logger, &scorer, &random_seed_bytes).is_ok() { continue 'load_endpoints; @@ -5570,6 +5892,43 @@ mod tests { } } } + + #[test] + fn honors_manual_penalties() { + let (secp_ctx, network_graph, _, _, logger) = build_line_graph(); + let (_, our_id, _, nodes) = get_nodes(&secp_ctx); + + let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); + let random_seed_bytes = keys_manager.get_secure_random_bytes(); + + let scorer_params = ProbabilisticScoringParameters::default(); + let mut scorer = ProbabilisticScorer::new(scorer_params, Arc::clone(&network_graph), Arc::clone(&logger)); + + // 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()); + + // Then check that we can't get a route if we ban an intermediate node. + scorer.add_banned(&NodeId::from_pubkey(&nodes[3])); + 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_err()); + + // Finally make sure we can route again, when we remove the ban. + scorer.remove_banned(&NodeId::from_pubkey(&nodes[3])); + 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()); + } } #[cfg(all(test, not(feature = "no-std")))] @@ -5608,9 +5967,10 @@ mod benches { use chain::keysinterface::{KeysManager,KeysInterface}; use ln::channelmanager::{ChannelCounterparty, ChannelDetails}; use ln::features::{InitFeatures, InvoiceFeatures}; - use routing::scoring::{FixedPenaltyScorer, ProbabilisticScorer, ProbabilisticScoringParameters, Scorer}; + use routing::gossip::NetworkGraph; + use routing::scoring::{FixedPenaltyScorer, ProbabilisticScorer, ProbabilisticScoringParameters}; use util::logger::{Logger, Record}; - use util::test_utils::TestLogger; + use util::ser::ReadableArgs; use test::Bencher; @@ -5619,9 +5979,9 @@ mod benches { fn log(&self, _record: &Record) {} } - fn read_network_graph() -> NetworkGraph { + fn read_network_graph(logger: &DummyLogger) -> NetworkGraph<&DummyLogger> { let mut d = test_utils::get_route_file().unwrap(); - NetworkGraph::read(&mut d).unwrap() + NetworkGraph::read(&mut d, logger).unwrap() } fn payer_pubkey() -> PublicKey { @@ -5647,6 +6007,7 @@ mod benches { channel_type: None, short_channel_id: Some(1), inbound_scid_alias: None, + outbound_scid_alias: None, channel_value_satoshis: 10_000_000, user_channel_id: 0, balance_msat: 10_000_000, @@ -5657,46 +6018,35 @@ mod benches { confirmations_required: None, force_close_spend_delay: None, is_outbound: true, - is_funding_locked: true, + is_channel_ready: true, is_usable: true, is_public: true, inbound_htlc_minimum_msat: None, inbound_htlc_maximum_msat: None, + config: None, } } #[bench] fn generate_routes_with_zero_penalty_scorer(bench: &mut Bencher) { - let network_graph = read_network_graph(); + let logger = DummyLogger {}; + let network_graph = read_network_graph(&logger); let scorer = FixedPenaltyScorer::with_penalty(0); generate_routes(bench, &network_graph, scorer, InvoiceFeatures::empty()); } #[bench] fn generate_mpp_routes_with_zero_penalty_scorer(bench: &mut Bencher) { - let network_graph = read_network_graph(); + let logger = DummyLogger {}; + let network_graph = read_network_graph(&logger); let scorer = FixedPenaltyScorer::with_penalty(0); generate_routes(bench, &network_graph, scorer, InvoiceFeatures::known()); } - #[bench] - fn generate_routes_with_default_scorer(bench: &mut Bencher) { - let network_graph = read_network_graph(); - let scorer = Scorer::default(); - generate_routes(bench, &network_graph, scorer, InvoiceFeatures::empty()); - } - - #[bench] - fn generate_mpp_routes_with_default_scorer(bench: &mut Bencher) { - let network_graph = read_network_graph(); - let scorer = Scorer::default(); - generate_routes(bench, &network_graph, scorer, InvoiceFeatures::known()); - } - #[bench] fn generate_routes_with_probabilistic_scorer(bench: &mut Bencher) { - let logger = TestLogger::new(); - let network_graph = read_network_graph(); + let logger = DummyLogger {}; + let network_graph = read_network_graph(&logger); let params = ProbabilisticScoringParameters::default(); let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); generate_routes(bench, &network_graph, scorer, InvoiceFeatures::empty()); @@ -5704,15 +6054,16 @@ mod benches { #[bench] fn generate_mpp_routes_with_probabilistic_scorer(bench: &mut Bencher) { - let logger = TestLogger::new(); - let network_graph = read_network_graph(); + let logger = DummyLogger {}; + let network_graph = read_network_graph(&logger); let params = ProbabilisticScoringParameters::default(); let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); generate_routes(bench, &network_graph, scorer, InvoiceFeatures::known()); } fn generate_routes( - bench: &mut Bencher, graph: &NetworkGraph, mut scorer: S, features: InvoiceFeatures + bench: &mut Bencher, graph: &NetworkGraph<&DummyLogger>, mut scorer: S, + features: InvoiceFeatures ) { let nodes = graph.read_only().nodes().clone(); let payer = payer_pubkey();