Skip to content

Commit 2e684ee

Browse files
committed
Use onion amount amt_to_forward for MPP set calculation
If routing nodes take less fees and pay the final node more than `amt_to_forward`, the receiver may see that `total_msat` has been met before all of the sender's intended HTLCs have arrived. The receiver may then prematurely claim the payment and release the payment hash, allowing routing nodes to claim the remaining HTLCs. Using the onion value `amt_to_forward` to determine when `total_msat` has been met allows the sender to control the set total.
1 parent b462f81 commit 2e684ee

File tree

2 files changed

+113
-7
lines changed

2 files changed

+113
-7
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ struct ClaimableHTLC {
191191
cltv_expiry: u32,
192192
/// The amount (in msats) of this MPP part
193193
value: u64,
194+
/// The amount (in msats) that the sender intended to be sent in this MPP
195+
/// part (used for validating total MPP amount)
196+
sender_intended_value: u64,
194197
onion_payload: OnionPayload,
195198
timer_ticks: u8,
196199
// The total value received for a payment (sum of all MPP parts if the payment is a MPP).
@@ -2162,7 +2165,7 @@ where
21622165
payment_hash,
21632166
incoming_shared_secret: shared_secret,
21642167
incoming_amt_msat: Some(amt_msat),
2165-
outgoing_amt_msat: amt_msat,
2168+
outgoing_amt_msat: hop_data.amt_to_forward,
21662169
outgoing_cltv_value: hop_data.outgoing_cltv_value,
21672170
})
21682171
}
@@ -3235,7 +3238,7 @@ where
32353238
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
32363239
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
32373240
forward_info: PendingHTLCInfo {
3238-
routing, incoming_shared_secret, payment_hash, outgoing_amt_msat, ..
3241+
routing, incoming_shared_secret, payment_hash, incoming_amt_msat, outgoing_amt_msat, ..
32393242
}
32403243
}) => {
32413244
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret) = match routing {
@@ -3257,7 +3260,11 @@ where
32573260
incoming_packet_shared_secret: incoming_shared_secret,
32583261
phantom_shared_secret,
32593262
},
3260-
value: outgoing_amt_msat,
3263+
// We differentiate the received value from the sender intended value
3264+
// if possible so that we don't prematurely mark MPP payments complete
3265+
// if routing nodes overpay
3266+
value: incoming_amt_msat.unwrap_or(outgoing_amt_msat),
3267+
sender_intended_value: outgoing_amt_msat,
32613268
timer_ticks: 0,
32623269
total_value_received: None,
32633270
total_msat: if let Some(data) = &payment_data { data.total_msat } else { outgoing_amt_msat },
@@ -3313,9 +3320,9 @@ where
33133320
continue
33143321
}
33153322
}
3316-
let mut total_value = claimable_htlc.value;
3323+
let mut total_value = claimable_htlc.sender_intended_value;
33173324
for htlc in htlcs.iter() {
3318-
total_value += htlc.value;
3325+
total_value += htlc.sender_intended_value;
33193326
match &htlc.onion_payload {
33203327
OnionPayload::Invoice { .. } => {
33213328
if htlc.total_msat != $payment_data.total_msat {
@@ -3330,7 +3337,7 @@ where
33303337
}
33313338
if total_value >= msgs::MAX_VALUE_MSAT {
33323339
fail_htlc!(claimable_htlc, payment_hash);
3333-
} else if total_value - claimable_htlc.value >= $payment_data.total_msat {
3340+
} else if total_value - claimable_htlc.sender_intended_value >= $payment_data.total_msat {
33343341
log_trace!(self.logger, "Failing HTLC with payment_hash {} as payment is already claimable",
33353342
log_bytes!(payment_hash.0));
33363343
fail_htlc!(claimable_htlc, payment_hash);
@@ -3405,7 +3412,7 @@ where
34053412
new_events.push(events::Event::PaymentClaimable {
34063413
receiver_node_id: Some(receiver_node_id),
34073414
payment_hash,
3408-
amount_msat: outgoing_amt_msat,
3415+
amount_msat,
34093416
purpose,
34103417
via_channel_id: Some(prev_channel_id),
34113418
via_user_channel_id: Some(prev_user_channel_id),
@@ -6784,6 +6791,7 @@ impl Writeable for ClaimableHTLC {
67846791
(0, self.prev_hop, required),
67856792
(1, self.total_msat, required),
67866793
(2, self.value, required),
6794+
(3, self.sender_intended_value, required),
67876795
(4, payment_data, option),
67886796
(5, self.total_value_received, option),
67896797
(6, self.cltv_expiry, required),
@@ -6797,6 +6805,7 @@ impl Readable for ClaimableHTLC {
67976805
fn read<R: Read>(reader: &mut R) -> Result<Self, DecodeError> {
67986806
let mut prev_hop = crate::util::ser::RequiredWrapper(None);
67996807
let mut value = 0;
6808+
let mut sender_intended_value = None;
68006809
let mut payment_data: Option<msgs::FinalOnionHopData> = None;
68016810
let mut cltv_expiry = 0;
68026811
let mut total_value_received = None;
@@ -6806,6 +6815,7 @@ impl Readable for ClaimableHTLC {
68066815
(0, prev_hop, required),
68076816
(1, total_msat, option),
68086817
(2, value, required),
6818+
(3, sender_intended_value, option),
68096819
(4, payment_data, option),
68106820
(5, total_value_received, option),
68116821
(6, cltv_expiry, required),
@@ -6835,6 +6845,7 @@ impl Readable for ClaimableHTLC {
68356845
prev_hop: prev_hop.0.unwrap(),
68366846
timer_ticks: 0,
68376847
value,
6848+
sender_intended_value: sender_intended_value.unwrap_or(value),
68386849
total_value_received,
68396850
total_msat: total_msat.unwrap(),
68406851
onion_payload,

lightning/src/ln/functional_tests.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7925,6 +7925,101 @@ fn test_can_not_accept_unknown_inbound_channel() {
79257925
}
79267926
}
79277927

7928+
#[test]
7929+
fn test_onion_value_mpp_set_calculation() {
7930+
// Test that we use the onion value `amt_to_forward` when
7931+
// calculating whether we've reached the `total_msat` of an MPP
7932+
// by having a routing node forward more than `amt_to_forward`
7933+
// and checking that the receiving node doesn't generate
7934+
// a PaymentClaimable event too early
7935+
let node_count = 4;
7936+
let chanmon_cfgs = create_chanmon_cfgs(node_count);
7937+
let node_cfgs = create_node_cfgs(node_count, &chanmon_cfgs);
7938+
let node_chanmgrs = create_node_chanmgrs(node_count, &node_cfgs, &vec![None; node_count]);
7939+
let mut nodes = create_network(node_count, &node_cfgs, &node_chanmgrs);
7940+
7941+
let chan_1_id = create_announced_chan_between_nodes(&nodes, 0, 1).0.contents.short_channel_id;
7942+
let chan_2_id = create_announced_chan_between_nodes(&nodes, 0, 2).0.contents.short_channel_id;
7943+
let chan_3_id = create_announced_chan_between_nodes(&nodes, 1, 3).0.contents.short_channel_id;
7944+
let chan_4_id = create_announced_chan_between_nodes(&nodes, 2, 3).0.contents.short_channel_id;
7945+
7946+
let total_msat = 100_000;
7947+
let expected_paths: &[&[&Node]] = &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]];
7948+
let (mut route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(&nodes[0], nodes[3], total_msat);
7949+
let sample_path = route.paths.pop().unwrap();
7950+
7951+
let mut path_1 = sample_path.clone();
7952+
path_1[0].pubkey = nodes[1].node.get_our_node_id();
7953+
path_1[0].short_channel_id = chan_1_id;
7954+
path_1[1].pubkey = nodes[3].node.get_our_node_id();
7955+
path_1[1].short_channel_id = chan_3_id;
7956+
path_1[1].fee_msat = 100_000;
7957+
route.paths.push(path_1);
7958+
7959+
let mut path_2 = sample_path.clone();
7960+
path_2[0].pubkey = nodes[2].node.get_our_node_id();
7961+
path_2[0].short_channel_id = chan_2_id;
7962+
path_2[1].pubkey = nodes[3].node.get_our_node_id();
7963+
path_2[1].short_channel_id = chan_4_id;
7964+
path_2[1].fee_msat = 1_000;
7965+
route.paths.push(path_2);
7966+
7967+
// Send payment
7968+
let payment_id = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes());
7969+
let onion_session_privs = nodes[0].node.test_add_new_pending_payment(our_payment_hash, Some(our_payment_secret), payment_id, &route).unwrap();
7970+
nodes[0].node.test_send_payment_internal(&route, our_payment_hash, &Some(our_payment_secret), None, payment_id, Some(total_msat), onion_session_privs).unwrap();
7971+
check_added_monitors!(nodes[0], expected_paths.len());
7972+
7973+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
7974+
assert_eq!(events.len(), expected_paths.len());
7975+
7976+
// First path
7977+
let ev = remove_first_msg_event_to_node(&expected_paths[0][0].node.get_our_node_id(), &mut events);
7978+
let mut payment_event = SendEvent::from_event(ev);
7979+
let mut prev_node = &nodes[0];
7980+
7981+
for (idx, &node) in expected_paths[0].iter().enumerate() {
7982+
assert_eq!(node.node.get_our_node_id(), payment_event.node_id);
7983+
7984+
if idx == 0 { // routing node
7985+
let session_priv = [3; 32];
7986+
let height = nodes[0].best_block_info().1;
7987+
let session_priv = SecretKey::from_slice(&session_priv).unwrap();
7988+
let mut onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
7989+
let (mut onion_payloads, _, _) = onion_utils::build_onion_payloads(&route.paths[0], 100_000, &Some(our_payment_secret), height + 1, &None).unwrap();
7990+
// Edit amt_to_forward to simulate the sender having set
7991+
// the final amount and the routing node taking less fee
7992+
onion_payloads[1].amt_to_forward = 99_000;
7993+
let new_onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash);
7994+
payment_event.msgs[0].onion_routing_packet = new_onion_packet;
7995+
}
7996+
7997+
node.node.handle_update_add_htlc(&prev_node.node.get_our_node_id(), &payment_event.msgs[0]);
7998+
check_added_monitors!(node, 0);
7999+
commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, false);
8000+
expect_pending_htlcs_forwardable!(node);
8001+
8002+
if idx == 0 {
8003+
let mut events_2 = node.node.get_and_clear_pending_msg_events();
8004+
assert_eq!(events_2.len(), 1);
8005+
check_added_monitors!(node, 1);
8006+
payment_event = SendEvent::from_event(events_2.remove(0));
8007+
assert_eq!(payment_event.msgs.len(), 1);
8008+
} else {
8009+
let events_2 = node.node.get_and_clear_pending_events();
8010+
assert!(events_2.is_empty());
8011+
}
8012+
8013+
prev_node = node;
8014+
}
8015+
8016+
// Second path
8017+
let ev = remove_first_msg_event_to_node(&expected_paths[1][0].node.get_our_node_id(), &mut events);
8018+
pass_along_path(&nodes[0], expected_paths[1], 101_000, our_payment_hash.clone(), Some(our_payment_secret), ev, true, None);
8019+
8020+
claim_payment_along_route(&nodes[0], expected_paths, false, our_payment_preimage);
8021+
}
8022+
79288023
fn do_test_overshoot_mpp(msat_amounts: &[u64], total_msat: u64) {
79298024

79308025
let routing_node_count = msat_amounts.len();

0 commit comments

Comments
 (0)