Skip to content

Commit 5c71a1e

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 3cfcf49 commit 5c71a1e

File tree

2 files changed

+116
-7
lines changed

2 files changed

+116
-7
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,10 @@ pub(super) struct PendingHTLCInfo {
119119
pub(super) routing: PendingHTLCRouting,
120120
pub(super) incoming_shared_secret: [u8; 32],
121121
payment_hash: PaymentHash,
122+
/// Amount received
122123
pub(super) incoming_amt_msat: Option<u64>, // Added in 0.0.113
124+
/// Sender intended amount to forward or receive (actual amount received
125+
/// may overshoot this in either case)
123126
pub(super) outgoing_amt_msat: u64,
124127
pub(super) outgoing_cltv_value: u32,
125128
}
@@ -191,6 +194,9 @@ struct ClaimableHTLC {
191194
cltv_expiry: u32,
192195
/// The amount (in msats) of this MPP part
193196
value: u64,
197+
/// The amount (in msats) that the sender intended to be sent in this MPP
198+
/// part (used for validating total MPP amount)
199+
sender_intended_value: u64,
194200
onion_payload: OnionPayload,
195201
timer_ticks: u8,
196202
/// The total value received for a payment (sum of all MPP parts if the payment is a MPP).
@@ -2162,7 +2168,7 @@ where
21622168
payment_hash,
21632169
incoming_shared_secret: shared_secret,
21642170
incoming_amt_msat: Some(amt_msat),
2165-
outgoing_amt_msat: amt_msat,
2171+
outgoing_amt_msat: hop_data.amt_to_forward,
21662172
outgoing_cltv_value: hop_data.outgoing_cltv_value,
21672173
})
21682174
}
@@ -3235,7 +3241,7 @@ where
32353241
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
32363242
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
32373243
forward_info: PendingHTLCInfo {
3238-
routing, incoming_shared_secret, payment_hash, outgoing_amt_msat, ..
3244+
routing, incoming_shared_secret, payment_hash, incoming_amt_msat, outgoing_amt_msat, ..
32393245
}
32403246
}) => {
32413247
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret) = match routing {
@@ -3257,7 +3263,11 @@ where
32573263
incoming_packet_shared_secret: incoming_shared_secret,
32583264
phantom_shared_secret,
32593265
},
3260-
value: outgoing_amt_msat,
3266+
// We differentiate the received value from the sender intended value
3267+
// if possible so that we don't prematurely mark MPP payments complete
3268+
// if routing nodes overpay
3269+
value: incoming_amt_msat.unwrap_or(outgoing_amt_msat),
3270+
sender_intended_value: outgoing_amt_msat,
32613271
timer_ticks: 0,
32623272
total_value_received: None,
32633273
total_msat: if let Some(data) = &payment_data { data.total_msat } else { outgoing_amt_msat },
@@ -3313,9 +3323,9 @@ where
33133323
continue
33143324
}
33153325
}
3316-
let mut total_value = claimable_htlc.value;
3326+
let mut total_value = claimable_htlc.sender_intended_value;
33173327
for htlc in htlcs.iter() {
3318-
total_value += htlc.value;
3328+
total_value += htlc.sender_intended_value;
33193329
match &htlc.onion_payload {
33203330
OnionPayload::Invoice { .. } => {
33213331
if htlc.total_msat != $payment_data.total_msat {
@@ -3330,7 +3340,7 @@ where
33303340
}
33313341
if total_value >= msgs::MAX_VALUE_MSAT {
33323342
fail_htlc!(claimable_htlc, payment_hash);
3333-
} else if total_value - claimable_htlc.value >= $payment_data.total_msat {
3343+
} else if total_value - claimable_htlc.sender_intended_value >= $payment_data.total_msat {
33343344
log_trace!(self.logger, "Failing HTLC with payment_hash {} as payment is already claimable",
33353345
log_bytes!(payment_hash.0));
33363346
fail_htlc!(claimable_htlc, payment_hash);
@@ -3405,7 +3415,7 @@ where
34053415
new_events.push(events::Event::PaymentClaimable {
34063416
receiver_node_id: Some(receiver_node_id),
34073417
payment_hash,
3408-
amount_msat: outgoing_amt_msat,
3418+
amount_msat,
34093419
purpose,
34103420
via_channel_id: Some(prev_channel_id),
34113421
via_user_channel_id: Some(prev_user_channel_id),
@@ -6784,6 +6794,7 @@ impl Writeable for ClaimableHTLC {
67846794
(0, self.prev_hop, required),
67856795
(1, self.total_msat, required),
67866796
(2, self.value, required),
6797+
(3, self.sender_intended_value, required),
67876798
(4, payment_data, option),
67886799
(5, self.total_value_received, option),
67896800
(6, self.cltv_expiry, required),
@@ -6797,6 +6808,7 @@ impl Readable for ClaimableHTLC {
67976808
fn read<R: Read>(reader: &mut R) -> Result<Self, DecodeError> {
67986809
let mut prev_hop = crate::util::ser::RequiredWrapper(None);
67996810
let mut value = 0;
6811+
let mut sender_intended_value = None;
68006812
let mut payment_data: Option<msgs::FinalOnionHopData> = None;
68016813
let mut cltv_expiry = 0;
68026814
let mut total_value_received = None;
@@ -6806,6 +6818,7 @@ impl Readable for ClaimableHTLC {
68066818
(0, prev_hop, required),
68076819
(1, total_msat, option),
68086820
(2, value, required),
6821+
(3, sender_intended_value, option),
68096822
(4, payment_data, option),
68106823
(5, total_value_received, option),
68116824
(6, cltv_expiry, required),
@@ -6835,6 +6848,7 @@ impl Readable for ClaimableHTLC {
68356848
prev_hop: prev_hop.0.unwrap(),
68366849
timer_ticks: 0,
68376850
value,
6851+
sender_intended_value: sender_intended_value.unwrap_or(value),
68386852
total_value_received,
68396853
total_msat: total_msat.unwrap(),
68406854
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)