Skip to content

Commit 1d31b0e

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 ee57738 commit 1d31b0e

File tree

2 files changed

+121
-8
lines changed

2 files changed

+121
-8
lines changed

lightning/src/ln/channelmanager.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,10 @@ pub(super) struct PendingHTLCInfo {
120120
pub(super) routing: PendingHTLCRouting,
121121
pub(super) incoming_shared_secret: [u8; 32],
122122
payment_hash: PaymentHash,
123+
/// Amount received
123124
pub(super) incoming_amt_msat: Option<u64>, // Added in 0.0.113
125+
/// Sender intended amount to forward or receive (actual amount received
126+
/// may overshoot this in either case)
124127
pub(super) outgoing_amt_msat: u64,
125128
pub(super) outgoing_cltv_value: u32,
126129
}
@@ -192,6 +195,9 @@ struct ClaimableHTLC {
192195
cltv_expiry: u32,
193196
/// The amount (in msats) of this MPP part
194197
value: u64,
198+
/// The amount (in msats) that the sender intended to be sent in this MPP
199+
/// part (used for validating total MPP amount)
200+
sender_intended_value: u64,
195201
onion_payload: OnionPayload,
196202
timer_ticks: u8,
197203
/// The total value received for a payment (sum of all MPP parts if the payment is a MPP).
@@ -2181,7 +2187,7 @@ where
21812187
payment_hash,
21822188
incoming_shared_secret: shared_secret,
21832189
incoming_amt_msat: Some(amt_msat),
2184-
outgoing_amt_msat: amt_msat,
2190+
outgoing_amt_msat: hop_data.amt_to_forward,
21852191
outgoing_cltv_value: hop_data.outgoing_cltv_value,
21862192
})
21872193
}
@@ -3261,7 +3267,7 @@ where
32613267
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
32623268
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
32633269
forward_info: PendingHTLCInfo {
3264-
routing, incoming_shared_secret, payment_hash, outgoing_amt_msat, ..
3270+
routing, incoming_shared_secret, payment_hash, incoming_amt_msat, outgoing_amt_msat, ..
32653271
}
32663272
}) => {
32673273
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret) = match routing {
@@ -3283,7 +3289,11 @@ where
32833289
incoming_packet_shared_secret: incoming_shared_secret,
32843290
phantom_shared_secret,
32853291
},
3286-
value: outgoing_amt_msat,
3292+
// We differentiate the received value from the sender intended value
3293+
// if possible so that we don't prematurely mark MPP payments complete
3294+
// if routing nodes overpay
3295+
value: incoming_amt_msat.unwrap_or(outgoing_amt_msat),
3296+
sender_intended_value: outgoing_amt_msat,
32873297
timer_ticks: 0,
32883298
total_value_received: None,
32893299
total_msat: if let Some(data) = &payment_data { data.total_msat } else { outgoing_amt_msat },
@@ -3339,9 +3349,9 @@ where
33393349
continue
33403350
}
33413351
}
3342-
let mut total_value = claimable_htlc.value;
3352+
let mut total_value = claimable_htlc.sender_intended_value;
33433353
for htlc in htlcs.iter() {
3344-
total_value += htlc.value;
3354+
total_value += htlc.sender_intended_value;
33453355
match &htlc.onion_payload {
33463356
OnionPayload::Invoice { .. } => {
33473357
if htlc.total_msat != $payment_data.total_msat {
@@ -3354,9 +3364,11 @@ where
33543364
_ => unreachable!(),
33553365
}
33563366
}
3367+
// The condition determining whether an MPP is complete must
3368+
// match exactly the condition used in `timer_tick_occurred`
33573369
if total_value >= msgs::MAX_VALUE_MSAT {
33583370
fail_htlc!(claimable_htlc, payment_hash);
3359-
} else if total_value - claimable_htlc.value >= $payment_data.total_msat {
3371+
} else if total_value - claimable_htlc.sender_intended_value >= $payment_data.total_msat {
33603372
log_trace!(self.logger, "Failing HTLC with payment_hash {} as payment is already claimable",
33613373
log_bytes!(payment_hash.0));
33623374
fail_htlc!(claimable_htlc, payment_hash);
@@ -3431,7 +3443,7 @@ where
34313443
new_events.push(events::Event::PaymentClaimable {
34323444
receiver_node_id: Some(receiver_node_id),
34333445
payment_hash,
3434-
amount_msat: outgoing_amt_msat,
3446+
amount_msat,
34353447
purpose,
34363448
via_channel_id: Some(prev_channel_id),
34373449
via_user_channel_id: Some(prev_user_channel_id),
@@ -3691,7 +3703,9 @@ where
36913703
if let OnionPayload::Invoice { .. } = htlcs[0].onion_payload {
36923704
// Check if we've received all the parts we need for an MPP (the value of the parts adds to total_msat).
36933705
// In this case we're not going to handle any timeouts of the parts here.
3694-
if htlcs[0].total_msat <= htlcs.iter().fold(0, |total, htlc| total + htlc.value) {
3706+
// This condition determining whether the MPP is complete here must match
3707+
// exactly the condition used in `process_pending_htlc_forwards`.
3708+
if htlcs[0].total_msat <= htlcs.iter().fold(0, |total, htlc| total + htlc.sender_intended_value) {
36953709
return true;
36963710
} else if htlcs.into_iter().any(|htlc| {
36973711
htlc.timer_ticks += 1;
@@ -6813,6 +6827,7 @@ impl Writeable for ClaimableHTLC {
68136827
(0, self.prev_hop, required),
68146828
(1, self.total_msat, required),
68156829
(2, self.value, required),
6830+
(3, self.sender_intended_value, required),
68166831
(4, payment_data, option),
68176832
(5, self.total_value_received, option),
68186833
(6, self.cltv_expiry, required),
@@ -6826,6 +6841,7 @@ impl Readable for ClaimableHTLC {
68266841
fn read<R: Read>(reader: &mut R) -> Result<Self, DecodeError> {
68276842
let mut prev_hop = crate::util::ser::RequiredWrapper(None);
68286843
let mut value = 0;
6844+
let mut sender_intended_value = None;
68296845
let mut payment_data: Option<msgs::FinalOnionHopData> = None;
68306846
let mut cltv_expiry = 0;
68316847
let mut total_value_received = None;
@@ -6835,6 +6851,7 @@ impl Readable for ClaimableHTLC {
68356851
(0, prev_hop, required),
68366852
(1, total_msat, option),
68376853
(2, value, required),
6854+
(3, sender_intended_value, option),
68386855
(4, payment_data, option),
68396856
(5, total_value_received, option),
68406857
(6, cltv_expiry, required),
@@ -6864,6 +6881,7 @@ impl Readable for ClaimableHTLC {
68646881
prev_hop: prev_hop.0.unwrap(),
68656882
timer_ticks: 0,
68666883
value,
6884+
sender_intended_value: sender_intended_value.unwrap_or(value),
68676885
total_value_received,
68686886
total_msat: total_msat.unwrap(),
68696887
onion_payload,

lightning/src/ln/functional_tests.rs

+95
Original file line numberDiff line numberDiff line change
@@ -7926,6 +7926,101 @@ fn test_can_not_accept_unknown_inbound_channel() {
79267926
}
79277927
}
79287928

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

79318026
let routing_node_count = msat_amounts.len();

0 commit comments

Comments
 (0)