From b5be4c210c804488c18a0f5fe912728fdede6984 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 20 Oct 2021 09:15:31 -0500 Subject: [PATCH 1/4] Define Payee abstraction for use in get_route A payee can be identified by a pubkey and optionally have an associated set of invoice features and route hints. Use this in get_route instead of three separate parameters. This may be included in PaymentPathFailed later to use when finding a new route. --- fuzz/src/full_stack.rs | 8 +- fuzz/src/router.rs | 6 +- lightning-invoice/src/lib.rs | 4 +- lightning-invoice/src/utils.rs | 8 +- lightning/src/ln/channelmanager.rs | 16 +- lightning/src/ln/functional_test_utils.rs | 22 +- lightning/src/ln/functional_tests.rs | 17 +- lightning/src/ln/shutdown_tests.rs | 8 +- lightning/src/routing/router.rs | 316 +++++++++++++--------- lightning/src/routing/scorer.rs | 6 +- 10 files changed, 247 insertions(+), 164 deletions(-) diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 84ba7bab502..b2feaa695a5 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -38,7 +38,7 @@ use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor,Ig use lightning::ln::msgs::DecodeError; use lightning::ln::script::ShutdownScript; use lightning::routing::network_graph::{NetGraphMsgHandler, NetworkGraph}; -use lightning::routing::router::get_route; +use lightning::routing::router::{get_route, Payee}; use lightning::routing::scorer::Scorer; use lightning::util::config::UserConfig; use lightning::util::errors::APIError; @@ -438,7 +438,8 @@ pub fn do_test(data: &[u8], logger: &Arc) { }, 4 => { let value = slice_to_be24(get_slice!(3)) as u64; - let route = match get_route(&our_id, &net_graph_msg_handler.network_graph, &get_pubkey!(), None, None, &Vec::new(), value, 42, Arc::clone(&logger), &scorer) { + let payee = Payee::new(get_pubkey!()); + let route = match get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, value, 42, Arc::clone(&logger), &scorer) { Ok(route) => route, Err(_) => return, }; @@ -455,7 +456,8 @@ pub fn do_test(data: &[u8], logger: &Arc) { }, 15 => { let value = slice_to_be24(get_slice!(3)) as u64; - let mut route = match get_route(&our_id, &net_graph_msg_handler.network_graph, &get_pubkey!(), None, None, &Vec::new(), value, 42, Arc::clone(&logger), &scorer) { + let payee = Payee::new(get_pubkey!()); + let mut route = match get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, value, 42, Arc::clone(&logger), &scorer) { Ok(route) => route, Err(_) => return, }; diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs index 6a207254ac9..6f54aa02df7 100644 --- a/fuzz/src/router.rs +++ b/fuzz/src/router.rs @@ -16,7 +16,7 @@ use lightning::chain::transaction::OutPoint; use lightning::ln::channelmanager::{ChannelDetails, ChannelCounterparty}; use lightning::ln::features::InitFeatures; use lightning::ln::msgs; -use lightning::routing::router::{get_route, RouteHint, RouteHintHop}; +use lightning::routing::router::{get_route, Payee, RouteHint, RouteHintHop}; use lightning::routing::scorer::Scorer; use lightning::util::logger::Logger; use lightning::util::ser::Readable; @@ -250,9 +250,9 @@ pub fn do_test(data: &[u8], out: Out) { } let scorer = Scorer::new(0); for target in node_pks.iter() { - let _ = get_route(&our_pubkey, &net_graph, target, None, + let payee = Payee::new(*target).with_route_hints(last_hops.clone()); + let _ = get_route(&our_pubkey, &payee, &net_graph, first_hops.map(|c| c.iter().collect::>()).as_ref().map(|a| a.as_slice()), - &last_hops.iter().collect::>(), slice_to_be64(get_slice!(8)), slice_to_be32(get_slice!(4)), Arc::clone(&logger), &scorer); } }, diff --git a/lightning-invoice/src/lib.rs b/lightning-invoice/src/lib.rs index 61e394da6db..0f125362c3a 100644 --- a/lightning-invoice/src/lib.rs +++ b/lightning-invoice/src/lib.rs @@ -1208,10 +1208,10 @@ impl Invoice { } /// Returns a list of all routes included in the invoice as the underlying hints - pub fn route_hints(&self) -> Vec<&RouteHint> { + pub fn route_hints(&self) -> Vec { find_all_extract!( self.signed_invoice.known_tagged_fields(), TaggedField::PrivateRoute(ref x), x - ).map(|route| &**route).collect() + ).map(|route| (**route).clone()).collect() } /// Returns the currency for which the invoice was issued diff --git a/lightning-invoice/src/utils.rs b/lightning-invoice/src/utils.rs index 31c1ab2550e..c9046abc062 100644 --- a/lightning-invoice/src/utils.rs +++ b/lightning-invoice/src/utils.rs @@ -113,19 +113,19 @@ mod test { assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64); assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string()))); + let payee = router::Payee::new(invoice.recover_payee_pub_key()) + .with_features(invoice.features().unwrap().clone()) + .with_route_hints(invoice.route_hints()); let amt_msat = invoice.amount_pico_btc().unwrap() / 10; let first_hops = nodes[0].node.list_usable_channels(); - let last_hops = invoice.route_hints(); let network_graph = &nodes[0].net_graph_msg_handler.network_graph; let logger = test_utils::TestLogger::new(); let scorer = Scorer::new(0); let route = router::get_route( &nodes[0].node.get_our_node_id(), + &payee, network_graph, - &invoice.recover_payee_pub_key(), - Some(invoice.features().unwrap().clone()), Some(&first_hops.iter().collect::>()), - &last_hops, amt_msat, invoice.min_final_cltv_expiry() as u32, &logger, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index f3085fe2011..418ef5f9b4a 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -5827,7 +5827,7 @@ mod tests { use ln::functional_test_utils::*; use ln::msgs; use ln::msgs::ChannelMessageHandler; - use routing::router::{get_keysend_route, get_route}; + use routing::router::{Payee, get_keysend_route, get_route}; use routing::scorer::Scorer; use util::errors::APIError; use util::events::{Event, MessageSendEvent, MessageSendEventsProvider}; @@ -6074,7 +6074,9 @@ mod tests { let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &expected_route, 100_000); // Next, attempt a keysend payment and make sure it fails. - let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, &logger, &scorer).unwrap(); + let payee = Payee::new(expected_route.last().unwrap().node.get_our_node_id()) + .with_features(InvoiceFeatures::known()); + let route = get_route(&nodes[0].node.get_our_node_id(), &payee, &nodes[0].net_graph_msg_handler.network_graph, None, 100_000, TEST_FINAL_CLTV, &logger, &scorer).unwrap(); nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap(); check_added_monitors!(nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); @@ -6102,7 +6104,7 @@ mod tests { // To start (2), send a keysend payment but don't claim it. let payment_preimage = PaymentPreimage([42; 32]); - let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, &logger, &scorer).unwrap(); + let route = get_route(&nodes[0].node.get_our_node_id(), &payee, &nodes[0].net_graph_msg_handler.network_graph, None, 100_000, TEST_FINAL_CLTV, &logger, &scorer).unwrap(); let (payment_hash, _) = nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap(); check_added_monitors!(nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); @@ -6256,7 +6258,7 @@ pub mod bench { use ln::functional_test_utils::*; use ln::msgs::{ChannelMessageHandler, Init}; use routing::network_graph::NetworkGraph; - use routing::router::get_route; + use routing::router::{Payee, get_route}; use routing::scorer::Scorer; use util::test_utils; use util::config::UserConfig; @@ -6365,9 +6367,11 @@ pub mod bench { macro_rules! send_payment { ($node_a: expr, $node_b: expr) => { let usable_channels = $node_a.list_usable_channels(); + let payee = Payee::new($node_b.get_our_node_id()) + .with_features(InvoiceFeatures::known()); let scorer = Scorer::new(0); - let route = get_route(&$node_a.get_our_node_id(), &dummy_graph, &$node_b.get_our_node_id(), Some(InvoiceFeatures::known()), - Some(&usable_channels.iter().map(|r| r).collect::>()), &[], 10_000, TEST_FINAL_CLTV, &logger_a, &scorer).unwrap(); + let route = get_route(&$node_a.get_our_node_id(), &payee, &dummy_graph, + Some(&usable_channels.iter().map(|r| r).collect::>()), 10_000, TEST_FINAL_CLTV, &logger_a, &scorer).unwrap(); let mut payment_preimage = PaymentPreimage([0; 32]); payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes()); diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 0f188ac38ee..b45fbd3184c 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -16,7 +16,7 @@ use chain::transaction::OutPoint; use ln::{PaymentPreimage, PaymentHash, PaymentSecret}; use ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure}; use routing::network_graph::{NetGraphMsgHandler, NetworkGraph}; -use routing::router::{Route, get_route}; +use routing::router::{Payee, Route, get_route}; use routing::scorer::Scorer; use ln::features::{InitFeatures, InvoiceFeatures}; use ln::msgs; @@ -1011,13 +1011,15 @@ macro_rules! get_route_and_payment_hash { }}; ($send_node: expr, $recv_node: expr, $last_hops: expr, $recv_value: expr, $cltv: expr) => {{ let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!($recv_node, Some($recv_value)); + let payee = $crate::routing::router::Payee::new($recv_node.node.get_our_node_id()) + .with_features($crate::ln::features::InvoiceFeatures::known()) + .with_route_hints($last_hops); let net_graph_msg_handler = &$send_node.net_graph_msg_handler; let scorer = ::routing::scorer::Scorer::new(0); let route = ::routing::router::get_route( - &$send_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph, - &$recv_node.node.get_our_node_id(), Some(::ln::features::InvoiceFeatures::known()), + &$send_node.node.get_our_node_id(), &payee, &net_graph_msg_handler.network_graph, Some(&$send_node.node.list_usable_channels().iter().collect::>()), - &$last_hops, $recv_value, $cltv, $send_node.logger, &scorer + $recv_value, $cltv, $send_node.logger, &scorer ).unwrap(); (route, payment_hash, payment_preimage, payment_secret) }} @@ -1327,11 +1329,13 @@ pub fn claim_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: pub const TEST_FINAL_CLTV: u32 = 70; pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) { + let payee = Payee::new(expected_route.last().unwrap().node.get_our_node_id()) + .with_features(InvoiceFeatures::known()); let net_graph_msg_handler = &origin_node.net_graph_msg_handler; let scorer = Scorer::new(0); - let route = get_route(&origin_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph, - &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), - Some(&origin_node.node.list_usable_channels().iter().collect::>()), &[], + let route = get_route( + &origin_node.node.get_our_node_id(), &payee, &net_graph_msg_handler.network_graph, + Some(&origin_node.node.list_usable_channels().iter().collect::>()), recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap(); assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0].len(), expected_route.len()); @@ -1343,9 +1347,11 @@ pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: } pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) { + let payee = Payee::new(expected_route.last().unwrap().node.get_our_node_id()) + .with_features(InvoiceFeatures::known()); let net_graph_msg_handler = &origin_node.net_graph_msg_handler; let scorer = Scorer::new(0); - let route = get_route(&origin_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph, &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap(); + let route = get_route(&origin_node.node.get_our_node_id(), &payee, &net_graph_msg_handler.network_graph, None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap(); assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0].len(), expected_route.len()); for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) { diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index eb7d868a95d..aeca6e85b7a 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -24,7 +24,7 @@ use ln::channel::{Channel, ChannelError}; use ln::{chan_utils, onion_utils}; use ln::chan_utils::HTLC_SUCCESS_TX_WEIGHT; use routing::network_graph::{NetworkUpdate, RoutingFees}; -use routing::router::{Route, RouteHop, RouteHint, RouteHintHop, get_route, get_keysend_route}; +use routing::router::{Payee, Route, RouteHop, RouteHint, RouteHintHop, get_route, get_keysend_route}; use routing::scorer::Scorer; use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures}; use ln::msgs; @@ -7299,7 +7299,8 @@ fn test_check_htlc_underpaying() { create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()); let scorer = Scorer::new(0); - let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap(); + let payee = Payee::new(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known()); + let route = get_route(&nodes[0].node.get_our_node_id(), &payee, &nodes[0].net_graph_msg_handler.network_graph, None, 10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap(); let (_, our_payment_hash, _) = get_payment_preimage_hash!(nodes[0]); let our_payment_secret = nodes[1].node.create_inbound_payment_for_hash(our_payment_hash, Some(100_000), 7200, 0).unwrap(); nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap(); @@ -7487,7 +7488,7 @@ fn test_priv_forwarding_rejection() { htlc_minimum_msat: None, htlc_maximum_msat: None, }]); - let last_hops = vec![&route_hint]; + let last_hops = vec![route_hint]; let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], last_hops, 10_000, TEST_FINAL_CLTV); nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap(); @@ -7696,12 +7697,14 @@ fn test_bump_penalty_txn_on_revoked_htlcs() { let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000, InitFeatures::known(), InitFeatures::known()); // Lock HTLC in both directions (using a slightly lower CLTV delay to provide timely RBF bumps) + let payee = Payee::new(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known()); let scorer = Scorer::new(0); - let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, - &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 3_000_000, 50, nodes[0].logger, &scorer).unwrap(); + let route = get_route(&nodes[0].node.get_our_node_id(), &payee, &nodes[0].net_graph_msg_handler.network_graph, + None, 3_000_000, 50, nodes[0].logger, &scorer).unwrap(); let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 3_000_000).0; - let route = get_route(&nodes[1].node.get_our_node_id(), &nodes[1].net_graph_msg_handler.network_graph, - &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 3_000_000, 50, nodes[0].logger, &scorer).unwrap(); + let payee = Payee::new(nodes[0].node.get_our_node_id()).with_features(InvoiceFeatures::known()); + let route = get_route(&nodes[1].node.get_our_node_id(), &payee, &nodes[1].net_graph_msg_handler.network_graph, + None, 3_000_000, 50, nodes[0].logger, &scorer).unwrap(); send_along_route(&nodes[1], route, &[&nodes[0]], 3_000_000); let revoked_local_txn = get_local_commitment_txn!(nodes[1], chan.2); diff --git a/lightning/src/ln/shutdown_tests.rs b/lightning/src/ln/shutdown_tests.rs index e82cdae60f2..cbf288962f5 100644 --- a/lightning/src/ln/shutdown_tests.rs +++ b/lightning/src/ln/shutdown_tests.rs @@ -13,7 +13,7 @@ use chain::keysinterface::KeysInterface; use chain::transaction::OutPoint; use ln::{PaymentPreimage, PaymentHash}; use ln::channelmanager::PaymentSendFailure; -use routing::router::get_route; +use routing::router::{Payee, get_route}; use routing::network_graph::NetworkUpdate; use routing::scorer::Scorer; use ln::features::{InitFeatures, InvoiceFeatures}; @@ -99,8 +99,10 @@ fn updates_shutdown_wait() { let net_graph_msg_handler0 = &nodes[0].net_graph_msg_handler; let net_graph_msg_handler1 = &nodes[1].net_graph_msg_handler; - let route_1 = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler0.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger, &scorer).unwrap(); - let route_2 = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler1.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger, &scorer).unwrap(); + let payee_1 = Payee::new(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known()); + let route_1 = get_route(&nodes[0].node.get_our_node_id(), &payee_1, &net_graph_msg_handler0.network_graph, None, 100000, TEST_FINAL_CLTV, &logger, &scorer).unwrap(); + let payee_2 = Payee::new(nodes[0].node.get_our_node_id()).with_features(InvoiceFeatures::known()); + let route_2 = get_route(&nodes[1].node.get_our_node_id(), &payee_2, &net_graph_msg_handler1.network_graph, None, 100000, TEST_FINAL_CLTV, &logger, &scorer).unwrap(); unwrap_send_err!(nodes[0].node.send_payment(&route_1, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable {..}, {}); unwrap_send_err!(nodes[1].node.send_payment(&route_2, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable {..}, {}); diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index b617eebd42d..9f48d4cb8cd 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -129,6 +129,53 @@ impl Readable for Route { } } +/// The recipient of a payment. +pub struct Payee { + /// The node id of the payee. + pubkey: PublicKey, + + /// Features supported by the payee. + /// + /// May be set from the payee's invoice or via [`for_keysend`]. May be `None` if the invoice + /// does not contain any features. + /// + /// [`for_keysend`]: Self::for_keysend + pub features: Option, + + /// Hints for routing to the payee, containing channels connecting the payee to public nodes. + pub route_hints: Vec, +} + +impl Payee { + /// Creates a payee with the node id of the given `pubkey`. + pub fn new(pubkey: PublicKey) -> Self { + Self { + pubkey, + features: None, + route_hints: vec![], + } + } + + /// Creates a payee with the node id of the given `pubkey` to use for keysend payments. + pub fn for_keysend(pubkey: PublicKey) -> Self { + Self::new(pubkey).with_features(InvoiceFeatures::for_keysend()) + } + + /// Includes the payee's features. + /// + /// (C-not exported) since bindings don't support move semantics + pub fn with_features(self, features: InvoiceFeatures) -> Self { + Self { features: Some(features), ..self } + } + + /// Includes hints for routing to the payee. + /// + /// (C-not exported) since bindings don't support move semantics + pub fn with_route_hints(self, route_hints: Vec) -> Self { + Self { route_hints, ..self } + } +} + /// A list of hops along a payment path terminating with a channel to the recipient. #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct RouteHint(pub Vec); @@ -362,26 +409,26 @@ fn compute_fees(amount_msat: u64, channel_fees: RoutingFees) -> Option { /// Gets a keysend route from us (payer) to the given target node (payee). This is needed because /// keysend payments do not have an invoice from which to pull the payee's supported features, which -/// makes it tricky to otherwise supply the `payee_features` parameter of `get_route`. +/// makes it tricky to otherwise supply the `payee` parameter of `get_route`. pub fn get_keysend_route( our_node_pubkey: &PublicKey, network: &NetworkGraph, payee: &PublicKey, first_hops: Option<&[&ChannelDetails]>, last_hops: &[&RouteHint], final_value_msat: u64, final_cltv: u32, logger: L, scorer: &S ) -> Result where L::Target: Logger { - let invoice_features = InvoiceFeatures::for_keysend(); + let route_hints = last_hops.iter().map(|hint| (*hint).clone()).collect(); + let payee = Payee::for_keysend(*payee).with_route_hints(route_hints); get_route( - our_node_pubkey, network, payee, Some(invoice_features), first_hops, last_hops, - final_value_msat, final_cltv, logger, scorer + our_node_pubkey, &payee, network, first_hops, final_value_msat, final_cltv, logger, scorer ) } /// Gets a route from us (payer) to the given target node (payee). /// -/// If the payee provided features in their invoice, they should be provided via payee_features. -/// Without this, MPP will only be used if the payee's features are available in the network graph. +/// If the payee provided features in their invoice, they should be provided via `payee`. Without +/// this, MPP will only be used if the payee's features are available in the network graph. /// -/// Private routing paths between a public node and the target may be included in `last_hops`. +/// Private routing paths between a public node and the target may be included in `payee`. /// Currently, only the last hop in each path is considered. /// /// If some channels aren't announced, it may be useful to fill in a first_hops with the @@ -396,12 +443,12 @@ where L::Target: Logger { /// equal), however the enabled/disabled bit on such channels as well as the /// htlc_minimum_msat/htlc_maximum_msat *are* checked as they may change based on the receiving node. pub fn get_route( - our_node_pubkey: &PublicKey, network: &NetworkGraph, payee: &PublicKey, - payee_features: Option, first_hops: Option<&[&ChannelDetails]>, - last_hops: &[&RouteHint], final_value_msat: u64, final_cltv: u32, logger: L, scorer: &S + our_node_pubkey: &PublicKey, payee: &Payee, network: &NetworkGraph, + first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv: u32, logger: L, + scorer: &S ) -> Result where L::Target: Logger { - let payee_node_id = NodeId::from_pubkey(&payee); + let payee_node_id = NodeId::from_pubkey(&payee.pubkey); let our_node_id = NodeId::from_pubkey(&our_node_pubkey); if payee_node_id == our_node_id { @@ -416,10 +463,10 @@ where L::Target: Logger { return Err(LightningError{err: "Cannot send a payment of 0 msat".to_owned(), action: ErrorAction::IgnoreError}); } - for route in last_hops.iter() { + for route in payee.route_hints.iter() { for hop in &route.0 { - if hop.src_node_id == *payee { - return Err(LightningError{err: "Last hop cannot have a payee as a source.".to_owned(), action: ErrorAction::IgnoreError}); + if hop.src_node_id == payee.pubkey { + return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError}); } } } @@ -500,15 +547,15 @@ where L::Target: Logger { // 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) = &payee_features { + let allow_mpp = if let Some(features) = &payee.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", our_node_pubkey, payee, - if allow_mpp { "with" } else { "without" }); + log_trace!(logger, "Searching for a route from payer {} to payee {} {} MPP", our_node_pubkey, + payee.pubkey, if allow_mpp { "with" } else { "without" }); // Step (1). // Prepare the data we'll use for payee-to-payer search by @@ -567,7 +614,7 @@ where L::Target: Logger { // - when we want to stop looking for new paths. let mut already_collected_value_msat = 0; - log_trace!(logger, "Building path from {} (payee) to {} (us/payer) for value {} msat.", payee, our_node_pubkey, final_value_msat); + log_trace!(logger, "Building path from {} (payee) to {} (us/payer) for value {} msat.", payee.pubkey, our_node_pubkey, final_value_msat); macro_rules! add_entry { // Adds entry which goes from $src_node_id to $dest_node_id @@ -937,7 +984,7 @@ where L::Target: Logger { // If a caller provided us with last hops, add them to routing targets. Since this happens // earlier than general path finding, they will be somewhat prioritized, although currently // it matters only if the fees are exactly the same. - for route in last_hops.iter().filter(|route| !route.0.is_empty()) { + for route in payee.route_hints.iter().filter(|route| !route.0.is_empty()) { let first_hop_in_route = &(route.0)[0]; let have_hop_src_in_graph = // Only add the hops in this route to our candidate set if either @@ -949,7 +996,7 @@ where L::Target: Logger { // We start building the path from reverse, i.e., from payee // to the first RouteHintHop in the path. let hop_iter = route.0.iter().rev(); - let prev_hop_iter = core::iter::once(payee).chain( + let prev_hop_iter = core::iter::once(&payee.pubkey).chain( route.0.iter().skip(1).rev().map(|hop| &hop.src_node_id)); let mut hop_used = true; let mut aggregate_next_hops_fee_msat: u64 = 0; @@ -1309,7 +1356,7 @@ where L::Target: Logger { }).collect()); } - if let Some(features) = &payee_features { + if let Some(features) = &payee.features { for path in selected_paths.iter_mut() { if let Ok(route_hop) = path.last_mut().unwrap() { route_hop.node_features = features.to_context(); @@ -1318,7 +1365,7 @@ where L::Target: Logger { } let route = Route { paths: selected_paths.into_iter().map(|path| path.into_iter().collect()).collect::, _>>()? }; - log_info!(logger, "Got route to {}: {}", payee, log_route!(route)); + log_info!(logger, "Got route to {}: {}", payee.pubkey, log_route!(route)); Ok(route) } @@ -1326,7 +1373,7 @@ where L::Target: Logger { mod tests { use routing; use routing::network_graph::{NetworkGraph, NetGraphMsgHandler, NodeId}; - use routing::router::{get_route, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees}; + use routing::router::{get_route, Payee, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees}; use routing::scorer::Scorer; use chain::transaction::OutPoint; use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures}; @@ -1790,15 +1837,16 @@ mod tests { fn simple_route_test() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (_, our_id, _, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[2]); let scorer = Scorer::new(0); // Simple route to 2 via 1 - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 0, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 0, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Cannot send a payment of 0 msat"); } else { panic!(); } - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 2); assert_eq!(route.paths[0][0].pubkey, nodes[1]); @@ -1820,17 +1868,18 @@ mod tests { fn invalid_first_hop_test() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (_, our_id, _, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[2]); let scorer = Scorer::new(0); // Simple route to 2 via 1 let our_chans = vec![get_channel_details(Some(2), our_id, InitFeatures::from_le_bytes(vec![0b11]), 100000)]; - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::>()), &Vec::new(), 100, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::>()), 100, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "First hop cannot have our_node_pubkey as a destination."); } else { panic!(); } - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 2); } @@ -1838,6 +1887,7 @@ mod tests { fn htlc_minimum_test() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[2]); let scorer = Scorer::new(0); // Simple route to 2 via 1 @@ -1935,7 +1985,7 @@ mod tests { }); // Not possible to send 199_999_999, because the minimum on channel=2 is 200_000_000. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 199_999_999, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 199_999_999, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a path to the given destination"); } else { panic!(); } @@ -1954,7 +2004,7 @@ mod tests { }); // A payment above the minimum should pass - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 199_999_999, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 199_999_999, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 2); } @@ -1962,6 +2012,7 @@ mod tests { fn htlc_minimum_overpay_test() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known()); let scorer = Scorer::new(0); // A route to node#2 via two paths. @@ -2032,8 +2083,7 @@ mod tests { excess_data: Vec::new() }); - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 60_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 60_000, 42, Arc::clone(&logger), &scorer).unwrap(); // Overpay fees to hit htlc_minimum_msat. let overpaid_fees = route.paths[0][0].fee_msat + route.paths[1][0].fee_msat; // TODO: this could be better balanced to overpay 10k and not 15k. @@ -2078,16 +2128,14 @@ mod tests { excess_data: Vec::new() }); - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 60_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 60_000, 42, Arc::clone(&logger), &scorer).unwrap(); // Fine to overpay for htlc_minimum_msat if it allows us to save fee. assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0][0].short_channel_id, 12); let fees = route.paths[0][0].fee_msat; assert_eq!(fees, 5_000); - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 50_000, 42, Arc::clone(&logger), &scorer).unwrap(); // Not fine to overpay for htlc_minimum_msat if it requires paying more than fee on // the other channel. assert_eq!(route.paths.len(), 1); @@ -2100,6 +2148,7 @@ mod tests { fn disable_channels_test() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[2]); let scorer = Scorer::new(0); // // Disable channels 4 and 12 by flags=2 @@ -2129,13 +2178,13 @@ mod tests { }); // If all the channels require some features we don't understand, route should fail - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a path to the given destination"); } else { panic!(); } // If we specify a channel to node7, that overrides our local channel view and that gets used let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)]; - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::>()), &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::>()), 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 2); assert_eq!(route.paths[0][0].pubkey, nodes[7]); @@ -2157,6 +2206,7 @@ mod tests { fn disable_node_test() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[2]); let scorer = Scorer::new(0); // Disable nodes 1, 2, and 8 by requiring unknown feature bits @@ -2166,13 +2216,13 @@ mod tests { add_or_update_node(&net_graph_msg_handler, &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, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a path to the given destination"); } else { panic!(); } // If we specify a channel to node7, that overrides our local channel view and that gets used let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)]; - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::>()), &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::>()), 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 2); assert_eq!(route.paths[0][0].pubkey, nodes[7]); @@ -2201,7 +2251,8 @@ mod tests { let scorer = Scorer::new(0); // Route to 1 via 2 and 3 because our channel to 1 is disabled - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[0], None, None, &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let payee = Payee::new(nodes[0]); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 3); assert_eq!(route.paths[0][0].pubkey, nodes[1]); @@ -2226,8 +2277,9 @@ mod tests { assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(3)); // If we specify a channel to node7, that overrides our local channel view and that gets used + let payee = Payee::new(nodes[2]); let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)]; - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::>()), &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::>()), 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 2); assert_eq!(route.paths[0][0].pubkey, nodes[7]); @@ -2347,12 +2399,14 @@ mod tests { let mut invalid_last_hops = last_hops_multi_private_channels(&nodes); invalid_last_hops.push(invalid_last_hop); { - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &invalid_last_hops.iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer) { - assert_eq!(err, "Last hop cannot have a payee as a source."); + let payee = Payee::new(nodes[6]).with_route_hints(invalid_last_hops); + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer) { + assert_eq!(err, "Route hint cannot have the payee as the source."); } else { panic!(); } } - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops_multi_private_channels(&nodes).iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let payee = Payee::new(nodes[6]).with_route_hints(last_hops_multi_private_channels(&nodes)); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 5); assert_eq!(route.paths[0][0].pubkey, nodes[1]); @@ -2421,11 +2475,12 @@ mod tests { fn ignores_empty_last_hops_test() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (_, our_id, _, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[6]).with_route_hints(empty_last_hop(&nodes)); let scorer = Scorer::new(0); // Test handling of an empty RouteHint passed in Invoice. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &empty_last_hop(&nodes).iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 5); assert_eq!(route.paths[0][0].pubkey, nodes[1]); @@ -2502,6 +2557,7 @@ mod tests { fn multi_hint_last_hops_test() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[6]).with_route_hints(multi_hint_last_hops(&nodes)); let scorer = Scorer::new(0); // Test through channels 2, 3, 5, 8. // Test shows that multiple hop hints are considered. @@ -2532,7 +2588,7 @@ mod tests { excess_data: Vec::new() }); - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &multi_hint_last_hops(&nodes).iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 4); assert_eq!(route.paths[0][0].pubkey, nodes[1]); @@ -2607,11 +2663,12 @@ mod tests { fn last_hops_with_public_channel_test() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (_, our_id, _, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[6]).with_route_hints(last_hops_with_public_channel(&nodes)); let scorer = Scorer::new(0); // This test shows that public routes can be present in the invoice // which would be handled in the same manner. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops_with_public_channel(&nodes).iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 5); assert_eq!(route.paths[0][0].pubkey, nodes[1]); @@ -2661,7 +2718,8 @@ mod tests { // Simple test with outbound channel to 4 to test that last_hops and first_hops connect let our_chans = vec![get_channel_details(Some(42), nodes[3].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)]; let mut last_hops = last_hops(&nodes); - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, Some(&our_chans.iter().collect::>()), &last_hops.iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let payee = Payee::new(nodes[6]).with_route_hints(last_hops.clone()); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::>()), 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 2); assert_eq!(route.paths[0][0].pubkey, nodes[3]); @@ -2681,7 +2739,8 @@ mod tests { last_hops[0].0[0].fees.base_msat = 1000; // Revert to via 6 as the fee on 8 goes up - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops.iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer).unwrap(); + let payee = Payee::new(nodes[6]).with_route_hints(last_hops); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 4); assert_eq!(route.paths[0][0].pubkey, nodes[1]); @@ -2715,7 +2774,7 @@ mod tests { assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly // ...but still use 8 for larger payments as 6 has a variable feerate - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops.iter().collect::>(), 2000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 2000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths[0].len(), 5); assert_eq!(route.paths[0][0].pubkey, nodes[1]); @@ -2773,9 +2832,10 @@ mod tests { htlc_minimum_msat: None, htlc_maximum_msat: last_hop_htlc_max, }]); + let payee = Payee::new(target_node_id).with_route_hints(vec![last_hops]); let our_chans = vec![get_channel_details(Some(42), middle_node_id, InitFeatures::from_le_bytes(vec![0b11]), outbound_capacity_msat)]; let scorer = Scorer::new(0); - get_route(&source_node_id, &NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()), &target_node_id, None, Some(&our_chans.iter().collect::>()), &vec![&last_hops], route_val, 42, &test_utils::TestLogger::new(), &scorer) + get_route(&source_node_id, &payee, &NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()), Some(&our_chans.iter().collect::>()), route_val, 42, &test_utils::TestLogger::new(), &scorer) } #[test] @@ -2829,6 +2889,7 @@ mod tests { let (secp_ctx, mut net_graph_msg_handler, chain_monitor, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known()); // We will use a simple single-path route from // our node to node2 via node0: channels {1, 3}. @@ -2891,16 +2952,15 @@ mod tests { { // Attempt to route more than available results in a failure. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 250_000_001, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 250_000_001, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } { // Now, attempt to route an exact amount we have should be fine. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 250_000_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 250_000_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); let path = route.paths.last().unwrap(); assert_eq!(path.len(), 2); @@ -2928,16 +2988,15 @@ mod tests { { // Attempt to route more than available results in a failure. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), Some(&our_chans.iter().collect::>()), &Vec::new(), 200_000_001, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( + &our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::>()), 200_000_001, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } { // Now, attempt to route an exact amount we have should be fine. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), Some(&our_chans.iter().collect::>()), &Vec::new(), 200_000_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::>()), 200_000_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); let path = route.paths.last().unwrap(); assert_eq!(path.len(), 2); @@ -2976,16 +3035,15 @@ mod tests { { // Attempt to route more than available results in a failure. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 15_001, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 15_001, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } { // Now, attempt to route an exact amount we have should be fine. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 15_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 15_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); let path = route.paths.last().unwrap(); assert_eq!(path.len(), 2); @@ -3047,16 +3105,15 @@ mod tests { { // Attempt to route more than available results in a failure. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 15_001, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 15_001, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } { // Now, attempt to route an exact amount we have should be fine. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 15_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 15_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); let path = route.paths.last().unwrap(); assert_eq!(path.len(), 2); @@ -3080,16 +3137,15 @@ mod tests { { // Attempt to route more than available results in a failure. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 10_001, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 10_001, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } { // Now, attempt to route an exact amount we have should be fine. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 10_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 10_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); let path = route.paths.last().unwrap(); assert_eq!(path.len(), 2); @@ -3105,6 +3161,7 @@ mod tests { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known()); // Path via {node7, node2, node4} is channels {12, 13, 6, 11}. // {12, 13, 11} have the capacities of 100, {6} has a capacity of 50. @@ -3189,16 +3246,15 @@ mod tests { }); { // Attempt to route more than available results in a failure. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3], - Some(InvoiceFeatures::known()), None, &Vec::new(), 60_000, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 60_000, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } { // Now, attempt to route 49 sats (just a bit below the capacity). - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3], - Some(InvoiceFeatures::known()), None, &Vec::new(), 49_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 49_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); let mut total_amount_paid_msat = 0; for path in &route.paths { @@ -3211,8 +3267,7 @@ mod tests { { // Attempt to route an exact amount is also fine - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3], - Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 50_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); let mut total_amount_paid_msat = 0; for path in &route.paths { @@ -3229,6 +3284,7 @@ mod tests { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(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 { @@ -3257,7 +3313,7 @@ mod tests { }); { - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 50_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 50_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); let mut total_amount_paid_msat = 0; for path in &route.paths { @@ -3274,6 +3330,7 @@ mod tests { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(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). @@ -3365,8 +3422,8 @@ mod tests { { // Attempt to route more than available results in a failure. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, - &nodes[2], Some(InvoiceFeatures::known()), None, &Vec::new(), 300_000, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 300_000, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } @@ -3374,8 +3431,7 @@ mod tests { { // 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, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 250_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 250_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 3); let mut total_amount_paid_msat = 0; for path in &route.paths { @@ -3388,8 +3444,7 @@ mod tests { { // Attempt to route an exact amount is also fine - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 290_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 290_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 3); let mut total_amount_paid_msat = 0; for path in &route.paths { @@ -3406,6 +3461,7 @@ mod tests { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known()); // We need a route consisting of 3 paths: // From our node to node3 via {node0, node2}, {node7, node2, node4} and {node7, node2}. @@ -3540,8 +3596,8 @@ mod tests { { // Attempt to route more than available results in a failure. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3], - Some(InvoiceFeatures::known()), None, &Vec::new(), 350_000, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 350_000, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } @@ -3549,8 +3605,7 @@ mod tests { { // Now, attempt to route 300 sats (exact amount we can route). // Our algorithm should provide us with these 3 paths, 100 sats each. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3], - Some(InvoiceFeatures::known()), None, &Vec::new(), 300_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 300_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 3); let mut total_amount_paid_msat = 0; @@ -3568,6 +3623,7 @@ mod tests { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known()); // This test checks that if we have two cheaper paths and one more expensive path, // so that liquidity-wise any 2 of 3 combination is sufficient, @@ -3707,8 +3763,7 @@ mod tests { { // Now, attempt to route 180 sats. // Our algorithm should provide us with these 2 paths. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3], - Some(InvoiceFeatures::known()), None, &Vec::new(), 180_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 180_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 2); let mut total_value_transferred_msat = 0; @@ -3735,6 +3790,7 @@ mod tests { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known()); // We need a route consisting of 2 paths: // From our node to node3 via {node0, node2} and {node7, node2, node4}. @@ -3874,16 +3930,15 @@ mod tests { { // Attempt to route more than available results in a failure. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3], - Some(InvoiceFeatures::known()), None, &Vec::new(), 210_000, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 210_000, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } { // Now, attempt to route 200 sats (exact amount we can route). - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3], - Some(InvoiceFeatures::known()), None, &Vec::new(), 200_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 200_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 2); let mut total_amount_paid_msat = 0; @@ -3904,6 +3959,7 @@ mod tests { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(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). @@ -3994,8 +4050,8 @@ mod tests { { // Attempt to route more than available results in a failure. - if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 150_000, 42, Arc::clone(&logger), &scorer) { + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route( + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 150_000, 42, Arc::clone(&logger), &scorer) { assert_eq!(err, "Failed to find a sufficient route to the given destination"); } else { panic!(); } } @@ -4003,8 +4059,7 @@ mod tests { { // Now, attempt to route 125 sats (just a bit below the capacity of 3 channels). // Our algorithm should provide us with these 3 paths. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 125_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 125_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 3); let mut total_amount_paid_msat = 0; for path in &route.paths { @@ -4017,8 +4072,7 @@ mod tests { { // Attempt to route without the last small cheap channel - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], - Some(InvoiceFeatures::known()), None, &Vec::new(), 90_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 90_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 2); let mut total_amount_paid_msat = 0; for path in &route.paths { @@ -4062,6 +4116,7 @@ mod tests { let net_graph_msg_handler = NetGraphMsgHandler::new(network_graph, None, Arc::clone(&logger)); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(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 { @@ -4154,7 +4209,7 @@ mod tests { { // Now ensure the route flows simply over nodes 1 and 4 to 6. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &Vec::new(), 10_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 10_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0].len(), 3); @@ -4190,6 +4245,7 @@ mod tests { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, _, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(nodes[2]); // We modify the graph to set the htlc_maximum of channel 2 to below the value we wish to // send. @@ -4222,7 +4278,7 @@ mod tests { { // Now, attempt to route 90 sats, which is exactly 90 sats at the last hop, plus the // 200% fee charged channel 13 in the 1-to-2 direction. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 90_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 90_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0].len(), 2); @@ -4251,6 +4307,7 @@ mod tests { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let scorer = Scorer::new(0); + let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known()); // 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 @@ -4284,7 +4341,7 @@ mod tests { // Now, attempt to route 90 sats, hitting the htlc_minimum on channel 4, but // overshooting the htlc_maximum on channel 2. Thus, we should pick the (absurdly // expensive) channels 12-13 path. - let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], Some(InvoiceFeatures::known()), None, &Vec::new(), 90_000, 42, Arc::clone(&logger), &scorer).unwrap(); + let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 90_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0].len(), 2); @@ -4317,12 +4374,13 @@ mod tests { let logger = Arc::new(test_utils::TestLogger::new()); let network_graph = NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()); let scorer = Scorer::new(0); + let payee = Payee::new(nodes[0]).with_features(InvoiceFeatures::known()); { - let route = get_route(&our_id, &network_graph, &nodes[0], Some(InvoiceFeatures::known()), Some(&[ + let route = get_route(&our_id, &payee, &network_graph, Some(&[ &get_channel_details(Some(3), nodes[0], InitFeatures::known(), 200_000), &get_channel_details(Some(2), nodes[0], InitFeatures::known(), 10_000), - ]), &[], 100_000, 42, Arc::clone(&logger), &scorer).unwrap(); + ]), 100_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0].len(), 1); @@ -4331,10 +4389,10 @@ mod tests { assert_eq!(route.paths[0][0].fee_msat, 100_000); } { - let route = get_route(&our_id, &network_graph, &nodes[0], Some(InvoiceFeatures::known()), Some(&[ + let route = get_route(&our_id, &payee, &network_graph, Some(&[ &get_channel_details(Some(3), nodes[0], InitFeatures::known(), 50_000), &get_channel_details(Some(2), nodes[0], InitFeatures::known(), 50_000), - ]), &[], 100_000, 42, Arc::clone(&logger), &scorer).unwrap(); + ]), 100_000, 42, Arc::clone(&logger), &scorer).unwrap(); assert_eq!(route.paths.len(), 2); assert_eq!(route.paths[0].len(), 1); assert_eq!(route.paths[1].len(), 1); @@ -4353,12 +4411,13 @@ mod tests { fn prefers_shorter_route_with_higher_fees() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (_, our_id, _, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[6]).with_route_hints(last_hops(&nodes)); // Without penalizing each hop 100 msats, a longer path with lower fees is chosen. let scorer = Scorer::new(0); let route = get_route( - &our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, - &last_hops(&nodes).iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, + Arc::clone(&logger), &scorer ).unwrap(); let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::>(); @@ -4370,8 +4429,8 @@ mod tests { // from nodes[2] rather than channel 6, 11, and 8, even though the longer path is cheaper. let scorer = Scorer::new(100); let route = get_route( - &our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, - &last_hops(&nodes).iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, + Arc::clone(&logger), &scorer ).unwrap(); let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::>(); @@ -4404,12 +4463,13 @@ mod tests { fn avoids_routing_through_bad_channels_and_nodes() { let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph(); let (_, our_id, _, nodes) = get_nodes(&secp_ctx); + let payee = Payee::new(nodes[6]).with_route_hints(last_hops(&nodes)); // A path to nodes[6] exists when no penalties are applied to any channel. let scorer = Scorer::new(0); let route = get_route( - &our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, - &last_hops(&nodes).iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, + Arc::clone(&logger), &scorer ).unwrap(); let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::>(); @@ -4420,8 +4480,8 @@ mod tests { // A different path to nodes[6] exists if channel 6 cannot be routed over. let scorer = BadChannelScorer { short_channel_id: 6 }; let route = get_route( - &our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, - &last_hops(&nodes).iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, + Arc::clone(&logger), &scorer ).unwrap(); let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::>(); @@ -4432,8 +4492,8 @@ mod tests { // A path to nodes[6] does not exist if nodes[2] cannot be routed through. let scorer = BadNodeScorer { node_id: NodeId::from_pubkey(&nodes[2]) }; match get_route( - &our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, - &last_hops(&nodes).iter().collect::>(), 100, 42, Arc::clone(&logger), &scorer + &our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, + Arc::clone(&logger), &scorer ) { Err(LightningError { err, .. } ) => { assert_eq!(err, "Failed to find a path to the given destination"); @@ -4543,9 +4603,10 @@ mod tests { seed = seed.overflowing_mul(0xdeadbeef).0; let src = &PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); seed = seed.overflowing_mul(0xdeadbeef).0; - let dst = &PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); + let dst = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); + let payee = Payee::new(dst); let amt = seed as u64 % 200_000_000; - if get_route(src, &graph, dst, None, None, &[], amt, 42, &test_utils::TestLogger::new(), &scorer).is_ok() { + if get_route(src, &payee, &graph, None, amt, 42, &test_utils::TestLogger::new(), &scorer).is_ok() { continue 'load_endpoints; } } @@ -4573,9 +4634,10 @@ mod tests { seed = seed.overflowing_mul(0xdeadbeef).0; let src = &PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); seed = seed.overflowing_mul(0xdeadbeef).0; - let dst = &PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); + let dst = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); + let payee = Payee::new(dst).with_features(InvoiceFeatures::known()); let amt = seed as u64 % 200_000_000; - if get_route(src, &graph, dst, Some(InvoiceFeatures::known()), None, &[], amt, 42, &test_utils::TestLogger::new(), &scorer).is_ok() { + if get_route(src, &payee, &graph, None, amt, 42, &test_utils::TestLogger::new(), &scorer).is_ok() { continue 'load_endpoints; } } @@ -4639,8 +4701,9 @@ mod benches { let src = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); seed *= 0xdeadbeef; let dst = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); + let payee = Payee::new(dst); let amt = seed as u64 % 1_000_000; - if get_route(&src, &graph, &dst, None, None, &[], amt, 42, &DummyLogger{}, &scorer).is_ok() { + if get_route(&src, &payee, &graph, None, amt, 42, &DummyLogger{}, &scorer).is_ok() { path_endpoints.push((src, dst, amt)); continue 'load_endpoints; } @@ -4651,7 +4714,8 @@ mod benches { let mut idx = 0; bench.iter(|| { let (src, dst, amt) = path_endpoints[idx % path_endpoints.len()]; - assert!(get_route(&src, &graph, &dst, None, None, &[], amt, 42, &DummyLogger{}, &scorer).is_ok()); + let payee = Payee::new(dst); + assert!(get_route(&src, &payee, &graph, None, amt, 42, &DummyLogger{}, &scorer).is_ok()); idx += 1; }); } @@ -4672,8 +4736,9 @@ mod benches { let src = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); seed *= 0xdeadbeef; let dst = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); + let payee = Payee::new(dst).with_features(InvoiceFeatures::known()); let amt = seed as u64 % 1_000_000; - if get_route(&src, &graph, &dst, Some(InvoiceFeatures::known()), None, &[], amt, 42, &DummyLogger{}, &scorer).is_ok() { + if get_route(&src, &payee, &graph, None, amt, 42, &DummyLogger{}, &scorer).is_ok() { path_endpoints.push((src, dst, amt)); continue 'load_endpoints; } @@ -4684,7 +4749,8 @@ mod benches { let mut idx = 0; bench.iter(|| { let (src, dst, amt) = path_endpoints[idx % path_endpoints.len()]; - assert!(get_route(&src, &graph, &dst, Some(InvoiceFeatures::known()), None, &[], amt, 42, &DummyLogger{}, &scorer).is_ok()); + let payee = Payee::new(dst).with_features(InvoiceFeatures::known()); + assert!(get_route(&src, &payee, &graph, None, amt, 42, &DummyLogger{}, &scorer).is_ok()); idx += 1; }); } diff --git a/lightning/src/routing/scorer.rs b/lightning/src/routing/scorer.rs index 0f43c3d7928..841d4bdbaba 100644 --- a/lightning/src/routing/scorer.rs +++ b/lightning/src/routing/scorer.rs @@ -18,7 +18,7 @@ //! # extern crate secp256k1; //! # //! # use lightning::routing::network_graph::NetworkGraph; -//! # use lightning::routing::router::get_route; +//! # use lightning::routing::router::{Payee, get_route}; //! # use lightning::routing::scorer::Scorer; //! # use lightning::util::logger::{Logger, Record}; //! # use secp256k1::key::PublicKey; @@ -27,7 +27,7 @@ //! # impl Logger for FakeLogger { //! # fn log(&self, record: &Record) { unimplemented!() } //! # } -//! # fn find_scored_route(payer: PublicKey, payee: PublicKey, network_graph: NetworkGraph) { +//! # fn find_scored_route(payer: PublicKey, payee: Payee, network_graph: NetworkGraph) { //! # let logger = FakeLogger {}; //! # //! // Use the default channel penalty. @@ -36,7 +36,7 @@ //! // Or use a custom channel penalty. //! let scorer = Scorer::new(1_000); //! -//! let route = get_route(&payer, &network_graph, &payee, None, None, &vec![], 1_000, 42, &logger, &scorer); +//! let route = get_route(&payer, &payee, &network_graph, None, 1_000, 42, &logger, &scorer); //! # } //! ``` //! From 8b9cf93b11c49ab8726d807a72c083367dda356e Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 20 Oct 2021 09:22:38 -0500 Subject: [PATCH 2/4] Remove outdated line from get_route docs --- lightning/src/routing/router.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 9f48d4cb8cd..6976943dd52 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -429,7 +429,6 @@ where L::Target: Logger { /// this, MPP will only be used if the payee's features are available in the network graph. /// /// Private routing paths between a public node and the target may be included in `payee`. -/// Currently, only the last hop in each path is considered. /// /// If some channels aren't announced, it may be useful to fill in a first_hops with the /// results from a local ChannelManager::list_usable_channels() call. If it is filled in, our From 46b68c517dba57bde545ea8e56b47fe3af768579 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Thu, 21 Oct 2021 17:52:53 -0500 Subject: [PATCH 3/4] Include PaymentPathRetry data in PaymentPathFailed When a payment path fails, it may be retried. Typically, this means re-computing the route after updating the NetworkGraph and channel scores in order to avoid the failing hop. The last hop in PaymentPathFailed's path field contains the pubkey, amount, and CLTV values needed to pass to get_route. However, it does not contain the payee's features and route hints from the invoice. Include the entire set of parameters in PaymentPathRetry and add it to the PaymentPathFailed event. Add a get_retry_route wrapper around get_route that takes PaymentPathRetry. This allows an EventHandler to retry failed payment paths using the payee's route hints and features. --- lightning/src/ln/channelmanager.rs | 3 + lightning/src/ln/functional_tests.rs | 4 +- lightning/src/ln/onion_route_tests.rs | 2 +- lightning/src/routing/network_graph.rs | 3 + lightning/src/routing/router.rs | 89 ++++++++++++++++++++++++-- lightning/src/util/events.rs | 18 +++++- 6 files changed, 108 insertions(+), 11 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 418ef5f9b4a..385a031cabb 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -3036,6 +3036,7 @@ impl ChannelMana all_paths_failed: payment.get().remaining_parts() == 0, path: path.clone(), short_channel_id: None, + retry: None, #[cfg(test)] error_code: None, #[cfg(test)] @@ -3103,6 +3104,7 @@ impl ChannelMana all_paths_failed, path: path.clone(), short_channel_id, + retry: None, #[cfg(test)] error_code: onion_error_code, #[cfg(test)] @@ -3131,6 +3133,7 @@ impl ChannelMana all_paths_failed, path: path.clone(), short_channel_id: Some(path.first().unwrap().short_channel_id), + retry: None, #[cfg(test)] error_code: Some(*failure_code), #[cfg(test)] diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index aeca6e85b7a..6d509f8f07b 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -6008,7 +6008,7 @@ fn test_fail_holding_cell_htlc_upon_free() { let events = nodes[0].node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); match &events[0] { - &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, ref error_code, ref error_data } => { + &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, retry: _, ref error_code, ref error_data } => { assert_eq!(our_payment_hash.clone(), *payment_hash); assert_eq!(*rejected_by_dest, false); assert_eq!(*all_paths_failed, true); @@ -6092,7 +6092,7 @@ fn test_free_and_fail_holding_cell_htlcs() { let events = nodes[0].node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); match &events[0] { - &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, ref error_code, ref error_data } => { + &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, retry: _, ref error_code, ref error_data } => { assert_eq!(payment_hash_2.clone(), *payment_hash); assert_eq!(*rejected_by_dest, false); assert_eq!(*all_paths_failed, true); diff --git a/lightning/src/ln/onion_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index 163c4ae15d5..39c1aa378d1 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -162,7 +162,7 @@ fn run_onion_failure_test_with_fail_intercept(_name: &str, test_case: let events = nodes[0].node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); - if let &Event::PaymentPathFailed { payment_hash: _, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, ref error_code, error_data: _ } = &events[0] { + if let &Event::PaymentPathFailed { payment_hash: _, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, retry: _, ref error_code, error_data: _ } = &events[0] { assert_eq!(*rejected_by_dest, !expected_retryable); assert_eq!(*all_paths_failed, true); assert_eq!(*error_code, expected_error_code); diff --git a/lightning/src/routing/network_graph.rs b/lightning/src/routing/network_graph.rs index f65b8fa657a..216cb45d4bd 100644 --- a/lightning/src/routing/network_graph.rs +++ b/lightning/src/routing/network_graph.rs @@ -1814,6 +1814,7 @@ mod tests { msg: valid_channel_update, }), short_channel_id: None, + retry: None, error_code: None, error_data: None, }); @@ -1840,6 +1841,7 @@ mod tests { is_permanent: false, }), short_channel_id: None, + retry: None, error_code: None, error_data: None, }); @@ -1864,6 +1866,7 @@ mod tests { is_permanent: true, }), short_channel_id: None, + retry: None, error_code: None, error_data: None, }); diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 6976943dd52..4d1deb61d25 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -129,7 +129,31 @@ impl Readable for Route { } } +/// Parameters needed to re-compute a [`Route`] for retrying a failed payment path. +/// +/// Provided in [`Event::PaymentPathFailed`] and passed to [`get_retry_route`]. +/// +/// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed +#[derive(Clone, Debug)] +pub struct PaymentPathRetry { + /// The recipient of the failed payment path. + pub payee: Payee, + + /// The amount in msats sent on the failed payment path. + pub final_value_msat: u64, + + /// The CLTV on the final hop of the failed payment path. + pub final_cltv_expiry_delta: u32, +} + +impl_writeable_tlv_based!(PaymentPathRetry, { + (0, payee, required), + (2, final_value_msat, required), + (4, final_cltv_expiry_delta, required), +}); + /// The recipient of a payment. +#[derive(Clone, Debug)] pub struct Payee { /// The node id of the payee. pubkey: PublicKey, @@ -146,6 +170,12 @@ pub struct Payee { pub route_hints: Vec, } +impl_writeable_tlv_based!(Payee, { + (0, pubkey, required), + (2, features, option), + (4, route_hints, vec_type), +}); + impl Payee { /// Creates a payee with the node id of the given `pubkey`. pub fn new(pubkey: PublicKey) -> Self { @@ -180,6 +210,28 @@ impl Payee { #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct RouteHint(pub Vec); + +impl Writeable for RouteHint { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { + (self.0.len() as u64).write(writer)?; + for hop in self.0.iter() { + hop.write(writer)?; + } + Ok(()) + } +} + +impl Readable for RouteHint { + fn read(reader: &mut R) -> Result { + let hop_count: u64 = Readable::read(reader)?; + let mut hops = Vec::with_capacity(cmp::min(hop_count, 16) as usize); + for _ in 0..hop_count { + hops.push(Readable::read(reader)?); + } + Ok(Self(hops)) + } +} + /// A channel descriptor for a hop along a payment path. #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct RouteHintHop { @@ -197,6 +249,15 @@ pub struct RouteHintHop { pub htlc_maximum_msat: Option, } +impl_writeable_tlv_based!(RouteHintHop, { + (0, src_node_id, required), + (1, htlc_minimum_msat, option), + (2, short_channel_id, required), + (3, htlc_maximum_msat, option), + (4, fees, required), + (6, cltv_expiry_delta, required), +}); + #[derive(Eq, PartialEq)] struct RouteGraphNode { node_id: NodeId, @@ -413,13 +474,31 @@ fn compute_fees(amount_msat: u64, channel_fees: RoutingFees) -> Option { pub fn get_keysend_route( our_node_pubkey: &PublicKey, network: &NetworkGraph, payee: &PublicKey, first_hops: Option<&[&ChannelDetails]>, last_hops: &[&RouteHint], final_value_msat: u64, - final_cltv: u32, logger: L, scorer: &S + final_cltv_expiry_delta: u32, logger: L, scorer: &S ) -> Result where L::Target: Logger { let route_hints = last_hops.iter().map(|hint| (*hint).clone()).collect(); let payee = Payee::for_keysend(*payee).with_route_hints(route_hints); get_route( - our_node_pubkey, &payee, network, first_hops, final_value_msat, final_cltv, logger, scorer + our_node_pubkey, &payee, network, first_hops, final_value_msat, final_cltv_expiry_delta, + logger, scorer + ) +} + +/// Gets a route suitable for retrying a failed payment path. +/// +/// Used to re-compute a [`Route`] when handling a [`Event::PaymentPathFailed`]. Any adjustments to +/// the [`NetworkGraph`] and channel scores should be made prior to calling this function. +/// +/// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed +pub fn get_retry_route( + our_node_pubkey: &PublicKey, retry: &PaymentPathRetry, network: &NetworkGraph, + first_hops: Option<&[&ChannelDetails]>, logger: L, scorer: &S +) -> Result +where L::Target: Logger { + get_route( + our_node_pubkey, &retry.payee, network, first_hops, retry.final_value_msat, + retry.final_cltv_expiry_delta, logger, scorer ) } @@ -443,8 +522,8 @@ where L::Target: Logger { /// htlc_minimum_msat/htlc_maximum_msat *are* checked as they may change based on the receiving node. pub fn get_route( our_node_pubkey: &PublicKey, payee: &Payee, network: &NetworkGraph, - first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv: u32, logger: L, - scorer: &S + first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32, + logger: L, scorer: &S ) -> Result where L::Target: Logger { let payee_node_id = NodeId::from_pubkey(&payee.pubkey); @@ -1154,7 +1233,7 @@ where L::Target: Logger { } ordered_hops.last_mut().unwrap().0.fee_msat = value_contribution_msat; ordered_hops.last_mut().unwrap().0.hop_use_fee_msat = 0; - ordered_hops.last_mut().unwrap().0.cltv_expiry_delta = final_cltv; + ordered_hops.last_mut().unwrap().0.cltv_expiry_delta = final_cltv_expiry_delta; log_trace!(logger, "Found a path back to us from the target with {} hops contributing up to {} msat: {:?}", ordered_hops.len(), value_contribution_msat, ordered_hops); diff --git a/lightning/src/util/events.rs b/lightning/src/util/events.rs index 7ea369f5514..df2532e4c4a 100644 --- a/lightning/src/util/events.rs +++ b/lightning/src/util/events.rs @@ -20,7 +20,7 @@ use ln::msgs::DecodeError; use ln::{PaymentPreimage, PaymentHash, PaymentSecret}; use routing::network_graph::NetworkUpdate; use util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReadable, Readable, VecReadWrapper, VecWriteWrapper}; -use routing::router::RouteHop; +use routing::router::{PaymentPathRetry, RouteHop}; use bitcoin::blockdata::script::Script; use bitcoin::hashes::Hash; @@ -216,6 +216,13 @@ pub enum Event { /// If this is `Some`, then the corresponding channel should be avoided when the payment is /// retried. May be `None` for older [`Event`] serializations. short_channel_id: Option, + /// Parameters needed to re-compute a [`Route`] for retrying the failed path. + /// + /// See [`get_retry_route`] for details. + /// + /// [`Route`]: crate::routing::router::Route + /// [`get_retry_route`]: crate::routing::router::get_retry_route + retry: Option, #[cfg(test)] error_code: Option, #[cfg(test)] @@ -322,8 +329,9 @@ impl Writeable for Event { (1, payment_hash, required), }); }, - &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, - ref all_paths_failed, ref path, ref short_channel_id, + &Event::PaymentPathFailed { + ref payment_hash, ref rejected_by_dest, ref network_update, + ref all_paths_failed, ref path, ref short_channel_id, ref retry, #[cfg(test)] ref error_code, #[cfg(test)] @@ -341,6 +349,7 @@ impl Writeable for Event { (3, all_paths_failed, required), (5, path, vec_type), (7, short_channel_id, option), + (9, retry, option), }); }, &Event::PendingHTLCsForwardable { time_forwardable: _ } => { @@ -452,6 +461,7 @@ impl MaybeReadable for Event { let mut all_paths_failed = Some(true); let mut path: Option> = Some(vec![]); let mut short_channel_id = None; + let mut retry = None; read_tlv_fields!(reader, { (0, payment_hash, required), (1, network_update, ignorable), @@ -459,6 +469,7 @@ impl MaybeReadable for Event { (3, all_paths_failed, option), (5, path, vec_type), (7, short_channel_id, ignorable), + (9, retry, option), }); Ok(Some(Event::PaymentPathFailed { payment_hash, @@ -467,6 +478,7 @@ impl MaybeReadable for Event { all_paths_failed: all_paths_failed.unwrap(), path: path.unwrap(), short_channel_id, + retry, #[cfg(test)] error_code, #[cfg(test)] From 8e7ceabe48a15f00b0baca64f1dfb86614f2c58b Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Fri, 22 Oct 2021 00:27:58 -0500 Subject: [PATCH 4/4] Use option TLV decoding for short_channel_id Using ignorable TLV decoding is only applicable for an Option containing an enum, but short_channel_id is an Option. Use option TLV encoding instead. --- lightning/src/util/events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/util/events.rs b/lightning/src/util/events.rs index df2532e4c4a..70a6a2dd4d1 100644 --- a/lightning/src/util/events.rs +++ b/lightning/src/util/events.rs @@ -468,7 +468,7 @@ impl MaybeReadable for Event { (2, rejected_by_dest, required), (3, all_paths_failed, option), (5, path, vec_type), - (7, short_channel_id, ignorable), + (7, short_channel_id, option), (9, retry, option), }); Ok(Some(Event::PaymentPathFailed {