Skip to content

Commit 877ba47

Browse files
Account for Path::blinded_tail in channelmanager+outbound_payment
1 parent b403b06 commit 877ba47

File tree

3 files changed

+51
-43
lines changed

3 files changed

+51
-43
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, No
4545
#[cfg(any(feature = "_test_utils", test))]
4646
use crate::ln::features::InvoiceFeatures;
4747
use crate::routing::gossip::NetworkGraph;
48-
use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters, RoutePath, Router};
48+
use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters, Router};
4949
use crate::routing::scoring::ProbabilisticScorer;
5050
use crate::ln::msgs;
5151
use crate::ln::onion_utils;
@@ -2531,7 +2531,7 @@ where
25312531
// The top-level caller should hold the total_consistency_lock read lock.
25322532
debug_assert!(self.total_consistency_lock.try_write().is_err());
25332533

2534-
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.hops.first().unwrap().short_channel_id);
2534+
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first_hop_scid());
25352535
let prng_seed = self.entropy_source.get_secure_random_bytes();
25362536
let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted");
25372537

@@ -2544,7 +2544,7 @@ where
25442544
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
25452545

25462546
let err: Result<(), _> = loop {
2547-
let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.hops.first().unwrap().short_channel_id) {
2547+
let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.first_hop_scid()) {
25482548
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()}),
25492549
Some((cp_id, chan_id)) => (cp_id.clone(), chan_id.clone()),
25502550
};
@@ -2595,7 +2595,7 @@ where
25952595
return Ok(());
25962596
};
25972597

2598-
match handle_error!(self, err, path.hops.first().unwrap().pubkey) {
2598+
match handle_error!(self, err, path.first_hop_node_id()) {
25992599
Ok(_) => unreachable!(),
26002600
Err(e) => {
26012601
Err(APIError::ChannelUnavailable { err: e.err })
@@ -7588,12 +7588,12 @@ where
75887588
if id_to_peer.get(&monitor.get_funding_txo().0.to_channel_id()).is_none() {
75897589
for (htlc_source, (htlc, _)) in monitor.get_pending_or_resolved_outbound_htlcs() {
75907590
if let HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } = htlc_source {
7591-
if path.hops.is_empty() {
7591+
if path.len() == 0 {
75927592
log_error!(args.logger, "Got an empty path for a pending payment");
75937593
return Err(DecodeError::InvalidValue);
75947594
}
75957595

7596-
let path_amt = path.hops.last().unwrap().fee_msat;
7596+
let path_amt = path.final_value_msat();
75977597
let mut session_priv_bytes = [0; 32];
75987598
session_priv_bytes[..].copy_from_slice(&session_priv[..]);
75997599
match pending_outbounds.pending_outbound_payments.lock().unwrap().entry(payment_id) {
@@ -7603,7 +7603,7 @@ where
76037603
if newly_added { "Added" } else { "Had" }, path_amt, log_bytes!(session_priv_bytes), log_bytes!(htlc.payment_hash.0));
76047604
},
76057605
hash_map::Entry::Vacant(entry) => {
7606-
let path_fee = path.hops.get_path_fees();
7606+
let path_fee = path.fee_msat();
76077607
entry.insert(PendingOutboundPayment::Retryable {
76087608
retry_strategy: None,
76097609
attempts: PaymentAttempts::new(),

lightning/src/ln/outbound_payment.rs

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::events::{self, PaymentFailureReason};
1818
use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
1919
use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId};
2020
use crate::ln::onion_utils::HTLCFailReason;
21-
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, RoutePath, Router};
21+
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
2222
use crate::util::errors::APIError;
2323
use crate::util::logger::Logger;
2424
use crate::util::time::Time;
@@ -171,10 +171,9 @@ impl PendingOutboundPayment {
171171
if remove_res {
172172
if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
173173
let path = path.expect("Fulfilling a payment should always come with a path");
174-
let path_last_hop = path.hops.last().expect("Outbound payments must have had a valid path");
175-
*pending_amt_msat -= path_last_hop.fee_msat;
174+
*pending_amt_msat -= path.final_value_msat();
176175
if let Some(fee_msat) = pending_fee_msat.as_mut() {
177-
*fee_msat -= path.hops.get_path_fees();
176+
*fee_msat -= path.fee_msat();
178177
}
179178
}
180179
}
@@ -192,10 +191,9 @@ impl PendingOutboundPayment {
192191
};
193192
if insert_res {
194193
if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
195-
let path_last_hop = path.hops.last().expect("Outbound payments must have had a valid path");
196-
*pending_amt_msat += path_last_hop.fee_msat;
194+
*pending_amt_msat += path.final_value_msat();
197195
if let Some(fee_msat) = pending_fee_msat.as_mut() {
198-
*fee_msat += path.hops.get_path_fees();
196+
*fee_msat += path.fee_msat();
199197
}
200198
}
201199
}
@@ -687,7 +685,7 @@ impl OutboundPayments {
687685
}
688686
};
689687
for path in route.paths.iter() {
690-
if path.hops.len() == 0 {
688+
if path.len() == 0 {
691689
log_error!(logger, "length-0 path in route");
692690
self.abandon_payment(payment_id, PaymentFailureReason::UnexpectedError, pending_events);
693691
return
@@ -723,7 +721,7 @@ impl OutboundPayments {
723721
PendingOutboundPayment::Retryable {
724722
total_msat, keysend_preimage, payment_secret, pending_amt_msat, ..
725723
} => {
726-
let retry_amt_msat: u64 = route.paths.iter().map(|path| path.hops.last().unwrap().fee_msat).sum();
724+
let retry_amt_msat = route.get_total_amount();
727725
if retry_amt_msat + *pending_amt_msat > *total_msat * (100 + RETRY_OVERFLOW_PERCENTAGE) / 100 {
728726
log_error!(logger, "retry_amt_msat of {} will put pending_amt_msat (currently: {}) more than 10% over total_payment_amt_msat of {}", retry_amt_msat, pending_amt_msat, total_msat);
729727
abandon_with_entry!(payment, PaymentFailureReason::UnexpectedError);
@@ -829,7 +827,7 @@ impl OutboundPayments {
829827
log_error!(logger, "Failed to send along path due to error: {:?}", e);
830828
let mut failed_scid = None;
831829
if let APIError::ChannelUnavailable { .. } = e {
832-
let scid = path.hops[0].short_channel_id;
830+
let scid = path.first_hop_scid();
833831
failed_scid = Some(scid);
834832
route_params.payment_params.previously_failed_channels.push(scid);
835833
}
@@ -863,7 +861,7 @@ impl OutboundPayments {
863861

864862
let payment_hash = probing_cookie_from_id(&payment_id, probing_cookie_secret);
865863

866-
if path.hops.len() < 2 {
864+
if path.len() < 2 {
867865
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError {
868866
err: "No need probing a path with less than two hops".to_string()
869867
}))
@@ -951,17 +949,20 @@ impl OutboundPayments {
951949
let our_node_id = node_signer.get_node_id(Recipient::Node).unwrap(); // TODO no unwrap
952950
let mut path_errs = Vec::with_capacity(route.paths.len());
953951
'path_check: for path in route.paths.iter() {
954-
if path.hops.len() < 1 || path.hops.len() > 20 {
952+
if path.len() < 1 || path.len() > 20 {
955953
path_errs.push(Err(APIError::InvalidRoute{err: "Path didn't go anywhere/had bogus size".to_owned()}));
956954
continue 'path_check;
957955
}
958-
for (idx, hop) in path.hops.iter().enumerate() {
959-
if idx != path.hops.len() - 1 && hop.pubkey == our_node_id {
960-
path_errs.push(Err(APIError::InvalidRoute{err: "Path went through us but wasn't a simple rebalance loop to us".to_owned()}));
961-
continue 'path_check;
962-
}
956+
let we_are_intermed_hop = path.blinded_tail.as_ref().map_or_else(
957+
|| path.hops.split_last().map_or(false, |(_, path_prefix)| path_prefix.iter().any(|hop| hop.pubkey == our_node_id)),
958+
|tail|
959+
(tail.path.introduction_node_id == our_node_id && tail.path.blinded_hops.len() > 1)
960+
|| path.hops.iter().any(|hop| hop.pubkey == our_node_id));
961+
if we_are_intermed_hop {
962+
path_errs.push(Err(APIError::InvalidRoute{err: "Path went through us but wasn't a simple rebalance loop to us".to_owned()}));
963+
continue 'path_check;
963964
}
964-
total_value += path.hops.last().unwrap().fee_msat;
965+
total_value += path.final_value_msat();
965966
path_errs.push(Ok(()));
966967
}
967968
if path_errs.iter().any(|e| e.is_err()) {
@@ -1009,7 +1010,7 @@ impl OutboundPayments {
10091010
has_err = true;
10101011
has_ok = true;
10111012
} else if res.is_err() {
1012-
pending_amt_unsent += path.hops.last().unwrap().fee_msat;
1013+
pending_amt_unsent += path.final_value_msat();
10131014
}
10141015
}
10151016
if has_err && has_ok {

lightning/src/routing/router.rs

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,16 @@ pub struct Path {
296296
}
297297

298298
impl Path {
299+
/// Returns the total amount of fees paid on this [`Path`].
300+
pub fn fee_msat(&self) -> u64 {
301+
match &self.blinded_tail {
302+
Some(tail) => self.hops.iter().map(|hop| hop.fee_msat).sum::<u64>() + tail.fee_msat,
303+
None =>
304+
self.hops.split_last().map_or(0,
305+
|(_, path_prefix)| path_prefix.iter().map(|hop| hop.fee_msat).sum())
306+
}
307+
}
308+
299309
/// Returns the total amount paid on this [`Path`], excluding the fees.
300310
pub fn final_value_msat(&self) -> u64 {
301311
match &self.blinded_tail {
@@ -308,6 +318,18 @@ impl Path {
308318
pub fn len(&self) -> usize {
309319
self.hops.len() + self.blinded_tail.as_ref().map_or(0, |tail| tail.hops.len().saturating_sub(1)) // Don't double count the intro node
310320
}
321+
322+
pub(crate) fn first_hop_node_id(&self) -> PublicKey {
323+
self.hops.first().map_or_else(
324+
|| self.blinded_tail.as_ref().expect("Path had 0 hops").path.introduction_node_id,
325+
|hop| hop.pubkey)
326+
}
327+
328+
pub(crate) fn first_hop_scid(&self) -> u64 {
329+
self.hops.first().map_or_else(
330+
|| self.blinded_tail.as_ref().expect("Path had 0 hops").intro_node_scid,
331+
|hop| hop.short_channel_id)
332+
}
311333
}
312334

313335
/// A route directs a payment from the sender (us) to the recipient. If the recipient supports MPP,
@@ -326,34 +348,19 @@ pub struct Route {
326348
pub payment_params: Option<PaymentParameters>,
327349
}
328350

329-
pub(crate) trait RoutePath {
330-
/// Gets the fees for a given path, excluding any excess paid to the recipient.
331-
fn get_path_fees(&self) -> u64;
332-
}
333-
impl RoutePath for Vec<RouteHop> {
334-
fn get_path_fees(&self) -> u64 {
335-
// Do not count last hop of each path since that's the full value of the payment
336-
self.split_last().map(|(_, path_prefix)| path_prefix).unwrap_or(&[])
337-
.iter().map(|hop| &hop.fee_msat)
338-
.sum()
339-
}
340-
}
341-
342351
impl Route {
343352
/// Returns the total amount of fees paid on this [`Route`].
344353
///
345354
/// This doesn't include any extra payment made to the recipient, which can happen in excess of
346355
/// the amount passed to [`find_route`]'s `params.final_value_msat`.
347356
pub fn get_total_fees(&self) -> u64 {
348-
self.paths.iter().map(|path| path.hops.get_path_fees()).sum()
357+
self.paths.iter().map(|path| path.fee_msat()).sum()
349358
}
350359

351360
/// Returns the total amount paid on this [`Route`], excluding the fees. Might be more than
352361
/// requested if we had to reach htlc_minimum_msat.
353362
pub fn get_total_amount(&self) -> u64 {
354-
return self.paths.iter()
355-
.map(|path| path.hops.split_last().map(|(hop, _)| hop.fee_msat).unwrap_or(0))
356-
.sum();
363+
self.paths.iter().map(|path| path.final_value_msat()).sum()
357364
}
358365
}
359366

0 commit comments

Comments
 (0)