Skip to content

Commit ed48a78

Browse files
outbound_payment: set max path length in PaymentParameters.
So the router knows how long the maximum payment path can be.
1 parent 0f776db commit ed48a78

File tree

2 files changed

+91
-4
lines changed

2 files changed

+91
-4
lines changed

lightning/src/ln/blinded_payment_tests.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,10 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) {
282282

283283
let amt_msat = 5000;
284284
let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(amt_msat), None);
285-
let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
285+
let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
286286
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(),
287287
&[&chan_upd_1_2, &chan_upd_2_3], &chanmon_cfgs[3].keys_manager);
288+
route_params.payment_params.max_intermediate_hops = 17;
288289

289290
let route = get_route(&nodes[0], &route_params).unwrap();
290291
node_cfgs[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
@@ -804,6 +805,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
804805
let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
805806
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
806807
&chanmon_cfgs[2].keys_manager);
808+
route_params.payment_params.max_intermediate_hops = 18;
807809

808810
let route = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck {
809811
let mut route = get_route(&nodes[0], &route_params).unwrap();

lightning/src/ln/outbound_payment.rs

+88-3
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ use crate::sign::{EntropySource, NodeSigner, Recipient};
1717
use crate::events::{self, PaymentFailureReason};
1818
use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
1919
use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, PaymentId};
20+
use crate::ln::msgs;
2021
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
2122
use crate::offers::invoice::Bolt12Invoice;
22-
use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
23+
use crate::routing::router::{BlindedTail, InFlightHtlcs, MAX_PATH_LENGTH_ESTIMATE, Path, Payee, PaymentParameters, Route, RouteParameters, Router};
2324
use crate::util::errors::APIError;
2425
use crate::util::logger::Logger;
2526
use crate::util::time::Time;
2627
#[cfg(all(feature = "std", test))]
2728
use crate::util::time::tests::SinceEpoch;
28-
use crate::util::ser::ReadableArgs;
29+
use crate::util::ser::{ReadableArgs, Writeable};
2930

3031
use core::fmt::{self, Display, Formatter};
3132
use core::ops::Deref;
@@ -421,6 +422,12 @@ pub enum RetryableSendFailure {
421422
/// [`Event::PaymentSent`]: crate::events::Event::PaymentSent
422423
/// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
423424
DuplicatePayment,
425+
/// The [`RecipientOnionFields::payment_metadata`], [`RecipientOnionFields::custom_tlvs`], or
426+
/// [`BlindedPath`]s provided are too large and caused us to exceed the maximum onion packet size
427+
/// of 1300 bytes.
428+
///
429+
/// [`BlindedPath`]: crate::blinded_path::BlindedPath;
430+
OnionPacketSizeExceeded,
424431
}
425432

426433
/// If a payment fails to send with [`ChannelManager::send_payment_with_route`], it can be in one
@@ -886,7 +893,7 @@ impl OutboundPayments {
886893
/// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
887894
fn send_payment_internal<R: Deref, NS: Deref, ES: Deref, IH, SP, L: Deref>(
888895
&self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
889-
keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, route_params: RouteParameters,
896+
keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, mut route_params: RouteParameters,
890897
router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES,
891898
node_signer: &NS, best_block_height: u32, logger: &L,
892899
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP,
@@ -907,6 +914,15 @@ impl OutboundPayments {
907914
}
908915
}
909916

917+
set_max_path_length(
918+
&mut route_params, &recipient_onion, keysend_preimage, best_block_height
919+
)
920+
.map_err(|()| {
921+
log_error!(logger, "Can't construct an onion packet without exceeding 1300-byte onion \
922+
hop_data length for payment with id {} and hash {}", payment_id, payment_hash);
923+
RetryableSendFailure::OnionPacketSizeExceeded
924+
})?;
925+
910926
let mut route = router.find_route_with_id(
911927
&node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
912928
Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs(),
@@ -1771,6 +1787,75 @@ impl OutboundPayments {
17711787
}
17721788
}
17731789

1790+
fn set_max_path_length(
1791+
route_params: &mut RouteParameters, recipient_onion: &RecipientOnionFields,
1792+
keysend_preimage: Option<PaymentPreimage>, best_block_height: u32,
1793+
) -> Result<(), ()> {
1794+
const PAYLOAD_HMAC_LEN: usize = 32;
1795+
const UNBLINDED_INTERMED_PAYLOAD_LEN: usize = PAYLOAD_HMAC_LEN
1796+
+ 1 + 1 + 8 // amt_to_forward
1797+
+ 1 + 1 + 4 // outgoing_cltv
1798+
+ 1 + 1 + 8; // short_channel_id
1799+
1800+
let num_reserved_bytes = match &route_params.payment_params.payee {
1801+
Payee::Blinded { route_hints, .. } => {
1802+
let largest_path = route_hints
1803+
.iter()
1804+
.map(|(_, path)| path)
1805+
.max_by(|path_a, path_b|
1806+
path_a.serialized_length().cmp(&path_b.serialized_length()))
1807+
.ok_or(())?;
1808+
1809+
largest_path.blinded_hops
1810+
.iter()
1811+
.rev()
1812+
.skip(1)
1813+
.map(|bh| msgs::OutboundOnionPayload::BlindedForward {
1814+
encrypted_tlvs: &bh.encrypted_payload,
1815+
// For simplicity, always set the intro_node_blinding_point in the
1816+
// final payload.
1817+
intro_node_blinding_point: None,
1818+
})
1819+
.chain(core::iter::once(msgs::OutboundOnionPayload::BlindedReceive {
1820+
sender_intended_htlc_amt_msat: route_params.final_value_msat,
1821+
total_msat: route_params.final_value_msat,
1822+
cltv_expiry_height:
1823+
best_block_height.saturating_add(route_params.payment_params.max_total_cltv_expiry_delta),
1824+
encrypted_tlvs: largest_path.blinded_hops
1825+
.last()
1826+
.map(|bh| &bh.encrypted_payload)
1827+
.unwrap_or(&Vec::new()),
1828+
intro_node_blinding_point: Some(largest_path.blinding_point),
1829+
keysend_preimage,
1830+
custom_tlvs: &recipient_onion.custom_tlvs,
1831+
}))
1832+
.map(|payload| payload.serialized_length().saturating_add(PAYLOAD_HMAC_LEN))
1833+
.fold(0, |acc: usize, payload_len| acc.saturating_add(payload_len))
1834+
},
1835+
Payee::Clear { final_cltv_expiry_delta, .. } => {
1836+
msgs::OutboundOnionPayload::Receive {
1837+
payment_data: recipient_onion.payment_secret.map(|payment_secret| {
1838+
msgs::FinalOnionHopData { payment_secret, total_msat: route_params.final_value_msat }
1839+
}),
1840+
payment_metadata: recipient_onion.payment_metadata.as_ref(),
1841+
keysend_preimage,
1842+
custom_tlvs: &recipient_onion.custom_tlvs,
1843+
sender_intended_htlc_amt_msat: route_params.final_value_msat,
1844+
cltv_expiry_height: best_block_height.saturating_add(*final_cltv_expiry_delta),
1845+
}
1846+
.serialized_length()
1847+
.saturating_add(PAYLOAD_HMAC_LEN)
1848+
}
1849+
};
1850+
let max_intermediate_unblinded_hops = 1300usize.checked_sub(num_reserved_bytes)
1851+
.map(|p| p / UNBLINDED_INTERMED_PAYLOAD_LEN)
1852+
.and_then(|l| u8::try_from(l).ok())
1853+
.ok_or(())?;
1854+
1855+
route_params.payment_params.max_intermediate_hops = core::cmp::min(max_intermediate_unblinded_hops, MAX_PATH_LENGTH_ESTIMATE);
1856+
Ok(())
1857+
}
1858+
17741859
/// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a
17751860
/// payment probe.
17761861
pub(super) fn payment_is_probe(payment_hash: &PaymentHash, payment_id: &PaymentId,

0 commit comments

Comments
 (0)