Skip to content

Commit bfc20f8

Browse files
Merge pull request #3156 from valentinewallace/2024-07-b12-max-path-len
Set max path length when paying BOLT 12 invoices.
2 parents 0c3196c + 678aac0 commit bfc20f8

File tree

3 files changed

+81
-6
lines changed

3 files changed

+81
-6
lines changed

lightning/src/ln/max_payment_path_len_tests.rs

+52-2
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,21 @@
1010
//! Tests for calculating the maximum length of a path based on the payment metadata, custom TLVs,
1111
//! and/or blinded paths present.
1212
13-
use bitcoin::secp256k1::Secp256k1;
14-
use crate::blinded_path::BlindedPath;
13+
use bitcoin::secp256k1::{Secp256k1, PublicKey};
14+
use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
1515
use crate::blinded_path::payment::{PaymentConstraints, PaymentContext, ReceiveTlvs};
1616
use crate::events::MessageSendEventsProvider;
1717
use crate::ln::PaymentSecret;
1818
use crate::ln::blinded_payment_tests::get_blinded_route_parameters;
1919
use crate::ln::channelmanager::PaymentId;
20+
use crate::ln::features::BlindedHopFeatures;
2021
use crate::ln::functional_test_utils::*;
2122
use crate::ln::msgs;
23+
use crate::ln::msgs::OnionMessageHandler;
2224
use crate::ln::onion_utils;
2325
use crate::ln::onion_utils::MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY;
2426
use crate::ln::outbound_payment::{RecipientOnionFields, Retry, RetryableSendFailure};
27+
use crate::offers::invoice::BlindedPayInfo;
2528
use crate::prelude::*;
2629
use crate::routing::router::{DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, PaymentParameters, RouteParameters};
2730
use crate::util::errors::APIError;
@@ -340,3 +343,50 @@ fn blinded_path_with_custom_tlv() {
340343
.with_custom_tlvs(recipient_onion_allows_2_hops.custom_tlvs)
341344
);
342345
}
346+
347+
#[test]
348+
fn bolt12_invoice_too_large_blinded_paths() {
349+
// Check that we'll fail paying BOLT 12 invoices with too-large blinded paths prior to
350+
// pathfinding.
351+
let chanmon_cfgs = create_chanmon_cfgs(2);
352+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
353+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
354+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
355+
create_announced_chan_between_nodes(&nodes, 0, 1);
356+
357+
nodes[1].router.expect_blinded_payment_paths(vec![(
358+
BlindedPayInfo {
359+
fee_base_msat: 42,
360+
fee_proportional_millionths: 42,
361+
cltv_expiry_delta: 42,
362+
htlc_minimum_msat: 42,
363+
htlc_maximum_msat: 42_000_000,
364+
features: BlindedHopFeatures::empty(),
365+
},
366+
BlindedPath {
367+
introduction_node: IntroductionNode::NodeId(PublicKey::from_slice(&[2; 33]).unwrap()),
368+
blinding_point: PublicKey::from_slice(&[2; 33]).unwrap(),
369+
blinded_hops: vec![
370+
BlindedHop {
371+
blinded_node_id: PublicKey::from_slice(&[2; 33]).unwrap(),
372+
encrypted_payload: vec![42; 1300],
373+
},
374+
BlindedHop {
375+
blinded_node_id: PublicKey::from_slice(&[2; 33]).unwrap(),
376+
encrypted_payload: vec![42; 1300],
377+
},
378+
],
379+
}
380+
)]);
381+
382+
let offer = nodes[1].node.create_offer_builder(None).unwrap().build().unwrap();
383+
let payment_id = PaymentId([1; 32]);
384+
nodes[0].node.pay_for_offer(&offer, None, Some(5000), None, payment_id, Retry::Attempts(0), None).unwrap();
385+
let invreq_om = nodes[0].onion_messenger.next_onion_message_for_peer(nodes[1].node.get_our_node_id()).unwrap();
386+
nodes[1].onion_messenger.handle_onion_message(&nodes[0].node.get_our_node_id(), &invreq_om);
387+
388+
let invoice_om = nodes[1].onion_messenger.next_onion_message_for_peer(nodes[0].node.get_our_node_id()).unwrap();
389+
nodes[0].onion_messenger.handle_onion_message(&nodes[1].node.get_our_node_id(), &invoice_om);
390+
// TODO: assert on the invoice error once we support replying to invoice OMs with failure info
391+
nodes[0].logger.assert_log_contains("lightning::ln::channelmanager", "Failed paying invoice: OnionPacketSizeExceeded", 1);
392+
}

lightning/src/ln/outbound_payment.rs

+14
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,11 @@ pub enum Bolt12PaymentError {
510510
UnexpectedInvoice,
511511
/// Payment for an invoice with the corresponding [`PaymentId`] was already initiated.
512512
DuplicateInvoice,
513+
/// The [`BlindedPath`]s provided are too large and caused us to exceed the maximum onion hop data
514+
/// size of 1300 bytes.
515+
///
516+
/// [`BlindedPath`]: crate::blinded_path::BlindedPath
517+
OnionPacketSizeExceeded,
513518
}
514519

515520
/// Indicates that we failed to send a payment probe. Further errors may be surfaced later via
@@ -837,6 +842,15 @@ impl OutboundPayments {
837842
let mut route_params = RouteParameters::from_payment_params_and_value(
838843
payment_params, amount_msat
839844
);
845+
onion_utils::set_max_path_length(
846+
&mut route_params, &RecipientOnionFields::spontaneous_empty(), None, best_block_height
847+
)
848+
.map_err(|()| {
849+
log_error!(logger, "Can't construct an onion packet without exceeding 1300-byte onion \
850+
hop_data length for payment with id {} and hash {}", payment_id, payment_hash);
851+
Bolt12PaymentError::OnionPacketSizeExceeded
852+
})?;
853+
840854
if let Some(max_fee_msat) = max_total_routing_fee_msat {
841855
route_params.max_total_routing_fee_msat = Some(max_fee_msat);
842856
}

lightning/src/util/test_utils.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ pub struct TestRouter<'a> {
116116
(),
117117
TestScorer,
118118
>,
119-
//pub entropy_source: &'a RandomBytes,
120119
pub network_graph: Arc<NetworkGraph<&'a TestLogger>>,
121120
pub next_routes: Mutex<VecDeque<(RouteParameters, Option<Result<Route, LightningError>>)>>,
121+
pub next_blinded_payment_paths: Mutex<Vec<(BlindedPayInfo, BlindedPath)>>,
122122
pub scorer: &'a RwLock<TestScorer>,
123123
}
124124

@@ -132,6 +132,7 @@ impl<'a> TestRouter<'a> {
132132
router: DefaultRouter::new(network_graph.clone(), logger, entropy_source, scorer, ()),
133133
network_graph,
134134
next_routes: Mutex::new(VecDeque::new()),
135+
next_blinded_payment_paths: Mutex::new(Vec::new()),
135136
scorer,
136137
}
137138
}
@@ -145,6 +146,11 @@ impl<'a> TestRouter<'a> {
145146
let mut expected_routes = self.next_routes.lock().unwrap();
146147
expected_routes.push_back((query, None));
147148
}
149+
150+
pub fn expect_blinded_payment_paths(&self, mut paths: Vec<(BlindedPayInfo, BlindedPath)>) {
151+
let mut expected_paths = self.next_blinded_payment_paths.lock().unwrap();
152+
core::mem::swap(&mut *expected_paths, &mut paths);
153+
}
148154
}
149155

150156
impl<'a> Router for TestRouter<'a> {
@@ -236,9 +242,14 @@ impl<'a> Router for TestRouter<'a> {
236242
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
237243
amount_msats: u64, secp_ctx: &Secp256k1<T>,
238244
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
239-
self.router.create_blinded_payment_paths(
240-
recipient, first_hops, tlvs, amount_msats, secp_ctx
241-
)
245+
let mut expected_paths = self.next_blinded_payment_paths.lock().unwrap();
246+
if expected_paths.is_empty() {
247+
self.router.create_blinded_payment_paths(
248+
recipient, first_hops, tlvs, amount_msats, secp_ctx
249+
)
250+
} else {
251+
Ok(core::mem::take(&mut *expected_paths))
252+
}
242253
}
243254
}
244255

0 commit comments

Comments
 (0)