Skip to content

Commit 223e2de

Browse files
Support sending blinded MPP payments
1 parent ef57ab0 commit 223e2de

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

lightning/src/ln/blinded_payment_tests.rs

+71-1
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ use bitcoin::secp256k1::Secp256k1;
1111
use crate::blinded_path::BlindedPath;
1212
use crate::blinded_path::payment::{ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs};
1313
use crate::events::MessageSendEventsProvider;
14+
use crate::ln::channelmanager;
1415
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields, RetryableSendFailure};
15-
use crate::ln::features::BlindedHopFeatures;
16+
use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures};
1617
use crate::ln::functional_test_utils::*;
1718
use crate::ln::msgs::ChannelMessageHandler;
1819
use crate::ln::outbound_payment::Retry;
1920
use crate::prelude::*;
2021
use crate::routing::router::{PaymentParameters, RouteParameters};
22+
use crate::util::config::UserConfig;
2123

2224
#[test]
2325
fn simple_blinded_payment() {
@@ -70,6 +72,74 @@ fn simple_blinded_payment() {
7072
claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3]], payment_preimage);
7173
}
7274

75+
#[test]
76+
fn blinded_mpp() {
77+
let chanmon_cfgs = create_chanmon_cfgs(4);
78+
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
79+
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
80+
let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
81+
let mut secp_ctx = Secp256k1::new();
82+
83+
create_announced_chan_between_nodes(&nodes, 0, 1);
84+
create_announced_chan_between_nodes(&nodes, 0, 2);
85+
let chan_upd_1_3 = create_announced_chan_between_nodes(&nodes, 1, 3).0.contents;
86+
let chan_upd_2_3 = create_announced_chan_between_nodes(&nodes, 2, 3).0.contents;
87+
88+
let amt_msat = 15_000_000;
89+
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(amt_msat), None);
90+
let mut blinded_paths = Vec::new();
91+
for (idx, chan_upd) in [chan_upd_1_3, chan_upd_2_3].iter().enumerate() {
92+
let intermediate_nodes = vec![(nodes[idx + 1].node.get_our_node_id(), ForwardTlvs {
93+
short_channel_id: chan_upd.short_channel_id,
94+
payment_relay: PaymentRelay {
95+
cltv_expiry_delta: chan_upd.cltv_expiry_delta,
96+
fee_proportional_millionths: chan_upd.fee_proportional_millionths,
97+
fee_base_msat: chan_upd.fee_base_msat,
98+
},
99+
payment_constraints: PaymentConstraints {
100+
max_cltv_expiry: u32::max_value(),
101+
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
102+
},
103+
features: BlindedHopFeatures::empty(),
104+
})];
105+
let payee_tlvs = ReceiveTlvs {
106+
payment_secret,
107+
payment_constraints: PaymentConstraints {
108+
max_cltv_expiry: u32::max_value(),
109+
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
110+
},
111+
};
112+
let blinded_path = BlindedPath::new_for_payment(
113+
&intermediate_nodes[..], nodes[3].node.get_our_node_id(), payee_tlvs,
114+
chan_upd.htlc_maximum_msat, &chanmon_cfgs[3].keys_manager, &secp_ctx
115+
).unwrap();
116+
blinded_paths.push(blinded_path);
117+
}
118+
119+
let bolt12_features: Bolt12InvoiceFeatures =
120+
channelmanager::provided_invoice_features(&UserConfig::default()).to_context();
121+
let route_params = RouteParameters {
122+
payment_params: PaymentParameters::blinded(blinded_paths)
123+
.with_bolt12_features(bolt12_features).unwrap(),
124+
final_value_msat: amt_msat,
125+
};
126+
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
127+
check_added_monitors(&nodes[0], 2);
128+
129+
let expected_route: &[&[&Node]] = &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]];
130+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
131+
assert_eq!(events.len(), 2);
132+
133+
let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
134+
pass_along_path(&nodes[0], expected_route[0], amt_msat, payment_hash.clone(),
135+
Some(payment_secret), ev.clone(), false, None);
136+
137+
let ev = remove_first_msg_event_to_node(&nodes[2].node.get_our_node_id(), &mut events);
138+
pass_along_path(&nodes[0], expected_route[1], amt_msat, payment_hash.clone(),
139+
Some(payment_secret), ev.clone(), true, None);
140+
claim_payment_along_route(&nodes[0], expected_route, false, payment_preimage);
141+
}
142+
73143
#[test]
74144
fn blinded_intercept_payment() {
75145
let chanmon_cfgs = create_chanmon_cfgs(3);

lightning/src/ln/outbound_payment.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,9 @@ impl OutboundPayments {
10641064
if route.paths.len() < 1 {
10651065
return Err(PaymentSendFailure::ParameterError(APIError::InvalidRoute{err: "There must be at least one path to send over".to_owned()}));
10661066
}
1067-
if recipient_onion.payment_secret.is_none() && route.paths.len() > 1 {
1067+
if recipient_onion.payment_secret.is_none() && route.paths.len() > 1
1068+
&& !route.paths.iter().any(|p| p.blinded_tail.is_some())
1069+
{
10681070
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_owned()}));
10691071
}
10701072
let mut total_value = 0;

0 commit comments

Comments
 (0)