From: Valentine Wallace Date: Sun, 9 Apr 2023 17:50:44 +0000 (-0400) Subject: Replace Vec with new Path struct X-Git-Tag: v0.0.115~5^2~9 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=d5b05e54c32cd53e0534849b57242c65747738b1;p=rust-lightning Replace Vec with new Path struct This lays groundwork for adding blinded path info to Path --- diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index c10b5c0e3..76305303a 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -50,7 +50,7 @@ use lightning::util::errors::APIError; use lightning::util::logger::Logger; use lightning::util::config::UserConfig; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; -use lightning::routing::router::{InFlightHtlcs, Route, RouteHop, RouteParameters, Router}; +use lightning::routing::router::{InFlightHtlcs, Path, Route, RouteHop, RouteParameters, Router}; use crate::utils::test_logger::{self, Output}; use crate::utils::test_persister::TestPersister; @@ -352,14 +352,14 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p payment_id[0..8].copy_from_slice(&payment_idx.to_ne_bytes()); *payment_idx += 1; if let Err(err) = source.send_payment_with_route(&Route { - paths: vec![vec![RouteHop { + paths: vec![Path { hops: vec![RouteHop { pubkey: dest.get_our_node_id(), node_features: dest.node_features(), short_channel_id: dest_chan_id, channel_features: dest.channel_features(), fee_msat: amt, cltv_expiry_delta: 200, - }]], + }]}], payment_params: None, }, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) { check_payment_err(err); @@ -374,7 +374,7 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des payment_id[0..8].copy_from_slice(&payment_idx.to_ne_bytes()); *payment_idx += 1; if let Err(err) = source.send_payment_with_route(&Route { - paths: vec![vec![RouteHop { + paths: vec![Path { hops: vec![RouteHop { pubkey: middle.get_our_node_id(), node_features: middle.node_features(), short_channel_id: middle_chan_id, @@ -388,7 +388,7 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des channel_features: dest.channel_features(), fee_msat: amt, cltv_expiry_delta: 200, - }]], + }]}], payment_params: None, }, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) { check_payment_err(err); diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index 7d705bdcc..18ed6da06 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -236,26 +236,21 @@ fn update_scorer<'a, S: 'static + Deref + Send + Sync, SC: 'a + Wri let mut score = scorer.lock(); match event { Event::PaymentPathFailed { ref path, short_channel_id: Some(scid), .. } => { - let path = path.iter().collect::>(); - score.payment_path_failed(&path, *scid); + score.payment_path_failed(path, *scid); }, Event::PaymentPathFailed { ref path, payment_failed_permanently: true, .. } => { // Reached if the destination explicitly failed it back. We treat this as a successful probe // because the payment made it all the way to the destination with sufficient liquidity. - let path = path.iter().collect::>(); - score.probe_successful(&path); + score.probe_successful(path); }, Event::PaymentPathSuccessful { path, .. } => { - let path = path.iter().collect::>(); - score.payment_path_successful(&path); + score.payment_path_successful(path); }, Event::ProbeSuccessful { path, .. } => { - let path = path.iter().collect::>(); - score.probe_successful(&path); + score.probe_successful(path); }, Event::ProbeFailed { path, short_channel_id: Some(scid), .. } => { - let path = path.iter().collect::>(); - score.probe_failed(&path, *scid); + score.probe_failed(path, *scid); }, _ => {}, } @@ -751,7 +746,7 @@ mod tests { use lightning::ln::msgs::{ChannelMessageHandler, Init}; use lightning::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler}; use lightning::routing::gossip::{NetworkGraph, NodeId, P2PGossipSync}; - use lightning::routing::router::{DefaultRouter, RouteHop}; + use lightning::routing::router::{DefaultRouter, Path, RouteHop}; use lightning::routing::scoring::{ChannelUsage, Score}; use lightning::util::config::UserConfig; use lightning::util::ser::Writeable; @@ -891,10 +886,10 @@ mod tests { #[derive(Debug)] enum TestResult { - PaymentFailure { path: Vec, short_channel_id: u64 }, - PaymentSuccess { path: Vec }, - ProbeFailure { path: Vec }, - ProbeSuccess { path: Vec }, + PaymentFailure { path: Path, short_channel_id: u64 }, + PaymentSuccess { path: Path }, + ProbeFailure { path: Path }, + ProbeSuccess { path: Path }, } impl TestScorer { @@ -916,11 +911,11 @@ mod tests { &self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId, _usage: ChannelUsage ) -> u64 { unimplemented!(); } - fn payment_path_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) { + fn payment_path_failed(&mut self, actual_path: &Path, actual_short_channel_id: u64) { if let Some(expectations) = &mut self.event_expectations { match expectations.pop_front().unwrap() { TestResult::PaymentFailure { path, short_channel_id } => { - assert_eq!(actual_path, &path.iter().collect::>()[..]); + assert_eq!(actual_path, &path); assert_eq!(actual_short_channel_id, short_channel_id); }, TestResult::PaymentSuccess { path } => { @@ -936,14 +931,14 @@ mod tests { } } - fn payment_path_successful(&mut self, actual_path: &[&RouteHop]) { + fn payment_path_successful(&mut self, actual_path: &Path) { if let Some(expectations) = &mut self.event_expectations { match expectations.pop_front().unwrap() { TestResult::PaymentFailure { path, .. } => { panic!("Unexpected payment path failure: {:?}", path) }, TestResult::PaymentSuccess { path } => { - assert_eq!(actual_path, &path.iter().collect::>()[..]); + assert_eq!(actual_path, &path); }, TestResult::ProbeFailure { path } => { panic!("Unexpected probe failure: {:?}", path) @@ -955,7 +950,7 @@ mod tests { } } - fn probe_failed(&mut self, actual_path: &[&RouteHop], _: u64) { + fn probe_failed(&mut self, actual_path: &Path, _: u64) { if let Some(expectations) = &mut self.event_expectations { match expectations.pop_front().unwrap() { TestResult::PaymentFailure { path, .. } => { @@ -965,7 +960,7 @@ mod tests { panic!("Unexpected payment path success: {:?}", path) }, TestResult::ProbeFailure { path } => { - assert_eq!(actual_path, &path.iter().collect::>()[..]); + assert_eq!(actual_path, &path); }, TestResult::ProbeSuccess { path } => { panic!("Unexpected probe success: {:?}", path) @@ -973,7 +968,7 @@ mod tests { } } } - fn probe_successful(&mut self, actual_path: &[&RouteHop]) { + fn probe_successful(&mut self, actual_path: &Path) { if let Some(expectations) = &mut self.event_expectations { match expectations.pop_front().unwrap() { TestResult::PaymentFailure { path, .. } => { @@ -986,7 +981,7 @@ mod tests { panic!("Unexpected probe failure: {:?}", path) }, TestResult::ProbeSuccess { path } => { - assert_eq!(actual_path, &path.iter().collect::>()[..]); + assert_eq!(actual_path, &path); } } } @@ -1510,14 +1505,14 @@ mod tests { let node_1_privkey = SecretKey::from_slice(&[42; 32]).unwrap(); let node_1_id = PublicKey::from_secret_key(&secp_ctx, &node_1_privkey); - let path = vec![RouteHop { + let path = Path { hops: vec![RouteHop { pubkey: node_1_id, node_features: NodeFeatures::empty(), short_channel_id: scored_scid, channel_features: ChannelFeatures::empty(), fee_msat: 0, cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA as u32, - }]; + }]}; $nodes[0].scorer.lock().unwrap().expect(TestResult::PaymentFailure { path: path.clone(), short_channel_id: scored_scid }); $nodes[0].node.push_pending_event(Event::PaymentPathFailed { diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index 1bd121459..dc066b1e7 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -30,7 +30,7 @@ use crate::routing::gossip::NetworkUpdate; use crate::util::errors::APIError; use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReadable, Readable, RequiredWrapper, UpgradableRequired, WithoutLength}; use crate::util::string::UntrustedString; -use crate::routing::router::{RouteHop, RouteParameters}; +use crate::routing::router::{Path, RouteHop, RouteParameters}; use bitcoin::{PackedLockTime, Transaction, OutPoint}; #[cfg(anchors)] @@ -503,7 +503,7 @@ pub enum Event { /// The payment path that was successful. /// /// May contain a closed channel if the HTLC sent along the path was fulfilled on chain. - path: Vec, + path: Path, }, /// Indicates an outbound HTLC we sent failed, likely due to an intermediary node being unable to /// handle the HTLC. @@ -535,7 +535,7 @@ pub enum Event { /// [`NetworkGraph`]: crate::routing::gossip::NetworkGraph failure: PathFailure, /// The payment path that failed. - path: Vec, + path: Path, /// The channel responsible for the failed payment path. /// /// Note that for route hints or for the first hop in a path this may be an SCID alias and @@ -561,7 +561,7 @@ pub enum Event { /// [`ChannelManager::send_probe`]: crate::ln::channelmanager::ChannelManager::send_probe payment_hash: PaymentHash, /// The payment path that was successful. - path: Vec, + path: Path, }, /// Indicates that a probe payment we sent failed at an intermediary node on the path. ProbeFailed { @@ -574,7 +574,7 @@ pub enum Event { /// [`ChannelManager::send_probe`]: crate::ln::channelmanager::ChannelManager::send_probe payment_hash: PaymentHash, /// The payment path that failed. - path: Vec, + path: Path, /// The channel responsible for the failed probe. /// /// Note that for route hints or for the first hop in a path this may be an SCID alias and @@ -884,7 +884,7 @@ impl Writeable for Event { (1, None::, option), // network_update in LDK versions prior to 0.0.114 (2, payment_failed_permanently, required), (3, false, required), // all_paths_failed in LDK versions prior to 0.0.114 - (5, *path, vec_type), + (5, path.hops, vec_type), (7, short_channel_id, option), (9, None::, option), // retry in LDK versions prior to 0.0.115 (11, payment_id, option), @@ -952,7 +952,7 @@ impl Writeable for Event { write_tlv_fields!(writer, { (0, payment_id, required), (2, payment_hash, option), - (4, *path, vec_type) + (4, path.hops, vec_type), }) }, &Event::PaymentFailed { ref payment_id, ref payment_hash, ref reason } => { @@ -982,7 +982,7 @@ impl Writeable for Event { write_tlv_fields!(writer, { (0, payment_id, required), (2, payment_hash, required), - (4, *path, vec_type) + (4, path.hops, vec_type), }) }, &Event::ProbeFailed { ref payment_id, ref payment_hash, ref path, ref short_channel_id } => { @@ -990,7 +990,7 @@ impl Writeable for Event { write_tlv_fields!(writer, { (0, payment_id, required), (2, payment_hash, required), - (4, *path, vec_type), + (4, path.hops, vec_type), (6, short_channel_id, option), }) }, @@ -1141,7 +1141,7 @@ impl MaybeReadable for Event { payment_hash, payment_failed_permanently, failure, - path: path.unwrap(), + path: Path { hops: path.unwrap() }, short_channel_id, #[cfg(test)] error_code, @@ -1255,7 +1255,7 @@ impl MaybeReadable for Event { Ok(Some(Event::PaymentPathSuccessful { payment_id, payment_hash, - path: path.unwrap(), + path: Path { hops: path.unwrap() }, })) }; f() @@ -1316,7 +1316,7 @@ impl MaybeReadable for Event { Ok(Some(Event::ProbeSuccessful { payment_id, payment_hash, - path: path.unwrap(), + path: Path { hops: path.unwrap() }, })) }; f() @@ -1336,7 +1336,7 @@ impl MaybeReadable for Event { Ok(Some(Event::ProbeFailed { payment_id, payment_hash, - path: path.unwrap(), + path: Path { hops: path.unwrap() }, short_channel_id, })) }; diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs index 01bb7dde4..3f210cb46 100644 --- a/lightning/src/ln/chanmon_update_fail_tests.rs +++ b/lightning/src/ln/chanmon_update_fail_tests.rs @@ -2000,12 +2000,12 @@ fn test_path_paused_mpp() { // Set us up to take multiple routes, one 0 -> 1 -> 3 and one 0 -> 2 -> 3: let path = route.paths[0].clone(); route.paths.push(path); - route.paths[0][0].pubkey = nodes[1].node.get_our_node_id(); - route.paths[0][0].short_channel_id = chan_1_id; - route.paths[0][1].short_channel_id = chan_3_id; - route.paths[1][0].pubkey = nodes[2].node.get_our_node_id(); - route.paths[1][0].short_channel_id = chan_2_ann.contents.short_channel_id; - route.paths[1][1].short_channel_id = chan_4_id; + route.paths[0].hops[0].pubkey = nodes[1].node.get_our_node_id(); + route.paths[0].hops[0].short_channel_id = chan_1_id; + route.paths[0].hops[1].short_channel_id = chan_3_id; + route.paths[1].hops[0].pubkey = nodes[2].node.get_our_node_id(); + route.paths[1].hops[0].short_channel_id = chan_2_ann.contents.short_channel_id; + route.paths[1].hops[1].short_channel_id = chan_4_id; // Set it so that the first monitor update (for the path 0 -> 1 -> 3) succeeds, but the second // (for the path 0 -> 2 -> 3) fails. diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index c67b7f695..79711080c 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -7024,6 +7024,7 @@ mod tests { use crate::chain::chaininterface::{FeeEstimator, LowerBoundedFeeEstimator, ConfirmationTarget}; use crate::chain::keysinterface::{ChannelSigner, InMemorySigner, EntropySource, SignerProvider}; use crate::chain::transaction::OutPoint; + use crate::routing::router::Path; use crate::util::config::UserConfig; use crate::util::enforcing_trait_impls::EnforcingSigner; use crate::util::errors::APIError; @@ -7201,7 +7202,7 @@ mod tests { cltv_expiry: 200000000, state: OutboundHTLCState::Committed, source: HTLCSource::OutboundRoute { - path: Vec::new(), + path: Path { hops: Vec::new() }, session_priv: SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(), first_hop_htlc_msat: 548, payment_id: PaymentId([42; 32]), diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index a3a2f80e3..5f77c43cc 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -45,7 +45,7 @@ use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, No #[cfg(any(feature = "_test_utils", test))] use crate::ln::features::InvoiceFeatures; use crate::routing::gossip::NetworkGraph; -use crate::routing::router::{DefaultRouter, InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters, RoutePath, Router}; +use crate::routing::router::{DefaultRouter, InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters, Router}; use crate::routing::scoring::ProbabilisticScorer; use crate::ln::msgs; use crate::ln::onion_utils; @@ -282,7 +282,7 @@ impl_writeable_tlv_based_enum!(SentHTLCId, pub(crate) enum HTLCSource { PreviousHopData(HTLCPreviousHopData), OutboundRoute { - path: Vec, + path: Path, session_priv: SecretKey, /// Technically we can recalculate this from the route, but we cache it here to avoid /// doing a double-pass on route when we get a failure back @@ -313,7 +313,7 @@ impl HTLCSource { #[cfg(test)] pub fn dummy() -> Self { HTLCSource::OutboundRoute { - path: Vec::new(), + path: Path { hops: Vec::new(), blinded_tail: None }, session_priv: SecretKey::from_slice(&[1; 32]).unwrap(), first_hop_htlc_msat: 0, payment_id: PaymentId([2; 32]), @@ -2639,16 +2639,16 @@ where } #[cfg(test)] - pub(crate) fn test_send_payment_along_path(&self, path: &Vec, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option, session_priv_bytes: [u8; 32]) -> Result<(), APIError> { + pub(crate) fn test_send_payment_along_path(&self, path: &Path, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option, session_priv_bytes: [u8; 32]) -> Result<(), APIError> { let _lck = self.total_consistency_lock.read().unwrap(); self.send_payment_along_path(path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage, session_priv_bytes) } - fn send_payment_along_path(&self, path: &Vec, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option, session_priv_bytes: [u8; 32]) -> Result<(), APIError> { + fn send_payment_along_path(&self, path: &Path, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option, session_priv_bytes: [u8; 32]) -> Result<(), APIError> { // The top-level caller should hold the total_consistency_lock read lock. debug_assert!(self.total_consistency_lock.try_write().is_err()); - log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id); + log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.hops.first().unwrap().short_channel_id); let prng_seed = self.entropy_source.get_secure_random_bytes(); let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted"); @@ -2661,7 +2661,7 @@ where let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash); let err: Result<(), _> = loop { - let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.first().unwrap().short_channel_id) { + let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.hops.first().unwrap().short_channel_id) { None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()}), Some((cp_id, chan_id)) => (cp_id.clone(), chan_id.clone()), }; @@ -2712,7 +2712,7 @@ where return Ok(()); }; - match handle_error!(self, err, path.first().unwrap().pubkey) { + match handle_error!(self, err, path.hops.first().unwrap().pubkey) { Ok(_) => unreachable!(), Err(e) => { Err(APIError::ChannelUnavailable { err: e.err }) @@ -2882,10 +2882,10 @@ where /// Send a payment that is probing the given route for liquidity. We calculate the /// [`PaymentHash`] of probes based on a static secret and a random [`PaymentId`], which allows /// us to easily discern them from real payments. - pub fn send_probe(&self, hops: Vec) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> { + pub fn send_probe(&self, path: Path) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> { let best_block_height = self.best_block.read().unwrap().height(); let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier); - self.pending_outbound_payments.send_probe(hops, self.probing_cookie_secret, &self.entropy_source, &self.node_signer, best_block_height, + self.pending_outbound_payments.send_probe(path, self.probing_cookie_secret, &self.entropy_source, &self.node_signer, best_block_height, |path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage, session_priv| self.send_payment_along_path(path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage, session_priv)) } @@ -7004,14 +7004,14 @@ impl Readable for HTLCSource { 0 => { let mut session_priv: crate::util::ser::RequiredWrapper = crate::util::ser::RequiredWrapper(None); let mut first_hop_htlc_msat: u64 = 0; - let mut path: Option> = Some(Vec::new()); + let mut path_hops: Option> = Some(Vec::new()); let mut payment_id = None; let mut payment_params: Option = None; read_tlv_fields!(reader, { (0, session_priv, required), (1, payment_id, option), (2, first_hop_htlc_msat, required), - (4, path, vec_type), + (4, path_hops, vec_type), (5, payment_params, (option: ReadableArgs, 0)), }); if payment_id.is_none() { @@ -7019,10 +7019,10 @@ impl Readable for HTLCSource { // instead. payment_id = Some(PaymentId(*session_priv.0.unwrap().as_ref())); } - if path.is_none() || path.as_ref().unwrap().is_empty() { + let path = Path { hops: path_hops.ok_or(DecodeError::InvalidValue)? }; + if path.hops.len() == 0 { return Err(DecodeError::InvalidValue); } - let path = path.unwrap(); if let Some(params) = payment_params.as_mut() { if params.final_cltv_expiry_delta == 0 { params.final_cltv_expiry_delta = path.final_cltv_expiry_delta(); @@ -7052,7 +7052,7 @@ impl Writeable for HTLCSource { (1, payment_id_opt, option), (2, first_hop_htlc_msat, required), // 3 was previously used to write a PaymentSecret for the payment. - (4, *path, vec_type), + (4, path.hops, vec_type), (5, None::, option), // payment_params in LDK versions prior to 0.0.115 }); } @@ -7720,7 +7720,7 @@ where if id_to_peer.get(&monitor.get_funding_txo().0.to_channel_id()).is_none() { for (htlc_source, (htlc, _)) in monitor.get_pending_or_resolved_outbound_htlcs() { if let HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } = htlc_source { - if path.is_empty() { + if path.hops.is_empty() { log_error!(args.logger, "Got an empty path for a pending payment"); return Err(DecodeError::InvalidValue); } @@ -8482,12 +8482,12 @@ mod tests { let (mut route, payment_hash, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[3], 100000); let path = route.paths[0].clone(); route.paths.push(path); - route.paths[0][0].pubkey = nodes[1].node.get_our_node_id(); - route.paths[0][0].short_channel_id = chan_1_id; - route.paths[0][1].short_channel_id = chan_3_id; - route.paths[1][0].pubkey = nodes[2].node.get_our_node_id(); - route.paths[1][0].short_channel_id = chan_2_id; - route.paths[1][1].short_channel_id = chan_4_id; + route.paths[0].hops[0].pubkey = nodes[1].node.get_our_node_id(); + route.paths[0].hops[0].short_channel_id = chan_1_id; + route.paths[0].hops[1].short_channel_id = chan_3_id; + route.paths[1].hops[0].pubkey = nodes[2].node.get_our_node_id(); + route.paths[1].hops[0].short_channel_id = chan_2_id; + route.paths[1].hops[1].short_channel_id = chan_4_id; match nodes[0].node.send_payment_with_route(&route, payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)) diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 6d9687762..cff4eccd3 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -2260,8 +2260,8 @@ pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: .with_features(expected_route.last().unwrap().node.invoice_features()); let route = get_route(origin_node, &payment_params, recv_value, TEST_FINAL_CLTV).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()) { + assert_eq!(route.paths[0].hops.len(), expected_route.len()); + for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) { assert_eq!(hop.pubkey, node.node.get_our_node_id()); } @@ -2281,8 +2281,8 @@ pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_rou &origin_node.node.get_our_node_id(), &payment_params, &network_graph, None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer, &random_seed_bytes).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()) { + assert_eq!(route.paths[0].hops.len(), expected_route.len()); + for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) { assert_eq!(hop.pubkey, node.node.get_our_node_id()); } @@ -2388,7 +2388,7 @@ pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe assert_eq!(payment_hash, our_payment_hash); assert!(payment_failed_permanently); for (idx, hop) in expected_route.iter().enumerate() { - assert_eq!(hop.node.get_our_node_id(), path[idx].pubkey); + assert_eq!(hop.node.get_our_node_id(), path.hops[idx].pubkey); } payment_id.unwrap() }, diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index f2b05b6e1..65ad811d8 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -26,7 +26,7 @@ use crate::ln::channel::{Channel, ChannelError}; use crate::ln::{chan_utils, onion_utils}; use crate::ln::chan_utils::{OFFERED_HTLC_SCRIPT_WEIGHT, htlc_success_tx_weight, htlc_timeout_tx_weight, HTLCOutputInCommitment}; use crate::routing::gossip::{NetworkGraph, NetworkUpdate}; -use crate::routing::router::{PaymentParameters, Route, RouteHop, RouteParameters, find_route, get_route}; +use crate::routing::router::{Path, PaymentParameters, Route, RouteHop, RouteParameters, find_route, get_route}; use crate::ln::features::{ChannelFeatures, NodeFeatures}; use crate::ln::msgs; use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction}; @@ -1044,7 +1044,7 @@ fn fake_network_test() { }); hops[1].fee_msat = chan_4.1.contents.fee_base_msat as u64 + chan_4.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000; hops[0].fee_msat = chan_3.0.contents.fee_base_msat as u64 + chan_3.0.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000; - let payment_preimage_1 = send_along_route(&nodes[1], Route { paths: vec![hops], payment_params: None }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0; + let payment_preimage_1 = send_along_route(&nodes[1], Route { paths: vec![Path { hops }], payment_params: None }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0; let mut hops = Vec::with_capacity(3); hops.push(RouteHop { @@ -1073,7 +1073,7 @@ fn fake_network_test() { }); hops[1].fee_msat = chan_2.1.contents.fee_base_msat as u64 + chan_2.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000; hops[0].fee_msat = chan_3.1.contents.fee_base_msat as u64 + chan_3.1.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000; - let payment_hash_2 = send_along_route(&nodes[1], Route { paths: vec![hops], payment_params: None }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1; + let payment_hash_2 = send_along_route(&nodes[1], Route { paths: vec![Path { hops }], payment_params: None }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1; // Claim the rebalances... fail_payment(&nodes[1], &vec!(&nodes[3], &nodes[2], &nodes[1])[..], payment_hash_2); @@ -1831,8 +1831,8 @@ fn test_channel_reserve_holding_cell_htlcs() { let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV) .with_features(nodes[2].node.invoice_features()).with_max_channel_saturation_power_of_half(0); let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, recv_value_0, TEST_FINAL_CLTV); - route.paths[0].last_mut().unwrap().fee_msat += 1; - assert!(route.paths[0].iter().rev().skip(1).all(|h| h.fee_msat == feemsat)); + route.paths[0].hops.last_mut().unwrap().fee_msat += 1; + assert!(route.paths[0].hops.iter().rev().skip(1).all(|h| h.fee_msat == feemsat)); unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash, RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0) @@ -5749,7 +5749,7 @@ fn test_fail_holding_cell_htlc_upon_free() { assert_eq!(PaymentId(our_payment_hash.0), *payment_id.as_ref().unwrap()); assert_eq!(our_payment_hash.clone(), *payment_hash); assert_eq!(*payment_failed_permanently, false); - assert_eq!(*short_channel_id, Some(route.paths[0][0].short_channel_id)); + assert_eq!(*short_channel_id, Some(route.paths[0].hops[0].short_channel_id)); }, _ => panic!("Unexpected event"), } @@ -5840,7 +5840,7 @@ fn test_free_and_fail_holding_cell_htlcs() { assert_eq!(payment_id_2, *payment_id.as_ref().unwrap()); assert_eq!(payment_hash_2.clone(), *payment_hash); assert_eq!(*payment_failed_permanently, false); - assert_eq!(*short_channel_id, Some(route_2.paths[0][0].short_channel_id)); + assert_eq!(*short_channel_id, Some(route_2.paths[0].hops[0].short_channel_id)); }, _ => panic!("Unexpected event"), } @@ -6037,7 +6037,7 @@ fn test_update_add_htlc_bolt2_sender_value_below_minimum_msat() { let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000); let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 100000); - route.paths[0][0].fee_msat = 100; + route.paths[0].hops[0].fee_msat = 100; unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash, RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0) @@ -6057,7 +6057,7 @@ fn test_update_add_htlc_bolt2_sender_zero_value_msat() { let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000); let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 100000); - route.paths[0][0].fee_msat = 0; + route.paths[0].hops[0].fee_msat = 0; unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash, RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0)), true, APIError::ChannelUnavailable { ref err }, @@ -6103,7 +6103,7 @@ fn test_update_add_htlc_bolt2_sender_cltv_expiry_too_high() { let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 0) .with_features(nodes[1].node.invoice_features()); let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, 100000000, 0); - route.paths[0].last_mut().unwrap().cltv_expiry_delta = 500000001; + route.paths[0].hops.last_mut().unwrap().cltv_expiry_delta = 500000001; unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash, RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0) ), true, APIError::InvalidRoute { ref err }, @@ -6172,7 +6172,7 @@ fn test_update_add_htlc_bolt2_sender_exceed_max_htlc_value_in_flight() { let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], max_in_flight); // Manually create a route over our max in flight (which our router normally automatically // limits us to. - route.paths[0][0].fee_msat = max_in_flight + 1; + route.paths[0].hops[0].fee_msat = max_in_flight + 1; unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash, RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0) ), true, APIError::ChannelUnavailable { ref err }, @@ -7671,7 +7671,7 @@ fn test_pending_claimed_htlc_no_balance_underflow() { // almost-claimed HTLC as available balance. let (mut route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[1], 10_000); route.payment_params = None; // This is all wrong, but unnecessary - route.paths[0][0].pubkey = nodes[0].node.get_our_node_id(); + route.paths[0].hops[0].pubkey = nodes[0].node.get_our_node_id(); let (_, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[0]); nodes[1].node.send_payment_with_route(&route, payment_hash_2, RecipientOnionFields::secret_only(payment_secret_2), PaymentId(payment_hash_2.0)).unwrap(); @@ -8043,19 +8043,19 @@ fn test_onion_value_mpp_set_calculation() { let sample_path = route.paths.pop().unwrap(); let mut path_1 = sample_path.clone(); - path_1[0].pubkey = nodes[1].node.get_our_node_id(); - path_1[0].short_channel_id = chan_1_id; - path_1[1].pubkey = nodes[3].node.get_our_node_id(); - path_1[1].short_channel_id = chan_3_id; - path_1[1].fee_msat = 100_000; + path_1.hops[0].pubkey = nodes[1].node.get_our_node_id(); + path_1.hops[0].short_channel_id = chan_1_id; + path_1.hops[1].pubkey = nodes[3].node.get_our_node_id(); + path_1.hops[1].short_channel_id = chan_3_id; + path_1.hops[1].fee_msat = 100_000; route.paths.push(path_1); let mut path_2 = sample_path.clone(); - path_2[0].pubkey = nodes[2].node.get_our_node_id(); - path_2[0].short_channel_id = chan_2_id; - path_2[1].pubkey = nodes[3].node.get_our_node_id(); - path_2[1].short_channel_id = chan_4_id; - path_2[1].fee_msat = 1_000; + path_2.hops[0].pubkey = nodes[2].node.get_our_node_id(); + path_2.hops[0].short_channel_id = chan_2_id; + path_2.hops[1].pubkey = nodes[3].node.get_our_node_id(); + path_2.hops[1].short_channel_id = chan_4_id; + path_2.hops[1].fee_msat = 1_000; route.paths.push(path_2); // Send payment @@ -8152,11 +8152,11 @@ fn do_test_overshoot_mpp(msat_amounts: &[u64], total_msat: u64) { for i in 0..routing_node_count { let routing_node = 2 + i; let mut path = sample_path.clone(); - path[0].pubkey = nodes[routing_node].node.get_our_node_id(); - path[0].short_channel_id = src_chan_ids[i]; - path[1].pubkey = nodes[dst_idx].node.get_our_node_id(); - path[1].short_channel_id = dst_chan_ids[i]; - path[1].fee_msat = msat_amounts[i]; + path.hops[0].pubkey = nodes[routing_node].node.get_our_node_id(); + path.hops[0].short_channel_id = src_chan_ids[i]; + path.hops[1].pubkey = nodes[dst_idx].node.get_our_node_id(); + path.hops[1].short_channel_id = dst_chan_ids[i]; + path.hops[1].fee_msat = msat_amounts[i]; route.paths.push(path); } @@ -8205,12 +8205,12 @@ fn test_simple_mpp() { let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(&nodes[0], nodes[3], 100000); let path = route.paths[0].clone(); route.paths.push(path); - route.paths[0][0].pubkey = nodes[1].node.get_our_node_id(); - route.paths[0][0].short_channel_id = chan_1_id; - route.paths[0][1].short_channel_id = chan_3_id; - route.paths[1][0].pubkey = nodes[2].node.get_our_node_id(); - route.paths[1][0].short_channel_id = chan_2_id; - route.paths[1][1].short_channel_id = chan_4_id; + route.paths[0].hops[0].pubkey = nodes[1].node.get_our_node_id(); + route.paths[0].hops[0].short_channel_id = chan_1_id; + route.paths[0].hops[1].short_channel_id = chan_3_id; + route.paths[1].hops[0].pubkey = nodes[2].node.get_our_node_id(); + route.paths[1].hops[0].short_channel_id = chan_2_id; + route.paths[1].hops[1].short_channel_id = chan_4_id; send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], 200_000, payment_hash, payment_secret); claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage); } @@ -9404,7 +9404,7 @@ fn test_inconsistent_mpp_params() { assert_eq!(route.paths.len(), 2); route.paths.sort_by(|path_a, _| { // Sort the path so that the path through nodes[1] comes first - if path_a[0].pubkey == nodes[1].node.get_our_node_id() { + if path_a.hops[0].pubkey == nodes[1].node.get_our_node_id() { core::cmp::Ordering::Less } else { core::cmp::Ordering::Greater } }); @@ -9580,7 +9580,7 @@ fn test_double_partial_claim() { assert_eq!(route.paths.len(), 2); route.paths.sort_by(|path_a, _| { // Sort the path so that the path through nodes[1] comes first - if path_a[0].pubkey == nodes[1].node.get_our_node_id() { + if path_a.hops[0].pubkey == nodes[1].node.get_our_node_id() { core::cmp::Ordering::Less } else { core::cmp::Ordering::Greater } }); diff --git a/lightning/src/ln/onion_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index aa3b3b7e8..d70016cf4 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -391,7 +391,7 @@ fn test_onion_failure() { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), NODE|2, &[0;0]); - }, ||{}, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: false}), Some(route.paths[0][0].short_channel_id)); + }, ||{}, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: false}), Some(route.paths[0].hops[0].short_channel_id)); // final node failure run_onion_failure_test_with_fail_intercept("temporary_node_failure", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| { @@ -401,7 +401,7 @@ fn test_onion_failure() { msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), NODE|2, &[0;0]); }, ||{ nodes[2].node.fail_htlc_backwards(&payment_hash); - }, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: false}), Some(route.paths[0][1].short_channel_id)); + }, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[1].pubkey, is_permanent: false}), Some(route.paths[0].hops[1].short_channel_id)); let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]); // intermediate node failure @@ -411,7 +411,7 @@ fn test_onion_failure() { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|2, &[0;0]); - }, ||{}, true, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}), Some(route.paths[0][0].short_channel_id)); + }, ||{}, true, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: true}), Some(route.paths[0].hops[0].short_channel_id)); // final node failure run_onion_failure_test_with_fail_intercept("permanent_node_failure", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| { @@ -420,7 +420,7 @@ fn test_onion_failure() { msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|2, &[0;0]); }, ||{ nodes[2].node.fail_htlc_backwards(&payment_hash); - }, false, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}), Some(route.paths[0][1].short_channel_id)); + }, false, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[1].pubkey, is_permanent: true}), Some(route.paths[0].hops[1].short_channel_id)); let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]); // intermediate node failure @@ -432,7 +432,7 @@ fn test_onion_failure() { msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|3, &[0;0]); }, ||{ nodes[2].node.fail_htlc_backwards(&payment_hash); - }, true, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}), Some(route.paths[0][0].short_channel_id)); + }, true, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: true}), Some(route.paths[0].hops[0].short_channel_id)); // final node failure run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| { @@ -441,7 +441,7 @@ fn test_onion_failure() { msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|3, &[0;0]); }, ||{ nodes[2].node.fail_htlc_backwards(&payment_hash); - }, false, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}), Some(route.paths[0][1].short_channel_id)); + }, false, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[1].pubkey, is_permanent: true}), Some(route.paths[0].hops[1].short_channel_id)); let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]); // Our immediate peer sent UpdateFailMalformedHTLC because it couldn't understand the onion in @@ -502,8 +502,8 @@ fn test_onion_failure() { }, ||{}, true, Some(PERM|9), Some(NetworkUpdate::ChannelFailure{short_channel_id, is_permanent: true}), Some(short_channel_id)); let mut bogus_route = route.clone(); - bogus_route.paths[0][1].short_channel_id -= 1; - let short_channel_id = bogus_route.paths[0][1].short_channel_id; + bogus_route.paths[0].hops[1].short_channel_id -= 1; + let short_channel_id = bogus_route.paths[0].hops[1].short_channel_id; run_onion_failure_test("unknown_next_peer", 0, &nodes, &bogus_route, &payment_hash, &payment_secret, |_| {}, ||{}, true, Some(PERM|10), Some(NetworkUpdate::ChannelFailure{short_channel_id, is_permanent:true}), Some(short_channel_id)); @@ -512,8 +512,8 @@ fn test_onion_failure() { .unwrap().lock().unwrap().channel_by_id.get(&channels[1].2).unwrap() .get_counterparty_htlc_minimum_msat() - 1; let mut bogus_route = route.clone(); - let route_len = bogus_route.paths[0].len(); - bogus_route.paths[0][route_len-1].fee_msat = amt_to_forward; + let route_len = bogus_route.paths[0].hops.len(); + bogus_route.paths[0].hops[route_len-1].fee_msat = amt_to_forward; run_onion_failure_test("amount_below_minimum", 0, &nodes, &bogus_route, &payment_hash, &payment_secret, |_| {}, ||{}, true, Some(UPDATE|11), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy(short_channel_id)}), Some(short_channel_id)); // Clear pending payments so that the following positive test has the correct payment hash. @@ -522,7 +522,7 @@ fn test_onion_failure() { } // Test a positive test-case with one extra msat, meeting the minimum. - bogus_route.paths[0][route_len-1].fee_msat = amt_to_forward + 1; + bogus_route.paths[0].hops[route_len-1].fee_msat = amt_to_forward + 1; let preimage = send_along_route(&nodes[0], bogus_route, &[&nodes[1], &nodes[2]], amt_to_forward+1).0; claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], preimage); @@ -603,14 +603,14 @@ fn test_onion_failure() { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let mut route = route.clone(); let height = nodes[2].best_block_info().1; - route.paths[0][1].cltv_expiry_delta += CLTV_FAR_FAR_AWAY + route.paths[0][0].cltv_expiry_delta + 1; + route.paths[0].hops[1].cltv_expiry_delta += CLTV_FAR_FAR_AWAY + route.paths[0].hops[0].cltv_expiry_delta + 1; let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); let (onion_payloads, _, htlc_cltv) = onion_utils::build_onion_payloads( &route.paths[0], 40000, RecipientOnionFields::spontaneous_empty(), height, &None).unwrap(); let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash); msg.cltv_expiry = htlc_cltv; msg.onion_routing_packet = onion_packet; - }, ||{}, true, Some(21), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}), Some(route.paths[0][0].short_channel_id)); + }, ||{}, true, Some(21), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: true}), Some(route.paths[0].hops[0].short_channel_id)); run_onion_failure_test_with_fail_intercept("mpp_timeout", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| { // Tamper returning error message @@ -864,7 +864,7 @@ fn test_always_create_tlv_format_onion_payloads() { .with_features(InvoiceFeatures::empty()); let (route, _payment_hash, _payment_preimage, _payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 40000, TEST_FINAL_CLTV); - let hops = &route.paths[0]; + let hops = &route.paths[0].hops; // Asserts that the first hop to `node[1]` signals no support for variable length onions. assert!(!hops[0].node_features.supports_variable_length_onion()); // Asserts that the first hop to `node[1]` signals no support for variable length onions. @@ -1206,7 +1206,7 @@ fn test_phantom_failure_too_low_cltv() { let (mut route, phantom_scid) = get_phantom_route!(nodes, recv_value_msat, channel); // Modify the route to have a too-low cltv. - route.paths[0][1].cltv_expiry_delta = 5; + route.paths[0].hops[1].cltv_expiry_delta = 5; // Route the HTLC through to the destination. nodes[0].node.send_payment_with_route(&route, payment_hash, @@ -1446,7 +1446,7 @@ fn test_phantom_failure_reject_payment() { nodes[1].node.process_pending_htlc_forwards(); expect_pending_htlcs_forwardable_ignore!(nodes[1]); nodes[1].node.process_pending_htlc_forwards(); - expect_payment_claimable!(nodes[1], payment_hash, payment_secret, recv_amt_msat, None, route.paths[0].last().unwrap().pubkey); + expect_payment_claimable!(nodes[1], payment_hash, payment_secret, recv_amt_msat, None, route.paths[0].hops.last().unwrap().pubkey); nodes[1].node.fail_htlc_backwards(&payment_hash); expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[1], vec![HTLCDestination::FailedPayment { payment_hash }]); nodes[1].node.process_pending_htlc_forwards(); diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index b9f36bbd8..df0b90312 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -12,7 +12,7 @@ use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields}; use crate::ln::msgs; use crate::ln::wire::Encode; use crate::routing::gossip::NetworkUpdate; -use crate::routing::router::RouteHop; +use crate::routing::router::{Path, RouteHop}; use crate::util::chacha20::{ChaCha20, ChaChaReader}; use crate::util::errors::{self, APIError}; use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, LengthCalculatingWriter}; @@ -128,10 +128,10 @@ pub(super) fn construct_onion_keys_callback(secp_ctx: &Secp256k1, path: &Vec, session_priv: &SecretKey) -> Result, secp256k1::Error> { - let mut res = Vec::with_capacity(path.len()); +pub(super) fn construct_onion_keys(secp_ctx: &Secp256k1, path: &Path, session_priv: &SecretKey) -> Result, secp256k1::Error> { + let mut res = Vec::with_capacity(path.hops.len()); - construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _, _| { + construct_onion_keys_callback(secp_ctx, &path.hops, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _, _| { let (rho, mu) = gen_rho_mu_from_shared_secret(shared_secret.as_ref()); res.push(OnionKeys { @@ -149,13 +149,13 @@ pub(super) fn construct_onion_keys(secp_ctx: &Secp256k1, total_msat: u64, mut recipient_onion: RecipientOnionFields, starting_htlc_offset: u32, keysend_preimage: &Option) -> Result<(Vec, u64, u32), APIError> { +pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_onion: RecipientOnionFields, starting_htlc_offset: u32, keysend_preimage: &Option) -> Result<(Vec, u64, u32), APIError> { let mut cur_value_msat = 0u64; let mut cur_cltv = starting_htlc_offset; let mut last_short_channel_id = 0; - let mut res: Vec = Vec::with_capacity(path.len()); + let mut res: Vec = Vec::with_capacity(path.hops.len()); - for (idx, hop) in path.iter().rev().enumerate() { + for (idx, hop) in path.hops.iter().rev().enumerate() { // First hop gets special values so that it can check, on receipt, that everything is // exactly as it should be (and the next hop isn't trying to probe to find out if we're // the intended recipient). @@ -403,7 +403,7 @@ pub(super) fn process_onion_failure(secp_ctx: & let mut is_from_final_node = false; // Handle packed channel/node updates for passing back for the route handler - construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _, _, route_hop, route_hop_idx| { + construct_onion_keys_callback(secp_ctx, &path.hops, session_priv, |shared_secret, _, _, route_hop, route_hop_idx| { if res.is_some() { return; } let amt_to_forward = htlc_msat - route_hop.fee_msat; @@ -419,8 +419,8 @@ pub(super) fn process_onion_failure(secp_ctx: & // The failing hop includes either the inbound channel to the recipient or the outbound // channel from the current hop (i.e., the next hop's inbound channel). - is_from_final_node = route_hop_idx + 1 == path.len(); - let failing_route_hop = if is_from_final_node { route_hop } else { &path[route_hop_idx + 1] }; + is_from_final_node = route_hop_idx + 1 == path.hops.len(); + let failing_route_hop = if is_from_final_node { route_hop } else { &path.hops[route_hop_idx + 1] }; if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) { let um = gen_um_from_shared_secret(shared_secret.as_ref()); @@ -726,7 +726,7 @@ impl HTLCFailReason { // generally ignores its view of our own channels as we provide them via // ChannelDetails. if let &HTLCSource::OutboundRoute { ref path, .. } = htlc_source { - (None, Some(path.first().unwrap().short_channel_id), true, Some(*failure_code), Some(data.clone())) + (None, Some(path.hops[0].short_channel_id), true, Some(*failure_code), Some(data.clone())) } else { unreachable!(); } } } @@ -883,7 +883,7 @@ mod tests { use crate::prelude::*; use crate::ln::PaymentHash; use crate::ln::features::{ChannelFeatures, NodeFeatures}; - use crate::routing::router::{Route, RouteHop}; + use crate::routing::router::{Path, Route, RouteHop}; use crate::ln::msgs; use crate::util::ser::{Writeable, Writer, VecWriter}; @@ -903,7 +903,7 @@ mod tests { let secp_ctx = Secp256k1::new(); let route = Route { - paths: vec![vec![ + paths: vec![Path { hops: vec![ RouteHop { pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(), channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(), @@ -929,12 +929,12 @@ mod tests { channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(), short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops. }, - ]], + ]}], payment_params: None, }; let onion_keys = super::construct_onion_keys(&secp_ctx, &route.paths[0], &get_test_session_key()).unwrap(); - assert_eq!(onion_keys.len(), route.paths[0].len()); + assert_eq!(onion_keys.len(), route.paths[0].hops.len()); onion_keys } diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 8b17bdbcb..18763685f 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -18,7 +18,7 @@ use crate::events::{self, PaymentFailureReason}; use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId}; use crate::ln::onion_utils::HTLCFailReason; -use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters, RoutePath, Router}; +use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router}; use crate::util::errors::APIError; use crate::util::logger::Logger; use crate::util::time::Time; @@ -160,7 +160,7 @@ impl PendingOutboundPayment { } /// panics if path is None and !self.is_fulfilled - fn remove(&mut self, session_priv: &[u8; 32], path: Option<&Vec>) -> bool { + fn remove(&mut self, session_priv: &[u8; 32], path: Option<&Path>) -> bool { let remove_res = match self { PendingOutboundPayment::Legacy { session_privs } | PendingOutboundPayment::Retryable { session_privs, .. } | @@ -181,7 +181,7 @@ impl PendingOutboundPayment { remove_res } - pub(super) fn insert(&mut self, session_priv: [u8; 32], path: &Vec) -> bool { + pub(super) fn insert(&mut self, session_priv: [u8; 32], path: &Path) -> bool { let insert_res = match self { PendingOutboundPayment::Legacy { session_privs } | PendingOutboundPayment::Retryable { session_privs, .. } => { @@ -495,7 +495,7 @@ impl OutboundPayments { NS::Target: NodeSigner, L::Target: Logger, IH: Fn() -> InFlightHtlcs, - SP: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError>, { self.send_payment_internal(payment_id, payment_hash, recipient_onion, None, retry_strategy, @@ -511,7 +511,7 @@ impl OutboundPayments { where ES::Target: EntropySource, NS::Target: NodeSigner, - F: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError> { let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(), payment_id, None, route, None, None, entropy_source, best_block_height)?; @@ -533,7 +533,7 @@ impl OutboundPayments { NS::Target: NodeSigner, L::Target: Logger, IH: Fn() -> InFlightHtlcs, - SP: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError>, { let preimage = payment_preimage @@ -553,7 +553,7 @@ impl OutboundPayments { where ES::Target: EntropySource, NS::Target: NodeSigner, - F: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError> { let preimage = payment_preimage @@ -582,7 +582,7 @@ impl OutboundPayments { R::Target: Router, ES::Target: EntropySource, NS::Target: NodeSigner, - SP: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError>, IH: Fn() -> InFlightHtlcs, FH: Fn() -> Vec, @@ -653,7 +653,7 @@ impl OutboundPayments { NS::Target: NodeSigner, L::Target: Logger, IH: Fn() -> InFlightHtlcs, - SP: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError> { #[cfg(feature = "std")] { @@ -694,7 +694,7 @@ impl OutboundPayments { NS::Target: NodeSigner, L::Target: Logger, IH: Fn() -> InFlightHtlcs, - SP: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError> { #[cfg(feature = "std")] { @@ -718,7 +718,7 @@ impl OutboundPayments { } }; for path in route.paths.iter() { - if path.len() == 0 { + if path.hops.len() == 0 { log_error!(logger, "length-0 path in route"); self.abandon_payment(payment_id, PaymentFailureReason::UnexpectedError, pending_events); return @@ -816,7 +816,7 @@ impl OutboundPayments { NS::Target: NodeSigner, L::Target: Logger, IH: Fn() -> InFlightHtlcs, - SP: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError> { match err { @@ -851,7 +851,7 @@ impl OutboundPayments { fn push_path_failed_evs_and_scids>, L: Deref>( payment_id: PaymentId, payment_hash: PaymentHash, route_params: &mut RouteParameters, - paths: Vec>, path_results: I, logger: &L, pending_events: &Mutex> + paths: Vec, path_results: I, logger: &L, pending_events: &Mutex> ) where L::Target: Logger { let mut events = pending_events.lock().unwrap(); debug_assert_eq!(paths.len(), path_results.len()); @@ -861,7 +861,7 @@ impl OutboundPayments { log_error!(logger, "Failed to send along path due to error: {:?}", e); let mut failed_scid = None; if let APIError::ChannelUnavailable { .. } = e { - let scid = path[0].short_channel_id; + let scid = path.hops[0].short_channel_id; failed_scid = Some(scid); route_params.payment_params.previously_failed_channels.push(scid); } @@ -882,26 +882,26 @@ impl OutboundPayments { } pub(super) fn send_probe( - &self, hops: Vec, probing_cookie_secret: [u8; 32], entropy_source: &ES, - node_signer: &NS, best_block_height: u32, send_payment_along_path: F + &self, path: Path, probing_cookie_secret: [u8; 32], entropy_source: &ES, node_signer: &NS, + best_block_height: u32, send_payment_along_path: F ) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> where ES::Target: EntropySource, NS::Target: NodeSigner, - F: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError> { let payment_id = PaymentId(entropy_source.get_secure_random_bytes()); let payment_hash = probing_cookie_from_id(&payment_id, probing_cookie_secret); - if hops.len() < 2 { + if path.hops.len() < 2 { return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError { err: "No need probing a path with less than two hops".to_string() })) } - let route = Route { paths: vec![hops], payment_params: None }; + let route = Route { paths: vec![path], payment_params: None }; let onion_session_privs = self.add_new_pending_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), payment_id, None, &route, None, None, entropy_source, best_block_height)?; @@ -983,7 +983,7 @@ impl OutboundPayments { ) -> Result<(), PaymentSendFailure> where NS::Target: NodeSigner, - F: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError> { if route.paths.len() < 1 { @@ -996,12 +996,12 @@ impl OutboundPayments { let our_node_id = node_signer.get_node_id(Recipient::Node).unwrap(); // TODO no unwrap let mut path_errs = Vec::with_capacity(route.paths.len()); 'path_check: for path in route.paths.iter() { - if path.len() < 1 || path.len() > 20 { + if path.hops.len() < 1 || path.hops.len() > 20 { path_errs.push(Err(APIError::InvalidRoute{err: "Path didn't go anywhere/had bogus size".to_owned()})); continue 'path_check; } - for (idx, hop) in path.iter().enumerate() { - if idx != path.len() - 1 && hop.pubkey == our_node_id { + for (idx, hop) in path.hops.iter().enumerate() { + if idx != path.hops.len() - 1 && hop.pubkey == our_node_id { path_errs.push(Err(APIError::InvalidRoute{err: "Path went through us but wasn't a simple rebalance loop to us".to_owned()})); continue 'path_check; } @@ -1086,7 +1086,7 @@ impl OutboundPayments { ) -> Result<(), PaymentSendFailure> where NS::Target: NodeSigner, - F: Fn(&Vec, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, + F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, &Option, [u8; 32]) -> Result<(), APIError> { self.pay_route_internal(route, payment_hash, recipient_onion, keysend_preimage, payment_id, @@ -1106,7 +1106,7 @@ impl OutboundPayments { pub(super) fn claim_htlc( &self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey, - path: Vec, from_onchain: bool, pending_events: &Mutex>, logger: &L + path: Path, from_onchain: bool, pending_events: &Mutex>, logger: &L ) where L::Target: Logger { let mut session_priv_bytes = [0; 32]; session_priv_bytes.copy_from_slice(&session_priv[..]); @@ -1215,9 +1215,8 @@ impl OutboundPayments { // Returns a bool indicating whether a PendingHTLCsForwardable event should be generated. pub(super) fn fail_htlc( &self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, - path: &Vec, session_priv: &SecretKey, payment_id: &PaymentId, - probing_cookie_secret: [u8; 32], secp_ctx: &Secp256k1, - pending_events: &Mutex>, logger: &L + path: &Path, session_priv: &SecretKey, payment_id: &PaymentId, probing_cookie_secret: [u8; 32], + secp_ctx: &Secp256k1, pending_events: &Mutex>, logger: &L ) -> bool where L::Target: Logger { #[cfg(test)] let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_error.decode_onion_failure(secp_ctx, logger, &source); @@ -1425,7 +1424,7 @@ mod tests { use crate::ln::msgs::{ErrorAction, LightningError}; use crate::ln::outbound_payment::{OutboundPayments, Retry, RetryableSendFailure}; use crate::routing::gossip::NetworkGraph; - use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters}; + use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters}; use crate::sync::{Arc, Mutex}; use crate::util::errors::APIError; use crate::util::test_utils; @@ -1546,14 +1545,14 @@ mod tests { }; let failed_scid = 42; let route = Route { - paths: vec![vec![RouteHop { + paths: vec![Path { hops: vec![RouteHop { pubkey: receiver_pk, node_features: NodeFeatures::empty(), short_channel_id: failed_scid, channel_features: ChannelFeatures::empty(), fee_msat: 0, cltv_expiry_delta: 0, - }]], + }]}], payment_params: Some(payment_params), }; router.expect_find_route(route_params.clone(), Ok(route.clone())); diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index 65b0fc006..b599bee34 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -23,7 +23,7 @@ use crate::ln::msgs; use crate::ln::msgs::ChannelMessageHandler; use crate::ln::outbound_payment::Retry; use crate::routing::gossip::{EffectiveCapacity, RoutingFees}; -use crate::routing::router::{get_route, PaymentParameters, Route, Router, RouteHint, RouteHintHop, RouteHop, RouteParameters}; +use crate::routing::router::{get_route, Path, PaymentParameters, Route, Router, RouteHint, RouteHintHop, RouteHop, RouteParameters}; use crate::routing::scoring::ChannelUsage; use crate::util::test_utils; use crate::util::errors::APIError; @@ -59,12 +59,12 @@ fn mpp_failure() { let (mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(&nodes[0], nodes[3], 100000); let path = route.paths[0].clone(); route.paths.push(path); - route.paths[0][0].pubkey = nodes[1].node.get_our_node_id(); - route.paths[0][0].short_channel_id = chan_1_id; - route.paths[0][1].short_channel_id = chan_3_id; - route.paths[1][0].pubkey = nodes[2].node.get_our_node_id(); - route.paths[1][0].short_channel_id = chan_2_id; - route.paths[1][1].short_channel_id = chan_4_id; + route.paths[0].hops[0].pubkey = nodes[1].node.get_our_node_id(); + route.paths[0].hops[0].short_channel_id = chan_1_id; + route.paths[0].hops[1].short_channel_id = chan_3_id; + route.paths[1].hops[0].pubkey = nodes[2].node.get_our_node_id(); + route.paths[1].hops[0].short_channel_id = chan_2_id; + route.paths[1].hops[1].short_channel_id = chan_4_id; send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], 200_000, payment_hash, payment_secret); fail_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_hash); } @@ -87,12 +87,12 @@ fn mpp_retry() { let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[3], amt_msat); let path = route.paths[0].clone(); route.paths.push(path); - route.paths[0][0].pubkey = nodes[1].node.get_our_node_id(); - route.paths[0][0].short_channel_id = chan_1_update.contents.short_channel_id; - route.paths[0][1].short_channel_id = chan_3_update.contents.short_channel_id; - route.paths[1][0].pubkey = nodes[2].node.get_our_node_id(); - route.paths[1][0].short_channel_id = chan_2_update.contents.short_channel_id; - route.paths[1][1].short_channel_id = chan_4_update.contents.short_channel_id; + route.paths[0].hops[0].pubkey = nodes[1].node.get_our_node_id(); + route.paths[0].hops[0].short_channel_id = chan_1_update.contents.short_channel_id; + route.paths[0].hops[1].short_channel_id = chan_3_update.contents.short_channel_id; + route.paths[1].hops[0].pubkey = nodes[2].node.get_our_node_id(); + route.paths[1].hops[0].short_channel_id = chan_2_update.contents.short_channel_id; + route.paths[1].hops[1].short_channel_id = chan_4_update.contents.short_channel_id; // Initiate the MPP payment. let payment_id = PaymentId(payment_hash.0); @@ -177,12 +177,12 @@ fn do_mpp_receive_timeout(send_partial_mpp: bool) { let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[3], 100_000); let path = route.paths[0].clone(); route.paths.push(path); - route.paths[0][0].pubkey = nodes[1].node.get_our_node_id(); - route.paths[0][0].short_channel_id = chan_1_update.contents.short_channel_id; - route.paths[0][1].short_channel_id = chan_3_update.contents.short_channel_id; - route.paths[1][0].pubkey = nodes[2].node.get_our_node_id(); - route.paths[1][0].short_channel_id = chan_2_update.contents.short_channel_id; - route.paths[1][1].short_channel_id = chan_4_update.contents.short_channel_id; + route.paths[0].hops[0].pubkey = nodes[1].node.get_our_node_id(); + route.paths[0].hops[0].short_channel_id = chan_1_update.contents.short_channel_id; + route.paths[0].hops[1].short_channel_id = chan_3_update.contents.short_channel_id; + route.paths[1].hops[0].pubkey = nodes[2].node.get_our_node_id(); + route.paths[1].hops[0].short_channel_id = chan_2_update.contents.short_channel_id; + route.paths[1].hops[1].short_channel_id = chan_4_update.contents.short_channel_id; // Initiate the MPP payment. nodes[0].node.send_payment_with_route(&route, payment_hash, @@ -437,7 +437,7 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) { let mut new_config = channel.config(); new_config.forwarding_fee_base_msat += 100_000; channel.update_config(&new_config); - new_route.paths[0][0].fee_msat += 100_000; + new_route.paths[0].hops[0].fee_msat += 100_000; } // Force expiration of the channel's previous config. @@ -454,7 +454,7 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) { assert_eq!(events.len(), 1); pass_along_path(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000, payment_hash, Some(payment_secret), events.pop().unwrap(), true, None); do_claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], false, payment_preimage); - expect_payment_sent!(nodes[0], payment_preimage, Some(new_route.paths[0][0].fee_msat)); + expect_payment_sent!(nodes[0], payment_preimage, Some(new_route.paths[0].hops[0].fee_msat)); } #[test] @@ -1839,56 +1839,56 @@ fn auto_retry_partial_failure() { // Configure the initial send, retry1 and retry2's paths. let send_route = Route { paths: vec![ - vec![RouteHop { + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chan_1_id, channel_features: nodes[1].node.channel_features(), fee_msat: amt_msat / 2, cltv_expiry_delta: 100, - }], - vec![RouteHop { + }]}, + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chan_2_id, channel_features: nodes[1].node.channel_features(), fee_msat: amt_msat / 2, cltv_expiry_delta: 100, - }], + }]}, ], payment_params: Some(route_params.payment_params.clone()), }; let retry_1_route = Route { paths: vec![ - vec![RouteHop { + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chan_1_id, channel_features: nodes[1].node.channel_features(), fee_msat: amt_msat / 4, cltv_expiry_delta: 100, - }], - vec![RouteHop { + }]}, + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chan_3_id, channel_features: nodes[1].node.channel_features(), fee_msat: amt_msat / 4, cltv_expiry_delta: 100, - }], + }]}, ], payment_params: Some(route_params.payment_params.clone()), }; let retry_2_route = Route { paths: vec![ - vec![RouteHop { + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chan_1_id, channel_features: nodes[1].node.channel_features(), fee_msat: amt_msat / 4, cltv_expiry_delta: 100, - }], + }]}, ], payment_params: Some(route_params.payment_params.clone()), }; @@ -2128,29 +2128,29 @@ fn retry_multi_path_single_failed_payment() { let chans = nodes[0].node.list_usable_channels(); let mut route = Route { paths: vec![ - vec![RouteHop { + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chans[0].short_channel_id.unwrap(), channel_features: nodes[1].node.channel_features(), fee_msat: 10_000, cltv_expiry_delta: 100, - }], - vec![RouteHop { + }]}, + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chans[1].short_channel_id.unwrap(), channel_features: nodes[1].node.channel_features(), fee_msat: 100_000_001, // Our default max-HTLC-value is 10% of the channel value, which this is one more than cltv_expiry_delta: 100, - }], + }]}, ], payment_params: Some(payment_params), }; nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); // On retry, split the payment across both channels. - route.paths[0][0].fee_msat = 50_000_001; - route.paths[1][0].fee_msat = 50_000_000; + route.paths[0].hops[0].fee_msat = 50_000_001; + route.paths[1].hops[0].fee_msat = 50_000_000; let mut pay_params = route.payment_params.clone().unwrap(); pay_params.previously_failed_channels.push(chans[1].short_channel_id.unwrap()); nodes[0].router.expect_find_route(RouteParameters { @@ -2180,7 +2180,7 @@ fn retry_multi_path_single_failed_payment() { short_channel_id: Some(expected_scid), .. } => { assert_eq!(payment_hash, ev_payment_hash); - assert_eq!(expected_scid, route.paths[1][0].short_channel_id); + assert_eq!(expected_scid, route.paths[1].hops[0].short_channel_id); assert!(err_msg.contains("max HTLC")); }, _ => panic!("Unexpected event"), @@ -2222,23 +2222,23 @@ fn immediate_retry_on_failure() { let chans = nodes[0].node.list_usable_channels(); let mut route = Route { paths: vec![ - vec![RouteHop { + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chans[0].short_channel_id.unwrap(), channel_features: nodes[1].node.channel_features(), fee_msat: 100_000_001, // Our default max-HTLC-value is 10% of the channel value, which this is one more than cltv_expiry_delta: 100, - }], + }]}, ], payment_params: Some(PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)), }; nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); // On retry, split the payment across both channels. route.paths.push(route.paths[0].clone()); - route.paths[0][0].short_channel_id = chans[1].short_channel_id.unwrap(); - route.paths[0][0].fee_msat = 50_000_000; - route.paths[1][0].fee_msat = 50_000_001; + route.paths[0].hops[0].short_channel_id = chans[1].short_channel_id.unwrap(); + route.paths[0].hops[0].fee_msat = 50_000_000; + route.paths[1].hops[0].fee_msat = 50_000_001; let mut pay_params = route_params.payment_params.clone(); pay_params.previously_failed_channels.push(chans[0].short_channel_id.unwrap()); nodes[0].router.expect_find_route(RouteParameters { @@ -2255,7 +2255,7 @@ fn immediate_retry_on_failure() { short_channel_id: Some(expected_scid), .. } => { assert_eq!(payment_hash, ev_payment_hash); - assert_eq!(expected_scid, route.paths[1][0].short_channel_id); + assert_eq!(expected_scid, route.paths[1].hops[0].short_channel_id); assert!(err_msg.contains("max HTLC")); }, _ => panic!("Unexpected event"), @@ -2310,7 +2310,7 @@ fn no_extra_retries_on_back_to_back_fail() { let mut route = Route { paths: vec![ - vec![RouteHop { + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chan_1_scid, @@ -2324,8 +2324,8 @@ fn no_extra_retries_on_back_to_back_fail() { channel_features: nodes[2].node.channel_features(), fee_msat: 100_000_000, cltv_expiry_delta: 100, - }], - vec![RouteHop { + }]}, + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chan_1_scid, @@ -2339,7 +2339,7 @@ fn no_extra_retries_on_back_to_back_fail() { channel_features: nodes[2].node.channel_features(), fee_msat: 100_000_000, cltv_expiry_delta: 100, - }] + }]} ], payment_params: Some(PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)), }; @@ -2348,7 +2348,7 @@ fn no_extra_retries_on_back_to_back_fail() { second_payment_params.previously_failed_channels = vec![chan_2_scid, chan_2_scid]; // On retry, we'll only return one path route.paths.remove(1); - route.paths[0][1].fee_msat = amt_msat; + route.paths[0].hops[1].fee_msat = amt_msat; nodes[0].router.expect_find_route(RouteParameters { payment_params: second_payment_params, final_value_msat: amt_msat, @@ -2512,7 +2512,7 @@ fn test_simple_partial_retry() { let mut route = Route { paths: vec![ - vec![RouteHop { + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chan_1_scid, @@ -2526,8 +2526,8 @@ fn test_simple_partial_retry() { channel_features: nodes[2].node.channel_features(), fee_msat: 100_000_000, cltv_expiry_delta: 100, - }], - vec![RouteHop { + }]}, + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chan_1_scid, @@ -2541,7 +2541,7 @@ fn test_simple_partial_retry() { channel_features: nodes[2].node.channel_features(), fee_msat: 100_000_000, cltv_expiry_delta: 100, - }] + }]} ], payment_params: Some(PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)), }; @@ -2678,7 +2678,7 @@ fn test_threaded_payment_retries() { let mut route = Route { paths: vec![ - vec![RouteHop { + Path { hops: vec![RouteHop { pubkey: nodes[1].node.get_our_node_id(), node_features: nodes[1].node.node_features(), short_channel_id: chan_1_scid, @@ -2692,8 +2692,8 @@ fn test_threaded_payment_retries() { channel_features: nodes[2].node.channel_features(), fee_msat: amt_msat / 1000, cltv_expiry_delta: 100, - }], - vec![RouteHop { + }]}, + Path { hops: vec![RouteHop { pubkey: nodes[2].node.get_our_node_id(), node_features: nodes[2].node.node_features(), short_channel_id: chan_3_scid, @@ -2707,7 +2707,7 @@ fn test_threaded_payment_retries() { channel_features: nodes[3].node.channel_features(), fee_msat: amt_msat - amt_msat / 1000, cltv_expiry_delta: 100, - }] + }]} ], payment_params: Some(PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)), }; @@ -2765,9 +2765,9 @@ fn test_threaded_payment_retries() { // we should still ultimately fail for the same reason - because we're trying to send too // many HTLCs at once. let mut new_route_params = route_params.clone(); - previously_failed_channels.push(route.paths[0][1].short_channel_id); + previously_failed_channels.push(route.paths[0].hops[1].short_channel_id); new_route_params.payment_params.previously_failed_channels = previously_failed_channels.clone(); - route.paths[0][1].short_channel_id += 1; + route.paths[0].hops[1].short_channel_id += 1; nodes[0].router.expect_find_route(new_route_params, Ok(route.clone())); let bs_fail_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id()); @@ -2913,13 +2913,13 @@ fn do_claim_from_closed_chan(fail_payment: bool) { let mut route = nodes[0].router.find_route(&nodes[0].node.get_our_node_id(), &route_params, None, &nodes[0].node.compute_inflight_htlcs()).unwrap(); // Make sure the route is ordered as the B->D path before C->D - route.paths.sort_by(|a, _| if a[0].pubkey == nodes[1].node.get_our_node_id() { + route.paths.sort_by(|a, _| if a.hops[0].pubkey == nodes[1].node.get_our_node_id() { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater }); // Note that we add an extra 1 in the send pipeline to compensate for any blocks found while // the HTLC is being relayed. - route.paths[0][1].cltv_expiry_delta = TEST_FINAL_CLTV + 8; - route.paths[1][1].cltv_expiry_delta = TEST_FINAL_CLTV + 12; + route.paths[0].hops[1].cltv_expiry_delta = TEST_FINAL_CLTV + 8; + route.paths[1].hops[1].cltv_expiry_delta = TEST_FINAL_CLTV + 12; let final_cltv = nodes[0].best_block_info().1 + TEST_FINAL_CLTV + 8 + 1; nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); diff --git a/lightning/src/ln/priv_short_conf_tests.rs b/lightning/src/ln/priv_short_conf_tests.rs index 6ca37203d..7bb1fd44f 100644 --- a/lightning/src/ln/priv_short_conf_tests.rs +++ b/lightning/src/ln/priv_short_conf_tests.rs @@ -239,7 +239,7 @@ fn test_routed_scid_alias() { .with_features(nodes[2].node.invoice_features()) .with_route_hints(hop_hints); let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000, 42); - assert_eq!(route.paths[0][1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap()); + assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap()); nodes[0].node.send_payment_with_route(&route, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap(); check_added_monitors!(nodes[0], 1); @@ -405,7 +405,7 @@ fn test_inbound_scid_privacy() { .with_features(nodes[2].node.invoice_features()) .with_route_hints(hop_hints.clone()); let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000, 42); - assert_eq!(route.paths[0][1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap()); + assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap()); nodes[0].node.send_payment_with_route(&route, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap(); check_added_monitors!(nodes[0], 1); @@ -421,7 +421,7 @@ fn test_inbound_scid_privacy() { .with_features(nodes[2].node.invoice_features()) .with_route_hints(hop_hints); let (route_2, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params_2, 100_000, 42); - assert_eq!(route_2.paths[0][1].short_channel_id, last_hop[0].short_channel_id.unwrap()); + assert_eq!(route_2.paths[0].hops[1].short_channel_id, last_hop[0].short_channel_id.unwrap()); nodes[0].node.send_payment_with_route(&route_2, payment_hash_2, RecipientOnionFields::secret_only(payment_secret_2), PaymentId(payment_hash_2.0)).unwrap(); check_added_monitors!(nodes[0], 1); @@ -473,9 +473,9 @@ fn test_scid_alias_returned() { .with_features(nodes[2].node.invoice_features()) .with_route_hints(hop_hints); let (mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000, 42); - assert_eq!(route.paths[0][1].short_channel_id, nodes[2].node.list_usable_channels()[0].inbound_scid_alias.unwrap()); + assert_eq!(route.paths[0].hops[1].short_channel_id, nodes[2].node.list_usable_channels()[0].inbound_scid_alias.unwrap()); - route.paths[0][1].fee_msat = 10_000_000; // Overshoot the last channel's value + route.paths[0].hops[1].fee_msat = 10_000_000; // Overshoot the last channel's value // Route the HTLC through to the destination. nodes[0].node.send_payment_with_route(&route, payment_hash, @@ -518,8 +518,8 @@ fn test_scid_alias_returned() { PaymentFailedConditions::new().blamed_scid(last_hop[0].inbound_scid_alias.unwrap()) .blamed_chan_closed(false).expected_htlc_error_data(0x1000|7, &err_data)); - route.paths[0][1].fee_msat = 10_000; // Reset to the correct payment amount - route.paths[0][0].fee_msat = 0; // But set fee paid to the middle hop to 0 + route.paths[0].hops[1].fee_msat = 10_000; // Reset to the correct payment amount + route.paths[0].hops[0].fee_msat = 0; // But set fee paid to the middle hop to 0 // Route the HTLC through to the destination. nodes[0].node.send_payment_with_route(&route, payment_hash, @@ -839,7 +839,7 @@ fn test_0conf_channel_reorg() { assert_eq!(nodes[1].node.list_usable_channels()[0].short_channel_id.unwrap(), real_scid); let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 10_000); - assert_eq!(route.paths[0][0].short_channel_id, real_scid); + assert_eq!(route.paths[0].hops[0].short_channel_id, real_scid); send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1]]], 10_000, payment_hash, payment_secret); claim_payment(&nodes[0], &[&nodes[1]], payment_preimage); diff --git a/lightning/src/ln/reload_tests.rs b/lightning/src/ln/reload_tests.rs index f7342671a..1199c0414 100644 --- a/lightning/src/ln/reload_tests.rs +++ b/lightning/src/ln/reload_tests.rs @@ -699,7 +699,7 @@ fn do_test_partial_claim_before_restart(persist_both_monitors: bool) { assert_eq!(route.paths.len(), 2); route.paths.sort_by(|path_a, _| { // Sort the path so that the path through nodes[1] comes first - if path_a[0].pubkey == nodes[1].node.get_our_node_id() { + if path_a.hops[0].pubkey == nodes[1].node.get_our_node_id() { core::cmp::Ordering::Less } else { core::cmp::Ordering::Greater } }); @@ -856,7 +856,7 @@ fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_ht let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000); if use_intercept { - route.paths[0][1].short_channel_id = intercept_scid; + route.paths[0].hops[1].short_channel_id = intercept_scid; } let payment_id = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes()); let htlc_expiry = nodes[0].best_block_info().1 + TEST_FINAL_CLTV; diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 0410c5c55..d4e7306c9 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -137,19 +137,19 @@ impl<'a, S: Score> Score for ScorerAccountingForInFlightHtlcs<'a, S> { } } - fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { + fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) { self.scorer.payment_path_failed(path, short_channel_id) } - fn payment_path_successful(&mut self, path: &[&RouteHop]) { + fn payment_path_successful(&mut self, path: &Path) { self.scorer.payment_path_successful(path) } - fn probe_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { + fn probe_failed(&mut self, path: &Path, short_channel_id: u64) { self.scorer.probe_failed(path, short_channel_id) } - fn probe_successful(&mut self, path: &[&RouteHop]) { + fn probe_successful(&mut self, path: &Path) { self.scorer.probe_successful(path) } } @@ -170,14 +170,14 @@ impl InFlightHtlcs { pub fn new() -> Self { InFlightHtlcs(HashMap::new()) } /// Takes in a path with payer's node id and adds the path's details to `InFlightHtlcs`. - pub fn process_path(&mut self, path: &[RouteHop], payer_node_id: PublicKey) { - if path.is_empty() { return }; + pub fn process_path(&mut self, path: &Path, payer_node_id: PublicKey) { + if path.hops.is_empty() { return }; // total_inflight_map needs to be direction-sensitive when keeping track of the HTLC value // that is held up. However, the `hops` array, which is a path returned by `find_route` in // the router excludes the payer node. In the following lines, the payer's information is // hardcoded with an inflight value of 0 so that we can correctly represent the first hop // in our sliding window of two. - let reversed_hops_with_payer = path.iter().rev().skip(1) + let reversed_hops_with_payer = path.hops.iter().rev().skip(1) .map(|hop| hop.pubkey) .chain(core::iter::once(payer_node_id)); let mut cumulative_msat = 0; @@ -185,7 +185,7 @@ impl InFlightHtlcs { // Taking the reversed vector from above, we zip it with just the reversed hops list to // work "backwards" of the given path, since the last hop's `fee_msat` actually represents // the total amount sent. - for (next_hop, prev_hop) in path.iter().rev().zip(reversed_hops_with_payer) { + for (next_hop, prev_hop) in path.hops.iter().rev().zip(reversed_hops_with_payer) { cumulative_msat += next_hop.fee_msat; self.0 .entry((next_hop.short_channel_id, NodeId::from_pubkey(&prev_hop) < NodeId::from_pubkey(&next_hop.pubkey))) @@ -243,6 +243,33 @@ impl_writeable_tlv_based!(RouteHop, { (10, cltv_expiry_delta, required), }); +/// A path in a [`Route`] to the payment recipient. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct Path { + /// The list of unblinded hops in this [`Path`]. + pub hops: Vec, +} + +impl Path { + /// Gets the fees for a given path, excluding any excess paid to the recipient. + pub fn fee_msat(&self) -> u64 { + // Do not count last hop of each path since that's the full value of the payment + self.hops.split_last().map(|(_, path_prefix)| path_prefix).unwrap_or(&[]) + .iter().map(|hop| &hop.fee_msat) + .sum() + } + + /// Gets the total amount paid on this [`Path`], excluding the fees. + pub fn final_value_msat(&self) -> u64 { + self.hops.last().map_or(0, |hop| hop.fee_msat) + } + + /// Gets the final hop's CLTV expiry delta. + pub fn final_cltv_expiry_delta(&self) -> u32 { + self.hops.last().map_or(0, |hop| hop.cltv_expiry_delta) + } +} + /// A route directs a payment from the sender (us) to the recipient. If the recipient supports MPP, /// it can take multiple paths. Each path is composed of one or more hops through the network. #[derive(Clone, Hash, PartialEq, Eq)] @@ -252,7 +279,7 @@ pub struct Route { /// the last hop is the destination. Thus, this must always be at least length one. While the /// maximum length of any given path is variable, keeping the length of any path less or equal to /// 19 should currently ensure it is viable. - pub paths: Vec>, + pub paths: Vec, /// The `payment_params` parameter passed to [`find_route`]. /// This is used by `ChannelManager` to track information which may be required for retries, /// provided back to you via [`Event::PaymentPathFailed`]. @@ -261,45 +288,6 @@ pub struct Route { pub payment_params: Option, } -// This trait is deleted in the next commit -pub(crate) trait RoutePath { - /// Gets the fees for a given path, excluding any excess paid to the recipient. - fn fee_msat(&self) -> u64; - - /// Gets the total amount paid on this path, excluding the fees. - fn final_value_msat(&self) -> u64; - - /// Gets the final hop's CLTV expiry delta. - fn final_cltv_expiry_delta(&self) -> u32; -} -impl RoutePath for Vec { - fn fee_msat(&self) -> u64 { - // Do not count last hop of each path since that's the full value of the payment - self.split_last().map(|(_, path_prefix)| path_prefix).unwrap_or(&[]) - .iter().map(|hop| &hop.fee_msat) - .sum() - } - fn final_value_msat(&self) -> u64 { - self.last().map_or(0, |hop| hop.fee_msat) - } - fn final_cltv_expiry_delta(&self) -> u32 { - self.last().map_or(0, |hop| hop.cltv_expiry_delta) - } -} -impl RoutePath for &[&RouteHop] { - fn fee_msat(&self) -> u64 { - self.split_last().map(|(_, path_prefix)| path_prefix).unwrap_or(&[]) - .iter().map(|hop| &hop.fee_msat) - .sum() - } - fn final_value_msat(&self) -> u64 { - self.last().map_or(0, |hop| hop.fee_msat) - } - fn final_cltv_expiry_delta(&self) -> u32 { - self.last().map_or(0, |hop| hop.cltv_expiry_delta) - } -} - impl Route { /// Returns the total amount of fees paid on this [`Route`]. /// @@ -323,9 +311,9 @@ impl Writeable for Route { fn write(&self, writer: &mut W) -> Result<(), io::Error> { write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION); (self.paths.len() as u64).write(writer)?; - for hops in self.paths.iter() { - (hops.len() as u8).write(writer)?; - for hop in hops.iter() { + for path in self.paths.iter() { + (path.hops.len() as u8).write(writer)?; + for hop in path.hops.iter() { hop.write(writer)?; } } @@ -352,7 +340,7 @@ impl Readable for Route { if hops.is_empty() { return Err(DecodeError::InvalidValue); } min_final_cltv_expiry_delta = cmp::min(min_final_cltv_expiry_delta, hops.last().unwrap().cltv_expiry_delta); - paths.push(hops); + paths.push(Path { hops }); } let mut payment_params = None; read_tlv_fields!(reader, { @@ -2029,8 +2017,14 @@ where L::Target: Logger { } } + let mut paths: Vec = Vec::new(); + for results_vec in selected_paths { + let mut hops = Vec::with_capacity(results_vec.len()); + for res in results_vec { hops.push(res?); } + paths.push(Path { hops }); + } let route = Route { - paths: selected_paths.into_iter().map(|path| path.into_iter().collect()).collect::, _>>()?, + paths, payment_params: Some(payment_params.clone()), }; log_info!(logger, "Got route to {}: {}", payment_params.payee_pubkey, log_route!(route)); @@ -2052,14 +2046,14 @@ fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters, // Remember the last three nodes of the random walk and avoid looping back on them. // Init with the last three nodes from the actual path, if possible. - let mut nodes_to_avoid: [NodeId; 3] = [NodeId::from_pubkey(&path.last().unwrap().pubkey), - NodeId::from_pubkey(&path.get(path.len().saturating_sub(2)).unwrap().pubkey), - NodeId::from_pubkey(&path.get(path.len().saturating_sub(3)).unwrap().pubkey)]; + let mut nodes_to_avoid: [NodeId; 3] = [NodeId::from_pubkey(&path.hops.last().unwrap().pubkey), + NodeId::from_pubkey(&path.hops.get(path.hops.len().saturating_sub(2)).unwrap().pubkey), + NodeId::from_pubkey(&path.hops.get(path.hops.len().saturating_sub(3)).unwrap().pubkey)]; // Choose the last publicly known node as the starting point for the random walk. let mut cur_hop: Option = None; let mut path_nonce = [0u8; 12]; - if let Some(starting_hop) = path.iter().rev() + if let Some(starting_hop) = path.hops.iter().rev() .find(|h| network_nodes.contains_key(&NodeId::from_pubkey(&h.pubkey))) { cur_hop = Some(NodeId::from_pubkey(&starting_hop.pubkey)); path_nonce.copy_from_slice(&cur_hop.unwrap().as_slice()[..12]); @@ -2108,7 +2102,7 @@ fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters, // Limit the offset so we never exceed the max_total_cltv_expiry_delta. To improve plausibility, // we choose the limit to be the largest possible multiple of MEDIAN_HOP_CLTV_EXPIRY_DELTA. - let path_total_cltv_expiry_delta: u32 = path.iter().map(|h| h.cltv_expiry_delta).sum(); + let path_total_cltv_expiry_delta: u32 = path.hops.iter().map(|h| h.cltv_expiry_delta).sum(); let mut max_path_offset = payment_params.max_total_cltv_expiry_delta - path_total_cltv_expiry_delta; max_path_offset = cmp::max( max_path_offset - (max_path_offset % MEDIAN_HOP_CLTV_EXPIRY_DELTA), @@ -2116,7 +2110,7 @@ fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters, shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, max_path_offset); // Add 'shadow' CLTV offset to the final hop - if let Some(last_hop) = path.last_mut() { + if let Some(last_hop) = path.hops.last_mut() { last_hop.cltv_expiry_delta = last_hop.cltv_expiry_delta .checked_add(shadow_ctlv_expiry_delta_offset).unwrap_or(last_hop.cltv_expiry_delta); } @@ -2170,13 +2164,13 @@ fn build_route_from_hops_internal( u64::max_value() } - fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {} + fn payment_path_failed(&mut self, _path: &Path, _short_channel_id: u64) {} - fn payment_path_successful(&mut self, _path: &[&RouteHop]) {} + fn payment_path_successful(&mut self, _path: &Path) {} - fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {} + fn probe_failed(&mut self, _path: &Path, _short_channel_id: u64) {} - fn probe_successful(&mut self, _path: &[&RouteHop]) {} + fn probe_successful(&mut self, _path: &Path) {} } impl<'a> Writeable for HopScorer { @@ -2207,7 +2201,7 @@ mod tests { use crate::routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, EffectiveCapacity}; use crate::routing::utxo::UtxoResult; use crate::routing::router::{get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features, - PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees, RoutePath, + Path, PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE}; use crate::routing::scoring::{ChannelUsage, FixedPenaltyScorer, Score, ProbabilisticScorer, ProbabilisticScoringParameters}; use crate::routing::test_utils::{add_channel, add_or_update_node, build_graph, build_line_graph, id_to_feature_flags, get_nodes, update_channel}; @@ -2291,21 +2285,21 @@ mod tests { } else { panic!(); } let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 2); + assert_eq!(route.paths[0].hops.len(), 2); - assert_eq!(route.paths[0][0].pubkey, nodes[1]); - assert_eq!(route.paths[0][0].short_channel_id, 2); - assert_eq!(route.paths[0][0].fee_msat, 100); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2)); + assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 2); + assert_eq!(route.paths[0].hops[0].fee_msat, 100); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (4 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(2)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(2)); - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 4); - assert_eq!(route.paths[0][1].fee_msat, 100); - assert_eq!(route.paths[0][1].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4)); + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 4); + assert_eq!(route.paths[0].hops[1].fee_msat, 100); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4)); } #[test] @@ -2327,7 +2321,7 @@ mod tests { } else { panic!(); } let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 2); + assert_eq!(route.paths[0].hops.len(), 2); } #[test] @@ -2454,7 +2448,7 @@ mod tests { // A payment above the minimum should pass let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 199_999_999, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 2); + assert_eq!(route.paths[0].hops.len(), 2); } #[test] @@ -2537,7 +2531,7 @@ mod tests { let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 60_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); // Overpay fees to hit htlc_minimum_msat. - let overpaid_fees = route.paths[0][0].fee_msat + route.paths[1][0].fee_msat; + let overpaid_fees = route.paths[0].hops[0].fee_msat + route.paths[1].hops[0].fee_msat; // TODO: this could be better balanced to overpay 10k and not 15k. assert_eq!(overpaid_fees, 15_000); @@ -2583,16 +2577,16 @@ mod tests { let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 60_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).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!(route.paths[0].hops[0].short_channel_id, 12); + let fees = route.paths[0].hops[0].fee_msat; assert_eq!(fees, 5_000); let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 50_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).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); - assert_eq!(route.paths[0][0].short_channel_id, 2); - let fees = route.paths[0][0].fee_msat; + assert_eq!(route.paths[0].hops[0].short_channel_id, 2); + let fees = route.paths[0].hops[0].fee_msat; assert_eq!(fees, 5_000); } @@ -2639,21 +2633,21 @@ mod tests { // 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, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 2); + assert_eq!(route.paths[0].hops.len(), 2); - assert_eq!(route.paths[0][0].pubkey, nodes[7]); - assert_eq!(route.paths[0][0].short_channel_id, 42); - assert_eq!(route.paths[0][0].fee_msat, 200); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features - assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion + assert_eq!(route.paths[0].hops[0].pubkey, nodes[7]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 42); + assert_eq!(route.paths[0].hops[0].fee_msat, 200); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (13 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 13); - assert_eq!(route.paths[0][1].fee_msat, 100); - assert_eq!(route.paths[0][1].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(13)); + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 13); + assert_eq!(route.paths[0].hops[1].fee_msat, 100); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(13)); } #[test] @@ -2680,21 +2674,21 @@ mod tests { // 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, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 2); - - assert_eq!(route.paths[0][0].pubkey, nodes[7]); - assert_eq!(route.paths[0][0].short_channel_id, 42); - assert_eq!(route.paths[0][0].fee_msat, 200); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features - assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 13); - assert_eq!(route.paths[0][1].fee_msat, 100); - assert_eq!(route.paths[0][1].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(13)); + assert_eq!(route.paths[0].hops.len(), 2); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[7]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 42); + assert_eq!(route.paths[0].hops[0].fee_msat, 200); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (13 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 13); + assert_eq!(route.paths[0].hops[1].fee_msat, 100); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(13)); // Note that we don't test disabling node 3 and failing to route to it, as we (somewhat // naively) assume that the user checked the feature bits on the invoice, which override @@ -2712,48 +2706,48 @@ mod tests { // Route to 1 via 2 and 3 because our channel to 1 is disabled let payment_params = PaymentParameters::from_node_id(nodes[0], 42); let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 3); - - assert_eq!(route.paths[0][0].pubkey, nodes[1]); - assert_eq!(route.paths[0][0].short_channel_id, 2); - assert_eq!(route.paths[0][0].fee_msat, 200); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2)); - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 4); - assert_eq!(route.paths[0][1].fee_msat, 100); - assert_eq!(route.paths[0][1].cltv_expiry_delta, (3 << 4) | 2); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4)); - - assert_eq!(route.paths[0][2].pubkey, nodes[0]); - assert_eq!(route.paths[0][2].short_channel_id, 3); - assert_eq!(route.paths[0][2].fee_msat, 100); - assert_eq!(route.paths[0][2].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(1)); - assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops.len(), 3); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 2); + assert_eq!(route.paths[0].hops[0].fee_msat, 200); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (4 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(2)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(2)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 4); + assert_eq!(route.paths[0].hops[1].fee_msat, 100); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, (3 << 4) | 2); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4)); + + assert_eq!(route.paths[0].hops[2].pubkey, nodes[0]); + assert_eq!(route.paths[0].hops[2].short_channel_id, 3); + assert_eq!(route.paths[0].hops[2].fee_msat, 100); + assert_eq!(route.paths[0].hops[2].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[2].node_features.le_flags(), &id_to_feature_flags(1)); + assert_eq!(route.paths[0].hops[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 payment_params = PaymentParameters::from_node_id(nodes[2], 42); 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, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 2); + assert_eq!(route.paths[0].hops.len(), 2); - assert_eq!(route.paths[0][0].pubkey, nodes[7]); - assert_eq!(route.paths[0][0].short_channel_id, 42); - assert_eq!(route.paths[0][0].fee_msat, 200); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion + assert_eq!(route.paths[0].hops[0].pubkey, nodes[7]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 42); + assert_eq!(route.paths[0].hops[0].fee_msat, 200); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (13 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &vec![0b11]); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 13); - assert_eq!(route.paths[0][1].fee_msat, 100); - assert_eq!(route.paths[0][1].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(13)); + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 13); + assert_eq!(route.paths[0].hops[1].fee_msat, 100); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(13)); } fn last_hops(nodes: &Vec) -> Vec { @@ -2868,44 +2862,44 @@ mod tests { let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops_multi_private_channels(&nodes)); let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 5); - - assert_eq!(route.paths[0][0].pubkey, nodes[1]); - assert_eq!(route.paths[0][0].short_channel_id, 2); - assert_eq!(route.paths[0][0].fee_msat, 100); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2)); - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 4); - assert_eq!(route.paths[0][1].fee_msat, 0); - assert_eq!(route.paths[0][1].cltv_expiry_delta, (6 << 4) | 1); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4)); - - assert_eq!(route.paths[0][2].pubkey, nodes[4]); - assert_eq!(route.paths[0][2].short_channel_id, 6); - assert_eq!(route.paths[0][2].fee_msat, 0); - assert_eq!(route.paths[0][2].cltv_expiry_delta, (11 << 4) | 1); - assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(5)); - assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(6)); - - assert_eq!(route.paths[0][3].pubkey, nodes[3]); - assert_eq!(route.paths[0][3].short_channel_id, 11); - assert_eq!(route.paths[0][3].fee_msat, 0); - assert_eq!(route.paths[0][3].cltv_expiry_delta, (8 << 4) | 1); + assert_eq!(route.paths[0].hops.len(), 5); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 2); + assert_eq!(route.paths[0].hops[0].fee_msat, 100); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (4 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(2)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(2)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 4); + assert_eq!(route.paths[0].hops[1].fee_msat, 0); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, (6 << 4) | 1); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4)); + + assert_eq!(route.paths[0].hops[2].pubkey, nodes[4]); + assert_eq!(route.paths[0].hops[2].short_channel_id, 6); + assert_eq!(route.paths[0].hops[2].fee_msat, 0); + assert_eq!(route.paths[0].hops[2].cltv_expiry_delta, (11 << 4) | 1); + assert_eq!(route.paths[0].hops[2].node_features.le_flags(), &id_to_feature_flags(5)); + assert_eq!(route.paths[0].hops[2].channel_features.le_flags(), &id_to_feature_flags(6)); + + assert_eq!(route.paths[0].hops[3].pubkey, nodes[3]); + assert_eq!(route.paths[0].hops[3].short_channel_id, 11); + assert_eq!(route.paths[0].hops[3].fee_msat, 0); + assert_eq!(route.paths[0].hops[3].cltv_expiry_delta, (8 << 4) | 1); // If we have a peer in the node map, we'll use their features here since we don't have // a way of figuring out their features from the invoice: - assert_eq!(route.paths[0][3].node_features.le_flags(), &id_to_feature_flags(4)); - assert_eq!(route.paths[0][3].channel_features.le_flags(), &id_to_feature_flags(11)); + assert_eq!(route.paths[0].hops[3].node_features.le_flags(), &id_to_feature_flags(4)); + assert_eq!(route.paths[0].hops[3].channel_features.le_flags(), &id_to_feature_flags(11)); - assert_eq!(route.paths[0][4].pubkey, nodes[6]); - assert_eq!(route.paths[0][4].short_channel_id, 8); - assert_eq!(route.paths[0][4].fee_msat, 100); - assert_eq!(route.paths[0][4].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][4].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0].hops[4].pubkey, nodes[6]); + assert_eq!(route.paths[0].hops[4].short_channel_id, 8); + assert_eq!(route.paths[0].hops[4].fee_msat, 100); + assert_eq!(route.paths[0].hops[4].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[4].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0].hops[4].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly } fn empty_last_hop(nodes: &Vec) -> Vec { @@ -2944,44 +2938,44 @@ mod tests { // Test handling of an empty RouteHint passed in Invoice. let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 5); - - assert_eq!(route.paths[0][0].pubkey, nodes[1]); - assert_eq!(route.paths[0][0].short_channel_id, 2); - assert_eq!(route.paths[0][0].fee_msat, 100); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2)); - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 4); - assert_eq!(route.paths[0][1].fee_msat, 0); - assert_eq!(route.paths[0][1].cltv_expiry_delta, (6 << 4) | 1); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4)); - - assert_eq!(route.paths[0][2].pubkey, nodes[4]); - assert_eq!(route.paths[0][2].short_channel_id, 6); - assert_eq!(route.paths[0][2].fee_msat, 0); - assert_eq!(route.paths[0][2].cltv_expiry_delta, (11 << 4) | 1); - assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(5)); - assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(6)); - - assert_eq!(route.paths[0][3].pubkey, nodes[3]); - assert_eq!(route.paths[0][3].short_channel_id, 11); - assert_eq!(route.paths[0][3].fee_msat, 0); - assert_eq!(route.paths[0][3].cltv_expiry_delta, (8 << 4) | 1); + assert_eq!(route.paths[0].hops.len(), 5); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 2); + assert_eq!(route.paths[0].hops[0].fee_msat, 100); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (4 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(2)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(2)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 4); + assert_eq!(route.paths[0].hops[1].fee_msat, 0); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, (6 << 4) | 1); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4)); + + assert_eq!(route.paths[0].hops[2].pubkey, nodes[4]); + assert_eq!(route.paths[0].hops[2].short_channel_id, 6); + assert_eq!(route.paths[0].hops[2].fee_msat, 0); + assert_eq!(route.paths[0].hops[2].cltv_expiry_delta, (11 << 4) | 1); + assert_eq!(route.paths[0].hops[2].node_features.le_flags(), &id_to_feature_flags(5)); + assert_eq!(route.paths[0].hops[2].channel_features.le_flags(), &id_to_feature_flags(6)); + + assert_eq!(route.paths[0].hops[3].pubkey, nodes[3]); + assert_eq!(route.paths[0].hops[3].short_channel_id, 11); + assert_eq!(route.paths[0].hops[3].fee_msat, 0); + assert_eq!(route.paths[0].hops[3].cltv_expiry_delta, (8 << 4) | 1); // If we have a peer in the node map, we'll use their features here since we don't have // a way of figuring out their features from the invoice: - assert_eq!(route.paths[0][3].node_features.le_flags(), &id_to_feature_flags(4)); - assert_eq!(route.paths[0][3].channel_features.le_flags(), &id_to_feature_flags(11)); + assert_eq!(route.paths[0].hops[3].node_features.le_flags(), &id_to_feature_flags(4)); + assert_eq!(route.paths[0].hops[3].channel_features.le_flags(), &id_to_feature_flags(11)); - assert_eq!(route.paths[0][4].pubkey, nodes[6]); - assert_eq!(route.paths[0][4].short_channel_id, 8); - assert_eq!(route.paths[0][4].fee_msat, 100); - assert_eq!(route.paths[0][4].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][4].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0].hops[4].pubkey, nodes[6]); + assert_eq!(route.paths[0].hops[4].short_channel_id, 8); + assert_eq!(route.paths[0].hops[4].fee_msat, 100); + assert_eq!(route.paths[0].hops[4].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[4].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0].hops[4].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly } /// Builds a trivial last-hop hint that passes through the two nodes given, with channel 0xff00 @@ -3050,35 +3044,35 @@ mod tests { }); let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 4); - - assert_eq!(route.paths[0][0].pubkey, nodes[1]); - assert_eq!(route.paths[0][0].short_channel_id, 2); - assert_eq!(route.paths[0][0].fee_msat, 200); - assert_eq!(route.paths[0][0].cltv_expiry_delta, 65); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2)); - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 4); - assert_eq!(route.paths[0][1].fee_msat, 100); - assert_eq!(route.paths[0][1].cltv_expiry_delta, 81); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4)); - - assert_eq!(route.paths[0][2].pubkey, nodes[3]); - assert_eq!(route.paths[0][2].short_channel_id, last_hops[0].0[0].short_channel_id); - assert_eq!(route.paths[0][2].fee_msat, 0); - assert_eq!(route.paths[0][2].cltv_expiry_delta, 129); - assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(4)); - assert_eq!(route.paths[0][2].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly - - assert_eq!(route.paths[0][3].pubkey, nodes[6]); - assert_eq!(route.paths[0][3].short_channel_id, last_hops[0].0[1].short_channel_id); - assert_eq!(route.paths[0][3].fee_msat, 100); - assert_eq!(route.paths[0][3].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][3].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0].hops.len(), 4); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 2); + assert_eq!(route.paths[0].hops[0].fee_msat, 200); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, 65); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(2)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(2)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 4); + assert_eq!(route.paths[0].hops[1].fee_msat, 100); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 81); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4)); + + assert_eq!(route.paths[0].hops[2].pubkey, nodes[3]); + assert_eq!(route.paths[0].hops[2].short_channel_id, last_hops[0].0[0].short_channel_id); + assert_eq!(route.paths[0].hops[2].fee_msat, 0); + assert_eq!(route.paths[0].hops[2].cltv_expiry_delta, 129); + assert_eq!(route.paths[0].hops[2].node_features.le_flags(), &id_to_feature_flags(4)); + assert_eq!(route.paths[0].hops[2].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + + assert_eq!(route.paths[0].hops[3].pubkey, nodes[6]); + assert_eq!(route.paths[0].hops[3].short_channel_id, last_hops[0].0[1].short_channel_id); + assert_eq!(route.paths[0].hops[3].fee_msat, 100); + assert_eq!(route.paths[0].hops[3].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[3].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0].hops[3].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly } #[test] @@ -3122,35 +3116,35 @@ mod tests { }); let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &[42u8; 32]).unwrap(); - assert_eq!(route.paths[0].len(), 4); - - assert_eq!(route.paths[0][0].pubkey, nodes[1]); - assert_eq!(route.paths[0][0].short_channel_id, 2); - assert_eq!(route.paths[0][0].fee_msat, 200); - assert_eq!(route.paths[0][0].cltv_expiry_delta, 65); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2)); - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 4); - assert_eq!(route.paths[0][1].fee_msat, 100); - assert_eq!(route.paths[0][1].cltv_expiry_delta, 81); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4)); - - assert_eq!(route.paths[0][2].pubkey, non_announced_pubkey); - assert_eq!(route.paths[0][2].short_channel_id, last_hops[0].0[0].short_channel_id); - assert_eq!(route.paths[0][2].fee_msat, 0); - assert_eq!(route.paths[0][2].cltv_expiry_delta, 129); - assert_eq!(route.paths[0][2].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][2].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly - - assert_eq!(route.paths[0][3].pubkey, nodes[6]); - assert_eq!(route.paths[0][3].short_channel_id, last_hops[0].0[1].short_channel_id); - assert_eq!(route.paths[0][3].fee_msat, 100); - assert_eq!(route.paths[0][3].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][3].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0].hops.len(), 4); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 2); + assert_eq!(route.paths[0].hops[0].fee_msat, 200); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, 65); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(2)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(2)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 4); + assert_eq!(route.paths[0].hops[1].fee_msat, 100); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 81); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4)); + + assert_eq!(route.paths[0].hops[2].pubkey, non_announced_pubkey); + assert_eq!(route.paths[0].hops[2].short_channel_id, last_hops[0].0[0].short_channel_id); + assert_eq!(route.paths[0].hops[2].fee_msat, 0); + assert_eq!(route.paths[0].hops[2].cltv_expiry_delta, 129); + assert_eq!(route.paths[0].hops[2].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0].hops[2].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + + assert_eq!(route.paths[0].hops[3].pubkey, nodes[6]); + assert_eq!(route.paths[0].hops[3].short_channel_id, last_hops[0].0[1].short_channel_id); + assert_eq!(route.paths[0].hops[3].fee_msat, 100); + assert_eq!(route.paths[0].hops[3].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[3].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0].hops[3].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly } fn last_hops_with_public_channel(nodes: &Vec) -> Vec { @@ -3204,44 +3198,44 @@ mod tests { // which would be handled in the same manner. let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 5); - - assert_eq!(route.paths[0][0].pubkey, nodes[1]); - assert_eq!(route.paths[0][0].short_channel_id, 2); - assert_eq!(route.paths[0][0].fee_msat, 100); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2)); - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 4); - assert_eq!(route.paths[0][1].fee_msat, 0); - assert_eq!(route.paths[0][1].cltv_expiry_delta, (6 << 4) | 1); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4)); - - assert_eq!(route.paths[0][2].pubkey, nodes[4]); - assert_eq!(route.paths[0][2].short_channel_id, 6); - assert_eq!(route.paths[0][2].fee_msat, 0); - assert_eq!(route.paths[0][2].cltv_expiry_delta, (11 << 4) | 1); - assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(5)); - assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(6)); - - assert_eq!(route.paths[0][3].pubkey, nodes[3]); - assert_eq!(route.paths[0][3].short_channel_id, 11); - assert_eq!(route.paths[0][3].fee_msat, 0); - assert_eq!(route.paths[0][3].cltv_expiry_delta, (8 << 4) | 1); + assert_eq!(route.paths[0].hops.len(), 5); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 2); + assert_eq!(route.paths[0].hops[0].fee_msat, 100); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (4 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(2)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(2)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 4); + assert_eq!(route.paths[0].hops[1].fee_msat, 0); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, (6 << 4) | 1); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4)); + + assert_eq!(route.paths[0].hops[2].pubkey, nodes[4]); + assert_eq!(route.paths[0].hops[2].short_channel_id, 6); + assert_eq!(route.paths[0].hops[2].fee_msat, 0); + assert_eq!(route.paths[0].hops[2].cltv_expiry_delta, (11 << 4) | 1); + assert_eq!(route.paths[0].hops[2].node_features.le_flags(), &id_to_feature_flags(5)); + assert_eq!(route.paths[0].hops[2].channel_features.le_flags(), &id_to_feature_flags(6)); + + assert_eq!(route.paths[0].hops[3].pubkey, nodes[3]); + assert_eq!(route.paths[0].hops[3].short_channel_id, 11); + assert_eq!(route.paths[0].hops[3].fee_msat, 0); + assert_eq!(route.paths[0].hops[3].cltv_expiry_delta, (8 << 4) | 1); // If we have a peer in the node map, we'll use their features here since we don't have // a way of figuring out their features from the invoice: - assert_eq!(route.paths[0][3].node_features.le_flags(), &id_to_feature_flags(4)); - assert_eq!(route.paths[0][3].channel_features.le_flags(), &id_to_feature_flags(11)); + assert_eq!(route.paths[0].hops[3].node_features.le_flags(), &id_to_feature_flags(4)); + assert_eq!(route.paths[0].hops[3].channel_features.le_flags(), &id_to_feature_flags(11)); - assert_eq!(route.paths[0][4].pubkey, nodes[6]); - assert_eq!(route.paths[0][4].short_channel_id, 8); - assert_eq!(route.paths[0][4].fee_msat, 100); - assert_eq!(route.paths[0][4].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][4].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0].hops[4].pubkey, nodes[6]); + assert_eq!(route.paths[0].hops[4].short_channel_id, 8); + assert_eq!(route.paths[0].hops[4].fee_msat, 100); + assert_eq!(route.paths[0].hops[4].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[4].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0].hops[4].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly } #[test] @@ -3257,99 +3251,99 @@ mod tests { let mut last_hops = last_hops(&nodes); let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone()); let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 2); - - assert_eq!(route.paths[0][0].pubkey, nodes[3]); - assert_eq!(route.paths[0][0].short_channel_id, 42); - assert_eq!(route.paths[0][0].fee_msat, 0); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (8 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion - - assert_eq!(route.paths[0][1].pubkey, nodes[6]); - assert_eq!(route.paths[0][1].short_channel_id, 8); - assert_eq!(route.paths[0][1].fee_msat, 100); - assert_eq!(route.paths[0][1].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][1].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][1].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0].hops.len(), 2); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[3]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 42); + assert_eq!(route.paths[0].hops[0].fee_msat, 0); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (8 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &vec![0b11]); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[6]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 8); + assert_eq!(route.paths[0].hops[1].fee_msat, 100); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly last_hops[0].0[0].fees.base_msat = 1000; // Revert to via 6 as the fee on 8 goes up let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops); let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 4); - - assert_eq!(route.paths[0][0].pubkey, nodes[1]); - assert_eq!(route.paths[0][0].short_channel_id, 2); - assert_eq!(route.paths[0][0].fee_msat, 200); // fee increased as its % of value transferred across node - assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2)); - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 4); - assert_eq!(route.paths[0][1].fee_msat, 100); - assert_eq!(route.paths[0][1].cltv_expiry_delta, (7 << 4) | 1); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4)); - - assert_eq!(route.paths[0][2].pubkey, nodes[5]); - assert_eq!(route.paths[0][2].short_channel_id, 7); - assert_eq!(route.paths[0][2].fee_msat, 0); - assert_eq!(route.paths[0][2].cltv_expiry_delta, (10 << 4) | 1); + assert_eq!(route.paths[0].hops.len(), 4); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 2); + assert_eq!(route.paths[0].hops[0].fee_msat, 200); // fee increased as its % of value transferred across node + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (4 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(2)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(2)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 4); + assert_eq!(route.paths[0].hops[1].fee_msat, 100); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, (7 << 4) | 1); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4)); + + assert_eq!(route.paths[0].hops[2].pubkey, nodes[5]); + assert_eq!(route.paths[0].hops[2].short_channel_id, 7); + assert_eq!(route.paths[0].hops[2].fee_msat, 0); + assert_eq!(route.paths[0].hops[2].cltv_expiry_delta, (10 << 4) | 1); // If we have a peer in the node map, we'll use their features here since we don't have // a way of figuring out their features from the invoice: - assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(6)); - assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(7)); + assert_eq!(route.paths[0].hops[2].node_features.le_flags(), &id_to_feature_flags(6)); + assert_eq!(route.paths[0].hops[2].channel_features.le_flags(), &id_to_feature_flags(7)); - assert_eq!(route.paths[0][3].pubkey, nodes[6]); - assert_eq!(route.paths[0][3].short_channel_id, 10); - assert_eq!(route.paths[0][3].fee_msat, 100); - assert_eq!(route.paths[0][3].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][3].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0].hops[3].pubkey, nodes[6]); + assert_eq!(route.paths[0].hops[3].short_channel_id, 10); + assert_eq!(route.paths[0].hops[3].fee_msat, 100); + assert_eq!(route.paths[0].hops[3].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[3].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0].hops[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, &payment_params, &network_graph.read_only(), None, 2000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - assert_eq!(route.paths[0].len(), 5); - - assert_eq!(route.paths[0][0].pubkey, nodes[1]); - assert_eq!(route.paths[0][0].short_channel_id, 2); - assert_eq!(route.paths[0][0].fee_msat, 3000); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2)); - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 4); - assert_eq!(route.paths[0][1].fee_msat, 0); - assert_eq!(route.paths[0][1].cltv_expiry_delta, (6 << 4) | 1); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4)); - - assert_eq!(route.paths[0][2].pubkey, nodes[4]); - assert_eq!(route.paths[0][2].short_channel_id, 6); - assert_eq!(route.paths[0][2].fee_msat, 0); - assert_eq!(route.paths[0][2].cltv_expiry_delta, (11 << 4) | 1); - assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(5)); - assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(6)); - - assert_eq!(route.paths[0][3].pubkey, nodes[3]); - assert_eq!(route.paths[0][3].short_channel_id, 11); - assert_eq!(route.paths[0][3].fee_msat, 1000); - assert_eq!(route.paths[0][3].cltv_expiry_delta, (8 << 4) | 1); + assert_eq!(route.paths[0].hops.len(), 5); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 2); + assert_eq!(route.paths[0].hops[0].fee_msat, 3000); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (4 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(2)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(2)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 4); + assert_eq!(route.paths[0].hops[1].fee_msat, 0); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, (6 << 4) | 1); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4)); + + assert_eq!(route.paths[0].hops[2].pubkey, nodes[4]); + assert_eq!(route.paths[0].hops[2].short_channel_id, 6); + assert_eq!(route.paths[0].hops[2].fee_msat, 0); + assert_eq!(route.paths[0].hops[2].cltv_expiry_delta, (11 << 4) | 1); + assert_eq!(route.paths[0].hops[2].node_features.le_flags(), &id_to_feature_flags(5)); + assert_eq!(route.paths[0].hops[2].channel_features.le_flags(), &id_to_feature_flags(6)); + + assert_eq!(route.paths[0].hops[3].pubkey, nodes[3]); + assert_eq!(route.paths[0].hops[3].short_channel_id, 11); + assert_eq!(route.paths[0].hops[3].fee_msat, 1000); + assert_eq!(route.paths[0].hops[3].cltv_expiry_delta, (8 << 4) | 1); // If we have a peer in the node map, we'll use their features here since we don't have // a way of figuring out their features from the invoice: - assert_eq!(route.paths[0][3].node_features.le_flags(), &id_to_feature_flags(4)); - assert_eq!(route.paths[0][3].channel_features.le_flags(), &id_to_feature_flags(11)); + assert_eq!(route.paths[0].hops[3].node_features.le_flags(), &id_to_feature_flags(4)); + assert_eq!(route.paths[0].hops[3].channel_features.le_flags(), &id_to_feature_flags(11)); - assert_eq!(route.paths[0][4].pubkey, nodes[6]); - assert_eq!(route.paths[0][4].short_channel_id, 8); - assert_eq!(route.paths[0][4].fee_msat, 2000); - assert_eq!(route.paths[0][4].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][4].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0].hops[4].pubkey, nodes[6]); + assert_eq!(route.paths[0].hops[4].short_channel_id, 8); + assert_eq!(route.paths[0].hops[4].fee_msat, 2000); + assert_eq!(route.paths[0].hops[4].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[4].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0].hops[4].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly } fn do_unannounced_path_test(last_hop_htlc_max: Option, last_hop_fee_prop: u32, outbound_capacity_msat: u64, route_val: u64) -> Result { @@ -3390,21 +3384,21 @@ mod tests { let middle_node_id = PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&hex::decode(format!("{:02}", 42).repeat(32)).unwrap()[..]).unwrap()); let target_node_id = PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&hex::decode(format!("{:02}", 43).repeat(32)).unwrap()[..]).unwrap()); - assert_eq!(route.paths[0].len(), 2); + assert_eq!(route.paths[0].hops.len(), 2); - assert_eq!(route.paths[0][0].pubkey, middle_node_id); - assert_eq!(route.paths[0][0].short_channel_id, 42); - assert_eq!(route.paths[0][0].fee_msat, 1001); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (8 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &[0b11]); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &[0; 0]); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0].hops[0].pubkey, middle_node_id); + assert_eq!(route.paths[0].hops[0].short_channel_id, 42); + assert_eq!(route.paths[0].hops[0].fee_msat, 1001); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (8 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &[0b11]); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &[0; 0]); // We can't learn any flags from invoices, sadly - assert_eq!(route.paths[0][1].pubkey, target_node_id); - assert_eq!(route.paths[0][1].short_channel_id, 8); - assert_eq!(route.paths[0][1].fee_msat, 1000000); - assert_eq!(route.paths[0][1].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][1].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][1].channel_features.le_flags(), &[0; 0]); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0].hops[1].pubkey, target_node_id); + assert_eq!(route.paths[0].hops[1].short_channel_id, 8); + assert_eq!(route.paths[0].hops[1].fee_msat, 1000000); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &[0; 0]); // We can't learn any flags from invoices, sadly } #[test] @@ -3509,8 +3503,8 @@ mod tests { let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 250_000_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); let path = route.paths.last().unwrap(); - assert_eq!(path.len(), 2); - assert_eq!(path.last().unwrap().pubkey, nodes[2]); + assert_eq!(path.hops.len(), 2); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]); assert_eq!(path.final_value_msat(), 250_000_000); } @@ -3545,8 +3539,8 @@ mod tests { let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::>()), 200_000_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); let path = route.paths.last().unwrap(); - assert_eq!(path.len(), 2); - assert_eq!(path.last().unwrap().pubkey, nodes[2]); + assert_eq!(path.hops.len(), 2); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]); assert_eq!(path.final_value_msat(), 200_000_000); } @@ -3592,8 +3586,8 @@ mod tests { let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 15_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); let path = route.paths.last().unwrap(); - assert_eq!(path.len(), 2); - assert_eq!(path.last().unwrap().pubkey, nodes[2]); + assert_eq!(path.hops.len(), 2); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]); assert_eq!(path.final_value_msat(), 15_000); } @@ -3663,8 +3657,8 @@ mod tests { let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 15_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); let path = route.paths.last().unwrap(); - assert_eq!(path.len(), 2); - assert_eq!(path.last().unwrap().pubkey, nodes[2]); + assert_eq!(path.hops.len(), 2); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]); assert_eq!(path.final_value_msat(), 15_000); } @@ -3695,8 +3689,8 @@ mod tests { let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 10_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); let path = route.paths.last().unwrap(); - assert_eq!(path.len(), 2); - assert_eq!(path.last().unwrap().pubkey, nodes[2]); + assert_eq!(path.hops.len(), 2); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]); assert_eq!(path.final_value_msat(), 10_000); } } @@ -3808,8 +3802,8 @@ mod tests { assert_eq!(route.paths.len(), 1); let mut total_amount_paid_msat = 0; for path in &route.paths { - assert_eq!(path.len(), 4); - assert_eq!(path.last().unwrap().pubkey, nodes[3]); + assert_eq!(path.hops.len(), 4); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[3]); total_amount_paid_msat += path.final_value_msat(); } assert_eq!(total_amount_paid_msat, 49_000); @@ -3821,8 +3815,8 @@ mod tests { assert_eq!(route.paths.len(), 1); let mut total_amount_paid_msat = 0; for path in &route.paths { - assert_eq!(path.len(), 4); - assert_eq!(path.last().unwrap().pubkey, nodes[3]); + assert_eq!(path.hops.len(), 4); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[3]); total_amount_paid_msat += path.final_value_msat(); } assert_eq!(total_amount_paid_msat, 50_000); @@ -3869,8 +3863,8 @@ mod tests { assert_eq!(route.paths.len(), 1); let mut total_amount_paid_msat = 0; for path in &route.paths { - assert_eq!(path.len(), 2); - assert_eq!(path.last().unwrap().pubkey, nodes[2]); + assert_eq!(path.hops.len(), 2); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]); total_amount_paid_msat += path.final_value_msat(); } assert_eq!(total_amount_paid_msat, 50_000); @@ -4015,8 +4009,8 @@ mod tests { assert_eq!(route.paths.len(), 3); let mut total_amount_paid_msat = 0; for path in &route.paths { - assert_eq!(path.len(), 2); - assert_eq!(path.last().unwrap().pubkey, nodes[2]); + assert_eq!(path.hops.len(), 2); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]); total_amount_paid_msat += path.final_value_msat(); } assert_eq!(total_amount_paid_msat, 250_000); @@ -4029,8 +4023,8 @@ mod tests { assert_eq!(route.paths.len(), 3); let mut total_amount_paid_msat = 0; for path in &route.paths { - assert_eq!(path.len(), 2); - assert_eq!(path.last().unwrap().pubkey, nodes[2]); + assert_eq!(path.hops.len(), 2); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]); total_amount_paid_msat += path.final_value_msat(); } assert_eq!(total_amount_paid_msat, 290_000); @@ -4194,7 +4188,7 @@ mod tests { let mut total_amount_paid_msat = 0; for path in &route.paths { - assert_eq!(path.last().unwrap().pubkey, nodes[3]); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[3]); total_amount_paid_msat += path.final_value_msat(); } assert_eq!(total_amount_paid_msat, 300_000); @@ -4356,9 +4350,9 @@ mod tests { let mut total_value_transferred_msat = 0; let mut total_paid_msat = 0; for path in &route.paths { - assert_eq!(path.last().unwrap().pubkey, nodes[3]); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[3]); total_value_transferred_msat += path.final_value_msat(); - for hop in path { + for hop in &path.hops { total_paid_msat += hop.fee_msat; } } @@ -4533,7 +4527,7 @@ mod tests { let mut total_amount_paid_msat = 0; for path in &route.paths { - assert_eq!(path.last().unwrap().pubkey, nodes[3]); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[3]); total_amount_paid_msat += path.final_value_msat(); } assert_eq!(total_amount_paid_msat, 200_000); @@ -4630,16 +4624,16 @@ mod tests { // overpay at all. let mut route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 2); - route.paths.sort_by_key(|path| path[0].short_channel_id); + route.paths.sort_by_key(|path| path.hops[0].short_channel_id); // Paths are manually ordered ordered by SCID, so: // * the first is channel 1 (0 fee, but 99 sat maximum) -> channel 3 -> channel 42 // * the second is channel 2 (1 msat fee) -> channel 4 -> channel 42 - assert_eq!(route.paths[0][0].short_channel_id, 1); - assert_eq!(route.paths[0][0].fee_msat, 0); - assert_eq!(route.paths[0][2].fee_msat, 99_000); - assert_eq!(route.paths[1][0].short_channel_id, 2); - assert_eq!(route.paths[1][0].fee_msat, 1); - assert_eq!(route.paths[1][2].fee_msat, 1_000); + assert_eq!(route.paths[0].hops[0].short_channel_id, 1); + assert_eq!(route.paths[0].hops[0].fee_msat, 0); + assert_eq!(route.paths[0].hops[2].fee_msat, 99_000); + assert_eq!(route.paths[1].hops[0].short_channel_id, 2); + assert_eq!(route.paths[1].hops[0].fee_msat, 1); + assert_eq!(route.paths[1].hops[2].fee_msat, 1_000); assert_eq!(route.get_total_fees(), 1); assert_eq!(route.get_total_amount(), 100_000); } @@ -4759,8 +4753,8 @@ mod tests { assert_eq!(route.paths.len(), 3); let mut total_amount_paid_msat = 0; for path in &route.paths { - assert_eq!(path.len(), 2); - assert_eq!(path.last().unwrap().pubkey, nodes[2]); + assert_eq!(path.hops.len(), 2); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]); total_amount_paid_msat += path.final_value_msat(); } assert_eq!(total_amount_paid_msat, 125_000); @@ -4772,8 +4766,8 @@ mod tests { assert_eq!(route.paths.len(), 2); let mut total_amount_paid_msat = 0; for path in &route.paths { - assert_eq!(path.len(), 2); - assert_eq!(path.last().unwrap().pubkey, nodes[2]); + assert_eq!(path.hops.len(), 2); + assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]); total_amount_paid_msat += path.final_value_msat(); } assert_eq!(total_amount_paid_msat, 90_000); @@ -4909,28 +4903,28 @@ mod tests { // Now ensure the route flows simply over nodes 1 and 4 to 6. let route = get_route(&our_id, &payment_params, &network.read_only(), None, 10_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); - assert_eq!(route.paths[0].len(), 3); - - assert_eq!(route.paths[0][0].pubkey, nodes[1]); - assert_eq!(route.paths[0][0].short_channel_id, 6); - assert_eq!(route.paths[0][0].fee_msat, 100); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (5 << 4) | 0); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(1)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(6)); - - assert_eq!(route.paths[0][1].pubkey, nodes[4]); - assert_eq!(route.paths[0][1].short_channel_id, 5); - assert_eq!(route.paths[0][1].fee_msat, 0); - assert_eq!(route.paths[0][1].cltv_expiry_delta, (1 << 4) | 0); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(4)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(5)); - - assert_eq!(route.paths[0][2].pubkey, nodes[6]); - assert_eq!(route.paths[0][2].short_channel_id, 1); - assert_eq!(route.paths[0][2].fee_msat, 10_000); - assert_eq!(route.paths[0][2].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(6)); - assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(1)); + assert_eq!(route.paths[0].hops.len(), 3); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 6); + assert_eq!(route.paths[0].hops[0].fee_msat, 100); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (5 << 4) | 0); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(1)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(6)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[4]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 5); + assert_eq!(route.paths[0].hops[1].fee_msat, 0); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, (1 << 4) | 0); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(4)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(5)); + + assert_eq!(route.paths[0].hops[2].pubkey, nodes[6]); + assert_eq!(route.paths[0].hops[2].short_channel_id, 1); + assert_eq!(route.paths[0].hops[2].fee_msat, 10_000); + assert_eq!(route.paths[0].hops[2].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[2].node_features.le_flags(), &id_to_feature_flags(6)); + assert_eq!(route.paths[0].hops[2].channel_features.le_flags(), &id_to_feature_flags(1)); } } @@ -4980,21 +4974,21 @@ mod tests { // 200% fee charged channel 13 in the 1-to-2 direction. let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); - assert_eq!(route.paths[0].len(), 2); - - assert_eq!(route.paths[0][0].pubkey, nodes[7]); - assert_eq!(route.paths[0][0].short_channel_id, 12); - assert_eq!(route.paths[0][0].fee_msat, 90_000*2); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(8)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(12)); - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 13); - assert_eq!(route.paths[0][1].fee_msat, 90_000); - assert_eq!(route.paths[0][1].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3)); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(13)); + assert_eq!(route.paths[0].hops.len(), 2); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[7]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 12); + assert_eq!(route.paths[0].hops[0].fee_msat, 90_000*2); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (13 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(8)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(12)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 13); + assert_eq!(route.paths[0].hops[1].fee_msat, 90_000); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(13)); } } @@ -5046,21 +5040,21 @@ mod tests { // expensive) channels 12-13 path. let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); - assert_eq!(route.paths[0].len(), 2); - - assert_eq!(route.paths[0][0].pubkey, nodes[7]); - assert_eq!(route.paths[0][0].short_channel_id, 12); - assert_eq!(route.paths[0][0].fee_msat, 90_000*2); - assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 4) | 1); - assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(8)); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(12)); - - assert_eq!(route.paths[0][1].pubkey, nodes[2]); - assert_eq!(route.paths[0][1].short_channel_id, 13); - assert_eq!(route.paths[0][1].fee_msat, 90_000); - assert_eq!(route.paths[0][1].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][1].node_features.le_flags(), channelmanager::provided_invoice_features(&config).le_flags()); - assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(13)); + assert_eq!(route.paths[0].hops.len(), 2); + + assert_eq!(route.paths[0].hops[0].pubkey, nodes[7]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 12); + assert_eq!(route.paths[0].hops[0].fee_msat, 90_000*2); + assert_eq!(route.paths[0].hops[0].cltv_expiry_delta, (13 << 4) | 1); + assert_eq!(route.paths[0].hops[0].node_features.le_flags(), &id_to_feature_flags(8)); + assert_eq!(route.paths[0].hops[0].channel_features.le_flags(), &id_to_feature_flags(12)); + + assert_eq!(route.paths[0].hops[1].pubkey, nodes[2]); + assert_eq!(route.paths[0].hops[1].short_channel_id, 13); + assert_eq!(route.paths[0].hops[1].fee_msat, 90_000); + assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42); + assert_eq!(route.paths[0].hops[1].node_features.le_flags(), channelmanager::provided_invoice_features(&config).le_flags()); + assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(13)); } } @@ -5088,11 +5082,11 @@ mod tests { &get_channel_details(Some(2), nodes[0], channelmanager::provided_init_features(&config), 10_000), ]), 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); - assert_eq!(route.paths[0].len(), 1); + assert_eq!(route.paths[0].hops.len(), 1); - assert_eq!(route.paths[0][0].pubkey, nodes[0]); - assert_eq!(route.paths[0][0].short_channel_id, 3); - assert_eq!(route.paths[0][0].fee_msat, 100_000); + assert_eq!(route.paths[0].hops[0].pubkey, nodes[0]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 3); + assert_eq!(route.paths[0].hops[0].fee_msat, 100_000); } { let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&[ @@ -5100,17 +5094,17 @@ mod tests { &get_channel_details(Some(2), nodes[0], channelmanager::provided_init_features(&config), 50_000), ]), 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 2); - assert_eq!(route.paths[0].len(), 1); - assert_eq!(route.paths[1].len(), 1); + assert_eq!(route.paths[0].hops.len(), 1); + assert_eq!(route.paths[1].hops.len(), 1); - assert!((route.paths[0][0].short_channel_id == 3 && route.paths[1][0].short_channel_id == 2) || - (route.paths[0][0].short_channel_id == 2 && route.paths[1][0].short_channel_id == 3)); + assert!((route.paths[0].hops[0].short_channel_id == 3 && route.paths[1].hops[0].short_channel_id == 2) || + (route.paths[0].hops[0].short_channel_id == 2 && route.paths[1].hops[0].short_channel_id == 3)); - assert_eq!(route.paths[0][0].pubkey, nodes[0]); - assert_eq!(route.paths[0][0].fee_msat, 50_000); + assert_eq!(route.paths[0].hops[0].pubkey, nodes[0]); + assert_eq!(route.paths[0].hops[0].fee_msat, 50_000); - assert_eq!(route.paths[1][0].pubkey, nodes[0]); - assert_eq!(route.paths[1][0].fee_msat, 50_000); + assert_eq!(route.paths[1].hops[0].pubkey, nodes[0]); + assert_eq!(route.paths[1].hops[0].fee_msat, 50_000); } { @@ -5132,11 +5126,11 @@ mod tests { &get_channel_details(Some(4), nodes[0], channelmanager::provided_init_features(&config), 1_000_000), ]), 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); - assert_eq!(route.paths[0].len(), 1); + assert_eq!(route.paths[0].hops.len(), 1); - assert_eq!(route.paths[0][0].pubkey, nodes[0]); - assert_eq!(route.paths[0][0].short_channel_id, 6); - assert_eq!(route.paths[0][0].fee_msat, 100_000); + assert_eq!(route.paths[0].hops[0].pubkey, nodes[0]); + assert_eq!(route.paths[0].hops[0].short_channel_id, 6); + assert_eq!(route.paths[0].hops[0].fee_msat, 100_000); } } @@ -5154,7 +5148,7 @@ mod tests { &our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes ).unwrap(); - let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::>(); + let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::>(); assert_eq!(route.get_total_fees(), 100); assert_eq!(route.get_total_amount(), 100); @@ -5167,7 +5161,7 @@ mod tests { &our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes ).unwrap(); - let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::>(); + let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::>(); assert_eq!(route.get_total_fees(), 300); assert_eq!(route.get_total_amount(), 100); @@ -5187,10 +5181,10 @@ mod tests { if short_channel_id == self.short_channel_id { u64::max_value() } else { 0 } } - fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {} - fn payment_path_successful(&mut self, _path: &[&RouteHop]) {} - fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {} - fn probe_successful(&mut self, _path: &[&RouteHop]) {} + fn payment_path_failed(&mut self, _path: &Path, _short_channel_id: u64) {} + fn payment_path_successful(&mut self, _path: &Path) {} + fn probe_failed(&mut self, _path: &Path, _short_channel_id: u64) {} + fn probe_successful(&mut self, _path: &Path) {} } struct BadNodeScorer { @@ -5207,10 +5201,10 @@ mod tests { if *target == self.node_id { u64::max_value() } else { 0 } } - fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {} - fn payment_path_successful(&mut self, _path: &[&RouteHop]) {} - fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {} - fn probe_successful(&mut self, _path: &[&RouteHop]) {} + fn payment_path_failed(&mut self, _path: &Path, _short_channel_id: u64) {} + fn payment_path_successful(&mut self, _path: &Path) {} + fn probe_failed(&mut self, _path: &Path, _short_channel_id: u64) {} + fn probe_successful(&mut self, _path: &Path) {} } #[test] @@ -5228,7 +5222,7 @@ mod tests { &our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes ).unwrap(); - let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::>(); + let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::>(); assert_eq!(route.get_total_fees(), 100); assert_eq!(route.get_total_amount(), 100); @@ -5240,7 +5234,7 @@ mod tests { &our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes ).unwrap(); - let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::>(); + let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::>(); assert_eq!(route.get_total_fees(), 300); assert_eq!(route.get_total_amount(), 100); @@ -5262,7 +5256,7 @@ mod tests { #[test] fn total_fees_single_path() { let route = Route { - paths: vec![vec![ + paths: vec![Path { hops: vec![ RouteHop { pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(), channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(), @@ -5278,7 +5272,7 @@ mod tests { channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(), short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0 }, - ]], + ]}], payment_params: None, }; @@ -5289,7 +5283,7 @@ mod tests { #[test] fn total_fees_multi_path() { let route = Route { - paths: vec![vec![ + paths: vec![Path { hops: vec![ RouteHop { pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(), channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(), @@ -5300,7 +5294,7 @@ mod tests { channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(), short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0 }, - ],vec![ + ]}, Path { hops: vec![ RouteHop { pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(), channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(), @@ -5311,7 +5305,7 @@ mod tests { channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(), short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0 }, - ]], + ]}], payment_params: None, }; @@ -5345,7 +5339,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 route = get_route(&our_id, &feasible_payment_params, &network_graph, None, 100, 0, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::>(); + let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::>(); assert_ne!(path.len(), 0); // But not if we exclude all paths on the basis of their accumulated CLTV delta @@ -5380,12 +5374,12 @@ mod tests { assert!(get_route(&our_id, &payment_params, &network_graph, None, 100, 0, Arc::clone(&logger), &scorer, &random_seed_bytes).is_ok()); loop { if let Ok(route) = get_route(&our_id, &payment_params, &network_graph, None, 100, 0, Arc::clone(&logger), &scorer, &random_seed_bytes) { - for chan in route.paths[0].iter() { + for chan in route.paths[0].hops.iter() { assert!(!payment_params.previously_failed_channels.contains(&chan.short_channel_id)); } let victim = (u64::from_ne_bytes(random_seed_bytes[0..8].try_into().unwrap()) as usize) - % route.paths[0].len(); - payment_params.previously_failed_channels.push(route.paths[0][victim].short_channel_id); + % route.paths[0].hops.len(); + payment_params.previously_failed_channels.push(route.paths[0].hops[victim].short_channel_id); } else { break; } } } @@ -5404,7 +5398,7 @@ mod tests { let feasible_payment_params = PaymentParameters::from_node_id(nodes[18], 0); let route = get_route(&our_id, &feasible_payment_params, &network_graph, None, 100, 0, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); - let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::>(); + let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::>(); assert!(path.len() == MAX_PATH_LENGTH_ESTIMATE.into()); // But we can't create a path surpassing the MAX_PATH_LENGTH_ESTIMATE limit. @@ -5432,12 +5426,12 @@ mod tests { let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); - let cltv_expiry_deltas_before = route.paths[0].iter().map(|h| h.cltv_expiry_delta).collect::>(); + let cltv_expiry_deltas_before = route.paths[0].hops.iter().map(|h| h.cltv_expiry_delta).collect::>(); // Check whether the offset added to the last hop by default is in [1 .. DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA] let mut route_default = route.clone(); add_random_cltv_offset(&mut route_default, &payment_params, &network_graph.read_only(), &random_seed_bytes); - let cltv_expiry_deltas_default = route_default.paths[0].iter().map(|h| h.cltv_expiry_delta).collect::>(); + let cltv_expiry_deltas_default = route_default.paths[0].hops.iter().map(|h| h.cltv_expiry_delta).collect::>(); assert_eq!(cltv_expiry_deltas_before.split_last().unwrap().1, cltv_expiry_deltas_default.split_last().unwrap().1); assert!(cltv_expiry_deltas_default.last() > cltv_expiry_deltas_before.last()); assert!(cltv_expiry_deltas_default.last().unwrap() <= &DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA); @@ -5447,7 +5441,7 @@ mod tests { let limited_max_total_cltv_expiry_delta = cltv_expiry_deltas_before.iter().sum(); let limited_payment_params = payment_params.with_max_total_cltv_expiry_delta(limited_max_total_cltv_expiry_delta); add_random_cltv_offset(&mut route_limited, &limited_payment_params, &network_graph.read_only(), &random_seed_bytes); - let cltv_expiry_deltas_limited = route_limited.paths[0].iter().map(|h| h.cltv_expiry_delta).collect::>(); + let cltv_expiry_deltas_limited = route_limited.paths[0].hops.iter().map(|h| h.cltv_expiry_delta).collect::>(); assert_eq!(cltv_expiry_deltas_before, cltv_expiry_deltas_limited); } @@ -5475,11 +5469,11 @@ mod tests { let mut random_bytes = [0u8; ::core::mem::size_of::()]; prng.process_in_place(&mut random_bytes); - let random_path_index = usize::from_be_bytes(random_bytes).wrapping_rem(p.len()); - let observation_point = NodeId::from_pubkey(&p.get(random_path_index).unwrap().pubkey); + let random_path_index = usize::from_be_bytes(random_bytes).wrapping_rem(p.hops.len()); + let observation_point = NodeId::from_pubkey(&p.hops.get(random_path_index).unwrap().pubkey); // 2. Calculate what CLTV expiry delta we would observe there - let observed_cltv_expiry_delta: u32 = p[random_path_index..].iter().map(|h| h.cltv_expiry_delta).sum(); + let observed_cltv_expiry_delta: u32 = p.hops[random_path_index..].iter().map(|h| h.cltv_expiry_delta).sum(); // 3. Starting from the observation point, find candidate paths let mut candidates: VecDeque<(NodeId, Vec)> = VecDeque::new(); @@ -5530,8 +5524,8 @@ mod tests { let hops = [nodes[1], nodes[2], nodes[4], nodes[3]]; let route = build_route_from_hops_internal(&our_id, &hops, &payment_params, &network_graph, 100, 0, Arc::clone(&logger), &random_seed_bytes).unwrap(); - let route_hop_pubkeys = route.paths[0].iter().map(|hop| hop.pubkey).collect::>(); - assert_eq!(hops.len(), route.paths[0].len()); + let route_hop_pubkeys = route.paths[0].hops.iter().map(|hop| hop.pubkey).collect::>(); + assert_eq!(hops.len(), route.paths[0].hops.len()); for (idx, hop_pubkey) in hops.iter().enumerate() { assert!(*hop_pubkey == route_hop_pubkeys[idx]); } @@ -5578,8 +5572,8 @@ mod tests { // 100,000 sats is less than the available liquidity on each channel, set above. let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 2); - assert!((route.paths[0][1].short_channel_id == 4 && route.paths[1][1].short_channel_id == 13) || - (route.paths[1][1].short_channel_id == 4 && route.paths[0][1].short_channel_id == 13)); + assert!((route.paths[0].hops[1].short_channel_id == 4 && route.paths[1].hops[1].short_channel_id == 13) || + (route.paths[1].hops[1].short_channel_id == 4 && route.paths[0].hops[1].short_channel_id == 13)); } #[cfg(not(feature = "no-std"))] @@ -5874,12 +5868,12 @@ mod benches { let amount = route.get_total_amount(); if amount < 250_000 { for path in route.paths { - scorer.payment_path_successful(&path.iter().collect::>()); + scorer.payment_path_successful(&path); } } else if amount > 750_000 { for path in route.paths { - let short_channel_id = path[path.len() / 2].short_channel_id; - scorer.payment_path_failed(&path.iter().collect::>(), short_channel_id); + let short_channel_id = path.hops[path.hops.len() / 2].short_channel_id; + scorer.payment_path_failed(&path, short_channel_id); } } } diff --git a/lightning/src/routing/scoring.rs b/lightning/src/routing/scoring.rs index 2ef2a8557..0756869c1 100644 --- a/lightning/src/routing/scoring.rs +++ b/lightning/src/routing/scoring.rs @@ -56,7 +56,7 @@ use crate::ln::msgs::DecodeError; use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId}; -use crate::routing::router::{RouteHop, RoutePath}; +use crate::routing::router::Path; use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer}; use crate::util::logger::Logger; use crate::util::time::Time; @@ -99,16 +99,16 @@ pub trait Score $(: $supertrait)* { ) -> u64; /// Handles updating channel penalties after failing to route through a channel. - fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64); + fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64); /// Handles updating channel penalties after successfully routing along a path. - fn payment_path_successful(&mut self, path: &[&RouteHop]); + fn payment_path_successful(&mut self, path: &Path); /// Handles updating channel penalties after a probe over the given path failed. - fn probe_failed(&mut self, path: &[&RouteHop], short_channel_id: u64); + fn probe_failed(&mut self, path: &Path, short_channel_id: u64); /// Handles updating channel penalties after a probe over the given path succeeded. - fn probe_successful(&mut self, path: &[&RouteHop]); + fn probe_successful(&mut self, path: &Path); } impl $(+ $supertrait)*> Score for T { @@ -118,19 +118,19 @@ impl $(+ $supertrait)*> Score for T { self.deref().channel_penalty_msat(short_channel_id, source, target, usage) } - fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { + fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) { self.deref_mut().payment_path_failed(path, short_channel_id) } - fn payment_path_successful(&mut self, path: &[&RouteHop]) { + fn payment_path_successful(&mut self, path: &Path) { self.deref_mut().payment_path_successful(path) } - fn probe_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { + fn probe_failed(&mut self, path: &Path, short_channel_id: u64) { self.deref_mut().probe_failed(path, short_channel_id) } - fn probe_successful(&mut self, path: &[&RouteHop]) { + fn probe_successful(&mut self, path: &Path) { self.deref_mut().probe_successful(path) } } @@ -195,16 +195,16 @@ impl<'a, T: Score + 'a> Score for MultiThreadedScoreLock<'a, T> { fn channel_penalty_msat(&self, scid: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage) -> u64 { self.0.channel_penalty_msat(scid, source, target, usage) } - fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { + fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) { self.0.payment_path_failed(path, short_channel_id) } - fn payment_path_successful(&mut self, path: &[&RouteHop]) { + fn payment_path_successful(&mut self, path: &Path) { self.0.payment_path_successful(path) } - fn probe_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { + fn probe_failed(&mut self, path: &Path, short_channel_id: u64) { self.0.probe_failed(path, short_channel_id) } - fn probe_successful(&mut self, path: &[&RouteHop]) { + fn probe_successful(&mut self, path: &Path) { self.0.probe_successful(path) } } @@ -290,13 +290,13 @@ impl Score for FixedPenaltyScorer { self.penalty_msat } - fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {} + fn payment_path_failed(&mut self, _path: &Path, _short_channel_id: u64) {} - fn payment_path_successful(&mut self, _path: &[&RouteHop]) {} + fn payment_path_successful(&mut self, _path: &Path) {} - fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {} + fn probe_failed(&mut self, _path: &Path, _short_channel_id: u64) {} - fn probe_successful(&mut self, _path: &[&RouteHop]) {} + fn probe_successful(&mut self, _path: &Path) {} } impl Writeable for FixedPenaltyScorer { @@ -1233,11 +1233,11 @@ impl>, L: Deref, T: Time> Score for Probabilis .saturating_add(base_penalty_msat) } - fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { + fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) { let amount_msat = path.final_value_msat(); log_trace!(self.logger, "Scoring path through to SCID {} as having failed at {} msat", short_channel_id, amount_msat); let network_graph = self.network_graph.read_only(); - for (hop_idx, hop) in path.iter().enumerate() { + for (hop_idx, hop) in path.hops.iter().enumerate() { let target = NodeId::from_pubkey(&hop.pubkey); let channel_directed_from_source = network_graph.channels() .get(&hop.short_channel_id) @@ -1272,12 +1272,12 @@ impl>, L: Deref, T: Time> Score for Probabilis } } - fn payment_path_successful(&mut self, path: &[&RouteHop]) { + fn payment_path_successful(&mut self, path: &Path) { let amount_msat = path.final_value_msat(); log_trace!(self.logger, "Scoring path through SCID {} as having succeeded at {} msat.", - path.split_last().map(|(hop, _)| hop.short_channel_id).unwrap_or(0), amount_msat); + path.hops.split_last().map(|(hop, _)| hop.short_channel_id).unwrap_or(0), amount_msat); let network_graph = self.network_graph.read_only(); - for hop in path { + for hop in &path.hops { let target = NodeId::from_pubkey(&hop.pubkey); let channel_directed_from_source = network_graph.channels() .get(&hop.short_channel_id) @@ -1298,11 +1298,11 @@ impl>, L: Deref, T: Time> Score for Probabilis } } - fn probe_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { + fn probe_failed(&mut self, path: &Path, short_channel_id: u64) { self.payment_path_failed(path, short_channel_id) } - fn probe_successful(&mut self, path: &[&RouteHop]) { + fn probe_successful(&mut self, path: &Path) { self.payment_path_failed(path, u64::max_value()) } } @@ -1709,7 +1709,7 @@ mod tests { use crate::ln::channelmanager; use crate::ln::msgs::{ChannelAnnouncement, ChannelUpdate, UnsignedChannelAnnouncement, UnsignedChannelUpdate}; use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId}; - use crate::routing::router::RouteHop; + use crate::routing::router::{Path, RouteHop}; use crate::routing::scoring::{ChannelUsage, Score}; use crate::util::ser::{ReadableArgs, Writeable}; use crate::util::test_utils::TestLogger; @@ -1858,12 +1858,14 @@ mod tests { } } - fn payment_path_for_amount(amount_msat: u64) -> Vec { - vec![ - path_hop(source_pubkey(), 41, 1), - path_hop(target_pubkey(), 42, 2), - path_hop(recipient_pubkey(), 43, amount_msat), - ] + fn payment_path_for_amount(amount_msat: u64) -> Path { + Path { + hops: vec![ + path_hop(source_pubkey(), 41, 1), + path_hop(target_pubkey(), 42, 2), + path_hop(recipient_pubkey(), 43, amount_msat), + ], + } } #[test] @@ -2161,10 +2163,10 @@ mod tests { assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301); - scorer.payment_path_failed(&failed_path.iter().collect::>(), 41); + scorer.payment_path_failed(&failed_path, 41); assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301); - scorer.payment_path_successful(&successful_path.iter().collect::>()); + scorer.payment_path_successful(&successful_path); assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301); } @@ -2192,7 +2194,7 @@ mod tests { let usage = ChannelUsage { amount_msat: 750, ..usage }; assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602); - scorer.payment_path_failed(&path.iter().collect::>(), 43); + scorer.payment_path_failed(&path, 43); let usage = ChannelUsage { amount_msat: 250, ..usage }; assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); @@ -2227,7 +2229,7 @@ mod tests { let usage = ChannelUsage { amount_msat: 750, ..usage }; assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602); - scorer.payment_path_failed(&path.iter().collect::>(), 42); + scorer.payment_path_failed(&path, 42); let usage = ChannelUsage { amount_msat: 250, ..usage }; assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); @@ -2287,7 +2289,7 @@ mod tests { assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage), 128); assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage), 128); - scorer.payment_path_failed(&path.iter().collect::>(), 43); + scorer.payment_path_failed(&Path { hops: path }, 43); assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage), 80); // Note that a default liquidity bound is used for B -> C as no channel exists @@ -2313,13 +2315,12 @@ mod tests { inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 }, }; - let path = payment_path_for_amount(500); assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 128); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128); assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage), 128); - scorer.payment_path_successful(&path.iter().collect::>()); + scorer.payment_path_successful(&payment_path_for_amount(500)); assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 128); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); @@ -2349,8 +2350,8 @@ mod tests { let usage = ChannelUsage { amount_msat: 1_023, ..usage }; assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000); - scorer.payment_path_failed(&payment_path_for_amount(768).iter().collect::>(), 42); - scorer.payment_path_failed(&payment_path_for_amount(128).iter().collect::>(), 43); + scorer.payment_path_failed(&payment_path_for_amount(768), 42); + scorer.payment_path_failed(&payment_path_for_amount(128), 43); let usage = ChannelUsage { amount_msat: 128, ..usage }; assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); @@ -2425,7 +2426,7 @@ mod tests { }; assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125); - scorer.payment_path_failed(&payment_path_for_amount(512).iter().collect::>(), 42); + scorer.payment_path_failed(&payment_path_for_amount(512), 42); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 281); // An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat @@ -2458,8 +2459,8 @@ mod tests { assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); // More knowledge gives higher confidence (256, 768), meaning a lower penalty. - scorer.payment_path_failed(&payment_path_for_amount(768).iter().collect::>(), 42); - scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::>(), 43); + scorer.payment_path_failed(&payment_path_for_amount(768), 42); + scorer.payment_path_failed(&payment_path_for_amount(256), 43); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 281); // Decaying knowledge gives less confidence (128, 896), meaning a higher penalty. @@ -2468,12 +2469,12 @@ mod tests { // Reducing the upper bound gives more confidence (128, 832) that the payment amount (512) // is closer to the upper bound, meaning a higher penalty. - scorer.payment_path_successful(&payment_path_for_amount(64).iter().collect::>()); + scorer.payment_path_successful(&payment_path_for_amount(64)); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 331); // Increasing the lower bound gives more confidence (256, 832) that the payment amount (512) // is closer to the lower bound, meaning a lower penalty. - scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::>(), 43); + scorer.payment_path_failed(&payment_path_for_amount(256), 43); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 245); // Further decaying affects the lower bound more than the upper bound (128, 928). @@ -2500,13 +2501,13 @@ mod tests { effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 }, }; - scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::>(), 42); + scorer.payment_path_failed(&payment_path_for_amount(500), 42); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); SinceEpoch::advance(Duration::from_secs(10)); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 473); - scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::>(), 43); + scorer.payment_path_failed(&payment_path_for_amount(250), 43); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); let mut serialized_scorer = Vec::new(); @@ -2537,7 +2538,7 @@ mod tests { effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 }, }; - scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::>(), 42); + scorer.payment_path_failed(&payment_path_for_amount(500), 42); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); let mut serialized_scorer = Vec::new(); @@ -2550,7 +2551,7 @@ mod tests { ::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap(); assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 473); - scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::>(), 43); + scorer.payment_path_failed(&payment_path_for_amount(250), 43); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); SinceEpoch::advance(Duration::from_secs(10)); @@ -2773,7 +2774,7 @@ mod tests { assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target), None); - scorer.payment_path_failed(&payment_path_for_amount(1).iter().collect::>(), 42); + scorer.payment_path_failed(&payment_path_for_amount(1), 42); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2048); // The "it failed" increment is 32, where the probability should lie fully in the first // octile. @@ -2782,7 +2783,7 @@ mod tests { // Even after we tell the scorer we definitely have enough available liquidity, it will // still remember that there was some failure in the past, and assign a non-0 penalty. - scorer.payment_path_failed(&payment_path_for_amount(1000).iter().collect::>(), 43); + scorer.payment_path_failed(&payment_path_for_amount(1000), 43); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 198); // The first octile should be decayed just slightly and the last octile has a new point. assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target), @@ -2802,7 +2803,7 @@ mod tests { inflight_htlc_msat: 1024, effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 }, }; - scorer.payment_path_failed(&payment_path_for_amount(1).iter().collect::>(), 42); + scorer.payment_path_failed(&payment_path_for_amount(1), 42); assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 409); let usage = ChannelUsage { @@ -2822,7 +2823,7 @@ mod tests { path_hop(source_pubkey(), 42, 1), path_hop(sender_pubkey(), 41, 0), ]; - scorer.payment_path_failed(&path.iter().collect::>(), 42); + scorer.payment_path_failed(&Path { hops: path }, 42); } #[test] diff --git a/lightning/src/util/macro_logger.rs b/lightning/src/util/macro_logger.rs index 6e98272f3..b7224be3e 100644 --- a/lightning/src/util/macro_logger.rs +++ b/lightning/src/util/macro_logger.rs @@ -65,7 +65,7 @@ impl<'a> core::fmt::Display for DebugRoute<'a> { fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { for (idx, p) in self.0.paths.iter().enumerate() { writeln!(f, "path {}:", idx)?; - for h in p.iter() { + for h in p.hops.iter() { writeln!(f, " node_id: {}, short_channel_id: {}, fee_msat: {}, cltv_expiry_delta: {}", log_pubkey!(h.pubkey), h.short_channel_id, h.fee_msat, h.cltv_expiry_delta)?; } } diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 46a7bb340..e39e894f5 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -25,7 +25,7 @@ use crate::ln::msgs::LightningError; use crate::ln::script::ShutdownScript; use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId}; use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult}; -use crate::routing::router::{find_route, InFlightHtlcs, Route, RouteHop, RouteParameters, Router, ScorerAccountingForInFlightHtlcs}; +use crate::routing::router::{find_route, InFlightHtlcs, Path, Route, RouteParameters, Router, ScorerAccountingForInFlightHtlcs}; use crate::routing::scoring::{ChannelUsage, Score}; use crate::util::config::UserConfig; use crate::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState}; @@ -106,7 +106,7 @@ impl<'a> Router for TestRouter<'a> { let scorer = ScorerAccountingForInFlightHtlcs::new(locked_scorer, inflight_htlcs); for path in &route.paths { let mut aggregate_msat = 0u64; - for (idx, hop) in path.iter().rev().enumerate() { + for (idx, hop) in path.hops.iter().rev().enumerate() { aggregate_msat += hop.fee_msat; let usage = ChannelUsage { amount_msat: aggregate_msat, @@ -116,11 +116,11 @@ impl<'a> Router for TestRouter<'a> { // Since the path is reversed, the last element in our iteration is the first // hop. - if idx == path.len() - 1 { + if idx == path.hops.len() - 1 { scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(payer), &NodeId::from_pubkey(&hop.pubkey), usage); } else { - let curr_hop_path_idx = path.len() - 1 - idx; - scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(&path[curr_hop_path_idx - 1].pubkey), &NodeId::from_pubkey(&hop.pubkey), usage); + let curr_hop_path_idx = path.hops.len() - 1 - idx; + scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(&path.hops[curr_hop_path_idx - 1].pubkey), &NodeId::from_pubkey(&hop.pubkey), usage); } } } @@ -967,13 +967,13 @@ impl Score for TestScorer { 0 } - fn payment_path_failed(&mut self, _actual_path: &[&RouteHop], _actual_short_channel_id: u64) {} + fn payment_path_failed(&mut self, _actual_path: &Path, _actual_short_channel_id: u64) {} - fn payment_path_successful(&mut self, _actual_path: &[&RouteHop]) {} + fn payment_path_successful(&mut self, _actual_path: &Path) {} - fn probe_failed(&mut self, _actual_path: &[&RouteHop], _: u64) {} + fn probe_failed(&mut self, _actual_path: &Path, _: u64) {} - fn probe_successful(&mut self, _actual_path: &[&RouteHop]) {} + fn probe_successful(&mut self, _actual_path: &Path) {} } impl Drop for TestScorer {