Skip to content

Commit 3622a42

Browse files
committed
Support receiving, validating, and claiming MPP keysend
1 parent 3c4f14b commit 3622a42

File tree

2 files changed

+89
-52
lines changed

2 files changed

+89
-52
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 43 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3336,9 +3336,13 @@ where
33363336
}
33373337

33383338
macro_rules! check_total_value {
3339-
($payment_data: expr, $payment_preimage: expr) => {{
3339+
($payment_data: expr, $payment_preimage: expr, $is_keysend: expr) => {{
33403340
let mut payment_claimable_generated = false;
3341-
let purpose = || {
3341+
let purpose = if $is_keysend {
3342+
events::PaymentPurpose::SpontaneousPayment(
3343+
$payment_preimage.expect("Should never call check_total_value with $is_keysend as true but no preimage")
3344+
)
3345+
} else {
33423346
events::PaymentPurpose::InvoicePayment {
33433347
payment_preimage: $payment_preimage,
33443348
payment_secret: $payment_data.payment_secret,
@@ -3350,8 +3354,8 @@ where
33503354
continue
33513355
}
33523356
let (_, ref mut htlcs) = claimable_payments.claimable_htlcs.entry(payment_hash)
3353-
.or_insert_with(|| (purpose(), Vec::new()));
3354-
if htlcs.len() == 1 {
3357+
.or_insert_with(|| (purpose.clone(), Vec::new()));
3358+
if htlcs.len() == 1 && !$is_keysend {
33553359
if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload {
33563360
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we already had an existing keysend HTLC with the same payment hash", log_bytes!(payment_hash.0));
33573361
fail_htlc!(claimable_htlc, payment_hash);
@@ -3361,17 +3365,12 @@ where
33613365
let mut total_value = claimable_htlc.sender_intended_value;
33623366
for htlc in htlcs.iter() {
33633367
total_value += htlc.sender_intended_value;
3364-
match &htlc.onion_payload {
3365-
OnionPayload::Invoice { .. } => {
3366-
if htlc.total_msat != $payment_data.total_msat {
3367-
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
3368-
log_bytes!(payment_hash.0), $payment_data.total_msat, htlc.total_msat);
3369-
total_value = msgs::MAX_VALUE_MSAT;
3370-
}
3371-
if total_value >= msgs::MAX_VALUE_MSAT { break; }
3372-
},
3373-
_ => unreachable!(),
3368+
if htlc.total_msat != $payment_data.total_msat {
3369+
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
3370+
log_bytes!(payment_hash.0), $payment_data.total_msat, htlc.total_msat);
3371+
total_value = msgs::MAX_VALUE_MSAT;
33743372
}
3373+
if total_value >= msgs::MAX_VALUE_MSAT { break; }
33753374
}
33763375
// The condition determining whether an MPP is complete must
33773376
// match exactly the condition used in `timer_tick_occurred`
@@ -3389,7 +3388,7 @@ where
33893388
new_events.push(events::Event::PaymentClaimable {
33903389
receiver_node_id: Some(receiver_node_id),
33913390
payment_hash,
3392-
purpose: purpose(),
3391+
purpose,
33933392
amount_msat,
33943393
via_channel_id: Some(prev_channel_id),
33953394
via_user_channel_id: Some(prev_user_channel_id),
@@ -3434,33 +3433,37 @@ where
34343433
continue;
34353434
}
34363435
}
3437-
check_total_value!(payment_data, payment_preimage);
3436+
check_total_value!(payment_data, payment_preimage, false);
34383437
},
34393438
OnionPayload::Spontaneous(preimage) => {
3440-
let mut claimable_payments = self.claimable_payments.lock().unwrap();
3441-
if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) {
3442-
fail_htlc!(claimable_htlc, payment_hash);
3443-
continue
3444-
}
3445-
match claimable_payments.claimable_htlcs.entry(payment_hash) {
3446-
hash_map::Entry::Vacant(e) => {
3447-
let amount_msat = claimable_htlc.value;
3448-
claimable_htlc.total_value_received = Some(amount_msat);
3449-
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
3450-
e.insert((purpose.clone(), vec![claimable_htlc]));
3451-
let prev_channel_id = prev_funding_outpoint.to_channel_id();
3452-
new_events.push(events::Event::PaymentClaimable {
3453-
receiver_node_id: Some(receiver_node_id),
3454-
payment_hash,
3455-
amount_msat,
3456-
purpose,
3457-
via_channel_id: Some(prev_channel_id),
3458-
via_user_channel_id: Some(prev_user_channel_id),
3459-
});
3460-
},
3461-
hash_map::Entry::Occupied(_) => {
3462-
log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} for a duplicative payment hash", log_bytes!(payment_hash.0));
3439+
if let Some(payment_data) = payment_data {
3440+
check_total_value!(payment_data, Some(preimage), true);
3441+
} else {
3442+
let mut claimable_payments = self.claimable_payments.lock().unwrap();
3443+
if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) {
34633444
fail_htlc!(claimable_htlc, payment_hash);
3445+
continue
3446+
}
3447+
match claimable_payments.claimable_htlcs.entry(payment_hash) {
3448+
hash_map::Entry::Vacant(e) => {
3449+
let amount_msat = claimable_htlc.value;
3450+
claimable_htlc.total_value_received = Some(amount_msat);
3451+
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
3452+
e.insert((purpose.clone(), vec![claimable_htlc]));
3453+
let prev_channel_id = prev_funding_outpoint.to_channel_id();
3454+
new_events.push(events::Event::PaymentClaimable {
3455+
receiver_node_id: Some(receiver_node_id),
3456+
payment_hash,
3457+
amount_msat,
3458+
purpose,
3459+
via_channel_id: Some(prev_channel_id),
3460+
via_user_channel_id: Some(prev_user_channel_id),
3461+
});
3462+
},
3463+
hash_map::Entry::Occupied(_) => {
3464+
log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} for a duplicative payment hash", log_bytes!(payment_hash.0));
3465+
fail_htlc!(claimable_htlc, payment_hash);
3466+
}
34643467
}
34653468
}
34663469
}
@@ -3481,7 +3484,7 @@ where
34813484
log_bytes!(payment_hash.0), payment_data.total_msat, inbound_payment.get().min_value_msat.unwrap());
34823485
fail_htlc!(claimable_htlc, payment_hash);
34833486
} else {
3484-
let payment_claimable_generated = check_total_value!(payment_data, inbound_payment.get().payment_preimage);
3487+
let payment_claimable_generated = check_total_value!(payment_data, inbound_payment.get().payment_preimage, false);
34853488
if payment_claimable_generated {
34863489
inbound_payment.remove_entry();
34873490
}
@@ -4037,17 +4040,6 @@ where
40374040
}
40384041
expected_amt_msat = htlc.total_value_received;
40394042

4040-
if let OnionPayload::Spontaneous(_) = &htlc.onion_payload {
4041-
// We don't currently support MPP for spontaneous payments, so just check
4042-
// that there's one payment here and move on.
4043-
if sources.len() != 1 {
4044-
log_error!(self.logger, "Somehow ended up with an MPP spontaneous payment - this should not be reachable!");
4045-
debug_assert!(false);
4046-
valid_mpp = false;
4047-
break;
4048-
}
4049-
}
4050-
40514043
claimable_amt_msat += htlc.value;
40524044
}
40534045
mem::drop(per_peer_state);

lightning/src/ln/functional_tests.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::ln::{chan_utils, onion_utils};
2727
use crate::ln::chan_utils::{OFFERED_HTLC_SCRIPT_WEIGHT, htlc_success_tx_weight, htlc_timeout_tx_weight, HTLCOutputInCommitment};
2828
use crate::routing::gossip::{NetworkGraph, NetworkUpdate};
2929
use crate::routing::router::{PaymentParameters, Route, RouteHop, RouteParameters, find_route, get_route};
30-
use crate::ln::features::{ChannelFeatures, NodeFeatures};
30+
use crate::ln::features::{ChannelFeatures, NodeFeatures, InvoiceFeatures};
3131
use crate::ln::msgs;
3232
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
3333
use crate::util::enforcing_trait_impls::EnforcingSigner;
@@ -8123,6 +8123,51 @@ fn test_simple_mpp() {
81238123
claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
81248124
}
81258125

8126+
#[test]
8127+
fn test_mpp_keysend() {
8128+
let chanmon_cfgs = create_chanmon_cfgs(4);
8129+
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
8130+
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
8131+
let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
8132+
8133+
let chan_1_id = create_announced_chan_between_nodes(&nodes, 0, 1).0.contents.short_channel_id;
8134+
let chan_2_id = create_announced_chan_between_nodes(&nodes, 0, 2).0.contents.short_channel_id;
8135+
let chan_3_id = create_announced_chan_between_nodes(&nodes, 1, 3).0.contents.short_channel_id;
8136+
let chan_4_id = create_announced_chan_between_nodes(&nodes, 2, 3).0.contents.short_channel_id;
8137+
let network_graph = nodes[0].network_graph.clone();
8138+
8139+
let payer_pubkey = nodes[0].node.get_our_node_id();
8140+
let payee_pubkey = nodes[3].node.get_our_node_id();
8141+
let recv_value = 15_000_000;
8142+
let mut invoice_features = InvoiceFeatures::for_keysend();
8143+
invoice_features.set_basic_mpp_optional();
8144+
let route_params = RouteParameters {
8145+
payment_params: PaymentParameters::from_node_id(payee_pubkey, 40).with_features(invoice_features),
8146+
// Approxiamating the amount to make router use MPP is not reliable in
8147+
// case those thresholds change, but helpful to test where the router may
8148+
// block mpp keysend
8149+
final_value_msat: recv_value,
8150+
};
8151+
let scorer = test_utils::TestScorer::new();
8152+
let random_seed_bytes = chanmon_cfgs[0].keys_manager.get_secure_random_bytes();
8153+
let route = find_route(&payer_pubkey, &route_params, &network_graph, None, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
8154+
8155+
let test_preimage = PaymentPreimage([42; 32]);
8156+
let payment_hash = nodes[0].node.send_spontaneous_payment(&route, Some(test_preimage), PaymentId(test_preimage.0)).unwrap();
8157+
check_added_monitors!(nodes[0], 2);
8158+
8159+
let expected_route: &[&[&Node]] = &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]];
8160+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
8161+
assert_eq!(events.len(), 2);
8162+
for (path_idx, expected_path) in expected_route.iter().enumerate() {
8163+
let ev = remove_first_msg_event_to_node(&expected_path[0].node.get_our_node_id(), &mut events);
8164+
let expect_payment = path_idx == expected_route.len() - 1;
8165+
pass_along_path(&nodes[0], *expected_path, recv_value, payment_hash.clone(), None, ev, expect_payment, Some(test_preimage));
8166+
}
8167+
8168+
claim_payment_along_route(&nodes[0], expected_route, false, test_preimage);
8169+
}
8170+
81268171
#[test]
81278172
fn test_preimage_storage() {
81288173
// Simple test of payment preimage storage allowing no client-side storage to claim payments

0 commit comments

Comments
 (0)