X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Frouting%2Frouter.rs;h=4c00dc5fcf9327126d0f9bd580823fd1447ea4f7;hb=93db815dc345d41d5b8dae51b50322a21a745547;hp=40cd439b1df261c6c1a4c433cf4ea892a0864448;hpb=e6d40a7c0eff2589e1f40bcf28d7fc96c687cf32;p=rust-lightning diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 40cd439b..4c00dc5f 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -238,8 +238,13 @@ pub struct PaymentParameters { /// 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 + /// Default value: 2 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, { @@ -248,8 +253,9 @@ impl_writeable_tlv_based!(PaymentParameters, { (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)), + (5, max_channel_saturation_power_of_half, (default_value, 2)), (6, expiry_time, option), + (7, previously_failed_channels, vec_type), }); impl PaymentParameters { @@ -262,7 +268,8 @@ impl PaymentParameters { 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, + max_channel_saturation_power_of_half: 2, + previously_failed_channels: Vec::new(), } } @@ -747,7 +754,7 @@ where L::Target: Logger, GL::Target: Logger { pub(crate) fn get_route( our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph, first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32, - logger: L, scorer: &S, random_seed_bytes: &[u8; 32] + logger: L, scorer: &S, _random_seed_bytes: &[u8; 32] ) -> Result where L::Target: Logger { let payee_node_id = NodeId::from_pubkey(&payment_params.payee_pubkey); @@ -789,11 +796,11 @@ where L::Target: Logger { // 4. See if we managed to collect paths which aggregately are able to transfer target value // (not recommended value). // 5. If yes, proceed. If not, fail routing. - // 6. Randomly combine paths into routes having enough to fulfill the payment. (TODO: knapsack) - // 7. Of all the found paths, select only those with the lowest total fee. - // 8. The last path in every selected route is likely to be more than we need. - // Reduce its value-to-transfer and recompute fees. - // 9. Choose the best route by the lowest total fee. + // 6. Select the paths which have the lowest cost (fee plus scorer penalty) per amount + // transferred up to the transfer target value. + // 7. Reduce the value of the last path until we are sending only the target value. + // 8. If our maximum channel saturation limit caused us to pick two identical paths, combine + // them so that we're not sending two HTLCs along the same path. // As for the actual search algorithm, // we do a payee-to-payer pseudo-Dijkstra's sorting by each node's distance from the payee @@ -1002,7 +1009,7 @@ where L::Target: Logger { 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 @@ -1013,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. @@ -1033,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 @@ -1465,7 +1476,7 @@ where L::Target: Logger { // Both these cases (and other cases except reaching recommended_value_msat) mean that // paths_collection will be stopped because found_new_path==false. // This is not necessarily a routing failure. - 'path_construction: while let Some(RouteGraphNode { node_id, lowest_fee_to_node, total_cltv_delta, value_contribution_msat, path_htlc_minimum_msat, path_penalty_msat, path_length_to_node, .. }) = targets.pop() { + 'path_construction: while let Some(RouteGraphNode { node_id, lowest_fee_to_node, total_cltv_delta, mut value_contribution_msat, path_htlc_minimum_msat, path_penalty_msat, path_length_to_node, .. }) = targets.pop() { // Since we're going payee-to-payer, hitting our node as a target means we should stop // traversing the graph and arrange the path out of what we found. @@ -1531,7 +1542,9 @@ where L::Target: Logger { // on some channels we already passed (assuming dest->source direction). Here, we // recompute the fees again, so that if that's the case, we match the currently // underpaid htlc_minimum_msat with fees. - payment_path.update_value_and_recompute_fees(cmp::min(value_contribution_msat, final_value_msat)); + debug_assert_eq!(payment_path.get_value_msat(), value_contribution_msat); + value_contribution_msat = cmp::min(value_contribution_msat, final_value_msat); + payment_path.update_value_and_recompute_fees(value_contribution_msat); // Since a path allows to transfer as much value as // the smallest channel it has ("bottleneck"), we should recompute @@ -1642,96 +1655,55 @@ where L::Target: Logger { return Err(LightningError{err: "Failed to find a sufficient route to the given destination".to_owned(), action: ErrorAction::IgnoreError}); } - // Sort by total fees and take the best paths. - payment_paths.sort_unstable_by_key(|path| path.get_total_fee_paid_msat()); - if payment_paths.len() > 50 { - payment_paths.truncate(50); - } + // Step (6). + let mut selected_route = payment_paths; - // Draw multiple sufficient routes by randomly combining the selected paths. - let mut drawn_routes = Vec::new(); - let mut prng = ChaCha20::new(random_seed_bytes, &[0u8; 12]); - let mut random_index_bytes = [0u8; ::core::mem::size_of::()]; + debug_assert_eq!(selected_route.iter().map(|p| p.get_value_msat()).sum::(), already_collected_value_msat); + let mut overpaid_value_msat = already_collected_value_msat - final_value_msat; - let num_permutations = payment_paths.len(); - for _ in 0..num_permutations { - let mut cur_route = Vec::::new(); - let mut aggregate_route_value_msat = 0; + // First, sort by the cost-per-value of the path, dropping the paths that cost the most for + // the value they contribute towards the payment amount. + // We sort in descending order as we will remove from the front in `retain`, next. + selected_route.sort_unstable_by(|a, b| + (((b.get_cost_msat() as u128) << 64) / (b.get_value_msat() as u128)) + .cmp(&(((a.get_cost_msat() as u128) << 64) / (a.get_value_msat() as u128))) + ); - // Step (6). - // Do a Fisher-Yates shuffle to create a random permutation of the payment paths - for cur_index in (1..payment_paths.len()).rev() { - prng.process_in_place(&mut random_index_bytes); - let random_index = usize::from_be_bytes(random_index_bytes).wrapping_rem(cur_index+1); - payment_paths.swap(cur_index, random_index); + // We should make sure that at least 1 path left. + let mut paths_left = selected_route.len(); + selected_route.retain(|path| { + if paths_left == 1 { + return true + } + let path_value_msat = path.get_value_msat(); + if path_value_msat <= overpaid_value_msat { + overpaid_value_msat -= path_value_msat; + paths_left -= 1; + return false; } + true + }); + debug_assert!(selected_route.len() > 0); + if overpaid_value_msat != 0 { // Step (7). - for payment_path in &payment_paths { - cur_route.push(payment_path.clone()); - aggregate_route_value_msat += payment_path.get_value_msat(); - if aggregate_route_value_msat > final_value_msat { - // Last path likely overpaid. Substract it from the most expensive - // (in terms of proportional fee) path in this route and recompute fees. - // This might be not the most economically efficient way, but fewer paths - // also makes routing more reliable. - let mut overpaid_value_msat = aggregate_route_value_msat - final_value_msat; - - // First, we drop some expensive low-value paths entirely if possible, since fewer - // paths is better: the payment is less likely to fail. In order to do so, we sort - // by value and fall back to total fees paid, i.e., in case of equal values we - // prefer lower cost paths. - cur_route.sort_unstable_by(|a, b| { - a.get_value_msat().cmp(&b.get_value_msat()) - // Reverse ordering for cost, so we drop higher-cost paths first - .then_with(|| b.get_cost_msat().cmp(&a.get_cost_msat())) - }); - - // We should make sure that at least 1 path left. - let mut paths_left = cur_route.len(); - cur_route.retain(|path| { - if paths_left == 1 { - return true - } - let mut keep = true; - let path_value_msat = path.get_value_msat(); - if path_value_msat <= overpaid_value_msat { - keep = false; - overpaid_value_msat -= path_value_msat; - paths_left -= 1; - } - keep - }); - - if overpaid_value_msat == 0 { - break; - } + // Now, subtract the remaining overpaid value from the most-expensive path. + // TODO: this could also be optimized by also sorting by feerate_per_sat_routed, + // so that the sender pays less fees overall. And also htlc_minimum_msat. + selected_route.sort_unstable_by(|a, b| { + let a_f = a.hops.iter().map(|hop| hop.0.candidate.fees().proportional_millionths as u64).sum::(); + let b_f = b.hops.iter().map(|hop| hop.0.candidate.fees().proportional_millionths as u64).sum::(); + a_f.cmp(&b_f).then_with(|| b.get_cost_msat().cmp(&a.get_cost_msat())) + }); + let expensive_payment_path = selected_route.first_mut().unwrap(); - assert!(cur_route.len() > 0); - - // Step (8). - // Now, subtract the overpaid value from the most-expensive path. - // TODO: this could also be optimized by also sorting by feerate_per_sat_routed, - // so that the sender pays less fees overall. And also htlc_minimum_msat. - cur_route.sort_unstable_by_key(|path| { path.hops.iter().map(|hop| hop.0.candidate.fees().proportional_millionths as u64).sum::() }); - let expensive_payment_path = cur_route.first_mut().unwrap(); - - // We already dropped all the small value paths above, meaning all the - // remaining paths are larger than remaining overpaid_value_msat. - // Thus, this can't be negative. - let expensive_path_new_value_msat = expensive_payment_path.get_value_msat() - overpaid_value_msat; - expensive_payment_path.update_value_and_recompute_fees(expensive_path_new_value_msat); - break; - } - } - drawn_routes.push(cur_route); + // We already dropped all the paths with value below `overpaid_value_msat` above, thus this + // can't go negative. + let expensive_path_new_value_msat = expensive_payment_path.get_value_msat() - overpaid_value_msat; + expensive_payment_path.update_value_and_recompute_fees(expensive_path_new_value_msat); } - // 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(); - + // Step (8). // 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 @@ -1960,7 +1932,7 @@ fn build_route_from_hops_internal( #[cfg(test)] mod tests { - use routing::gossip::{NetworkGraph, P2PGossipSync, NodeId}; + use routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, EffectiveCapacity}; use routing::router::{get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features, PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE}; @@ -1968,8 +1940,8 @@ mod tests { use chain::transaction::OutPoint; use chain::keysinterface::KeysInterface; use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures}; - use ln::msgs::{ErrorAction, LightningError, OptionalField, UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler, - NodeAnnouncement, UnsignedNodeAnnouncement, ChannelUpdate, UnsignedChannelUpdate}; + use ln::msgs::{ErrorAction, LightningError, UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler, + NodeAnnouncement, UnsignedNodeAnnouncement, ChannelUpdate, UnsignedChannelUpdate, MAX_VALUE_MSAT}; use ln::channelmanager; use util::test_utils; use util::chacha20::ChaCha20; @@ -1993,6 +1965,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 { @@ -2159,7 +2133,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2171,7 +2145,7 @@ mod tests { flags: 1, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2265,7 +2239,7 @@ mod tests { flags: 1, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2281,7 +2255,7 @@ mod tests { flags: 0, cltv_expiry_delta: (5 << 4) | 3, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: u32::max_value(), fee_proportional_millionths: u32::max_value(), excess_data: Vec::new() @@ -2293,7 +2267,7 @@ mod tests { flags: 1, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2309,7 +2283,7 @@ mod tests { flags: 0, cltv_expiry_delta: (5 << 4) | 3, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: u32::max_value(), fee_proportional_millionths: u32::max_value(), excess_data: Vec::new() @@ -2321,7 +2295,7 @@ mod tests { flags: 1, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2337,7 +2311,7 @@ mod tests { flags: 0, cltv_expiry_delta: (3 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2349,7 +2323,7 @@ mod tests { flags: 1, cltv_expiry_delta: (3 << 4) | 2, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 100, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2363,7 +2337,7 @@ mod tests { flags: 0, cltv_expiry_delta: (4 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 1000000, excess_data: Vec::new() @@ -2375,7 +2349,7 @@ mod tests { flags: 1, cltv_expiry_delta: (4 << 4) | 2, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2389,7 +2363,7 @@ mod tests { flags: 0, cltv_expiry_delta: (13 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 2000000, excess_data: Vec::new() @@ -2401,7 +2375,7 @@ mod tests { flags: 1, cltv_expiry_delta: (13 << 4) | 2, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2417,7 +2391,7 @@ mod tests { flags: 0, cltv_expiry_delta: (6 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2429,7 +2403,7 @@ mod tests { flags: 1, cltv_expiry_delta: (6 << 4) | 2, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new(), @@ -2443,7 +2417,7 @@ mod tests { flags: 0, cltv_expiry_delta: (11 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2455,7 +2429,7 @@ mod tests { flags: 1, cltv_expiry_delta: (11 << 4) | 2, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2473,7 +2447,7 @@ mod tests { flags: 0, cltv_expiry_delta: (7 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 1000000, excess_data: Vec::new() @@ -2485,7 +2459,7 @@ mod tests { flags: 1, cltv_expiry_delta: (7 << 4) | 2, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2570,7 +2544,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2582,7 +2556,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2594,7 +2568,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2606,7 +2580,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2618,7 +2592,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2633,7 +2607,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 200_000_000, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2648,7 +2622,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(199_999_999), + htlc_maximum_msat: 199_999_999, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2667,7 +2641,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2697,7 +2671,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 35_000, - htlc_maximum_msat: OptionalField::Present(40_000), + htlc_maximum_msat: 40_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2709,7 +2683,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 35_000, - htlc_maximum_msat: OptionalField::Present(40_000), + htlc_maximum_msat: 40_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2723,7 +2697,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2735,7 +2709,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2749,7 +2723,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2770,7 +2744,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 65_000, - htlc_maximum_msat: OptionalField::Present(80_000), + htlc_maximum_msat: 80_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2782,7 +2756,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2794,7 +2768,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 100_000, excess_data: Vec::new() @@ -2833,7 +2807,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -2845,7 +2819,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3251,7 +3225,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3263,7 +3237,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3323,7 +3297,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3335,7 +3309,7 @@ mod tests { flags: 2, // to disable cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3668,7 +3642,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3680,7 +3654,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3695,7 +3669,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(1_000_000_000), + htlc_maximum_msat: 1_000_000_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3710,7 +3684,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: 250_000_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3743,7 +3717,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(1_000_000_000), + htlc_maximum_msat: 1_000_000_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3778,7 +3752,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(1_000_000_000), + htlc_maximum_msat: 1_000_000_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3793,7 +3767,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(15_000), + htlc_maximum_msat: 15_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3828,7 +3802,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3851,7 +3825,7 @@ mod tests { flags: 0, cltv_expiry_delta: (3 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: 15_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3863,7 +3837,7 @@ mod tests { flags: 1, cltv_expiry_delta: (3 << 4) | 2, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: 15_000, fee_base_msat: 100, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3895,7 +3869,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(10_000), + htlc_maximum_msat: 10_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3943,7 +3917,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3955,7 +3929,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3970,7 +3944,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3982,7 +3956,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -3995,7 +3969,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(50_000), + htlc_maximum_msat: 50_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4007,7 +3981,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4064,7 +4038,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 1_000_000, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4076,7 +4050,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(50_000), + htlc_maximum_msat: 50_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4121,7 +4095,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4133,7 +4107,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(50_000), + htlc_maximum_msat: 50_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4148,7 +4122,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(60_000), + htlc_maximum_msat: 60_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4160,7 +4134,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(60_000), + htlc_maximum_msat: 60_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4175,7 +4149,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(200_000), + htlc_maximum_msat: 200_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4187,7 +4161,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(180_000), + htlc_maximum_msat: 180_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4278,7 +4252,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4290,7 +4264,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4304,7 +4278,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4316,7 +4290,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4331,7 +4305,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(200_000), + htlc_maximum_msat: 200_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4347,7 +4321,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(200_000), + htlc_maximum_msat: 200_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4359,7 +4333,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(200_000), + htlc_maximum_msat: 200_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4372,7 +4346,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4384,7 +4358,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4446,7 +4420,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4458,7 +4432,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4472,7 +4446,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4484,7 +4458,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4499,7 +4473,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(200_000), + htlc_maximum_msat: 200_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4515,7 +4489,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(200_000), + htlc_maximum_msat: 200_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4527,7 +4501,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(200_000), + htlc_maximum_msat: 200_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4540,7 +4514,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 1_000, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4552,7 +4526,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4613,7 +4587,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4626,7 +4600,7 @@ mod tests { flags: 2, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4640,7 +4614,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4652,7 +4626,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4666,7 +4640,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4689,7 +4663,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(250_000), + htlc_maximum_msat: 250_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4701,7 +4675,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4714,7 +4688,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 150_000, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4726,7 +4700,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4783,7 +4757,7 @@ mod tests { cltv_expiry_delta: 42, htlc_minimum_msat: None, htlc_maximum_msat: None, - }])]); + }])]).with_max_channel_saturation_power_of_half(0); // Keep only two paths from us to nodes[2], both with a 99sat HTLC maximum, with one with // no fee and one with a 1msat fee. Previously, trying to route 100 sats to nodes[2] here @@ -4797,7 +4771,7 @@ mod tests { flags: 0, cltv_expiry_delta: (5 << 4) | 5, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(99_000), + htlc_maximum_msat: 99_000, fee_base_msat: u32::max_value(), fee_proportional_millionths: u32::max_value(), excess_data: Vec::new() @@ -4809,7 +4783,7 @@ mod tests { flags: 0, cltv_expiry_delta: (5 << 4) | 3, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(99_000), + htlc_maximum_msat: 99_000, fee_base_msat: u32::max_value(), fee_proportional_millionths: u32::max_value(), excess_data: Vec::new() @@ -4821,7 +4795,7 @@ mod tests { flags: 0, cltv_expiry_delta: (4 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 1, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4833,7 +4807,7 @@ mod tests { flags: 0|2, // Channel disabled cltv_expiry_delta: (13 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 2000000, excess_data: Vec::new() @@ -4886,7 +4860,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(100_000), + htlc_maximum_msat: 100_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4898,7 +4872,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(50_000), + htlc_maximum_msat: 50_000, fee_base_msat: 100, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4912,7 +4886,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(60_000), + htlc_maximum_msat: 60_000, fee_base_msat: 100, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4924,7 +4898,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(60_000), + htlc_maximum_msat: 60_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4938,7 +4912,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(20_000), + htlc_maximum_msat: 20_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -4950,7 +4924,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(20_000), + htlc_maximum_msat: 20_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5037,7 +5011,7 @@ mod tests { flags: 0, cltv_expiry_delta: (6 << 4) | 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5052,7 +5026,7 @@ mod tests { flags: 0, cltv_expiry_delta: (5 << 4) | 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 100, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5067,7 +5041,7 @@ mod tests { flags: 0, cltv_expiry_delta: (4 << 4) | 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5082,7 +5056,7 @@ mod tests { flags: 0, cltv_expiry_delta: (3 << 4) | 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5097,7 +5071,7 @@ mod tests { flags: 0, cltv_expiry_delta: (2 << 4) | 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5111,7 +5085,7 @@ mod tests { flags: 0, cltv_expiry_delta: (1 << 4) | 0, htlc_minimum_msat: 100, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5169,7 +5143,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(85_000), + htlc_maximum_msat: 85_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5182,7 +5156,7 @@ mod tests { flags: 0, cltv_expiry_delta: (4 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(270_000), + htlc_maximum_msat: 270_000, fee_base_msat: 0, fee_proportional_millionths: 1000000, excess_data: Vec::new() @@ -5234,7 +5208,7 @@ mod tests { flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(80_000), + htlc_maximum_msat: 80_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5246,7 +5220,7 @@ mod tests { flags: 0, cltv_expiry_delta: (4 << 4) | 1, htlc_minimum_msat: 90_000, - htlc_maximum_msat: OptionalField::Absent, + htlc_maximum_msat: MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5573,6 +5547,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(); @@ -5738,7 +5741,7 @@ mod tests { flags: 0, cltv_expiry_delta: (4 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(200_000_000), + htlc_maximum_msat: 250_000_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5750,7 +5753,7 @@ mod tests { flags: 0, cltv_expiry_delta: (13 << 4) | 1, htlc_minimum_msat: 0, - htlc_maximum_msat: OptionalField::Present(200_000_000), + htlc_maximum_msat: 250_000_000, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: Vec::new() @@ -5759,8 +5762,8 @@ mod tests { 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(); + // 100,000 sats is less than the available liquidity on each channel, set above. + let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_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)); @@ -5852,7 +5855,7 @@ mod tests { } #[test] - fn avoids_banned_nodes() { + fn honors_manual_penalties() { let (secp_ctx, network_graph, _, _, logger) = build_line_graph(); let (_, our_id, _, nodes) = get_nodes(&secp_ctx); @@ -5862,7 +5865,17 @@ mod tests { let scorer_params = ProbabilisticScoringParameters::default(); let mut scorer = ProbabilisticScorer::new(scorer_params, Arc::clone(&network_graph), Arc::clone(&logger)); - // First check we can get a route. + // First check set manual penalties are returned by the scorer. + let usage = ChannelUsage { + amount_msat: 0, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: Some(1_000) }, + }; + scorer.set_manual_penalty(&NodeId::from_pubkey(&nodes[3]), 123); + scorer.set_manual_penalty(&NodeId::from_pubkey(&nodes[4]), 456); + assert_eq!(scorer.channel_penalty_msat(42, &NodeId::from_pubkey(&nodes[3]), &NodeId::from_pubkey(&nodes[4]), usage), 456); + + // Then check we can get a normal route let payment_params = PaymentParameters::from_node_id(nodes[10]); let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes); assert!(route.is_ok());