Skip to content

Commit 0708966

Browse files
committed
Allow overshooting total_msat for an MPP
While retrying a failed path of an MPP, a node may want to overshoot the `total_msat` in order to use a path with an `htlc_minimum_msat` greater than the remaining value being sent. This commit no longer fails MPPs that overshoot the `total_msat`, however it does fail HTLCs with the same payment hash that are received *after* a payment has become claimable.
1 parent 0b1a64f commit 0708966

File tree

5 files changed

+84
-10
lines changed

5 files changed

+84
-10
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2584,7 +2584,7 @@ where
25842584
}
25852585

25862586
#[cfg(test)]
2587-
fn test_send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: Vec<[u8; 32]>) -> Result<(), PaymentSendFailure> {
2587+
pub(super) fn test_send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: Vec<[u8; 32]>) -> Result<(), PaymentSendFailure> {
25882588
let best_block_height = self.best_block.read().unwrap().height();
25892589
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
25902590
self.pending_outbound_payments.test_send_payment_internal(route, payment_hash, payment_secret, keysend_preimage, payment_id, recv_value_msat, onion_session_privs, &self.node_signer, best_block_height,
@@ -3274,11 +3274,15 @@ where
32743274
_ => unreachable!(),
32753275
}
32763276
}
3277-
if total_value >= msgs::MAX_VALUE_MSAT || total_value > $payment_data.total_msat {
3278-
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)",
3279-
log_bytes!(payment_hash.0), total_value, $payment_data.total_msat);
3277+
if total_value >= msgs::MAX_VALUE_MSAT {
3278+
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over the maximum possible msats {} (or HTLCs were inconsistent)",
3279+
log_bytes!(payment_hash.0), total_value, msgs::MAX_VALUE_MSAT);
32803280
fail_htlc!(claimable_htlc, payment_hash);
3281-
} else if total_value == $payment_data.total_msat {
3281+
} else if total_value - claimable_htlc.value >= $payment_data.total_msat {
3282+
log_trace!(self.logger, "Failing HTLC with payment_hash {} as payment is already claimable",
3283+
log_bytes!(payment_hash.0));
3284+
fail_htlc!(claimable_htlc, payment_hash);
3285+
} else if total_value >= $payment_data.total_msat {
32823286
let prev_channel_id = prev_funding_outpoint.to_channel_id();
32833287
htlcs.push(claimable_htlc);
32843288
new_events.push(events::Event::PaymentClaimable {
@@ -3935,7 +3939,7 @@ where
39353939
log_info!(self.logger, "Attempted to claim an incomplete payment which no longer had any available HTLCs!");
39363940
return;
39373941
}
3938-
if claimable_amt_msat != expected_amt_msat.unwrap() {
3942+
if claimable_amt_msat < expected_amt_msat.unwrap() {
39393943
self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
39403944
log_info!(self.logger, "Attempted to claim an incomplete payment, expected {} msat, had {} available to claim.",
39413945
expected_amt_msat.unwrap(), claimable_amt_msat);

lightning/src/ln/functional_test_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1925,7 +1925,7 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p
19251925
assert!(our_payment_secret.is_none());
19261926
},
19271927
}
1928-
assert_eq!(amount_msat, recv_value);
1928+
assert!(amount_msat >= recv_value);
19291929
assert!(node.node.list_channels().iter().any(|details| details.channel_id == via_channel_id.unwrap()));
19301930
assert!(node.node.list_channels().iter().any(|details| details.user_channel_id == via_user_channel_id.unwrap()));
19311931
},

lightning/src/ln/functional_tests.rs

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

7928+
fn do_test_overshoot_mpp(msat_amounts: &[u64], total_msat: u64) {
7929+
7930+
let routing_node_count = msat_amounts.len();
7931+
let node_count = routing_node_count + 2;
7932+
7933+
let chanmon_cfgs = create_chanmon_cfgs(node_count);
7934+
let node_cfgs = create_node_cfgs(node_count, &chanmon_cfgs);
7935+
let node_chanmgrs = create_node_chanmgrs(node_count, &node_cfgs, &vec![None; node_count]);
7936+
let nodes = create_network(node_count, &node_cfgs, &node_chanmgrs);
7937+
7938+
let src_idx = 0;
7939+
let dst_idx = 1;
7940+
7941+
// Create channels for each amount
7942+
let mut expected_paths = Vec::with_capacity(routing_node_count);
7943+
let mut src_chan_ids = Vec::with_capacity(routing_node_count);
7944+
let mut dst_chan_ids = Vec::with_capacity(routing_node_count);
7945+
for i in 0..routing_node_count {
7946+
let routing_node = 2 + i;
7947+
let src_chan_id = create_announced_chan_between_nodes(&nodes, src_idx, routing_node).0.contents.short_channel_id;
7948+
src_chan_ids.push(src_chan_id);
7949+
let dst_chan_id = create_announced_chan_between_nodes(&nodes, routing_node, dst_idx).0.contents.short_channel_id;
7950+
dst_chan_ids.push(dst_chan_id);
7951+
let path = vec![&nodes[routing_node], &nodes[dst_idx]];
7952+
expected_paths.push(path);
7953+
}
7954+
let expected_paths: Vec<&[&Node]> = expected_paths.iter().map(|route| route.as_slice()).collect();
7955+
7956+
// Create a route for each amount
7957+
let example_amount = 100000;
7958+
let (mut route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(&nodes[src_idx], nodes[dst_idx], example_amount);
7959+
let sample_path = route.paths.pop().unwrap();
7960+
for i in 0..routing_node_count {
7961+
let routing_node = 2 + i;
7962+
let mut path = sample_path.clone();
7963+
path[0].pubkey = nodes[routing_node].node.get_our_node_id();
7964+
path[0].short_channel_id = src_chan_ids[i];
7965+
path[1].pubkey = nodes[dst_idx].node.get_our_node_id();
7966+
path[1].short_channel_id = dst_chan_ids[i];
7967+
path[1].fee_msat = msat_amounts[i];
7968+
route.paths.push(path);
7969+
}
7970+
7971+
// Send payment with manually set total_msat
7972+
let payment_id = PaymentId(nodes[src_idx].keys_manager.backing.get_secure_random_bytes());
7973+
let onion_session_privs = nodes[src_idx].node.test_add_new_pending_payment(our_payment_hash, Some(our_payment_secret), payment_id, &route).unwrap();
7974+
nodes[src_idx].node.test_send_payment_internal(&route, our_payment_hash, &Some(our_payment_secret), None, payment_id, Some(total_msat), onion_session_privs).unwrap();
7975+
check_added_monitors!(nodes[src_idx], expected_paths.len());
7976+
7977+
// pass_along_route except it can expect PaymentClaimable before final path
7978+
let mut events = nodes[src_idx].node.get_and_clear_pending_msg_events();
7979+
assert_eq!(events.len(), expected_paths.len());
7980+
let mut amount_received = 0;
7981+
for (path_idx, expected_path) in expected_paths.iter().enumerate() {
7982+
let ev = remove_first_msg_event_to_node(&expected_path[0].node.get_our_node_id(), &mut events);
7983+
7984+
let current_path_amount = msat_amounts[path_idx];
7985+
amount_received += current_path_amount;
7986+
let became_claimable_now = amount_received >= total_msat && amount_received - current_path_amount < total_msat;
7987+
pass_along_path(&nodes[src_idx], expected_path, total_msat, our_payment_hash.clone(), Some(our_payment_secret), ev, became_claimable_now, None);
7988+
}
7989+
7990+
claim_payment_along_route(&nodes[src_idx], &expected_paths, false, our_payment_preimage);
7991+
}
7992+
7993+
#[test]
7994+
fn test_overshoot_mpp() {
7995+
do_test_overshoot_mpp(&[100_000, 101_000], 200_000);
7996+
do_test_overshoot_mpp(&[100_000, 10_000, 100_000], 200_000);
7997+
}
7998+
79287999
#[test]
79298000
fn test_simple_mpp() {
79308001
// Simple test of sending a multi-path payment.

lightning/src/ln/outbound_payment.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,6 @@ impl OutboundPayments {
915915
return Err(PaymentSendFailure::PathParameterError(path_errs));
916916
}
917917
if let Some(amt_msat) = recv_value_msat {
918-
debug_assert!(amt_msat >= total_value);
919918
total_value = amt_msat;
920919
}
921920

lightning/src/util/events.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ pub enum HTLCDestination {
230230
///
231231
/// Some of the reasons may include:
232232
/// * HTLC Timeouts
233-
/// * Expected MPP amount to claim does not equal HTLC total
233+
/// * Expected MPP amount has already been reached
234234
/// * Claimable amount does not match expected amount
235235
FailedPayment {
236236
/// The payment hash of the payment we attempted to process.
@@ -929,7 +929,7 @@ pub enum Event {
929929
/// * Insufficient capacity in the outbound channel
930930
/// * While waiting to forward the HTLC, the channel it is meant to be forwarded through closes
931931
/// * When an unknown SCID is requested for forwarding a payment.
932-
/// * Claiming an amount for an MPP payment that exceeds the HTLC total
932+
/// * Expected MPP amount has already been reached
933933
/// * The HTLC has timed out
934934
///
935935
/// This event, however, does not get generated if an HTLC fails to meet the forwarding

0 commit comments

Comments
 (0)