mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-24 23:08:36 +01:00
Expand the Route object to include multiple paths.
Rather big diff, but its all mechanical and doesn't introduce any new features.
This commit is contained in:
parent
d39f25839a
commit
5260e81033
7 changed files with 319 additions and 277 deletions
|
@ -409,14 +409,14 @@ pub fn do_test(data: &[u8]) {
|
||||||
let payment_hash = Sha256::hash(&[payment_id; 1]);
|
let payment_hash = Sha256::hash(&[payment_id; 1]);
|
||||||
payment_id = payment_id.wrapping_add(1);
|
payment_id = payment_id.wrapping_add(1);
|
||||||
if let Err(_) = $source.send_payment(Route {
|
if let Err(_) = $source.send_payment(Route {
|
||||||
hops: vec![RouteHop {
|
paths: vec![vec![RouteHop {
|
||||||
pubkey: $dest.0.get_our_node_id(),
|
pubkey: $dest.0.get_our_node_id(),
|
||||||
node_features: NodeFeatures::empty(),
|
node_features: NodeFeatures::empty(),
|
||||||
short_channel_id: $dest.1,
|
short_channel_id: $dest.1,
|
||||||
channel_features: ChannelFeatures::empty(),
|
channel_features: ChannelFeatures::empty(),
|
||||||
fee_msat: 5000000,
|
fee_msat: 5000000,
|
||||||
cltv_expiry_delta: 200,
|
cltv_expiry_delta: 200,
|
||||||
}],
|
}]],
|
||||||
}, PaymentHash(payment_hash.into_inner()), &None) {
|
}, PaymentHash(payment_hash.into_inner()), &None) {
|
||||||
// Probably ran out of funds
|
// Probably ran out of funds
|
||||||
test_return!();
|
test_return!();
|
||||||
|
@ -426,7 +426,7 @@ pub fn do_test(data: &[u8]) {
|
||||||
let payment_hash = Sha256::hash(&[payment_id; 1]);
|
let payment_hash = Sha256::hash(&[payment_id; 1]);
|
||||||
payment_id = payment_id.wrapping_add(1);
|
payment_id = payment_id.wrapping_add(1);
|
||||||
if let Err(_) = $source.send_payment(Route {
|
if let Err(_) = $source.send_payment(Route {
|
||||||
hops: vec![RouteHop {
|
paths: vec![vec![RouteHop {
|
||||||
pubkey: $middle.0.get_our_node_id(),
|
pubkey: $middle.0.get_our_node_id(),
|
||||||
node_features: NodeFeatures::empty(),
|
node_features: NodeFeatures::empty(),
|
||||||
short_channel_id: $middle.1,
|
short_channel_id: $middle.1,
|
||||||
|
@ -440,7 +440,7 @@ pub fn do_test(data: &[u8]) {
|
||||||
channel_features: ChannelFeatures::empty(),
|
channel_features: ChannelFeatures::empty(),
|
||||||
fee_msat: 5000000,
|
fee_msat: 5000000,
|
||||||
cltv_expiry_delta: 200,
|
cltv_expiry_delta: 200,
|
||||||
}],
|
}]],
|
||||||
}, PaymentHash(payment_hash.into_inner()), &None) {
|
}, PaymentHash(payment_hash.into_inner()), &None) {
|
||||||
// Probably ran out of funds
|
// Probably ran out of funds
|
||||||
test_return!();
|
test_return!();
|
||||||
|
|
|
@ -30,7 +30,7 @@ use chain::transaction::OutPoint;
|
||||||
use ln::channel::{Channel, ChannelError};
|
use ln::channel::{Channel, ChannelError};
|
||||||
use ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, ManyChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
|
use ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, ManyChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
|
||||||
use ln::features::{InitFeatures, NodeFeatures};
|
use ln::features::{InitFeatures, NodeFeatures};
|
||||||
use ln::router::Route;
|
use ln::router::{Route, RouteHop};
|
||||||
use ln::msgs;
|
use ln::msgs;
|
||||||
use ln::onion_utils;
|
use ln::onion_utils;
|
||||||
use ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
|
use ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
|
||||||
|
@ -136,7 +136,7 @@ struct ClaimableHTLC {
|
||||||
pub(super) enum HTLCSource {
|
pub(super) enum HTLCSource {
|
||||||
PreviousHopData(HTLCPreviousHopData),
|
PreviousHopData(HTLCPreviousHopData),
|
||||||
OutboundRoute {
|
OutboundRoute {
|
||||||
route: Route,
|
path: Vec<RouteHop>,
|
||||||
session_priv: SecretKey,
|
session_priv: SecretKey,
|
||||||
/// Technically we can recalculate this from the route, but we cache it here to avoid
|
/// 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
|
/// doing a double-pass on route when we get a failure back
|
||||||
|
@ -147,7 +147,7 @@ pub(super) enum HTLCSource {
|
||||||
impl HTLCSource {
|
impl HTLCSource {
|
||||||
pub fn dummy() -> Self {
|
pub fn dummy() -> Self {
|
||||||
HTLCSource::OutboundRoute {
|
HTLCSource::OutboundRoute {
|
||||||
route: Route { hops: Vec::new() },
|
path: Vec::new(),
|
||||||
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
|
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
|
||||||
first_hop_htlc_msat: 0,
|
first_hop_htlc_msat: 0,
|
||||||
}
|
}
|
||||||
|
@ -1231,13 +1231,16 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
|
||||||
/// bit set (either as required or as available). If multiple paths are present in the Route,
|
/// bit set (either as required or as available). If multiple paths are present in the Route,
|
||||||
/// we assume the invoice had the basic_mpp feature set.
|
/// we assume the invoice had the basic_mpp feature set.
|
||||||
pub fn send_payment(&self, route: Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>) -> Result<(), APIError> {
|
pub fn send_payment(&self, route: Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>) -> Result<(), APIError> {
|
||||||
if route.hops.len() < 1 || route.hops.len() > 20 {
|
if route.paths.len() < 1 || route.paths.len() > 1 {
|
||||||
return Err(APIError::RouteError{err: "Route didn't go anywhere/had bogus size"});
|
return Err(APIError::RouteError{err: "We currently don't support MPP, and we need at least one path"});
|
||||||
|
}
|
||||||
|
if route.paths[0].len() < 1 || route.paths[0].len() > 20 {
|
||||||
|
return Err(APIError::RouteError{err: "Path didn't go anywhere/had bogus size"});
|
||||||
}
|
}
|
||||||
let our_node_id = self.get_our_node_id();
|
let our_node_id = self.get_our_node_id();
|
||||||
for (idx, hop) in route.hops.iter().enumerate() {
|
for (idx, hop) in route.paths[0].iter().enumerate() {
|
||||||
if idx != route.hops.len() - 1 && hop.pubkey == our_node_id {
|
if idx != route.paths[0].len() - 1 && hop.pubkey == our_node_id {
|
||||||
return Err(APIError::RouteError{err: "Route went through us but wasn't a simple rebalance loop to us"});
|
return Err(APIError::RouteError{err: "Path went through us but wasn't a simple rebalance loop to us"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1245,9 +1248,9 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
|
||||||
|
|
||||||
let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
||||||
|
|
||||||
let onion_keys = secp_call!(onion_utils::construct_onion_keys(&self.secp_ctx, &route, &session_priv),
|
let onion_keys = secp_call!(onion_utils::construct_onion_keys(&self.secp_ctx, &route.paths[0], &session_priv),
|
||||||
APIError::RouteError{err: "Pubkey along hop was maliciously selected"});
|
APIError::RouteError{err: "Pubkey along hop was maliciously selected"});
|
||||||
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route, payment_secret, cur_height)?;
|
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], payment_secret, cur_height)?;
|
||||||
if onion_utils::route_size_insane(&onion_payloads) {
|
if onion_utils::route_size_insane(&onion_payloads) {
|
||||||
return Err(APIError::RouteError{err: "Route size too large considering onion data"});
|
return Err(APIError::RouteError{err: "Route size too large considering onion data"});
|
||||||
}
|
}
|
||||||
|
@ -1257,7 +1260,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
|
||||||
|
|
||||||
let err: Result<(), _> = loop {
|
let err: Result<(), _> = loop {
|
||||||
let mut channel_lock = self.channel_state.lock().unwrap();
|
let mut channel_lock = self.channel_state.lock().unwrap();
|
||||||
let id = match channel_lock.short_to_id.get(&route.hops.first().unwrap().short_channel_id) {
|
let id = match channel_lock.short_to_id.get(&route.paths[0].first().unwrap().short_channel_id) {
|
||||||
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!"}),
|
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!"}),
|
||||||
Some(id) => id.clone(),
|
Some(id) => id.clone(),
|
||||||
};
|
};
|
||||||
|
@ -1265,14 +1268,14 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
|
||||||
let channel_state = &mut *channel_lock;
|
let channel_state = &mut *channel_lock;
|
||||||
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
|
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
|
||||||
match {
|
match {
|
||||||
if chan.get().get_their_node_id() != route.hops.first().unwrap().pubkey {
|
if chan.get().get_their_node_id() != route.paths[0].first().unwrap().pubkey {
|
||||||
return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"});
|
return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"});
|
||||||
}
|
}
|
||||||
if !chan.get().is_live() {
|
if !chan.get().is_live() {
|
||||||
return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!"});
|
return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!"});
|
||||||
}
|
}
|
||||||
break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute {
|
break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute {
|
||||||
route: route.clone(),
|
path: route.paths[0].clone(),
|
||||||
session_priv: session_priv.clone(),
|
session_priv: session_priv.clone(),
|
||||||
first_hop_htlc_msat: htlc_msat,
|
first_hop_htlc_msat: htlc_msat,
|
||||||
}, onion_packet), channel_state, chan)
|
}, onion_packet), channel_state, chan)
|
||||||
|
@ -1288,7 +1291,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
|
channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
|
||||||
node_id: route.hops.first().unwrap().pubkey,
|
node_id: route.paths[0].first().unwrap().pubkey,
|
||||||
updates: msgs::CommitmentUpdate {
|
updates: msgs::CommitmentUpdate {
|
||||||
update_add_htlcs: vec![update_add],
|
update_add_htlcs: vec![update_add],
|
||||||
update_fulfill_htlcs: Vec::new(),
|
update_fulfill_htlcs: Vec::new(),
|
||||||
|
@ -1305,7 +1308,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
match handle_error!(self, err, route.hops.first().unwrap().pubkey) {
|
match handle_error!(self, err, route.paths[0].first().unwrap().pubkey) {
|
||||||
Ok(_) => unreachable!(),
|
Ok(_) => unreachable!(),
|
||||||
Err(e) => { Err(APIError::ChannelUnavailable { err: e.err }) }
|
Err(e) => { Err(APIError::ChannelUnavailable { err: e.err }) }
|
||||||
}
|
}
|
||||||
|
@ -1750,7 +1753,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
|
||||||
//between the branches here. We should make this async and move it into the forward HTLCs
|
//between the branches here. We should make this async and move it into the forward HTLCs
|
||||||
//timer handling.
|
//timer handling.
|
||||||
match source {
|
match source {
|
||||||
HTLCSource::OutboundRoute { ref route, .. } => {
|
HTLCSource::OutboundRoute { ref path, .. } => {
|
||||||
log_trace!(self, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
|
log_trace!(self, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
|
||||||
mem::drop(channel_state_lock);
|
mem::drop(channel_state_lock);
|
||||||
match &onion_error {
|
match &onion_error {
|
||||||
|
@ -1792,7 +1795,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
|
||||||
self.pending_events.lock().unwrap().push(
|
self.pending_events.lock().unwrap().push(
|
||||||
events::Event::PaymentFailed {
|
events::Event::PaymentFailed {
|
||||||
payment_hash: payment_hash.clone(),
|
payment_hash: payment_hash.clone(),
|
||||||
rejected_by_dest: route.hops.len() == 1,
|
rejected_by_dest: path.len() == 1,
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
error_code: Some(*failure_code),
|
error_code: Some(*failure_code),
|
||||||
}
|
}
|
||||||
|
@ -1856,9 +1859,19 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
|
||||||
let mut channel_state = Some(self.channel_state.lock().unwrap());
|
let mut channel_state = Some(self.channel_state.lock().unwrap());
|
||||||
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&(payment_hash, *payment_secret));
|
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&(payment_hash, *payment_secret));
|
||||||
if let Some(mut sources) = removed_source {
|
if let Some(mut sources) = removed_source {
|
||||||
|
assert!(!sources.is_empty());
|
||||||
|
let valid_mpp_amount = if let &Some(ref data) = &sources[0].payment_data {
|
||||||
|
assert!(payment_secret.is_some());
|
||||||
|
data.total_msat == expected_amount
|
||||||
|
} else {
|
||||||
|
assert!(payment_secret.is_none());
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut claimed_any_htlcs = false;
|
||||||
for htlc in sources.drain(..) {
|
for htlc in sources.drain(..) {
|
||||||
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
|
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
|
||||||
if htlc.value < expected_amount || htlc.value > expected_amount * 2 {
|
if !valid_mpp_amount && (htlc.value < expected_amount || htlc.value > expected_amount * 2) {
|
||||||
let mut htlc_msat_data = byte_utils::be64_to_array(htlc.value).to_vec();
|
let mut htlc_msat_data = byte_utils::be64_to_array(htlc.value).to_vec();
|
||||||
let mut height_data = byte_utils::be32_to_array(self.latest_block_height.load(Ordering::Acquire) as u32).to_vec();
|
let mut height_data = byte_utils::be32_to_array(self.latest_block_height.load(Ordering::Acquire) as u32).to_vec();
|
||||||
htlc_msat_data.append(&mut height_data);
|
htlc_msat_data.append(&mut height_data);
|
||||||
|
@ -1867,9 +1880,10 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
|
||||||
HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_data });
|
HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_data });
|
||||||
} else {
|
} else {
|
||||||
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc.prev_hop), payment_preimage);
|
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc.prev_hop), payment_preimage);
|
||||||
|
claimed_any_htlcs = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
claimed_any_htlcs
|
||||||
} else { false }
|
} else { false }
|
||||||
}
|
}
|
||||||
fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard<ChannelHolder<ChanSigner>>, source: HTLCSource, payment_preimage: PaymentPreimage) {
|
fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard<ChannelHolder<ChanSigner>>, source: HTLCSource, payment_preimage: PaymentPreimage) {
|
||||||
|
@ -3271,9 +3285,9 @@ impl Writeable for HTLCSource {
|
||||||
0u8.write(writer)?;
|
0u8.write(writer)?;
|
||||||
hop_data.write(writer)?;
|
hop_data.write(writer)?;
|
||||||
},
|
},
|
||||||
&HTLCSource::OutboundRoute { ref route, ref session_priv, ref first_hop_htlc_msat } => {
|
&HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat } => {
|
||||||
1u8.write(writer)?;
|
1u8.write(writer)?;
|
||||||
route.write(writer)?;
|
path.write(writer)?;
|
||||||
session_priv.write(writer)?;
|
session_priv.write(writer)?;
|
||||||
first_hop_htlc_msat.write(writer)?;
|
first_hop_htlc_msat.write(writer)?;
|
||||||
}
|
}
|
||||||
|
@ -3287,7 +3301,7 @@ impl Readable for HTLCSource {
|
||||||
match <u8 as Readable>::read(reader)? {
|
match <u8 as Readable>::read(reader)? {
|
||||||
0 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
|
0 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
|
||||||
1 => Ok(HTLCSource::OutboundRoute {
|
1 => Ok(HTLCSource::OutboundRoute {
|
||||||
route: Readable::read(reader)?,
|
path: Readable::read(reader)?,
|
||||||
session_priv: Readable::read(reader)?,
|
session_priv: Readable::read(reader)?,
|
||||||
first_hop_htlc_msat: Readable::read(reader)?,
|
first_hop_htlc_msat: Readable::read(reader)?,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -897,8 +897,9 @@ pub const TEST_FINAL_CLTV: u32 = 32;
|
||||||
|
|
||||||
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash) {
|
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash) {
|
||||||
let route = origin_node.router.get_route(&expected_route.last().unwrap().node.get_our_node_id(), None, &Vec::new(), recv_value, TEST_FINAL_CLTV).unwrap();
|
let route = origin_node.router.get_route(&expected_route.last().unwrap().node.get_our_node_id(), None, &Vec::new(), recv_value, TEST_FINAL_CLTV).unwrap();
|
||||||
assert_eq!(route.hops.len(), expected_route.len());
|
assert_eq!(route.paths.len(), 1);
|
||||||
for (node, hop) in expected_route.iter().zip(route.hops.iter()) {
|
assert_eq!(route.paths[0].len(), expected_route.len());
|
||||||
|
for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
|
||||||
assert_eq!(hop.pubkey, node.node.get_our_node_id());
|
assert_eq!(hop.pubkey, node.node.get_our_node_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,8 +908,9 @@ pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route:
|
||||||
|
|
||||||
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
|
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
|
||||||
let route = origin_node.router.get_route(&expected_route.last().unwrap().node.get_our_node_id(), None, &Vec::new(), recv_value, TEST_FINAL_CLTV).unwrap();
|
let route = origin_node.router.get_route(&expected_route.last().unwrap().node.get_our_node_id(), None, &Vec::new(), recv_value, TEST_FINAL_CLTV).unwrap();
|
||||||
assert_eq!(route.hops.len(), expected_route.len());
|
assert_eq!(route.paths.len(), 1);
|
||||||
for (node, hop) in expected_route.iter().zip(route.hops.iter()) {
|
assert_eq!(route.paths[0].len(), expected_route.len());
|
||||||
|
for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
|
||||||
assert_eq!(hop.pubkey, node.node.get_our_node_id());
|
assert_eq!(hop.pubkey, node.node.get_our_node_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1226,7 +1226,7 @@ fn fake_network_test() {
|
||||||
});
|
});
|
||||||
hops[1].fee_msat = chan_4.1.contents.fee_base_msat as u64 + chan_4.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
|
hops[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;
|
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 { hops }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
|
let payment_preimage_1 = send_along_route(&nodes[1], Route { paths: vec![hops] }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
|
||||||
|
|
||||||
let mut hops = Vec::with_capacity(3);
|
let mut hops = Vec::with_capacity(3);
|
||||||
hops.push(RouteHop {
|
hops.push(RouteHop {
|
||||||
|
@ -1255,7 +1255,7 @@ fn fake_network_test() {
|
||||||
});
|
});
|
||||||
hops[1].fee_msat = chan_2.1.contents.fee_base_msat as u64 + chan_2.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
|
hops[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;
|
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 { hops }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
|
let payment_hash_2 = send_along_route(&nodes[1], Route { paths: vec![hops] }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
|
||||||
|
|
||||||
// Claim the rebalances...
|
// Claim the rebalances...
|
||||||
fail_payment(&nodes[1], &vec!(&nodes[3], &nodes[2], &nodes[1])[..], payment_hash_2);
|
fail_payment(&nodes[1], &vec!(&nodes[3], &nodes[2], &nodes[1])[..], payment_hash_2);
|
||||||
|
@ -1562,7 +1562,7 @@ fn do_channel_reserve_test(test_recv: bool) {
|
||||||
// attempt to send amt_msat > their_max_htlc_value_in_flight_msat
|
// attempt to send amt_msat > their_max_htlc_value_in_flight_msat
|
||||||
{
|
{
|
||||||
let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_0 + 1);
|
let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_0 + 1);
|
||||||
assert!(route.hops.iter().rev().skip(1).all(|h| h.fee_msat == feemsat));
|
assert!(route.paths[0].iter().rev().skip(1).all(|h| h.fee_msat == feemsat));
|
||||||
let err = nodes[0].node.send_payment(route, our_payment_hash, &None).err().unwrap();
|
let err = nodes[0].node.send_payment(route, our_payment_hash, &None).err().unwrap();
|
||||||
match err {
|
match err {
|
||||||
APIError::ChannelUnavailable{err} => assert_eq!(err, "Cannot send value that would put us over the max HTLC value in flight our peer will accept"),
|
APIError::ChannelUnavailable{err} => assert_eq!(err, "Cannot send value that would put us over the max HTLC value in flight our peer will accept"),
|
||||||
|
@ -1651,8 +1651,8 @@ fn do_channel_reserve_test(test_recv: bool) {
|
||||||
}).expect("RNG is bad!");
|
}).expect("RNG is bad!");
|
||||||
|
|
||||||
let cur_height = nodes[0].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
let cur_height = nodes[0].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route, &session_priv).unwrap();
|
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
|
||||||
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route, &None, cur_height).unwrap();
|
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], &None, cur_height).unwrap();
|
||||||
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash);
|
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash);
|
||||||
let msg = msgs::UpdateAddHTLC {
|
let msg = msgs::UpdateAddHTLC {
|
||||||
channel_id: chan_1.2,
|
channel_id: chan_1.2,
|
||||||
|
@ -3038,8 +3038,8 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_height = nodes[1].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
let current_height = nodes[1].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
||||||
let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads(&route, &None, current_height).unwrap();
|
let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads(&route.paths[0], &None, current_height).unwrap();
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route, &session_priv).unwrap();
|
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
|
||||||
let onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
|
let onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
|
||||||
|
|
||||||
// Send a 0-msat update_add_htlc to fail the channel.
|
// Send a 0-msat update_add_htlc to fail the channel.
|
||||||
|
@ -5470,8 +5470,8 @@ fn test_onion_failure() {
|
||||||
run_onion_failure_test("invalid_realm", 0, &nodes, &route, &payment_hash, |msg| {
|
run_onion_failure_test("invalid_realm", 0, &nodes, &route, &payment_hash, |msg| {
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let cur_height = nodes[0].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
let cur_height = nodes[0].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).unwrap();
|
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
|
||||||
let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route, &None, cur_height).unwrap();
|
let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], &None, cur_height).unwrap();
|
||||||
let mut new_payloads = Vec::new();
|
let mut new_payloads = Vec::new();
|
||||||
for payload in onion_payloads.drain(..) {
|
for payload in onion_payloads.drain(..) {
|
||||||
new_payloads.push(BogusOnionHopData::new(payload));
|
new_payloads.push(BogusOnionHopData::new(payload));
|
||||||
|
@ -5486,8 +5486,8 @@ fn test_onion_failure() {
|
||||||
run_onion_failure_test("invalid_realm", 3, &nodes, &route, &payment_hash, |msg| {
|
run_onion_failure_test("invalid_realm", 3, &nodes, &route, &payment_hash, |msg| {
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let cur_height = nodes[0].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
let cur_height = nodes[0].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).unwrap();
|
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
|
||||||
let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route, &None, cur_height).unwrap();
|
let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], &None, cur_height).unwrap();
|
||||||
let mut new_payloads = Vec::new();
|
let mut new_payloads = Vec::new();
|
||||||
for payload in onion_payloads.drain(..) {
|
for payload in onion_payloads.drain(..) {
|
||||||
new_payloads.push(BogusOnionHopData::new(payload));
|
new_payloads.push(BogusOnionHopData::new(payload));
|
||||||
|
@ -5507,57 +5507,57 @@ fn test_onion_failure() {
|
||||||
}, |msg| {
|
}, |msg| {
|
||||||
// and tamper returning error message
|
// and tamper returning error message
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).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[..], NODE|2, &[0;0]);
|
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], NODE|2, &[0;0]);
|
||||||
}, ||{}, true, Some(NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.hops[0].pubkey, is_permanent: false}));
|
}, ||{}, true, Some(NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: false}));
|
||||||
|
|
||||||
// final node failure
|
// final node failure
|
||||||
run_onion_failure_test_with_fail_intercept("temporary_node_failure", 200, &nodes, &route, &payment_hash, |_msg| {}, |msg| {
|
run_onion_failure_test_with_fail_intercept("temporary_node_failure", 200, &nodes, &route, &payment_hash, |_msg| {}, |msg| {
|
||||||
// and tamper returning error message
|
// and tamper returning error message
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).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[1].shared_secret[..], NODE|2, &[0;0]);
|
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[1].shared_secret[..], NODE|2, &[0;0]);
|
||||||
}, ||{
|
}, ||{
|
||||||
nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
|
nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
|
||||||
}, true, Some(NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.hops[1].pubkey, is_permanent: false}));
|
}, true, Some(NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: false}));
|
||||||
|
|
||||||
// intermediate node failure
|
// intermediate node failure
|
||||||
run_onion_failure_test_with_fail_intercept("permanent_node_failure", 100, &nodes, &route, &payment_hash, |msg| {
|
run_onion_failure_test_with_fail_intercept("permanent_node_failure", 100, &nodes, &route, &payment_hash, |msg| {
|
||||||
msg.amount_msat -= 1;
|
msg.amount_msat -= 1;
|
||||||
}, |msg| {
|
}, |msg| {
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).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[..], PERM|NODE|2, &[0;0]);
|
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|NODE|2, &[0;0]);
|
||||||
}, ||{}, true, Some(PERM|NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.hops[0].pubkey, is_permanent: true}));
|
}, ||{}, true, Some(PERM|NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
|
||||||
|
|
||||||
// final node failure
|
// final node failure
|
||||||
run_onion_failure_test_with_fail_intercept("permanent_node_failure", 200, &nodes, &route, &payment_hash, |_msg| {}, |msg| {
|
run_onion_failure_test_with_fail_intercept("permanent_node_failure", 200, &nodes, &route, &payment_hash, |_msg| {}, |msg| {
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).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[1].shared_secret[..], PERM|NODE|2, &[0;0]);
|
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[1].shared_secret[..], PERM|NODE|2, &[0;0]);
|
||||||
}, ||{
|
}, ||{
|
||||||
nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
|
nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
|
||||||
}, false, Some(PERM|NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.hops[1].pubkey, is_permanent: true}));
|
}, false, Some(PERM|NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
|
||||||
|
|
||||||
// intermediate node failure
|
// intermediate node failure
|
||||||
run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 100, &nodes, &route, &payment_hash, |msg| {
|
run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 100, &nodes, &route, &payment_hash, |msg| {
|
||||||
msg.amount_msat -= 1;
|
msg.amount_msat -= 1;
|
||||||
}, |msg| {
|
}, |msg| {
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).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[..], PERM|NODE|3, &[0;0]);
|
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|NODE|3, &[0;0]);
|
||||||
}, ||{
|
}, ||{
|
||||||
nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
|
nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
|
||||||
}, true, Some(PERM|NODE|3), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.hops[0].pubkey, is_permanent: true}));
|
}, true, Some(PERM|NODE|3), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
|
||||||
|
|
||||||
// final node failure
|
// final node failure
|
||||||
run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 200, &nodes, &route, &payment_hash, |_msg| {}, |msg| {
|
run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 200, &nodes, &route, &payment_hash, |_msg| {}, |msg| {
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).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[1].shared_secret[..], PERM|NODE|3, &[0;0]);
|
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[1].shared_secret[..], PERM|NODE|3, &[0;0]);
|
||||||
}, ||{
|
}, ||{
|
||||||
nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
|
nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
|
||||||
}, false, Some(PERM|NODE|3), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.hops[1].pubkey, is_permanent: true}));
|
}, false, Some(PERM|NODE|3), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
|
||||||
|
|
||||||
run_onion_failure_test("invalid_onion_version", 0, &nodes, &route, &payment_hash, |msg| { msg.onion_routing_packet.version = 1; }, ||{}, true,
|
run_onion_failure_test("invalid_onion_version", 0, &nodes, &route, &payment_hash, |msg| { msg.onion_routing_packet.version = 1; }, ||{}, true,
|
||||||
Some(BADONION|PERM|4), None);
|
Some(BADONION|PERM|4), None);
|
||||||
|
@ -5572,7 +5572,7 @@ fn test_onion_failure() {
|
||||||
msg.amount_msat -= 1;
|
msg.amount_msat -= 1;
|
||||||
}, |msg| {
|
}, |msg| {
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).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[..], UPDATE|7, &ChannelUpdate::dummy().encode_with_len()[..]);
|
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], UPDATE|7, &ChannelUpdate::dummy().encode_with_len()[..]);
|
||||||
}, ||{}, true, Some(UPDATE|7), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
|
}, ||{}, true, Some(UPDATE|7), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
|
||||||
|
|
||||||
|
@ -5580,7 +5580,7 @@ fn test_onion_failure() {
|
||||||
msg.amount_msat -= 1;
|
msg.amount_msat -= 1;
|
||||||
}, |msg| {
|
}, |msg| {
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).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[..], PERM|8, &[0;0]);
|
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|8, &[0;0]);
|
||||||
// short_channel_id from the processing node
|
// short_channel_id from the processing node
|
||||||
}, ||{}, true, Some(PERM|8), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
|
}, ||{}, true, Some(PERM|8), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
|
||||||
|
@ -5589,20 +5589,20 @@ fn test_onion_failure() {
|
||||||
msg.amount_msat -= 1;
|
msg.amount_msat -= 1;
|
||||||
}, |msg| {
|
}, |msg| {
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).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[..], PERM|9, &[0;0]);
|
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|9, &[0;0]);
|
||||||
// short_channel_id from the processing node
|
// short_channel_id from the processing node
|
||||||
}, ||{}, true, Some(PERM|9), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
|
}, ||{}, true, Some(PERM|9), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
|
||||||
|
|
||||||
let mut bogus_route = route.clone();
|
let mut bogus_route = route.clone();
|
||||||
bogus_route.hops[1].short_channel_id -= 1;
|
bogus_route.paths[0][1].short_channel_id -= 1;
|
||||||
run_onion_failure_test("unknown_next_peer", 0, &nodes, &bogus_route, &payment_hash, |_| {}, ||{}, true, Some(PERM|10),
|
run_onion_failure_test("unknown_next_peer", 0, &nodes, &bogus_route, &payment_hash, |_| {}, ||{}, true, Some(PERM|10),
|
||||||
Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: bogus_route.hops[1].short_channel_id, is_permanent:true}));
|
Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: bogus_route.paths[0][1].short_channel_id, is_permanent:true}));
|
||||||
|
|
||||||
let amt_to_forward = nodes[1].node.channel_state.lock().unwrap().by_id.get(&channels[1].2).unwrap().get_their_htlc_minimum_msat() - 1;
|
let amt_to_forward = nodes[1].node.channel_state.lock().unwrap().by_id.get(&channels[1].2).unwrap().get_their_htlc_minimum_msat() - 1;
|
||||||
let mut bogus_route = route.clone();
|
let mut bogus_route = route.clone();
|
||||||
let route_len = bogus_route.hops.len();
|
let route_len = bogus_route.paths[0].len();
|
||||||
bogus_route.hops[route_len-1].fee_msat = amt_to_forward;
|
bogus_route.paths[0][route_len-1].fee_msat = amt_to_forward;
|
||||||
run_onion_failure_test("amount_below_minimum", 0, &nodes, &bogus_route, &payment_hash, |_| {}, ||{}, true, Some(UPDATE|11), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
|
run_onion_failure_test("amount_below_minimum", 0, &nodes, &bogus_route, &payment_hash, |_| {}, ||{}, true, Some(UPDATE|11), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
|
||||||
|
|
||||||
//TODO: with new config API, we will be able to generate both valid and
|
//TODO: with new config API, we will be able to generate both valid and
|
||||||
|
@ -5670,9 +5670,9 @@ fn test_onion_failure() {
|
||||||
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
|
||||||
let mut route = route.clone();
|
let mut route = route.clone();
|
||||||
let height = 1;
|
let height = 1;
|
||||||
route.hops[1].cltv_expiry_delta += CLTV_FAR_FAR_AWAY + route.hops[0].cltv_expiry_delta + 1;
|
route.paths[0][1].cltv_expiry_delta += CLTV_FAR_FAR_AWAY + route.paths[0][0].cltv_expiry_delta + 1;
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).unwrap();
|
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, &None, height).unwrap();
|
let (onion_payloads, _, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], &None, height).unwrap();
|
||||||
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
|
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
|
||||||
msg.cltv_expiry = htlc_cltv;
|
msg.cltv_expiry = htlc_cltv;
|
||||||
msg.onion_routing_packet = onion_packet;
|
msg.onion_routing_packet = onion_packet;
|
||||||
|
@ -5762,7 +5762,7 @@ fn test_update_add_htlc_bolt2_sender_value_below_minimum_msat() {
|
||||||
let mut route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV).unwrap();
|
let mut route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV).unwrap();
|
||||||
let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]);
|
let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]);
|
||||||
|
|
||||||
route.hops[0].fee_msat = 100;
|
route.paths[0][0].fee_msat = 100;
|
||||||
|
|
||||||
let err = nodes[0].node.send_payment(route, our_payment_hash, &None);
|
let err = nodes[0].node.send_payment(route, our_payment_hash, &None);
|
||||||
|
|
||||||
|
@ -5786,7 +5786,7 @@ fn test_update_add_htlc_bolt2_sender_zero_value_msat() {
|
||||||
let mut route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV).unwrap();
|
let mut route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV).unwrap();
|
||||||
let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]);
|
let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]);
|
||||||
|
|
||||||
route.hops[0].fee_msat = 0;
|
route.paths[0][0].fee_msat = 0;
|
||||||
|
|
||||||
let err = nodes[0].node.send_payment(route, our_payment_hash, &None);
|
let err = nodes[0].node.send_payment(route, our_payment_hash, &None);
|
||||||
|
|
||||||
|
@ -5992,8 +5992,8 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
|
||||||
}).expect("RNG is bad!");
|
}).expect("RNG is bad!");
|
||||||
|
|
||||||
let cur_height = nodes[0].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
let cur_height = nodes[0].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
|
||||||
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::signing_only(), &route, &session_priv).unwrap();
|
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::signing_only(), &route.paths[0], &session_priv).unwrap();
|
||||||
let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route, &None, cur_height).unwrap();
|
let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], &None, cur_height).unwrap();
|
||||||
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash);
|
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash);
|
||||||
|
|
||||||
let mut msg = msgs::UpdateAddHTLC {
|
let mut msg = msgs::UpdateAddHTLC {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ln::channelmanager::{PaymentHash, PaymentSecret, HTLCSource};
|
use ln::channelmanager::{PaymentHash, PaymentSecret, HTLCSource};
|
||||||
use ln::msgs;
|
use ln::msgs;
|
||||||
use ln::router::{Route,RouteHop};
|
use ln::router::RouteHop;
|
||||||
use util::byte_utils;
|
use util::byte_utils;
|
||||||
use util::chacha20::ChaCha20;
|
use util::chacha20::ChaCha20;
|
||||||
use util::errors::{self, APIError};
|
use util::errors::{self, APIError};
|
||||||
|
@ -63,11 +63,11 @@ pub(super) fn gen_ammag_from_shared_secret(shared_secret: &[u8]) -> [u8; 32] {
|
||||||
|
|
||||||
// can only fail if an intermediary hop has an invalid public key or session_priv is invalid
|
// can only fail if an intermediary hop has an invalid public key or session_priv is invalid
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(SharedSecret, [u8; 32], PublicKey, &RouteHop)> (secp_ctx: &Secp256k1<T>, route: &Route, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {
|
pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(SharedSecret, [u8; 32], PublicKey, &RouteHop)> (secp_ctx: &Secp256k1<T>, path: &Vec<RouteHop>, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {
|
||||||
let mut blinded_priv = session_priv.clone();
|
let mut blinded_priv = session_priv.clone();
|
||||||
let mut blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv);
|
let mut blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv);
|
||||||
|
|
||||||
for hop in route.hops.iter() {
|
for hop in path.iter() {
|
||||||
let shared_secret = SharedSecret::new(&hop.pubkey, &blinded_priv);
|
let shared_secret = SharedSecret::new(&hop.pubkey, &blinded_priv);
|
||||||
|
|
||||||
let mut sha = Sha256::engine();
|
let mut sha = Sha256::engine();
|
||||||
|
@ -87,10 +87,10 @@ pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(
|
||||||
}
|
}
|
||||||
|
|
||||||
// can only fail if an intermediary hop has an invalid public key or session_priv is invalid
|
// 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>, route: &Route, session_priv: &SecretKey) -> Result<Vec<OnionKeys>, secp256k1::Error> {
|
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(route.hops.len());
|
let mut res = Vec::with_capacity(path.len());
|
||||||
|
|
||||||
construct_onion_keys_callback(secp_ctx, route, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _| {
|
construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _| {
|
||||||
let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret[..]);
|
let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret[..]);
|
||||||
|
|
||||||
res.push(OnionKeys {
|
res.push(OnionKeys {
|
||||||
|
@ -108,13 +108,13 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
|
/// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
|
||||||
pub(super) fn build_onion_payloads(route: &Route, payment_secret_option: &Option<PaymentSecret>, starting_htlc_offset: u32) -> Result<(Vec<msgs::OnionHopData>, u64, u32), APIError> {
|
pub(super) fn build_onion_payloads(path: &Vec<RouteHop>, payment_secret_option: &Option<PaymentSecret>, starting_htlc_offset: u32) -> Result<(Vec<msgs::OnionHopData>, u64, u32), APIError> {
|
||||||
let mut cur_value_msat = 0u64;
|
let mut cur_value_msat = 0u64;
|
||||||
let mut cur_cltv = starting_htlc_offset;
|
let mut cur_cltv = starting_htlc_offset;
|
||||||
let mut last_short_channel_id = 0;
|
let mut last_short_channel_id = 0;
|
||||||
let mut res: Vec<msgs::OnionHopData> = Vec::with_capacity(route.hops.len());
|
let mut res: Vec<msgs::OnionHopData> = Vec::with_capacity(path.len());
|
||||||
|
|
||||||
for (idx, hop) in route.hops.iter().rev().enumerate() {
|
for (idx, hop) in path.iter().rev().enumerate() {
|
||||||
// First hop gets special values so that it can check, on receipt, that everything is
|
// 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
|
// exactly as it should be (and the next hop isn't trying to probe to find out if we're
|
||||||
// the intended recipient).
|
// the intended recipient).
|
||||||
|
@ -318,7 +318,7 @@ pub(super) fn build_first_hop_failure_packet(shared_secret: &[u8], failure_type:
|
||||||
/// OutboundRoute).
|
/// OutboundRoute).
|
||||||
/// Returns update, a boolean indicating that the payment itself failed, and the error code.
|
/// Returns update, a boolean indicating that the payment itself failed, and the error code.
|
||||||
pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, logger: &Arc<Logger>, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>) -> (Option<msgs::HTLCFailChannelUpdate>, bool, Option<u16>) {
|
pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, logger: &Arc<Logger>, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>) -> (Option<msgs::HTLCFailChannelUpdate>, bool, Option<u16>) {
|
||||||
if let &HTLCSource::OutboundRoute { ref route, ref session_priv, ref first_hop_htlc_msat } = htlc_source {
|
if let &HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat } = htlc_source {
|
||||||
let mut res = None;
|
let mut res = None;
|
||||||
let mut htlc_msat = *first_hop_htlc_msat;
|
let mut htlc_msat = *first_hop_htlc_msat;
|
||||||
let mut error_code_ret = None;
|
let mut error_code_ret = None;
|
||||||
|
@ -326,7 +326,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<
|
||||||
let mut is_from_final_node = false;
|
let mut is_from_final_node = false;
|
||||||
|
|
||||||
// Handle packed channel/node updates for passing back for the route handler
|
// Handle packed channel/node updates for passing back for the route handler
|
||||||
construct_onion_keys_callback(secp_ctx, route, session_priv, |shared_secret, _, _, route_hop| {
|
construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _, _, route_hop| {
|
||||||
next_route_hop_ix += 1;
|
next_route_hop_ix += 1;
|
||||||
if res.is_some() { return; }
|
if res.is_some() { return; }
|
||||||
|
|
||||||
|
@ -341,7 +341,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<
|
||||||
chacha.process(&packet_decrypted, &mut decryption_tmp[..]);
|
chacha.process(&packet_decrypted, &mut decryption_tmp[..]);
|
||||||
packet_decrypted = decryption_tmp;
|
packet_decrypted = decryption_tmp;
|
||||||
|
|
||||||
is_from_final_node = route.hops.last().unwrap().pubkey == route_hop.pubkey;
|
is_from_final_node = path.last().unwrap().pubkey == route_hop.pubkey;
|
||||||
|
|
||||||
if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
|
if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
|
||||||
let um = gen_um_from_shared_secret(&shared_secret[..]);
|
let um = gen_um_from_shared_secret(&shared_secret[..]);
|
||||||
|
@ -374,7 +374,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<
|
||||||
}
|
}
|
||||||
else if error_code & PERM == PERM {
|
else if error_code & PERM == PERM {
|
||||||
fail_channel_update = if payment_failed {None} else {Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
|
fail_channel_update = if payment_failed {None} else {Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
|
||||||
short_channel_id: route.hops[next_route_hop_ix - if next_route_hop_ix == route.hops.len() { 1 } else { 0 }].short_channel_id,
|
short_channel_id: path[next_route_hop_ix - if next_route_hop_ix == path.len() { 1 } else { 0 }].short_channel_id,
|
||||||
is_permanent: true,
|
is_permanent: true,
|
||||||
})};
|
})};
|
||||||
}
|
}
|
||||||
|
@ -485,7 +485,7 @@ mod tests {
|
||||||
let secp_ctx = Secp256k1::new();
|
let secp_ctx = Secp256k1::new();
|
||||||
|
|
||||||
let route = Route {
|
let route = Route {
|
||||||
hops: vec!(
|
paths: vec![vec![
|
||||||
RouteHop {
|
RouteHop {
|
||||||
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
|
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(),
|
||||||
|
@ -511,13 +511,13 @@ mod tests {
|
||||||
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 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
|
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
|
||||||
},
|
},
|
||||||
),
|
]],
|
||||||
};
|
};
|
||||||
|
|
||||||
let session_priv = SecretKey::from_slice(&hex::decode("4141414141414141414141414141414141414141414141414141414141414141").unwrap()[..]).unwrap();
|
let session_priv = SecretKey::from_slice(&hex::decode("4141414141414141414141414141414141414141414141414141414141414141").unwrap()[..]).unwrap();
|
||||||
|
|
||||||
let onion_keys = super::construct_onion_keys(&secp_ctx, &route, &session_priv).unwrap();
|
let onion_keys = super::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
|
||||||
assert_eq!(onion_keys.len(), route.hops.len());
|
assert_eq!(onion_keys.len(), route.paths[0].len());
|
||||||
onion_keys
|
onion_keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,19 +47,10 @@ pub struct RouteHop {
|
||||||
pub cltv_expiry_delta: u32,
|
pub cltv_expiry_delta: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A route from us through the network to a destination
|
impl Writeable for Vec<RouteHop> {
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
pub struct Route {
|
|
||||||
/// The list of hops, NOT INCLUDING our own, where the last hop is the destination. Thus, this
|
|
||||||
/// must always be at least length one. By protocol rules, this may not currently exceed 20 in
|
|
||||||
/// length.
|
|
||||||
pub hops: Vec<RouteHop>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Writeable for Route {
|
|
||||||
fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
|
fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
|
||||||
(self.hops.len() as u8).write(writer)?;
|
(self.len() as u8).write(writer)?;
|
||||||
for hop in self.hops.iter() {
|
for hop in self.iter() {
|
||||||
hop.pubkey.write(writer)?;
|
hop.pubkey.write(writer)?;
|
||||||
hop.node_features.write(writer)?;
|
hop.node_features.write(writer)?;
|
||||||
hop.short_channel_id.write(writer)?;
|
hop.short_channel_id.write(writer)?;
|
||||||
|
@ -71,8 +62,8 @@ impl Writeable for Route {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Readable for Route {
|
impl Readable for Vec<RouteHop> {
|
||||||
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Route, DecodeError> {
|
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Vec<RouteHop>, DecodeError> {
|
||||||
let hops_count: u8 = Readable::read(reader)?;
|
let hops_count: u8 = Readable::read(reader)?;
|
||||||
let mut hops = Vec::with_capacity(hops_count as usize);
|
let mut hops = Vec::with_capacity(hops_count as usize);
|
||||||
for _ in 0..hops_count {
|
for _ in 0..hops_count {
|
||||||
|
@ -85,9 +76,41 @@ impl Readable for Route {
|
||||||
cltv_expiry_delta: Readable::read(reader)?,
|
cltv_expiry_delta: Readable::read(reader)?,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(Route {
|
Ok(hops)
|
||||||
hops
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
/// 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, PartialEq)]
|
||||||
|
pub struct Route {
|
||||||
|
/// The list of routes taken for a single (potentially-)multi-part payment. The pubkey of the
|
||||||
|
/// last RouteHop in each path must be the same.
|
||||||
|
/// Each entry represents a list of hops, NOT INCLUDING our own, where 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 to less than 20 should currently
|
||||||
|
/// ensure it is viable.
|
||||||
|
pub paths: Vec<Vec<RouteHop>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Writeable for Route {
|
||||||
|
fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
|
||||||
|
(self.paths.len() as u64).write(writer)?;
|
||||||
|
for hops in self.paths.iter() {
|
||||||
|
hops.write(writer)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Readable for Route {
|
||||||
|
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Route, DecodeError> {
|
||||||
|
let path_count: u64 = Readable::read(reader)?;
|
||||||
|
let mut paths = Vec::with_capacity(cmp::min(path_count, 128) as usize);
|
||||||
|
for _ in 0..path_count {
|
||||||
|
paths.push(Readable::read(reader)?);
|
||||||
|
}
|
||||||
|
Ok(Route { paths })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,14 +891,14 @@ impl Router {
|
||||||
let short_channel_id = chan.short_channel_id.expect("first_hops should be filled in with usable channels, not pending ones");
|
let short_channel_id = chan.short_channel_id.expect("first_hops should be filled in with usable channels, not pending ones");
|
||||||
if chan.remote_network_id == *target {
|
if chan.remote_network_id == *target {
|
||||||
return Ok(Route {
|
return Ok(Route {
|
||||||
hops: vec![RouteHop {
|
paths: vec![vec![RouteHop {
|
||||||
pubkey: chan.remote_network_id,
|
pubkey: chan.remote_network_id,
|
||||||
node_features: NodeFeatures::with_known_relevant_init_flags(&chan.counterparty_features),
|
node_features: NodeFeatures::with_known_relevant_init_flags(&chan.counterparty_features),
|
||||||
short_channel_id,
|
short_channel_id,
|
||||||
channel_features: ChannelFeatures::with_known_relevant_init_flags(&chan.counterparty_features),
|
channel_features: ChannelFeatures::with_known_relevant_init_flags(&chan.counterparty_features),
|
||||||
fee_msat: final_value_msat,
|
fee_msat: final_value_msat,
|
||||||
cltv_expiry_delta: final_cltv,
|
cltv_expiry_delta: final_cltv,
|
||||||
}],
|
}]],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
first_hop_targets.insert(chan.remote_network_id, (short_channel_id, chan.counterparty_features.clone()));
|
first_hop_targets.insert(chan.remote_network_id, (short_channel_id, chan.counterparty_features.clone()));
|
||||||
|
@ -1032,7 +1055,7 @@ impl Router {
|
||||||
}
|
}
|
||||||
res.last_mut().unwrap().fee_msat = final_value_msat;
|
res.last_mut().unwrap().fee_msat = final_value_msat;
|
||||||
res.last_mut().unwrap().cltv_expiry_delta = final_cltv;
|
res.last_mut().unwrap().cltv_expiry_delta = final_cltv;
|
||||||
let route = Route { hops: res };
|
let route = Route { paths: vec![res] };
|
||||||
log_trace!(self, "Got route: {}", log_route!(route));
|
log_trace!(self, "Got route: {}", log_route!(route));
|
||||||
return Ok(route);
|
return Ok(route);
|
||||||
}
|
}
|
||||||
|
@ -1497,21 +1520,21 @@ mod tests {
|
||||||
|
|
||||||
{ // Simple route to 3 via 2
|
{ // Simple route to 3 via 2
|
||||||
let route = router.get_route(&node3, None, &Vec::new(), 100, 42).unwrap();
|
let route = router.get_route(&node3, None, &Vec::new(), 100, 42).unwrap();
|
||||||
assert_eq!(route.hops.len(), 2);
|
assert_eq!(route.paths[0].len(), 2);
|
||||||
|
|
||||||
assert_eq!(route.hops[0].pubkey, node2);
|
assert_eq!(route.paths[0][0].pubkey, node2);
|
||||||
assert_eq!(route.hops[0].short_channel_id, 2);
|
assert_eq!(route.paths[0][0].short_channel_id, 2);
|
||||||
assert_eq!(route.hops[0].fee_msat, 100);
|
assert_eq!(route.paths[0][0].fee_msat, 100);
|
||||||
assert_eq!(route.hops[0].cltv_expiry_delta, (4 << 8) | 1);
|
assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 8) | 1);
|
||||||
assert_eq!(route.hops[0].node_features.le_flags(), &id_to_feature_flags!(2));
|
assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags!(2));
|
||||||
assert_eq!(route.hops[0].channel_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.hops[1].pubkey, node3);
|
assert_eq!(route.paths[0][1].pubkey, node3);
|
||||||
assert_eq!(route.hops[1].short_channel_id, 4);
|
assert_eq!(route.paths[0][1].short_channel_id, 4);
|
||||||
assert_eq!(route.hops[1].fee_msat, 100);
|
assert_eq!(route.paths[0][1].fee_msat, 100);
|
||||||
assert_eq!(route.hops[1].cltv_expiry_delta, 42);
|
assert_eq!(route.paths[0][1].cltv_expiry_delta, 42);
|
||||||
assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3));
|
assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags!(3));
|
||||||
assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(4));
|
assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags!(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Disable channels 4 and 12 by requiring unknown feature bits
|
{ // Disable channels 4 and 12 by requiring unknown feature bits
|
||||||
|
@ -1539,21 +1562,21 @@ mod tests {
|
||||||
is_live: true,
|
is_live: true,
|
||||||
}];
|
}];
|
||||||
let route = router.get_route(&node3, Some(&our_chans), &Vec::new(), 100, 42).unwrap();
|
let route = router.get_route(&node3, Some(&our_chans), &Vec::new(), 100, 42).unwrap();
|
||||||
assert_eq!(route.hops.len(), 2);
|
assert_eq!(route.paths[0].len(), 2);
|
||||||
|
|
||||||
assert_eq!(route.hops[0].pubkey, node8);
|
assert_eq!(route.paths[0][0].pubkey, node8);
|
||||||
assert_eq!(route.hops[0].short_channel_id, 42);
|
assert_eq!(route.paths[0][0].short_channel_id, 42);
|
||||||
assert_eq!(route.hops[0].fee_msat, 200);
|
assert_eq!(route.paths[0][0].fee_msat, 200);
|
||||||
assert_eq!(route.hops[0].cltv_expiry_delta, (13 << 8) | 1);
|
assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 8) | 1);
|
||||||
assert_eq!(route.hops[0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features
|
assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features
|
||||||
assert_eq!(route.hops[0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion
|
assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion
|
||||||
|
|
||||||
assert_eq!(route.hops[1].pubkey, node3);
|
assert_eq!(route.paths[0][1].pubkey, node3);
|
||||||
assert_eq!(route.hops[1].short_channel_id, 13);
|
assert_eq!(route.paths[0][1].short_channel_id, 13);
|
||||||
assert_eq!(route.hops[1].fee_msat, 100);
|
assert_eq!(route.paths[0][1].fee_msat, 100);
|
||||||
assert_eq!(route.hops[1].cltv_expiry_delta, 42);
|
assert_eq!(route.paths[0][1].cltv_expiry_delta, 42);
|
||||||
assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3));
|
assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags!(3));
|
||||||
assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(13));
|
assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags!(13));
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Re-enable channels 4 and 12 by wiping the unknown feature bits
|
{ // Re-enable channels 4 and 12 by wiping the unknown feature bits
|
||||||
|
@ -1588,21 +1611,21 @@ mod tests {
|
||||||
is_live: true,
|
is_live: true,
|
||||||
}];
|
}];
|
||||||
let route = router.get_route(&node3, Some(&our_chans), &Vec::new(), 100, 42).unwrap();
|
let route = router.get_route(&node3, Some(&our_chans), &Vec::new(), 100, 42).unwrap();
|
||||||
assert_eq!(route.hops.len(), 2);
|
assert_eq!(route.paths[0].len(), 2);
|
||||||
|
|
||||||
assert_eq!(route.hops[0].pubkey, node8);
|
assert_eq!(route.paths[0][0].pubkey, node8);
|
||||||
assert_eq!(route.hops[0].short_channel_id, 42);
|
assert_eq!(route.paths[0][0].short_channel_id, 42);
|
||||||
assert_eq!(route.hops[0].fee_msat, 200);
|
assert_eq!(route.paths[0][0].fee_msat, 200);
|
||||||
assert_eq!(route.hops[0].cltv_expiry_delta, (13 << 8) | 1);
|
assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 8) | 1);
|
||||||
assert_eq!(route.hops[0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features
|
assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features
|
||||||
assert_eq!(route.hops[0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion
|
assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion
|
||||||
|
|
||||||
assert_eq!(route.hops[1].pubkey, node3);
|
assert_eq!(route.paths[0][1].pubkey, node3);
|
||||||
assert_eq!(route.hops[1].short_channel_id, 13);
|
assert_eq!(route.paths[0][1].short_channel_id, 13);
|
||||||
assert_eq!(route.hops[1].fee_msat, 100);
|
assert_eq!(route.paths[0][1].fee_msat, 100);
|
||||||
assert_eq!(route.hops[1].cltv_expiry_delta, 42);
|
assert_eq!(route.paths[0][1].cltv_expiry_delta, 42);
|
||||||
assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3));
|
assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags!(3));
|
||||||
assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(13));
|
assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags!(13));
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Re-enable nodes 1, 2, and 8
|
{ // Re-enable nodes 1, 2, and 8
|
||||||
|
@ -1618,28 +1641,28 @@ mod tests {
|
||||||
|
|
||||||
{ // Route to 1 via 2 and 3 because our channel to 1 is disabled
|
{ // Route to 1 via 2 and 3 because our channel to 1 is disabled
|
||||||
let route = router.get_route(&node1, None, &Vec::new(), 100, 42).unwrap();
|
let route = router.get_route(&node1, None, &Vec::new(), 100, 42).unwrap();
|
||||||
assert_eq!(route.hops.len(), 3);
|
assert_eq!(route.paths[0].len(), 3);
|
||||||
|
|
||||||
assert_eq!(route.hops[0].pubkey, node2);
|
assert_eq!(route.paths[0][0].pubkey, node2);
|
||||||
assert_eq!(route.hops[0].short_channel_id, 2);
|
assert_eq!(route.paths[0][0].short_channel_id, 2);
|
||||||
assert_eq!(route.hops[0].fee_msat, 200);
|
assert_eq!(route.paths[0][0].fee_msat, 200);
|
||||||
assert_eq!(route.hops[0].cltv_expiry_delta, (4 << 8) | 1);
|
assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 8) | 1);
|
||||||
assert_eq!(route.hops[0].node_features.le_flags(), &id_to_feature_flags!(2));
|
assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags!(2));
|
||||||
assert_eq!(route.hops[0].channel_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.hops[1].pubkey, node3);
|
assert_eq!(route.paths[0][1].pubkey, node3);
|
||||||
assert_eq!(route.hops[1].short_channel_id, 4);
|
assert_eq!(route.paths[0][1].short_channel_id, 4);
|
||||||
assert_eq!(route.hops[1].fee_msat, 100);
|
assert_eq!(route.paths[0][1].fee_msat, 100);
|
||||||
assert_eq!(route.hops[1].cltv_expiry_delta, (3 << 8) | 2);
|
assert_eq!(route.paths[0][1].cltv_expiry_delta, (3 << 8) | 2);
|
||||||
assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3));
|
assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags!(3));
|
||||||
assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(4));
|
assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags!(4));
|
||||||
|
|
||||||
assert_eq!(route.hops[2].pubkey, node1);
|
assert_eq!(route.paths[0][2].pubkey, node1);
|
||||||
assert_eq!(route.hops[2].short_channel_id, 3);
|
assert_eq!(route.paths[0][2].short_channel_id, 3);
|
||||||
assert_eq!(route.hops[2].fee_msat, 100);
|
assert_eq!(route.paths[0][2].fee_msat, 100);
|
||||||
assert_eq!(route.hops[2].cltv_expiry_delta, 42);
|
assert_eq!(route.paths[0][2].cltv_expiry_delta, 42);
|
||||||
assert_eq!(route.hops[2].node_features.le_flags(), &id_to_feature_flags!(1));
|
assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags!(1));
|
||||||
assert_eq!(route.hops[2].channel_features.le_flags(), &id_to_feature_flags!(3));
|
assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags!(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // If we specify a channel to node8, that overrides our local channel view and that gets used
|
{ // If we specify a channel to node8, that overrides our local channel view and that gets used
|
||||||
|
@ -1655,21 +1678,21 @@ mod tests {
|
||||||
is_live: true,
|
is_live: true,
|
||||||
}];
|
}];
|
||||||
let route = router.get_route(&node3, Some(&our_chans), &Vec::new(), 100, 42).unwrap();
|
let route = router.get_route(&node3, Some(&our_chans), &Vec::new(), 100, 42).unwrap();
|
||||||
assert_eq!(route.hops.len(), 2);
|
assert_eq!(route.paths[0].len(), 2);
|
||||||
|
|
||||||
assert_eq!(route.hops[0].pubkey, node8);
|
assert_eq!(route.paths[0][0].pubkey, node8);
|
||||||
assert_eq!(route.hops[0].short_channel_id, 42);
|
assert_eq!(route.paths[0][0].short_channel_id, 42);
|
||||||
assert_eq!(route.hops[0].fee_msat, 200);
|
assert_eq!(route.paths[0][0].fee_msat, 200);
|
||||||
assert_eq!(route.hops[0].cltv_expiry_delta, (13 << 8) | 1);
|
assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 8) | 1);
|
||||||
assert_eq!(route.hops[0].node_features.le_flags(), &vec![0b11]);
|
assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]);
|
||||||
assert_eq!(route.hops[0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion
|
assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion
|
||||||
|
|
||||||
assert_eq!(route.hops[1].pubkey, node3);
|
assert_eq!(route.paths[0][1].pubkey, node3);
|
||||||
assert_eq!(route.hops[1].short_channel_id, 13);
|
assert_eq!(route.paths[0][1].short_channel_id, 13);
|
||||||
assert_eq!(route.hops[1].fee_msat, 100);
|
assert_eq!(route.paths[0][1].fee_msat, 100);
|
||||||
assert_eq!(route.hops[1].cltv_expiry_delta, 42);
|
assert_eq!(route.paths[0][1].cltv_expiry_delta, 42);
|
||||||
assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3));
|
assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags!(3));
|
||||||
assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(13));
|
assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags!(13));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut last_hops = vec!(RouteHint {
|
let mut last_hops = vec!(RouteHint {
|
||||||
|
@ -1697,44 +1720,44 @@ mod tests {
|
||||||
|
|
||||||
{ // Simple test across 2, 3, 5, and 4 via a last_hop channel
|
{ // Simple test across 2, 3, 5, and 4 via a last_hop channel
|
||||||
let route = router.get_route(&node7, None, &last_hops, 100, 42).unwrap();
|
let route = router.get_route(&node7, None, &last_hops, 100, 42).unwrap();
|
||||||
assert_eq!(route.hops.len(), 5);
|
assert_eq!(route.paths[0].len(), 5);
|
||||||
|
|
||||||
assert_eq!(route.hops[0].pubkey, node2);
|
assert_eq!(route.paths[0][0].pubkey, node2);
|
||||||
assert_eq!(route.hops[0].short_channel_id, 2);
|
assert_eq!(route.paths[0][0].short_channel_id, 2);
|
||||||
assert_eq!(route.hops[0].fee_msat, 100);
|
assert_eq!(route.paths[0][0].fee_msat, 100);
|
||||||
assert_eq!(route.hops[0].cltv_expiry_delta, (4 << 8) | 1);
|
assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 8) | 1);
|
||||||
assert_eq!(route.hops[0].node_features.le_flags(), &id_to_feature_flags!(2));
|
assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags!(2));
|
||||||
assert_eq!(route.hops[0].channel_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.hops[1].pubkey, node3);
|
assert_eq!(route.paths[0][1].pubkey, node3);
|
||||||
assert_eq!(route.hops[1].short_channel_id, 4);
|
assert_eq!(route.paths[0][1].short_channel_id, 4);
|
||||||
assert_eq!(route.hops[1].fee_msat, 0);
|
assert_eq!(route.paths[0][1].fee_msat, 0);
|
||||||
assert_eq!(route.hops[1].cltv_expiry_delta, (6 << 8) | 1);
|
assert_eq!(route.paths[0][1].cltv_expiry_delta, (6 << 8) | 1);
|
||||||
assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3));
|
assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags!(3));
|
||||||
assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(4));
|
assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags!(4));
|
||||||
|
|
||||||
assert_eq!(route.hops[2].pubkey, node5);
|
assert_eq!(route.paths[0][2].pubkey, node5);
|
||||||
assert_eq!(route.hops[2].short_channel_id, 6);
|
assert_eq!(route.paths[0][2].short_channel_id, 6);
|
||||||
assert_eq!(route.hops[2].fee_msat, 0);
|
assert_eq!(route.paths[0][2].fee_msat, 0);
|
||||||
assert_eq!(route.hops[2].cltv_expiry_delta, (11 << 8) | 1);
|
assert_eq!(route.paths[0][2].cltv_expiry_delta, (11 << 8) | 1);
|
||||||
assert_eq!(route.hops[2].node_features.le_flags(), &id_to_feature_flags!(5));
|
assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags!(5));
|
||||||
assert_eq!(route.hops[2].channel_features.le_flags(), &id_to_feature_flags!(6));
|
assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags!(6));
|
||||||
|
|
||||||
assert_eq!(route.hops[3].pubkey, node4);
|
assert_eq!(route.paths[0][3].pubkey, node4);
|
||||||
assert_eq!(route.hops[3].short_channel_id, 11);
|
assert_eq!(route.paths[0][3].short_channel_id, 11);
|
||||||
assert_eq!(route.hops[3].fee_msat, 0);
|
assert_eq!(route.paths[0][3].fee_msat, 0);
|
||||||
assert_eq!(route.hops[3].cltv_expiry_delta, (8 << 8) | 1);
|
assert_eq!(route.paths[0][3].cltv_expiry_delta, (8 << 8) | 1);
|
||||||
// If we have a peer in the node map, we'll use their features here since we don't have
|
// 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:
|
// a way of figuring out their features from the invoice:
|
||||||
assert_eq!(route.hops[3].node_features.le_flags(), &id_to_feature_flags!(4));
|
assert_eq!(route.paths[0][3].node_features.le_flags(), &id_to_feature_flags!(4));
|
||||||
assert_eq!(route.hops[3].channel_features.le_flags(), &id_to_feature_flags!(11));
|
assert_eq!(route.paths[0][3].channel_features.le_flags(), &id_to_feature_flags!(11));
|
||||||
|
|
||||||
assert_eq!(route.hops[4].pubkey, node7);
|
assert_eq!(route.paths[0][4].pubkey, node7);
|
||||||
assert_eq!(route.hops[4].short_channel_id, 8);
|
assert_eq!(route.paths[0][4].short_channel_id, 8);
|
||||||
assert_eq!(route.hops[4].fee_msat, 100);
|
assert_eq!(route.paths[0][4].fee_msat, 100);
|
||||||
assert_eq!(route.hops[4].cltv_expiry_delta, 42);
|
assert_eq!(route.paths[0][4].cltv_expiry_delta, 42);
|
||||||
assert_eq!(route.hops[4].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet
|
assert_eq!(route.paths[0][4].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet
|
||||||
assert_eq!(route.hops[4].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly
|
assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Simple test with outbound channel to 4 to test that last_hops and first_hops connect
|
{ // Simple test with outbound channel to 4 to test that last_hops and first_hops connect
|
||||||
|
@ -1750,100 +1773,100 @@ mod tests {
|
||||||
is_live: true,
|
is_live: true,
|
||||||
}];
|
}];
|
||||||
let route = router.get_route(&node7, Some(&our_chans), &last_hops, 100, 42).unwrap();
|
let route = router.get_route(&node7, Some(&our_chans), &last_hops, 100, 42).unwrap();
|
||||||
assert_eq!(route.hops.len(), 2);
|
assert_eq!(route.paths[0].len(), 2);
|
||||||
|
|
||||||
assert_eq!(route.hops[0].pubkey, node4);
|
assert_eq!(route.paths[0][0].pubkey, node4);
|
||||||
assert_eq!(route.hops[0].short_channel_id, 42);
|
assert_eq!(route.paths[0][0].short_channel_id, 42);
|
||||||
assert_eq!(route.hops[0].fee_msat, 0);
|
assert_eq!(route.paths[0][0].fee_msat, 0);
|
||||||
assert_eq!(route.hops[0].cltv_expiry_delta, (8 << 8) | 1);
|
assert_eq!(route.paths[0][0].cltv_expiry_delta, (8 << 8) | 1);
|
||||||
assert_eq!(route.hops[0].node_features.le_flags(), &vec![0b11]);
|
assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]);
|
||||||
assert_eq!(route.hops[0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion
|
assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion
|
||||||
|
|
||||||
assert_eq!(route.hops[1].pubkey, node7);
|
assert_eq!(route.paths[0][1].pubkey, node7);
|
||||||
assert_eq!(route.hops[1].short_channel_id, 8);
|
assert_eq!(route.paths[0][1].short_channel_id, 8);
|
||||||
assert_eq!(route.hops[1].fee_msat, 100);
|
assert_eq!(route.paths[0][1].fee_msat, 100);
|
||||||
assert_eq!(route.hops[1].cltv_expiry_delta, 42);
|
assert_eq!(route.paths[0][1].cltv_expiry_delta, 42);
|
||||||
assert_eq!(route.hops[1].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet
|
assert_eq!(route.paths[0][1].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet
|
||||||
assert_eq!(route.hops[1].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly
|
assert_eq!(route.paths[0][1].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly
|
||||||
}
|
}
|
||||||
|
|
||||||
last_hops[0].fee_base_msat = 1000;
|
last_hops[0].fee_base_msat = 1000;
|
||||||
|
|
||||||
{ // Revert to via 6 as the fee on 8 goes up
|
{ // Revert to via 6 as the fee on 8 goes up
|
||||||
let route = router.get_route(&node7, None, &last_hops, 100, 42).unwrap();
|
let route = router.get_route(&node7, None, &last_hops, 100, 42).unwrap();
|
||||||
assert_eq!(route.hops.len(), 4);
|
assert_eq!(route.paths[0].len(), 4);
|
||||||
|
|
||||||
assert_eq!(route.hops[0].pubkey, node2);
|
assert_eq!(route.paths[0][0].pubkey, node2);
|
||||||
assert_eq!(route.hops[0].short_channel_id, 2);
|
assert_eq!(route.paths[0][0].short_channel_id, 2);
|
||||||
assert_eq!(route.hops[0].fee_msat, 200); // fee increased as its % of value transferred across node
|
assert_eq!(route.paths[0][0].fee_msat, 200); // fee increased as its % of value transferred across node
|
||||||
assert_eq!(route.hops[0].cltv_expiry_delta, (4 << 8) | 1);
|
assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 8) | 1);
|
||||||
assert_eq!(route.hops[0].node_features.le_flags(), &id_to_feature_flags!(2));
|
assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags!(2));
|
||||||
assert_eq!(route.hops[0].channel_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.hops[1].pubkey, node3);
|
assert_eq!(route.paths[0][1].pubkey, node3);
|
||||||
assert_eq!(route.hops[1].short_channel_id, 4);
|
assert_eq!(route.paths[0][1].short_channel_id, 4);
|
||||||
assert_eq!(route.hops[1].fee_msat, 100);
|
assert_eq!(route.paths[0][1].fee_msat, 100);
|
||||||
assert_eq!(route.hops[1].cltv_expiry_delta, (7 << 8) | 1);
|
assert_eq!(route.paths[0][1].cltv_expiry_delta, (7 << 8) | 1);
|
||||||
assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3));
|
assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags!(3));
|
||||||
assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(4));
|
assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags!(4));
|
||||||
|
|
||||||
assert_eq!(route.hops[2].pubkey, node6);
|
assert_eq!(route.paths[0][2].pubkey, node6);
|
||||||
assert_eq!(route.hops[2].short_channel_id, 7);
|
assert_eq!(route.paths[0][2].short_channel_id, 7);
|
||||||
assert_eq!(route.hops[2].fee_msat, 0);
|
assert_eq!(route.paths[0][2].fee_msat, 0);
|
||||||
assert_eq!(route.hops[2].cltv_expiry_delta, (10 << 8) | 1);
|
assert_eq!(route.paths[0][2].cltv_expiry_delta, (10 << 8) | 1);
|
||||||
// If we have a peer in the node map, we'll use their features here since we don't have
|
// 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:
|
// a way of figuring out their features from the invoice:
|
||||||
assert_eq!(route.hops[2].node_features.le_flags(), &id_to_feature_flags!(6));
|
assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags!(6));
|
||||||
assert_eq!(route.hops[2].channel_features.le_flags(), &id_to_feature_flags!(7));
|
assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags!(7));
|
||||||
|
|
||||||
assert_eq!(route.hops[3].pubkey, node7);
|
assert_eq!(route.paths[0][3].pubkey, node7);
|
||||||
assert_eq!(route.hops[3].short_channel_id, 10);
|
assert_eq!(route.paths[0][3].short_channel_id, 10);
|
||||||
assert_eq!(route.hops[3].fee_msat, 100);
|
assert_eq!(route.paths[0][3].fee_msat, 100);
|
||||||
assert_eq!(route.hops[3].cltv_expiry_delta, 42);
|
assert_eq!(route.paths[0][3].cltv_expiry_delta, 42);
|
||||||
assert_eq!(route.hops[3].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet
|
assert_eq!(route.paths[0][3].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet
|
||||||
assert_eq!(route.hops[3].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly
|
assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // ...but still use 8 for larger payments as 6 has a variable feerate
|
{ // ...but still use 8 for larger payments as 6 has a variable feerate
|
||||||
let route = router.get_route(&node7, None, &last_hops, 2000, 42).unwrap();
|
let route = router.get_route(&node7, None, &last_hops, 2000, 42).unwrap();
|
||||||
assert_eq!(route.hops.len(), 5);
|
assert_eq!(route.paths[0].len(), 5);
|
||||||
|
|
||||||
assert_eq!(route.hops[0].pubkey, node2);
|
assert_eq!(route.paths[0][0].pubkey, node2);
|
||||||
assert_eq!(route.hops[0].short_channel_id, 2);
|
assert_eq!(route.paths[0][0].short_channel_id, 2);
|
||||||
assert_eq!(route.hops[0].fee_msat, 3000);
|
assert_eq!(route.paths[0][0].fee_msat, 3000);
|
||||||
assert_eq!(route.hops[0].cltv_expiry_delta, (4 << 8) | 1);
|
assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 8) | 1);
|
||||||
assert_eq!(route.hops[0].node_features.le_flags(), &id_to_feature_flags!(2));
|
assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags!(2));
|
||||||
assert_eq!(route.hops[0].channel_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.hops[1].pubkey, node3);
|
assert_eq!(route.paths[0][1].pubkey, node3);
|
||||||
assert_eq!(route.hops[1].short_channel_id, 4);
|
assert_eq!(route.paths[0][1].short_channel_id, 4);
|
||||||
assert_eq!(route.hops[1].fee_msat, 0);
|
assert_eq!(route.paths[0][1].fee_msat, 0);
|
||||||
assert_eq!(route.hops[1].cltv_expiry_delta, (6 << 8) | 1);
|
assert_eq!(route.paths[0][1].cltv_expiry_delta, (6 << 8) | 1);
|
||||||
assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3));
|
assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags!(3));
|
||||||
assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(4));
|
assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags!(4));
|
||||||
|
|
||||||
assert_eq!(route.hops[2].pubkey, node5);
|
assert_eq!(route.paths[0][2].pubkey, node5);
|
||||||
assert_eq!(route.hops[2].short_channel_id, 6);
|
assert_eq!(route.paths[0][2].short_channel_id, 6);
|
||||||
assert_eq!(route.hops[2].fee_msat, 0);
|
assert_eq!(route.paths[0][2].fee_msat, 0);
|
||||||
assert_eq!(route.hops[2].cltv_expiry_delta, (11 << 8) | 1);
|
assert_eq!(route.paths[0][2].cltv_expiry_delta, (11 << 8) | 1);
|
||||||
assert_eq!(route.hops[2].node_features.le_flags(), &id_to_feature_flags!(5));
|
assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags!(5));
|
||||||
assert_eq!(route.hops[2].channel_features.le_flags(), &id_to_feature_flags!(6));
|
assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags!(6));
|
||||||
|
|
||||||
assert_eq!(route.hops[3].pubkey, node4);
|
assert_eq!(route.paths[0][3].pubkey, node4);
|
||||||
assert_eq!(route.hops[3].short_channel_id, 11);
|
assert_eq!(route.paths[0][3].short_channel_id, 11);
|
||||||
assert_eq!(route.hops[3].fee_msat, 1000);
|
assert_eq!(route.paths[0][3].fee_msat, 1000);
|
||||||
assert_eq!(route.hops[3].cltv_expiry_delta, (8 << 8) | 1);
|
assert_eq!(route.paths[0][3].cltv_expiry_delta, (8 << 8) | 1);
|
||||||
// If we have a peer in the node map, we'll use their features here since we don't have
|
// 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:
|
// a way of figuring out their features from the invoice:
|
||||||
assert_eq!(route.hops[3].node_features.le_flags(), &id_to_feature_flags!(4));
|
assert_eq!(route.paths[0][3].node_features.le_flags(), &id_to_feature_flags!(4));
|
||||||
assert_eq!(route.hops[3].channel_features.le_flags(), &id_to_feature_flags!(11));
|
assert_eq!(route.paths[0][3].channel_features.le_flags(), &id_to_feature_flags!(11));
|
||||||
|
|
||||||
assert_eq!(route.hops[4].pubkey, node7);
|
assert_eq!(route.paths[0][4].pubkey, node7);
|
||||||
assert_eq!(route.hops[4].short_channel_id, 8);
|
assert_eq!(route.paths[0][4].short_channel_id, 8);
|
||||||
assert_eq!(route.hops[4].fee_msat, 2000);
|
assert_eq!(route.paths[0][4].fee_msat, 2000);
|
||||||
assert_eq!(route.hops[4].cltv_expiry_delta, 42);
|
assert_eq!(route.paths[0][4].cltv_expiry_delta, 42);
|
||||||
assert_eq!(route.hops[4].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet
|
assert_eq!(route.paths[0][4].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet
|
||||||
assert_eq!(route.hops[4].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly
|
assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Test Router serialization/deserialization
|
{ // Test Router serialization/deserialization
|
||||||
|
|
|
@ -73,8 +73,11 @@ macro_rules! log_funding_info {
|
||||||
pub(crate) struct DebugRoute<'a>(pub &'a Route);
|
pub(crate) struct DebugRoute<'a>(pub &'a Route);
|
||||||
impl<'a> std::fmt::Display for DebugRoute<'a> {
|
impl<'a> std::fmt::Display for DebugRoute<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||||
for h in self.0.hops.iter() {
|
for (idx, p) in self.0.paths.iter().enumerate() {
|
||||||
write!(f, "node_id: {}, short_channel_id: {}, fee_msat: {}, cltv_expiry_delta: {}\n", log_pubkey!(h.pubkey), h.short_channel_id, h.fee_msat, h.cltv_expiry_delta)?;
|
write!(f, "path {}:\n", idx)?;
|
||||||
|
for h in p.iter() {
|
||||||
|
write!(f, " node_id: {}, short_channel_id: {}, fee_msat: {}, cltv_expiry_delta: {}\n", log_pubkey!(h.pubkey), h.short_channel_id, h.fee_msat, h.cltv_expiry_delta)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue