Skip to content

Commit 03e3280

Browse files
committed
Support receiving, validating, and claiming MPP keysend
1 parent fd66f88 commit 03e3280

File tree

3 files changed

+118
-50
lines changed

3 files changed

+118
-50
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3543,9 +3543,13 @@ where
35433543
}
35443544

35453545
macro_rules! check_total_value {
3546-
($payment_data: expr, $payment_preimage: expr) => {{
3546+
($payment_data: expr, $payment_preimage: expr, $is_keysend: expr) => {{
35473547
let mut payment_claimable_generated = false;
3548-
let purpose = || {
3548+
let purpose = if $is_keysend {
3549+
events::PaymentPurpose::SpontaneousPayment(
3550+
$payment_preimage.expect("Should never call check_total_value with $is_keysend as true but no preimage")
3551+
)
3552+
} else {
35493553
events::PaymentPurpose::InvoicePayment {
35503554
payment_preimage: $payment_preimage,
35513555
payment_secret: $payment_data.payment_secret,
@@ -3561,7 +3565,7 @@ where
35613565
.or_insert_with(|| {
35623566
committed_to_claimable = true;
35633567
ClaimablePayment {
3564-
purpose: purpose(), htlcs: Vec::new(), onion_fields: None,
3568+
purpose: purpose.clone(), htlcs: Vec::new(), onion_fields: None,
35653569
}
35663570
});
35673571
if let Some(earlier_fields) = &mut claimable_payment.onion_fields {
@@ -3572,7 +3576,7 @@ where
35723576
claimable_payment.onion_fields = Some(onion_fields);
35733577
}
35743578
let ref mut htlcs = &mut claimable_payment.htlcs;
3575-
if htlcs.len() == 1 {
3579+
if !htlcs.is_empty() && !$is_keysend {
35763580
if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload {
35773581
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));
35783582
fail_htlc!(claimable_htlc, payment_hash);
@@ -3583,17 +3587,12 @@ where
35833587
for htlc in htlcs.iter() {
35843588
total_value += htlc.sender_intended_value;
35853589
earliest_expiry = cmp::min(earliest_expiry, htlc.cltv_expiry);
3586-
match &htlc.onion_payload {
3587-
OnionPayload::Invoice { .. } => {
3588-
if htlc.total_msat != $payment_data.total_msat {
3589-
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
3590-
log_bytes!(payment_hash.0), $payment_data.total_msat, htlc.total_msat);
3591-
total_value = msgs::MAX_VALUE_MSAT;
3592-
}
3593-
if total_value >= msgs::MAX_VALUE_MSAT { break; }
3594-
},
3595-
_ => unreachable!(),
3590+
if htlc.total_msat != $payment_data.total_msat {
3591+
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
3592+
log_bytes!(payment_hash.0), $payment_data.total_msat, htlc.total_msat);
3593+
total_value = msgs::MAX_VALUE_MSAT;
35963594
}
3595+
if total_value >= msgs::MAX_VALUE_MSAT { break; }
35973596
}
35983597
// The condition determining whether an MPP is complete must
35993598
// match exactly the condition used in `timer_tick_occurred`
@@ -3614,7 +3613,7 @@ where
36143613
new_events.push_back((events::Event::PaymentClaimable {
36153614
receiver_node_id: Some(receiver_node_id),
36163615
payment_hash,
3617-
purpose: purpose(),
3616+
purpose,
36183617
amount_msat,
36193618
via_channel_id: Some(prev_channel_id),
36203619
via_user_channel_id: Some(prev_user_channel_id),
@@ -3662,40 +3661,44 @@ where
36623661
fail_htlc!(claimable_htlc, payment_hash);
36633662
}
36643663
}
3665-
check_total_value!(payment_data, payment_preimage);
3664+
check_total_value!(payment_data, payment_preimage, false);
36663665
},
36673666
OnionPayload::Spontaneous(preimage) => {
3668-
let mut claimable_payments = self.claimable_payments.lock().unwrap();
3669-
if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) {
3670-
fail_htlc!(claimable_htlc, payment_hash);
3671-
}
3672-
match claimable_payments.claimable_payments.entry(payment_hash) {
3673-
hash_map::Entry::Vacant(e) => {
3674-
let amount_msat = claimable_htlc.value;
3675-
claimable_htlc.total_value_received = Some(amount_msat);
3676-
let claim_deadline = Some(claimable_htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER);
3677-
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
3678-
e.insert(ClaimablePayment {
3679-
purpose: purpose.clone(),
3680-
onion_fields: Some(onion_fields.clone()),
3681-
htlcs: vec![claimable_htlc],
3682-
});
3683-
let prev_channel_id = prev_funding_outpoint.to_channel_id();
3684-
new_events.push_back((events::Event::PaymentClaimable {
3685-
receiver_node_id: Some(receiver_node_id),
3686-
payment_hash,
3687-
amount_msat,
3688-
purpose,
3689-
via_channel_id: Some(prev_channel_id),
3690-
via_user_channel_id: Some(prev_user_channel_id),
3691-
claim_deadline,
3692-
onion_fields: Some(onion_fields),
3693-
}, None));
3694-
},
3695-
hash_map::Entry::Occupied(_) => {
3696-
log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} for a duplicative payment hash", log_bytes!(payment_hash.0));
3667+
if let Some(payment_data) = payment_data {
3668+
check_total_value!(payment_data, Some(preimage), true);
3669+
} else {
3670+
let mut claimable_payments = self.claimable_payments.lock().unwrap();
3671+
if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) {
36973672
fail_htlc!(claimable_htlc, payment_hash);
36983673
}
3674+
match claimable_payments.claimable_payments.entry(payment_hash) {
3675+
hash_map::Entry::Vacant(e) => {
3676+
let amount_msat = claimable_htlc.value;
3677+
claimable_htlc.total_value_received = Some(amount_msat);
3678+
let claim_deadline = Some(claimable_htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER);
3679+
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
3680+
e.insert(ClaimablePayment {
3681+
purpose: purpose.clone(),
3682+
onion_fields: Some(onion_fields.clone()),
3683+
htlcs: vec![claimable_htlc],
3684+
});
3685+
let prev_channel_id = prev_funding_outpoint.to_channel_id();
3686+
new_events.push_back((events::Event::PaymentClaimable {
3687+
receiver_node_id: Some(receiver_node_id),
3688+
payment_hash,
3689+
amount_msat,
3690+
purpose,
3691+
via_channel_id: Some(prev_channel_id),
3692+
via_user_channel_id: Some(prev_user_channel_id),
3693+
claim_deadline,
3694+
onion_fields: Some(onion_fields),
3695+
}, None));
3696+
},
3697+
hash_map::Entry::Occupied(_) => {
3698+
log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} for a duplicative payment hash", log_bytes!(payment_hash.0));
3699+
fail_htlc!(claimable_htlc, payment_hash);
3700+
}
3701+
}
36993702
}
37003703
}
37013704
}
@@ -3714,7 +3717,7 @@ where
37143717
log_bytes!(payment_hash.0), payment_data.total_msat, inbound_payment.get().min_value_msat.unwrap());
37153718
fail_htlc!(claimable_htlc, payment_hash);
37163719
} else {
3717-
let payment_claimable_generated = check_total_value!(payment_data, inbound_payment.get().payment_preimage);
3720+
let payment_claimable_generated = check_total_value!(payment_data, inbound_payment.get().payment_preimage, false);
37183721
if payment_claimable_generated {
37193722
inbound_payment.remove_entry();
37203723
}
@@ -4189,12 +4192,16 @@ where
41894192
/// event matches your expectation. If you fail to do so and call this method, you may provide
41904193
/// the sender "proof-of-payment" when they did not fulfill the full expected payment.
41914194
///
4195+
/// To accept multi-part keysend payments you must set [`UserConfig::accept_mpp_keysend`] to
4196+
/// true.
4197+
///
41924198
/// [`Event::PaymentClaimable`]: crate::events::Event::PaymentClaimable
41934199
/// [`Event::PaymentClaimable::claim_deadline`]: crate::events::Event::PaymentClaimable::claim_deadline
41944200
/// [`Event::PaymentClaimed`]: crate::events::Event::PaymentClaimed
41954201
/// [`process_pending_events`]: EventsProvider::process_pending_events
41964202
/// [`create_inbound_payment`]: Self::create_inbound_payment
41974203
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
4204+
/// [`UserConfig::accept_mpp_keysend`]: crate::util::config::UserConfig::accept_mpp_keysend
41984205
pub fn claim_funds(&self, payment_preimage: PaymentPreimage) {
41994206
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
42004207

@@ -4255,9 +4262,9 @@ where
42554262
expected_amt_msat = htlc.total_value_received;
42564263

42574264
if let OnionPayload::Spontaneous(_) = &htlc.onion_payload {
4258-
// We don't currently support MPP for spontaneous payments, so just check
4265+
// If the user chooses not to support MPP for spontaneous payments, just check
42594266
// that there's one payment here and move on.
4260-
if sources.len() != 1 {
4267+
if !self.default_configuration.accept_mpp_keysend && sources.len() != 1 {
42614268
log_error!(self.logger, "Somehow ended up with an MPP spontaneous payment - this should not be reachable!");
42624269
debug_assert!(false);
42634270
valid_mpp = false;

lightning/src/ln/functional_test_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2108,7 +2108,7 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p
21082108
},
21092109
PaymentPurpose::SpontaneousPayment(payment_preimage) => {
21102110
assert_eq!(expected_preimage.unwrap(), *payment_preimage);
2111-
assert!(our_payment_secret.is_none());
2111+
assert_eq!(our_payment_secret, onion_fields.as_ref().unwrap().payment_secret);
21122112
},
21132113
}
21142114
assert_eq!(*amount_msat, recv_value);

lightning/src/ln/functional_tests.rs

Lines changed: 62 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::{Path, 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;
@@ -8215,6 +8215,67 @@ fn test_simple_mpp() {
82158215
claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
82168216
}
82178217

8218+
fn do_test_mpp_keysend(accept_mpp_keysend: bool) {
8219+
let mut mpp_keysend_config = test_default_channel_config();
8220+
mpp_keysend_config.accept_mpp_keysend = accept_mpp_keysend;
8221+
let chanmon_cfgs = create_chanmon_cfgs(4);
8222+
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
8223+
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, Some(mpp_keysend_config)]);
8224+
let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
8225+
8226+
create_announced_chan_between_nodes(&nodes, 0, 1).0.contents.short_channel_id;
8227+
create_announced_chan_between_nodes(&nodes, 0, 2).0.contents.short_channel_id;
8228+
create_announced_chan_between_nodes(&nodes, 1, 3).0.contents.short_channel_id;
8229+
create_announced_chan_between_nodes(&nodes, 2, 3).0.contents.short_channel_id;
8230+
let network_graph = nodes[0].network_graph.clone();
8231+
8232+
let payer_pubkey = nodes[0].node.get_our_node_id();
8233+
let payee_pubkey = nodes[3].node.get_our_node_id();
8234+
let recv_value = 15_000_000;
8235+
let mut invoice_features = InvoiceFeatures::for_keysend(true);
8236+
invoice_features.set_basic_mpp_optional();
8237+
let route_params = RouteParameters {
8238+
payment_params: PaymentParameters::from_node_id(payee_pubkey, 40).with_bolt11_features(invoice_features).unwrap(),
8239+
// Approxiamating the amount to make the router use MPP is not reliable in
8240+
// case those thresholds change, but helpful to test where the router may
8241+
// block mpp keysend
8242+
final_value_msat: recv_value,
8243+
};
8244+
let scorer = test_utils::TestScorer::new();
8245+
let random_seed_bytes = chanmon_cfgs[0].keys_manager.get_secure_random_bytes();
8246+
let route = find_route(&payer_pubkey, &route_params, &network_graph, None, nodes[0].logger,
8247+
&scorer, &random_seed_bytes).unwrap();
8248+
8249+
let payment_preimage = PaymentPreimage([42; 32]);
8250+
let payment_secret = PaymentSecret(payment_preimage.0);
8251+
let payment_hash = nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage),
8252+
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_preimage.0)).unwrap();
8253+
check_added_monitors!(nodes[0], 2);
8254+
8255+
let expected_route: &[&[&Node]] = &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]];
8256+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
8257+
assert_eq!(events.len(), 2);
8258+
for (path_idx, expected_path) in expected_route.iter().enumerate() {
8259+
let ev = remove_first_msg_event_to_node(&expected_path[0].node.get_our_node_id(), &mut events);
8260+
let expect_payment = path_idx == expected_route.len() - 1;
8261+
pass_along_path(&nodes[0], *expected_path, recv_value, payment_hash.clone(),
8262+
Some(payment_secret), ev, expect_payment, Some(payment_preimage));
8263+
}
8264+
8265+
claim_payment_along_route(&nodes[0], expected_route, false, payment_preimage);
8266+
}
8267+
8268+
#[test]
8269+
fn test_mpp_keysend() {
8270+
do_test_mpp_keysend(true);
8271+
}
8272+
8273+
#[test]
8274+
#[should_panic]
8275+
fn test_mpp_keysend_fail() {
8276+
do_test_mpp_keysend(false);
8277+
}
8278+
82188279
#[test]
82198280
fn test_preimage_storage() {
82208281
// Simple test of payment preimage storage allowing no client-side storage to claim payments

0 commit comments

Comments
 (0)