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;
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);
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,
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);
let mut score = scorer.lock();
match event {
Event::PaymentPathFailed { ref path, short_channel_id: Some(scid), .. } => {
- let path = path.iter().collect::<Vec<_>>();
- 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::<Vec<_>>();
- score.probe_successful(&path);
+ score.probe_successful(path);
},
Event::PaymentPathSuccessful { path, .. } => {
- let path = path.iter().collect::<Vec<_>>();
- score.payment_path_successful(&path);
+ score.payment_path_successful(path);
},
Event::ProbeSuccessful { path, .. } => {
- let path = path.iter().collect::<Vec<_>>();
- score.probe_successful(&path);
+ score.probe_successful(path);
},
Event::ProbeFailed { path, short_channel_id: Some(scid), .. } => {
- let path = path.iter().collect::<Vec<_>>();
- score.probe_failed(&path, *scid);
+ score.probe_failed(path, *scid);
},
_ => {},
}
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;
#[derive(Debug)]
enum TestResult {
- PaymentFailure { path: Vec<RouteHop>, short_channel_id: u64 },
- PaymentSuccess { path: Vec<RouteHop> },
- ProbeFailure { path: Vec<RouteHop> },
- ProbeSuccess { path: Vec<RouteHop> },
+ PaymentFailure { path: Path, short_channel_id: u64 },
+ PaymentSuccess { path: Path },
+ ProbeFailure { path: Path },
+ ProbeSuccess { path: Path },
}
impl TestScorer {
&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::<Vec<_>>()[..]);
+ assert_eq!(actual_path, &path);
assert_eq!(actual_short_channel_id, short_channel_id);
},
TestResult::PaymentSuccess { path } => {
}
}
- 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::<Vec<_>>()[..]);
+ assert_eq!(actual_path, &path);
},
TestResult::ProbeFailure { path } => {
panic!("Unexpected probe failure: {:?}", path)
}
}
- 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, .. } => {
panic!("Unexpected payment path success: {:?}", path)
},
TestResult::ProbeFailure { path } => {
- assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
+ assert_eq!(actual_path, &path);
},
TestResult::ProbeSuccess { path } => {
panic!("Unexpected probe success: {:?}", path)
}
}
}
- 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, .. } => {
panic!("Unexpected probe failure: {:?}", path)
},
TestResult::ProbeSuccess { path } => {
- assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
+ assert_eq!(actual_path, &path);
}
}
}
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 {
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)]
/// The payment path that was successful.
///
/// May contain a closed channel if the HTLC sent along the path was fulfilled on chain.
- path: Vec<RouteHop>,
+ path: Path,
},
/// Indicates an outbound HTLC we sent failed, likely due to an intermediary node being unable to
/// handle the HTLC.
/// [`NetworkGraph`]: crate::routing::gossip::NetworkGraph
failure: PathFailure,
/// The payment path that failed.
- path: Vec<RouteHop>,
+ 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
/// [`ChannelManager::send_probe`]: crate::ln::channelmanager::ChannelManager::send_probe
payment_hash: PaymentHash,
/// The payment path that was successful.
- path: Vec<RouteHop>,
+ path: Path,
},
/// Indicates that a probe payment we sent failed at an intermediary node on the path.
ProbeFailed {
/// [`ChannelManager::send_probe`]: crate::ln::channelmanager::ChannelManager::send_probe
payment_hash: PaymentHash,
/// The payment path that failed.
- path: Vec<RouteHop>,
+ 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
(1, None::<NetworkUpdate>, 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::<RouteParameters>, option), // retry in LDK versions prior to 0.0.115
(11, payment_id, option),
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 } => {
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 } => {
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),
})
},
payment_hash,
payment_failed_permanently,
failure,
- path: path.unwrap(),
+ path: Path { hops: path.unwrap() },
short_channel_id,
#[cfg(test)]
error_code,
Ok(Some(Event::PaymentPathSuccessful {
payment_id,
payment_hash,
- path: path.unwrap(),
+ path: Path { hops: path.unwrap() },
}))
};
f()
Ok(Some(Event::ProbeSuccessful {
payment_id,
payment_hash,
- path: path.unwrap(),
+ path: Path { hops: path.unwrap() },
}))
};
f()
Ok(Some(Event::ProbeFailed {
payment_id,
payment_hash,
- path: path.unwrap(),
+ path: Path { hops: path.unwrap() },
short_channel_id,
}))
};
// 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.
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;
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]),
#[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;
pub(crate) enum HTLCSource {
PreviousHopData(HTLCPreviousHopData),
OutboundRoute {
- path: Vec<RouteHop>,
+ 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
#[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]),
}
#[cfg(test)]
- pub(crate) fn test_send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, 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<PaymentPreimage>, 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<RouteHop>, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, 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<PaymentPreimage>, 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");
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()),
};
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 })
/// 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<RouteHop>) -> 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))
}
0 => {
let mut session_priv: crate::util::ser::RequiredWrapper<SecretKey> = crate::util::ser::RequiredWrapper(None);
let mut first_hop_htlc_msat: u64 = 0;
- let mut path: Option<Vec<RouteHop>> = Some(Vec::new());
+ let mut path_hops: Option<Vec<RouteHop>> = Some(Vec::new());
let mut payment_id = None;
let mut payment_params: Option<PaymentParameters> = 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() {
// 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();
(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::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
});
}
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);
}
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))
.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());
}
&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());
}
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()
},
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};
});
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 {
});
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);
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)
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"),
}
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"),
}
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)
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 },
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 },
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 },
// 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();
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
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);
}
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);
}
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 }
});
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 }
});
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| {
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
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| {
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
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| {
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
}, ||{}, 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));
.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.
}
// 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);
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
.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.
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,
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();
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};
}
// can only fail if an intermediary hop has an invalid public key or session_priv is invalid
-pub(super) fn construct_onion_keys<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, path: &Vec<RouteHop>, session_priv: &SecretKey) -> Result<Vec<OnionKeys>, secp256k1::Error> {
- let mut res = Vec::with_capacity(path.len());
+pub(super) fn construct_onion_keys<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, path: &Path, session_priv: &SecretKey) -> Result<Vec<OnionKeys>, 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 {
}
/// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
-pub(super) fn build_onion_payloads(path: &Vec<RouteHop>, total_msat: u64, mut recipient_onion: RecipientOnionFields, starting_htlc_offset: u32, keysend_preimage: &Option<PaymentPreimage>) -> Result<(Vec<msgs::OnionHopData>, 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<PaymentPreimage>) -> Result<(Vec<msgs::OnionHopData>, 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<msgs::OnionHopData> = Vec::with_capacity(path.len());
+ let mut res: Vec<msgs::OnionHopData> = 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).
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;
// 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());
// 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!(); }
}
}
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};
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(),
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
}
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;
}
/// panics if path is None and !self.is_fulfilled
- fn remove(&mut self, session_priv: &[u8; 32], path: Option<&Vec<RouteHop>>) -> 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, .. } |
remove_res
}
- pub(super) fn insert(&mut self, session_priv: [u8; 32], path: &Vec<RouteHop>) -> 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, .. } => {
NS::Target: NodeSigner,
L::Target: Logger,
IH: Fn() -> InFlightHtlcs,
- SP: Fn(&Vec<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>,
{
self.send_payment_internal(payment_id, payment_hash, recipient_onion, None, retry_strategy,
where
ES::Target: EntropySource,
NS::Target: NodeSigner,
- F: Fn(&Vec<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [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)?;
NS::Target: NodeSigner,
L::Target: Logger,
IH: Fn() -> InFlightHtlcs,
- SP: Fn(&Vec<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>,
{
let preimage = payment_preimage
where
ES::Target: EntropySource,
NS::Target: NodeSigner,
- F: Fn(&Vec<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
{
let preimage = payment_preimage
R::Target: Router,
ES::Target: EntropySource,
NS::Target: NodeSigner,
- SP: Fn(&Vec<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>,
IH: Fn() -> InFlightHtlcs,
FH: Fn() -> Vec<ChannelDetails>,
NS::Target: NodeSigner,
L::Target: Logger,
IH: Fn() -> InFlightHtlcs,
- SP: Fn(&Vec<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
{
#[cfg(feature = "std")] {
NS::Target: NodeSigner,
L::Target: Logger,
IH: Fn() -> InFlightHtlcs,
- SP: Fn(&Vec<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
{
#[cfg(feature = "std")] {
}
};
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
NS::Target: NodeSigner,
L::Target: Logger,
IH: Fn() -> InFlightHtlcs,
- SP: Fn(&Vec<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
{
match err {
fn push_path_failed_evs_and_scids<I: ExactSizeIterator + Iterator<Item = Result<(), APIError>>, L: Deref>(
payment_id: PaymentId, payment_hash: PaymentHash, route_params: &mut RouteParameters,
- paths: Vec<Vec<RouteHop>>, path_results: I, logger: &L, pending_events: &Mutex<Vec<events::Event>>
+ paths: Vec<Path>, path_results: I, logger: &L, pending_events: &Mutex<Vec<events::Event>>
) where L::Target: Logger {
let mut events = pending_events.lock().unwrap();
debug_assert_eq!(paths.len(), path_results.len());
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);
}
}
pub(super) fn send_probe<ES: Deref, NS: Deref, F>(
- &self, hops: Vec<RouteHop>, 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<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [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)?;
) -> Result<(), PaymentSendFailure>
where
NS::Target: NodeSigner,
- F: Fn(&Vec<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
{
if route.paths.len() < 1 {
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;
}
) -> Result<(), PaymentSendFailure>
where
NS::Target: NodeSigner,
- F: Fn(&Vec<RouteHop>, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
+ F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId,
&Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
{
self.pay_route_internal(route, payment_hash, recipient_onion, keysend_preimage, payment_id,
pub(super) fn claim_htlc<L: Deref>(
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
- path: Vec<RouteHop>, from_onchain: bool, pending_events: &Mutex<Vec<events::Event>>, logger: &L
+ path: Path, from_onchain: bool, pending_events: &Mutex<Vec<events::Event>>, logger: &L
) where L::Target: Logger {
let mut session_priv_bytes = [0; 32];
session_priv_bytes.copy_from_slice(&session_priv[..]);
// Returns a bool indicating whether a PendingHTLCsForwardable event should be generated.
pub(super) fn fail_htlc<L: Deref>(
&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason,
- path: &Vec<RouteHop>, session_priv: &SecretKey, payment_id: &PaymentId,
- probing_cookie_secret: [u8; 32], secp_ctx: &Secp256k1<secp256k1::All>,
- pending_events: &Mutex<Vec<events::Event>>, logger: &L
+ path: &Path, session_priv: &SecretKey, payment_id: &PaymentId, probing_cookie_secret: [u8; 32],
+ secp_ctx: &Secp256k1<secp256k1::All>, pending_events: &Mutex<Vec<events::Event>>, 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);
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;
};
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()));
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;
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);
}
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);
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,
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.
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]
// 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()),
};
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 {
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"),
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 {
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"),
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,
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,
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)),
};
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,
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,
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,
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)),
};
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,
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,
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)),
};
// 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());
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()));
.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);
.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);
.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);
.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,
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,
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);
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 }
});
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;
}
}
- 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)
}
}
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;
// 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)))
(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<RouteHop>,
+}
+
+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)]
/// 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<Vec<RouteHop>>,
+ pub paths: Vec<Path>,
/// 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`].
pub payment_params: Option<PaymentParameters>,
}
-// 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<RouteHop> {
- 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`].
///
fn write<W: crate::util::ser::Writer>(&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)?;
}
}
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, {
}
}
+ let mut paths: Vec<Path> = 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::<Result<Vec<_>, _>>()?,
+ paths,
payment_params: Some(payment_params.clone()),
};
log_info!(logger, "Got route to {}: {}", payment_params.payee_pubkey, log_route!(route));
// 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<NodeId> = 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]);
// 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),
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);
}
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 {
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};
} 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]
} 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]
// 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]
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);
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);
}
// 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::<Vec<_>>()), 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::<u8>::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::<u8>::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]
// 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::<Vec<_>>()), 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::<u8>::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::<u8>::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
// 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::<Vec<_>>()), 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::<u8>::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::<u8>::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<PublicKey>) -> Vec<RouteHint> {
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::<u8>::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::<u8>::new()); // We can't learn any flags from invoices, sadly
}
fn empty_last_hop(nodes: &Vec<PublicKey>) -> Vec<RouteHint> {
// 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::<u8>::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::<u8>::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
});
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::<u8>::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::<u8>::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::<u8>::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::<u8>::new()); // We can't learn any flags from invoices, sadly
}
#[test]
});
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::<u8>::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::<u8>::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::<u8>::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::<u8>::new()); // We can't learn any flags from invoices, sadly
}
fn last_hops_with_public_channel(nodes: &Vec<PublicKey>) -> Vec<RouteHint> {
// 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::<u8>::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::<u8>::new()); // We can't learn any flags from invoices, sadly
}
#[test]
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::<Vec<_>>()), 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::<u8>::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::<u8>::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::<u8>::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::<u8>::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::<u8>::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::<u8>::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::<u8>::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::<u8>::new()); // We can't learn any flags from invoices, sadly
}
fn do_unannounced_path_test(last_hop_htlc_max: Option<u64>, last_hop_fee_prop: u32, outbound_capacity_msat: u64, route_val: u64) -> Result<Route, LightningError> {
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]
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);
}
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 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);
}
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);
}
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);
}
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);
}
}
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);
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);
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);
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);
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);
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);
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;
}
}
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);
// 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);
}
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);
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);
// 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));
}
}
// 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));
}
}
// 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));
}
}
&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(&[
&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);
}
{
&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);
}
}
&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::<Vec<_>>();
+ let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
assert_eq!(route.get_total_fees(), 100);
assert_eq!(route.get_total_amount(), 100);
&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::<Vec<_>>();
+ let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
assert_eq!(route.get_total_fees(), 300);
assert_eq!(route.get_total_amount(), 100);
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 {
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]
&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::<Vec<_>>();
+ let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
assert_eq!(route.get_total_fees(), 100);
assert_eq!(route.get_total_amount(), 100);
&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::<Vec<_>>();
+ let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
assert_eq!(route.get_total_fees(), 300);
assert_eq!(route.get_total_amount(), 100);
#[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(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0
},
- ]],
+ ]}],
payment_params: None,
};
#[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(),
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(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
},
- ]],
+ ]}],
payment_params: None,
};
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::<Vec<_>>();
+ let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
assert_ne!(path.len(), 0);
// But not if we exclude all paths on the basis of their accumulated CLTV delta
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; }
}
}
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::<Vec<_>>();
+ let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
assert!(path.len() == MAX_PATH_LENGTH_ESTIMATE.into());
// But we can't create a path surpassing the MAX_PATH_LENGTH_ESTIMATE limit.
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::<Vec<u32>>();
+ let cltv_expiry_deltas_before = route.paths[0].hops.iter().map(|h| h.cltv_expiry_delta).collect::<Vec<u32>>();
// 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::<Vec<u32>>();
+ let cltv_expiry_deltas_default = route_default.paths[0].hops.iter().map(|h| h.cltv_expiry_delta).collect::<Vec<u32>>();
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);
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::<Vec<u32>>();
+ let cltv_expiry_deltas_limited = route_limited.paths[0].hops.iter().map(|h| h.cltv_expiry_delta).collect::<Vec<u32>>();
assert_eq!(cltv_expiry_deltas_before, cltv_expiry_deltas_limited);
}
let mut random_bytes = [0u8; ::core::mem::size_of::<usize>()];
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<u32>)> = VecDeque::new();
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::<Vec<_>>();
- assert_eq!(hops.len(), route.paths[0].len());
+ let route_hop_pubkeys = route.paths[0].hops.iter().map(|hop| hop.pubkey).collect::<Vec<_>>();
+ assert_eq!(hops.len(), route.paths[0].hops.len());
for (idx, hop_pubkey) in hops.iter().enumerate() {
assert!(*hop_pubkey == route_hop_pubkeys[idx]);
}
// 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"))]
let amount = route.get_total_amount();
if amount < 250_000 {
for path in route.paths {
- scorer.payment_path_successful(&path.iter().collect::<Vec<_>>());
+ 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::<Vec<_>>(), short_channel_id);
+ let short_channel_id = path.hops[path.hops.len() / 2].short_channel_id;
+ scorer.payment_path_failed(&path, short_channel_id);
}
}
}
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;
) -> 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<S: Score, T: DerefMut<Target=S> $(+ $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)
}
}
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)
}
}
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 {
.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)
}
}
- 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)
}
}
- 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())
}
}
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;
}
}
- fn payment_path_for_amount(amount_msat: u64) -> Vec<RouteHop> {
- 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]
assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301);
- scorer.payment_path_failed(&failed_path.iter().collect::<Vec<_>>(), 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::<Vec<_>>());
+ scorer.payment_path_successful(&successful_path);
assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301);
}
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::<Vec<_>>(), 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);
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::<Vec<_>>(), 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);
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::<Vec<_>>(), 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
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::<Vec<_>>());
+ 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);
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::<Vec<_>>(), 42);
- scorer.payment_path_failed(&payment_path_for_amount(128).iter().collect::<Vec<_>>(), 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);
};
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
- scorer.payment_path_failed(&payment_path_for_amount(512).iter().collect::<Vec<_>>(), 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
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::<Vec<_>>(), 42);
- scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::<Vec<_>>(), 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.
// 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::<Vec<_>>());
+ 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::<Vec<_>>(), 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).
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::<Vec<_>>(), 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::<Vec<_>>(), 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();
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::<Vec<_>>(), 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();
<ProbabilisticScorer>::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::<Vec<_>>(), 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));
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
None);
- scorer.payment_path_failed(&payment_path_for_amount(1).iter().collect::<Vec<_>>(), 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.
// 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::<Vec<_>>(), 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),
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::<Vec<_>>(), 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 {
path_hop(source_pubkey(), 42, 1),
path_hop(sender_pubkey(), 41, 0),
];
- scorer.payment_path_failed(&path.iter().collect::<Vec<_>>(), 42);
+ scorer.payment_path_failed(&Path { hops: path }, 42);
}
#[test]
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)?;
}
}
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};
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,
// 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);
}
}
}
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 {