X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Frouting%2Frouter.rs;h=f547d9e4420ce27d14e34611e691aa66e79237ec;hb=91dc76721b4d6cd7b1ca812d59d70dd4c666ead9;hp=17641c3da669ca159c39af0fdd57f77436cb639f;hpb=cea78f585ab710e6494111420660ef11c2ea7db5;p=rust-lightning diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 17641c3d..f547d9e4 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -29,7 +29,7 @@ use crate::io; use crate::prelude::*; use crate::sync::Mutex; use alloc::collections::BinaryHeap; -use core::cmp; +use core::{cmp, fmt}; use core::ops::Deref; /// A [`Router`] implemented using [`find_route`]. @@ -493,17 +493,6 @@ const MAX_PATH_LENGTH_ESTIMATE: u8 = 19; /// Information used to route a payment. #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct PaymentParameters { - /// The node id of the payee. - pub 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, - /// Information about the payee, such as their features and route hints for their channels. pub payee: Payee, @@ -551,9 +540,9 @@ impl Writeable for PaymentParameters { Payee::Blinded { route_hints } => blinded_hints = route_hints, } write_tlv_fields!(writer, { - (0, self.payee_pubkey, required), + (0, self.payee.node_id(), option), (1, self.max_total_cltv_expiry_delta, required), - (2, self.features, option), + (2, if let Payee::Clear { features, .. } = &self.payee { features } else { &None }, option), (3, self.max_path_count, required), (4, *clear_hints, vec_type), (5, self.max_channel_saturation_power_of_half, required), @@ -569,7 +558,7 @@ impl Writeable for PaymentParameters { impl ReadableArgs for PaymentParameters { fn read(reader: &mut R, default_final_cltv_expiry_delta: u32) -> Result { _init_and_read_tlv_fields!(reader, { - (0, payee_pubkey, required), + (0, payee_pubkey, option), (1, max_total_cltv_expiry_delta, (default_value, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA)), (2, features, option), (3, max_path_count, (default_value, DEFAULT_MAX_PATH_COUNT)), @@ -583,15 +572,17 @@ impl ReadableArgs for PaymentParameters { let clear_route_hints = route_hints.unwrap_or(vec![]); let blinded_route_hints = blinded_route_hints.unwrap_or(vec![]); let payee = if blinded_route_hints.len() != 0 { - if clear_route_hints.len() != 0 { return Err(DecodeError::InvalidValue) } + if clear_route_hints.len() != 0 || payee_pubkey.is_some() { return Err(DecodeError::InvalidValue) } Payee::Blinded { route_hints: blinded_route_hints } } else { - Payee::Clear { route_hints: clear_route_hints } + Payee::Clear { + route_hints: clear_route_hints, + node_id: payee_pubkey.ok_or(DecodeError::InvalidValue)?, + features, + } }; Ok(Self { - payee_pubkey: _init_tlv_based_struct_field!(payee_pubkey, required), max_total_cltv_expiry_delta: _init_tlv_based_struct_field!(max_total_cltv_expiry_delta, (default_value, unused)), - features, max_path_count: _init_tlv_based_struct_field!(max_path_count, (default_value, unused)), payee, max_channel_saturation_power_of_half: _init_tlv_based_struct_field!(max_channel_saturation_power_of_half, (default_value, unused)), @@ -610,9 +601,7 @@ impl PaymentParameters { /// provided. pub fn from_node_id(payee_pubkey: PublicKey, final_cltv_expiry_delta: u32) -> Self { Self { - payee_pubkey, - features: None, - payee: Payee::Clear { route_hints: vec![] }, + payee: Payee::Clear { node_id: payee_pubkey, route_hints: vec![], features: None }, expiry_time: None, max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, max_path_count: DEFAULT_MAX_PATH_COUNT, @@ -627,14 +616,19 @@ impl PaymentParameters { /// The `final_cltv_expiry_delta` should match the expected final CLTV delta the recipient has /// provided. pub fn for_keysend(payee_pubkey: PublicKey, final_cltv_expiry_delta: u32) -> Self { - Self::from_node_id(payee_pubkey, final_cltv_expiry_delta).with_features(InvoiceFeatures::for_keysend()) + Self::from_node_id(payee_pubkey, final_cltv_expiry_delta).with_bolt11_features(InvoiceFeatures::for_keysend()).expect("PaymentParameters::from_node_id should always initialize the payee as unblinded") } - /// Includes the payee's features. + /// Includes the payee's features. Errors if the parameters were initialized with blinded payment + /// paths. /// /// This is not exported to bindings users since bindings don't support move semantics - pub fn with_features(self, features: InvoiceFeatures) -> Self { - Self { features: Some(features), ..self } + pub fn with_bolt11_features(self, features: InvoiceFeatures) -> Result { + match self.payee { + Payee::Blinded { .. } => Err(()), + Payee::Clear { route_hints, node_id, .. } => + Ok(Self { payee: Payee::Clear { route_hints, node_id, features: Some(features) }, ..self }) + } } /// Includes hints for routing to the payee. Errors if the parameters were initialized with @@ -644,8 +638,8 @@ impl PaymentParameters { pub fn with_route_hints(self, route_hints: Vec) -> Result { match self.payee { Payee::Blinded { .. } => Err(()), - Payee::Clear { .. } => - Ok(Self { payee: Payee::Clear { route_hints }, ..self }) + Payee::Clear { node_id, features, .. } => + Ok(Self { payee: Payee::Clear { route_hints, node_id, features }, ..self }) } } @@ -691,11 +685,41 @@ pub enum Payee { }, /// The recipient included these route hints in their BOLT11 invoice. Clear { + /// The node id of the payee. + node_id: PublicKey, /// Hints for routing to the payee, containing channels connecting the payee to public nodes. route_hints: Vec, + /// 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`]: PaymentParameters::for_keysend + features: Option, }, } +impl Payee { + fn node_id(&self) -> Option { + match self { + Self::Clear { node_id, .. } => Some(*node_id), + _ => None, + } + } + fn node_features(&self) -> Option { + match self { + Self::Clear { features, .. } => features.as_ref().map(|f| f.to_context()), + _ => None, + } + } + fn supports_basic_mpp(&self) -> bool { + match self { + Self::Clear { features, .. } => features.as_ref().map_or(false, |f| f.supports_basic_mpp()), + _ => false, + } + } +} + /// 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); @@ -1080,6 +1104,21 @@ fn default_node_features() -> NodeFeatures { features } +struct LoggedPayeePubkey(Option); +impl fmt::Display for LoggedPayeePubkey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + Some(pk) => { + "payee node id ".fmt(f)?; + pk.fmt(f) + }, + None => { + "blinded payee".fmt(f) + }, + } + } +} + /// Finds a route from us (payer) to the given target node (payee). /// /// If the payee provided features in their invoice, they should be provided via `params.payee`. @@ -1129,10 +1168,16 @@ pub(crate) fn get_route( _random_seed_bytes: &[u8; 32] ) -> Result where L::Target: Logger { - let payee_node_id = NodeId::from_pubkey(&payment_params.payee_pubkey); + // If we're routing to a blinded recipient, we won't have their node id. Therefore, keep the + // unblinded payee id as an option. We also need a non-optional "payee id" for path construction, + // so use a dummy id for this in the blinded case. + let payee_node_id_opt = payment_params.payee.node_id().map(|pk| NodeId::from_pubkey(&pk)); + const DUMMY_BLINDED_PAYEE_ID: [u8; 33] = [42u8; 33]; + let maybe_dummy_payee_pk = payment_params.payee.node_id().unwrap_or_else(|| PublicKey::from_slice(&DUMMY_BLINDED_PAYEE_ID).unwrap()); + let maybe_dummy_payee_node_id = NodeId::from_pubkey(&maybe_dummy_payee_pk); let our_node_id = NodeId::from_pubkey(&our_node_pubkey); - if payee_node_id == our_node_id { + if payee_node_id_opt.map_or(false, |payee| payee == our_node_id) { return Err(LightningError{err: "Cannot generate a route to ourselves".to_owned(), action: ErrorAction::IgnoreError}); } @@ -1145,10 +1190,10 @@ where L::Target: Logger { } match &payment_params.payee { - Payee::Clear { route_hints } => { + Payee::Clear { route_hints, node_id, .. } => { for route in route_hints.iter() { for hop in &route.0 { - if hop.src_node_id == payment_params.payee_pubkey { + if hop.src_node_id == *node_id { return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError}); } } @@ -1229,16 +1274,15 @@ where L::Target: Logger { // work reliably. let allow_mpp = if payment_params.max_path_count == 1 { false - } else if let Some(features) = &payment_params.features { - features.supports_basic_mpp() - } else if let Some(node) = network_nodes.get(&payee_node_id) { - if let Some(node_info) = node.announcement_info.as_ref() { - node_info.features.supports_basic_mpp() - } else { false } + } else if payment_params.payee.supports_basic_mpp() { + true + } else if let Some(payee) = payee_node_id_opt { + network_nodes.get(&payee).map_or(false, |node| node.announcement_info.as_ref().map_or(false, + |info| info.features.supports_basic_mpp())) } else { false }; - log_trace!(logger, "Searching for a route from payer {} to payee {} {} MPP and {} first hops {}overriding the network graph", our_node_pubkey, - payment_params.payee_pubkey, if allow_mpp { "with" } else { "without" }, + log_trace!(logger, "Searching for a route from payer {} to {} {} MPP and {} first hops {}overriding the network graph", our_node_pubkey, + LoggedPayeePubkey(payment_params.payee.node_id()), if allow_mpp { "with" } else { "without" }, first_hops.map(|hops| hops.len()).unwrap_or(0), if first_hops.is_some() { "" } else { "not " }); // Step (1). @@ -1341,7 +1385,8 @@ where L::Target: Logger { }); } - log_trace!(logger, "Building path from {} (payee) to {} (us/payer) for value {} msat.", payment_params.payee_pubkey, our_node_pubkey, final_value_msat); + log_trace!(logger, "Building path from {} to payer {} for value {} msat.", + LoggedPayeePubkey(payment_params.payee.node_id()), our_node_pubkey, final_value_msat); macro_rules! add_entry { // Adds entry which goes from $src_node_id to $dest_node_id over the $candidate hop. @@ -1590,7 +1635,7 @@ where L::Target: Logger { // Entries are added to dist in add_entry!() when there is a channel from a node. // Because there are no channels from payee, it will not have a dist entry at this point. // If we're processing any other node, it is always be the result of a channel from it. - assert_eq!($node_id, payee_node_id); + debug_assert_eq!($node_id, maybe_dummy_payee_node_id); false }; @@ -1650,35 +1695,35 @@ where L::Target: Logger { // If first hop is a private channel and the only way to reach the payee, this is the only // place where it could be added. - if let Some(first_channels) = first_hop_targets.get(&payee_node_id) { + payee_node_id_opt.map(|payee| first_hop_targets.get(&payee).map(|first_channels| { for details in first_channels { let candidate = CandidateRouteHop::FirstHop { details }; - let added = add_entry!(candidate, our_node_id, payee_node_id, 0, path_value_msat, + let added = add_entry!(candidate, our_node_id, payee, 0, path_value_msat, 0, 0u64, 0, 0); log_trace!(logger, "{} direct route to payee via SCID {}", if added { "Added" } else { "Skipped" }, candidate.short_channel_id()); } - } + })); // Add the payee as a target, so that the payee-to-payer // search algorithm knows what to start with. - match network_nodes.get(&payee_node_id) { + payee_node_id_opt.map(|payee| match network_nodes.get(&payee) { // The payee is not in our network graph, so nothing to add here. // There is still a chance of reaching them via last_hops though, // so don't yet fail the payment here. // If not, targets.pop() will not even let us enter the loop in step 2. None => {}, Some(node) => { - add_entries_to_cheapest_to_target_node!(node, payee_node_id, 0, path_value_msat, 0, 0u64, 0, 0); + add_entries_to_cheapest_to_target_node!(node, payee, 0, path_value_msat, 0, 0u64, 0, 0); }, - } + }); // Step (2). // 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. let route_hints = match &payment_params.payee { - Payee::Clear { route_hints } => route_hints, + Payee::Clear { route_hints, .. } => route_hints, _ => return Err(LightningError{err: "Routing to blinded paths isn't supported yet".to_owned(), action: ErrorAction::IgnoreError}), }; for route in route_hints.iter().filter(|route| !route.0.is_empty()) { @@ -1693,7 +1738,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(&payment_params.payee_pubkey).chain( + let prev_hop_iter = core::iter::once(&maybe_dummy_payee_pk).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; @@ -1853,7 +1898,7 @@ where L::Target: Logger { // save this path for the payment route. Also, update the liquidity // remaining on the used hops, so that we take them into account // while looking for more paths. - if ordered_hops.last().unwrap().0.node_id == payee_node_id { + if ordered_hops.last().unwrap().0.node_id == maybe_dummy_payee_node_id { break 'path_walk; } @@ -1936,7 +1981,7 @@ where L::Target: Logger { // If we found a path back to the payee, we shouldn't try to process it again. This is // the equivalent of the `elem.was_processed` check in // add_entries_to_cheapest_to_target_node!() (see comment there for more info). - if node_id == payee_node_id { continue 'path_construction; } + if node_id == maybe_dummy_payee_node_id { continue 'path_construction; } // Otherwise, since the current target node is not us, // keep "unrolling" the payment graph from payee to payer by @@ -2088,10 +2133,10 @@ where L::Target: Logger { // Make sure we would never create a route with more paths than we allow. debug_assert!(selected_paths.len() <= payment_params.max_path_count.into()); - if let Some(features) = &payment_params.features { + if let Some(node_features) = payment_params.payee.node_features() { for path in selected_paths.iter_mut() { if let Ok(route_hop) = path.last_mut().unwrap() { - route_hop.node_features = features.to_context(); + route_hop.node_features = node_features.clone(); } } } @@ -2106,7 +2151,7 @@ where L::Target: Logger { paths, payment_params: Some(payment_params.clone()), }; - log_info!(logger, "Got route to {}: {}", payment_params.payee_pubkey, log_route!(route)); + log_info!(logger, "Got route: {}", log_route!(route)); Ok(route) } @@ -2541,7 +2586,7 @@ mod tests { let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph(); let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config)); + let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); let scorer = ln_test_utils::TestScorer::new(); let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); @@ -3514,7 +3559,7 @@ mod tests { let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config)); + let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); // We will use a simple single-path route from // our node to node2 via node0: channels {1, 3}. @@ -3790,7 +3835,7 @@ mod tests { let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config)); + let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); // 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. @@ -3965,7 +4010,7 @@ mod tests { let random_seed_bytes = keys_manager.get_secure_random_bytes(); let config = UserConfig::default(); let payment_params = PaymentParameters::from_node_id(nodes[2], 42) - .with_features(channelmanager::provided_invoice_features(&config)); + .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); // We need a route consisting of 3 paths: // From our node to node2 via node0, node7, node1 (three paths one hop each). @@ -4124,7 +4169,7 @@ mod tests { let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config)); + let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); // We need a route consisting of 3 paths: // From our node to node3 via {node0, node2}, {node7, node2, node4} and {node7, node2}. @@ -4289,7 +4334,7 @@ mod tests { let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config)); + let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); // 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, @@ -4459,7 +4504,7 @@ mod tests { let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config)); + let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); // We need a route consisting of 2 paths: // From our node to node3 via {node0, node2} and {node7, node2, node4}. @@ -4641,7 +4686,7 @@ mod tests { let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap(), 42).with_features(channelmanager::provided_invoice_features(&config)) + let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap(), 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap() .with_route_hints(vec![RouteHint(vec![RouteHintHop { src_node_id: nodes[2], short_channel_id: 42, @@ -4733,7 +4778,7 @@ mod tests { let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config)) + let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap() .with_max_channel_saturation_power_of_half(0); // We need a route consisting of 3 paths: @@ -5089,7 +5134,7 @@ mod tests { let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config)); + let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); // 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 @@ -5157,7 +5202,7 @@ mod tests { let network_graph = NetworkGraph::new(Network::Testnet, Arc::clone(&logger)); let scorer = ln_test_utils::TestScorer::new(); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(nodes[0], 42).with_features(channelmanager::provided_invoice_features(&config)); + let payment_params = PaymentParameters::from_node_id(nodes[0], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); @@ -5651,7 +5696,7 @@ mod tests { }); let config = UserConfig::default(); - let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config)); + let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); // 100,000 sats is less than the available liquidity on each channel, set above. @@ -5736,7 +5781,7 @@ mod tests { let src = &PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); seed = seed.overflowing_mul(0xdeadbeef).0; let dst = PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); - let payment_params = PaymentParameters::from_node_id(dst, 42).with_features(channelmanager::provided_invoice_features(&config)); + let payment_params = PaymentParameters::from_node_id(dst, 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap(); let amt = seed as u64 % 200_000_000; let params = ProbabilisticScoringParameters::default(); let scorer = ProbabilisticScorer::new(params, &graph, &logger); @@ -6082,7 +6127,7 @@ mod benches { let src = PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); seed *= 0xdeadbeef; let dst = PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap(); - let params = PaymentParameters::from_node_id(dst, 42).with_features(features.clone()); + let params = PaymentParameters::from_node_id(dst, 42).with_bolt11_features(features.clone()).unwrap(); let first_hop = first_hop(src); let amt = seed as u64 % 1_000_000; if let Ok(route) = get_route(&payer, ¶ms, &graph.read_only(), Some(&[&first_hop]), amt, &DummyLogger{}, &scorer, &random_seed_bytes) {