From b3180c99e2afbc8fab90281f78079312c1018b87 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sun, 1 Feb 2026 13:24:04 +0000 Subject: [PATCH 1/9] Add missing ` to ChannelManager::send_payment docs. --- lightning/src/ln/channelmanager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 13197ea44ec..c7fd4823d5c 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -5350,7 +5350,7 @@ impl< /// using [`ChannelMonitorUpdateStatus::InProgress`]), the payment may be lost on restart. See /// [`ChannelManager::list_recent_payments`] for more information. /// - /// Routes are automatically found using the [`Router] provided on startup. To fix a route for a + /// Routes are automatically found using the [`Router`] provided on startup. To fix a route for a /// particular payment, use [`Self::send_payment_with_route`] or match the [`PaymentId`] passed to /// [`Router::find_route_with_id`]. /// From 41071579c9c0ce5e86d1cecacd990c072cdcfcf1 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sun, 1 Feb 2026 01:30:13 +0000 Subject: [PATCH 2/9] Add a `custom` TLV read/write variant At various points we've been stuck in our TLV read/write variants but just want to break out and write some damn code to initialize a field and some more code to decide what to write for a TLV. We added the write-side part of this with the `legacy` TLV read/write variant, but its useful to also be able to specify a function which is called on the read side. Here we add a `custom` TLV read/write variant which calls a method both on read and write to either decide what to write or to map a read value (if any) to the final field. --- lightning-macros/src/lib.rs | 6 ++--- lightning/src/util/ser_macros.rs | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/lightning-macros/src/lib.rs b/lightning-macros/src/lib.rs index e784acf72fb..778da45ee8f 100644 --- a/lightning-macros/src/lib.rs +++ b/lightning-macros/src/lib.rs @@ -138,7 +138,7 @@ fn process_fields(group: Group) -> proc_macro::TokenStream { if let TokenTree::Group(group) = ty_info { let first_group_tok = group.stream().into_iter().next().unwrap(); if let TokenTree::Ident(ident) = first_group_tok { - if ident.to_string() == "legacy" { + if ident.to_string() == "legacy" || ident.to_string() == "custom" { continue; } } @@ -155,13 +155,13 @@ fn process_fields(group: Group) -> proc_macro::TokenStream { computed_fields } -/// Scans a match statement for legacy fields which should be skipped. +/// Scans a match statement for legacy or custom fields which should be skipped. /// /// This is used internally in LDK's TLV serialization logic and is not expected to be used by /// other crates. /// /// Wraps a `match self {..}` statement and scans the fields in the match patterns (in the form -/// `ref $field_name: $field_ty`) for types marked `legacy`, skipping those fields. +/// `ref $field_name: $field_ty`) for types marked `legacy` or `custom`, skipping those fields. /// /// Specifically, it expects input like the following, simply dropping `field3` and the /// `: $field_ty` after each field name. diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs index 86b24e1b849..bd2b5d1983a 100644 --- a/lightning/src/util/ser_macros.rs +++ b/lightning/src/util/ser_macros.rs @@ -63,6 +63,9 @@ macro_rules! _encode_tlv { } $crate::_encode_tlv!($stream, $optional_type, value, option); } }; + ($stream: expr, $optional_type: expr, $optional_field: expr, (custom, $fieldty: ty, $read: expr, $write: expr) $(, $self: ident)?) => { { + $crate::_encode_tlv!($stream, $optional_type, $optional_field, (legacy, $fieldty, $write) $(, $self)?); + } }; ($stream: expr, $type: expr, $field: expr, optional_vec $(, $self: ident)?) => { if !$field.is_empty() { $crate::_encode_tlv!($stream, $type, $field, required_vec); @@ -232,6 +235,9 @@ macro_rules! _get_varint_length_prefixed_tlv_length { ($len: expr, $optional_type: expr, $optional_field: expr, (legacy, $fieldty: ty, $write: expr) $(, $self: ident)?) => { $crate::_get_varint_length_prefixed_tlv_length!($len, $optional_type, $write($($self)?), option); }; + ($len: expr, $optional_type: expr, $optional_field: expr, (custom, $fieldty: ty, $read: expr, $write: expr) $(, $self: ident)?) => { + $crate::_get_varint_length_prefixed_tlv_length!($len, $optional_type, $optional_field, (legacy, $fieldty, $write) $(, $self)?); + }; ($len: expr, $type: expr, $field: expr, optional_vec $(, $self: ident)?) => { if !$field.is_empty() { $crate::_get_varint_length_prefixed_tlv_length!($len, $type, $field, required_vec); @@ -317,6 +323,16 @@ macro_rules! _check_decoded_tlv_order { ($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (legacy, $fieldty: ty, $write: expr)) => {{ // no-op }}; + ($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (custom, $fieldty: ty, $read: expr, $write: expr) $(, $self: ident)?) => {{ + // Note that $type may be 0 making the second comparison always false + #[allow(unused_comparisons)] + let invalid_order = + ($last_seen_type.is_none() || $last_seen_type.unwrap() < $type) && $typ.0 > $type; + if invalid_order { + let read_result: Result<_, DecodeError> = $read(None); + $field = read_result?.into(); + } + }}; ($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (required, explicit_type: $fieldty: ty)) => {{ _check_decoded_tlv_order!($last_seen_type, $typ, $type, $field, required); }}; @@ -385,6 +401,15 @@ macro_rules! _check_missing_tlv { ($last_seen_type: expr, $type: expr, $field: ident, (legacy, $fieldty: ty, $write: expr)) => {{ // no-op }}; + ($last_seen_type: expr, $type: expr, $field: ident, (custom, $fieldty: ty, $read: expr, $write: expr)) => {{ + // Note that $type may be 0 making the second comparison always false + #[allow(unused_comparisons)] + let missing_req_type = $last_seen_type.is_none() || $last_seen_type.unwrap() < $type; + if missing_req_type { + let read_result: Result<_, DecodeError> = $read(None); + $field = read_result?.into(); + } + }}; ($last_seen_type: expr, $type: expr, $field: ident, (required, explicit_type: $fieldty: ty)) => {{ _check_missing_tlv!($last_seen_type, $type, $field, required); }}; @@ -441,6 +466,12 @@ macro_rules! _decode_tlv { ($outer_reader: expr, $reader: expr, $field: ident, (legacy, $fieldty: ty, $write: expr)) => {{ $crate::_decode_tlv!($outer_reader, $reader, $field, (option, explicit_type: $fieldty)); }}; + ($outer_reader: expr, $reader: expr, $field: ident, (custom, $fieldty: ty, $read: expr, $write: expr)) => {{ + let read_field: $fieldty; + $crate::_decode_tlv!($outer_reader, $reader, read_field, required); + let read_result: Result<_, DecodeError> = $read(Some(read_field)); + $field = read_result?.into(); + }}; ($outer_reader: expr, $reader: expr, $field: ident, (required, explicit_type: $fieldty: ty)) => {{ let _field: &$fieldty = &$field; _decode_tlv!($outer_reader, $reader, $field, required); @@ -830,6 +861,9 @@ macro_rules! _init_tlv_based_struct_field { ($field: ident, (legacy, $fieldty: ty, $write: expr)) => { $crate::_init_tlv_based_struct_field!($field, option) }; + ($field: ident, (custom, $fieldty: ty, $read: expr, $write: expr)) => { + $crate::_init_tlv_based_struct_field!($field, required) + }; ($field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => { $crate::_init_tlv_based_struct_field!($field, option) }; @@ -896,6 +930,9 @@ macro_rules! _init_tlv_field_var { ($field: ident, (legacy, $fieldty: ty, $write: expr)) => { $crate::_init_tlv_field_var!($field, (option, explicit_type: $fieldty)); }; + ($field: ident, (custom, $fieldty: ty, $read: expr, $write: expr)) => { + $crate::_init_tlv_field_var!($field, required); + }; ($field: ident, (required, explicit_type: $fieldty: ty)) => { let mut $field = $crate::util::ser::RequiredWrapper::<$fieldty>(None); }; @@ -979,6 +1016,12 @@ macro_rules! _decode_and_build { /// called with the object being serialized and a returned `Option` and is written as a TLV if /// `Some`. When reading, an optional field of type `$ty` is read (which can be used in later /// `default_value` or `static_value` fields by referring to the value by name). +/// If `$fieldty` is `(custom, $ty, $read, $write)` then, when writing, the same behavior as +/// `legacy`, above is used. When reading, if a TLV is present, it is read as `$ty` and the +/// `$read` method is called with `Some(decoded_$ty_object)`. If no TLV is present, the field +/// will be initialized by calling `$read(None)`. `$read` should return a +/// `Result` (note that the processed field type may differ from `$ty`; +/// `$ty` is the type as de/serialized, not necessarily the actual field type). /// /// For example, /// ``` From 3cb1fa28187fcbb223bc33c0ca631061662d0ded Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sun, 1 Feb 2026 12:29:08 +0000 Subject: [PATCH 3/9] Validate the `Router` is meeting MPP and max-fee limitations given When `OutboundPayments` calls the provided `Router` to fetch a `Route` it passes a `RouteParameters` with a specific max-fee. Here we validate that the `Route` returned sticks to the limits provided, and also that it meets the MPP rules of not having any single MPP part which can be removed while still meeting the desired payment amount. --- lightning/src/ln/chanmon_update_fail_tests.rs | 5 +- lightning/src/ln/channelmanager.rs | 1 + lightning/src/ln/functional_tests.rs | 3 +- lightning/src/ln/outbound_payment.rs | 101 ++++++++++++++---- lightning/src/ln/payment_tests.rs | 4 + 5 files changed, 92 insertions(+), 22 deletions(-) diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs index 96ac147635d..f3eda9b38ac 100644 --- a/lightning/src/ln/chanmon_update_fail_tests.rs +++ b/lightning/src/ln/chanmon_update_fail_tests.rs @@ -2311,6 +2311,7 @@ fn test_path_paused_mpp() { route.paths[1].hops[0].pubkey = node_c_id; route.paths[1].hops[0].short_channel_id = chan_2_ann.contents.short_channel_id; route.paths[1].hops[1].short_channel_id = chan_4_id; + route.route_params.as_mut().unwrap().final_value_msat *= 2; // Set it so that the first monitor update (for the path 0 -> 1 -> 3) succeeds, but the second // (for the path 0 -> 2 -> 3) fails. @@ -4244,7 +4245,7 @@ fn do_test_partial_claim_mon_update_compl_actions(reload_a: bool, reload_b: bool let chan_4_scid = chan_4_update.contents.short_channel_id; let (mut route, payment_hash, preimage, payment_secret) = - get_route_and_payment_hash!(&nodes[0], nodes[3], 100000); + get_route_and_payment_hash!(&nodes[0], nodes[3], 100_000); let path = route.paths[0].clone(); route.paths.push(path); route.paths[0].hops[0].pubkey = node_b_id; @@ -4253,6 +4254,8 @@ fn do_test_partial_claim_mon_update_compl_actions(reload_a: bool, reload_b: bool route.paths[1].hops[0].pubkey = node_c_id; route.paths[1].hops[0].short_channel_id = chan_2_scid; route.paths[1].hops[1].short_channel_id = chan_4_scid; + route.route_params.as_mut().unwrap().final_value_msat *= 2; + let paths = &[&[&nodes[1], &nodes[3]][..], &[&nodes[2], &nodes[3]][..]]; send_along_route_with_secret(&nodes[0], route, paths, 200_000, payment_hash, payment_secret); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index c7fd4823d5c..a733cae11d5 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -19963,6 +19963,7 @@ mod tests { route.paths[1].hops[0].pubkey = nodes[2].node.get_our_node_id(); route.paths[1].hops[0].short_channel_id = chan_2_id; route.paths[1].hops[1].short_channel_id = chan_4_id; + route.route_params.as_mut().unwrap().final_value_msat *= 2; nodes[0].node.send_payment_with_route(route, payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap(); diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 8e854b31150..21c2af696c1 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -7104,7 +7104,7 @@ pub fn test_simple_mpp() { let chan_4_id = create_announced_chan_between_nodes(&nodes, 2, 3).0.contents.short_channel_id; let (mut route, payment_hash, payment_preimage, payment_secret) = - get_route_and_payment_hash!(&nodes[0], nodes[3], 100000); + get_route_and_payment_hash!(&nodes[0], nodes[3], 100_000); let path = route.paths[0].clone(); route.paths.push(path); route.paths[0].hops[0].pubkey = node_b_id; @@ -7113,6 +7113,7 @@ pub fn test_simple_mpp() { route.paths[1].hops[0].pubkey = node_c_id; route.paths[1].hops[0].short_channel_id = chan_2_id; route.paths[1].hops[1].short_channel_id = chan_4_id; + route.route_params.as_mut().unwrap().final_value_msat = 200_000; let paths: &[&[_]] = &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]]; send_along_route_with_secret(&nodes[0], route, paths, 200_000, payment_hash, payment_secret); claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], paths, payment_preimage)); diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index ea33bb5d263..85f2c99e879 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -894,6 +894,80 @@ impl OutboundPayments { } } +/// Validate that a [`Route`] picked by our [`Router`] is sane for the [`RouteParameters`] used to +/// request it. Failure here indicates a critical bug in the [`Router`]. +fn validate_found_route( + route: &mut Route, route_params: &RouteParameters, logger: &WithContext, +) -> Result<(), ()> { + if route.route_params.as_ref() != Some(route_params) { + debug_assert!( + false, + "Routers are expected to return a Route which includes the requested RouteParameters. Got {:?}, expected {route_params:?}", + route.route_params + ); + log_error!( + logger, + "Routers are expected to return a Route which includes the requested RouteParameters. Got {:?}, expected {route_params:?}", + route.route_params + ); + route.route_params = Some(route_params.clone()); + } + + // Check that the path returned actually pays less than the max fee we set. + if let Some(max_total_fee) = route_params.max_total_routing_fee_msat { + let payment_amount = route.get_total_amount(); + let max_payment_amount = route_params.final_value_msat.saturating_add(max_total_fee); + if payment_amount > max_payment_amount { + debug_assert!( + false, + "Router returned an attempt to pay more ({payment_amount}msat) than the limit we gave it ({max_payment_amount}msat). Your router is critically buggy!" + ); + log_error!( + logger, + "Router returned an attempt to pay more ({payment_amount}msat) than the limit we gave it ({max_payment_amount}msat). Your router is critically buggy!" + ); + return Err(()); + } + } + + // Test that the route doesn't contain any "extra" MPP parts - while we're allows to + // overshoot the `final_value_msat` specified in the `route_params`, we aren't allowed to + // have any MPP parts which aren't needed to meet `route_params.final_value_msat`. + let min_mpp_part = route.paths.iter().map(|h| h.final_value_msat()).min().unwrap_or(0); + if route.get_total_amount() - min_mpp_part >= route_params.final_value_msat { + debug_assert!( + false, + "Router returned an attempt to include more MPP parts than needed. The smallest MPP part ({min_mpp_part}msat) was not needed for a payment of {}msat. Your router is critically buggy!", + route_params.final_value_msat + ); + log_error!( + logger, + "Router returned an attempt to include more MPP parts than needed. The smallest MPP part ({min_mpp_part}msat) was not needed for a payment of {}msat. Your router is critically buggy!", + route_params.final_value_msat + ); + return Err(()); + } + + if route.paths.is_empty() { + debug_assert!(false, "Selected route had no paths"); + log_error!(logger, "Selected route had no paths, your router is buggy!"); + return Err(()); + } + + for path in route.paths.iter() { + if path.hops.is_empty() { + debug_assert!(false, "Unusable path in route (path.hops.len() must be at least 1)"); + log_error!( + logger, + "Unusable path in route (path.hops.len() must be at least 1). Your router is buggy!" + ); + return Err(()); + } + } + + Ok(()) +} + impl OutboundPayments { #[rustfmt::skip] pub(super) fn send_payment( @@ -1462,12 +1536,8 @@ impl OutboundPayments { RetryableSendFailure::RouteNotFound })?; - if route.route_params.as_ref() != Some(route_params) { - debug_assert!(false, - "Routers are expected to return a Route which includes the requested RouteParameters. Got {:?}, expected {:?}", - route.route_params, route_params); - route.route_params = Some(route_params.clone()); - } + validate_found_route(&mut route, route_params, logger) + .map_err(|()| RetryableSendFailure::RouteNotFound)?; Ok(route) } @@ -1552,18 +1622,9 @@ impl OutboundPayments { } }; - if route.route_params.as_ref() != Some(&route_params) { - debug_assert!(false, - "Routers are expected to return a Route which includes the requested RouteParameters"); - route.route_params = Some(route_params.clone()); - } - - for path in route.paths.iter() { - if path.hops.len() == 0 { - log_error!(logger, "Unusable path in route (path.hops.len() must be at least 1"); - self.abandon_payment(payment_id, PaymentFailureReason::UnexpectedError, pending_events); - return - } + if validate_found_route(&mut route, &route_params, logger).is_err() { + self.abandon_payment(payment_id, PaymentFailureReason::RouteNotFound, pending_events); + return } macro_rules! abandon_with_entry { @@ -2967,7 +3028,7 @@ mod tests { let sender_pk = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let receiver_pk = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap()); let payment_params = PaymentParameters::from_node_id(sender_pk, 0); - let route_params = RouteParameters::from_payment_params_and_value(payment_params.clone(), 0); + let route_params = RouteParameters::from_payment_params_and_value(payment_params.clone(), 1); let failed_scid = 42; let route = Route { paths: vec![Path { hops: vec![RouteHop { @@ -2975,7 +3036,7 @@ mod tests { node_features: NodeFeatures::empty(), short_channel_id: failed_scid, channel_features: ChannelFeatures::empty(), - fee_msat: 0, + fee_msat: 1, cltv_expiry_delta: 0, maybe_announced_channel: true, }], blinded_tail: None }], diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index 2b3a2633205..e6c3c6eb121 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -97,6 +97,8 @@ fn mpp_failure() { route.paths[1].hops[0].pubkey = node_c_id; route.paths[1].hops[0].short_channel_id = chan_2_id; route.paths[1].hops[1].short_channel_id = chan_4_id; + route.route_params.as_mut().unwrap().final_value_msat *= 2; + let paths: &[&[_]] = &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]]; send_along_route_with_secret(&nodes[0], route, paths, 200_000, payment_hash, payment_secret); fail_payment_along_route(&nodes[0], paths, false, payment_hash); @@ -137,6 +139,7 @@ fn mpp_retry() { route.paths[1].hops[0].pubkey = node_c_id; route.paths[1].hops[0].short_channel_id = chan_2_update.contents.short_channel_id; route.paths[1].hops[1].short_channel_id = chan_4_update.contents.short_channel_id; + route.route_params.as_mut().unwrap().final_value_msat *= 2; // Initiate the MPP payment. let id = PaymentId(hash.0); @@ -360,6 +363,7 @@ fn do_mpp_receive_timeout(send_partial_mpp: bool) { route.paths[1].hops[0].pubkey = node_c_id; route.paths[1].hops[0].short_channel_id = chan_2_update.contents.short_channel_id; route.paths[1].hops[1].short_channel_id = chan_4_update.contents.short_channel_id; + route.route_params.as_mut().unwrap().final_value_msat *= 2; // Initiate the MPP payment. let onion = RecipientOnionFields::secret_only(payment_secret); From 778cb3fdc6d0edbc3ef44a12b3a4f6472f134cb7 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 6 Feb 2026 13:44:42 +0000 Subject: [PATCH 4/9] f move validation into `Route`, also addressing other feedback --- lightning/src/ln/outbound_payment.rs | 52 +--------------------- lightning/src/routing/router.rs | 66 +++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 53 deletions(-) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 85f2c99e879..3380e4f0120 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -913,57 +913,7 @@ fn validate_found_route( route.route_params = Some(route_params.clone()); } - // Check that the path returned actually pays less than the max fee we set. - if let Some(max_total_fee) = route_params.max_total_routing_fee_msat { - let payment_amount = route.get_total_amount(); - let max_payment_amount = route_params.final_value_msat.saturating_add(max_total_fee); - if payment_amount > max_payment_amount { - debug_assert!( - false, - "Router returned an attempt to pay more ({payment_amount}msat) than the limit we gave it ({max_payment_amount}msat). Your router is critically buggy!" - ); - log_error!( - logger, - "Router returned an attempt to pay more ({payment_amount}msat) than the limit we gave it ({max_payment_amount}msat). Your router is critically buggy!" - ); - return Err(()); - } - } - - // Test that the route doesn't contain any "extra" MPP parts - while we're allows to - // overshoot the `final_value_msat` specified in the `route_params`, we aren't allowed to - // have any MPP parts which aren't needed to meet `route_params.final_value_msat`. - let min_mpp_part = route.paths.iter().map(|h| h.final_value_msat()).min().unwrap_or(0); - if route.get_total_amount() - min_mpp_part >= route_params.final_value_msat { - debug_assert!( - false, - "Router returned an attempt to include more MPP parts than needed. The smallest MPP part ({min_mpp_part}msat) was not needed for a payment of {}msat. Your router is critically buggy!", - route_params.final_value_msat - ); - log_error!( - logger, - "Router returned an attempt to include more MPP parts than needed. The smallest MPP part ({min_mpp_part}msat) was not needed for a payment of {}msat. Your router is critically buggy!", - route_params.final_value_msat - ); - return Err(()); - } - - if route.paths.is_empty() { - debug_assert!(false, "Selected route had no paths"); - log_error!(logger, "Selected route had no paths, your router is buggy!"); - return Err(()); - } - - for path in route.paths.iter() { - if path.hops.is_empty() { - debug_assert!(false, "Unusable path in route (path.hops.len() must be at least 1)"); - log_error!( - logger, - "Unusable path in route (path.hops.len() must be at least 1). Your router is buggy!" - ); - return Err(()); - } - } + route.debug_assert_route_meets_params(logger)?; Ok(()) } diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index b27dee1a450..39ed9009194 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -633,7 +633,7 @@ impl Path { } } - /// Gets the final hop's CLTV expiry delta. + /// Gets the final hop's CLTV expiry delta, if there's a final non-blinded hop. #[rustfmt::skip] pub fn final_cltv_expiry_delta(&self) -> Option { match &self.blinded_tail { @@ -688,6 +688,66 @@ impl Route { pub fn get_total_amount(&self) -> u64 { self.paths.iter().map(|path| path.final_value_msat()).sum() } + + pub(crate) fn debug_assert_route_meets_params(&self, logger: L) -> Result<(), ()> { + if let Some(route_params) = self.route_params.as_ref() { + // Check that we actually pay less than the max fee we set. + if let Some(max_total_fee) = route_params.max_total_routing_fee_msat { + let total_fee = self.get_total_fees(); + if total_fee > max_total_fee { + let err = format!("Router returned an attempt to pay with a higher fee ({total_fee}msat) than we allowed ({max_total_fee}msat). Your router is critically buggy!"); + debug_assert!(false, "{}", err); + log_error!(logger, "{}", err); + return Err(()); + } + } + + // Test that we don't contain any "extra" MPP parts - while we're allowed to overshoot + // the `final_value_msat` specified in the `route_params`, we aren't allowed to have + // any MPP parts which aren't needed to meet `route_params.final_value_msat`. + let min_mpp_part = self.paths.iter().map(|h| h.final_value_msat()).min().unwrap_or(0); + if self.get_total_amount() - min_mpp_part >= route_params.final_value_msat { + let err = format!( + "Router returned an attempt to include more MPP parts than needed. The smallest MPP part ({min_mpp_part}msat) was not needed for a payment of {}msat. Your router is critically buggy!", + route_params.final_value_msat + ); + debug_assert!(false, "{}", err); + log_error!(logger, "{}", err); + return Err(()); + } + + if self.paths.is_empty() { + let err = "Selected route had no paths. Your router is buggy!"; + debug_assert!(false, "{}", err); + log_error!(logger, "{}", err); + return Err(()); + } + + for path in self.paths.iter() { + if path.hops.is_empty() { + let err = "Unusable path in route (path.hops.len() must be at least 1)"; + debug_assert!(false, "{}", err); + log_error!(logger, "{}", err); + return Err(()); + } + + if path.hops.len() > route_params.payment_params.max_path_length.into() { + let err = format!( + "Path had a length of {}, which is greater than the maximum we're allowed ({})", + path.hops.len(), + route_params.payment_params.max_path_length, + ); + #[cfg(any(test, feature = "_test_utils"))] + debug_assert!(false, "{}", err); + log_error!(logger, "{}", err); + // This is a bug, but there's not a material safety risk to making this + // payment, so we don't bother to error here. + } + } + } + + Ok(()) + } } impl fmt::Display for Route { @@ -2491,9 +2551,11 @@ pub fn find_route( scorer: &S, score_params: &S::ScoreParams, random_seed_bytes: &[u8; 32] ) -> Result { let graph_lock = network_graph.read_only(); - let mut route = get_route(our_node_pubkey, &route_params, &graph_lock, first_hops, logger, + let mut route = get_route(our_node_pubkey, &route_params, &graph_lock, first_hops, &logger, scorer, score_params, random_seed_bytes)?; add_random_cltv_offset(&mut route, &route_params.payment_params, &graph_lock, random_seed_bytes); + route.debug_assert_route_meets_params(&logger) + .map_err(|()| "Generated route doesn't comply with the parameters you specified. This indicates a bug in the router. Please report this bug!")?; Ok(route) } From 45c4d57d2be3784384151a7b91985a24b0b34dc2 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sun, 1 Feb 2026 20:42:54 +0000 Subject: [PATCH 5/9] Include MPP payment amount in `RecipientOnionFields` In some uses of LDK we need the ability to send HTLCs for only a portion of some larger MPP payment. This allows payers to make single payments which spend funds from multiple wallets, which may be important for ecash wallets holding funds in multiple mints or graduated wallets which hold funds across a trusted wallet and a self-custodial wallet. In order to allow for this, we need to separate the concept of the payment amount from the onion MPP amount. Here we start this process by adding a `total_mpp_amount_msat` field to `RecipientOnionFields` (which is the appropriate place for a field describing something in the recipient onion). We currently always assert that it is equal to the existing fields, but will relax this in the coming commit(s). We also start including a payment preimage on probe attempts, which appears to have been the intent of the code, but which did not work correctly. The bulk of the test updates were done by Claude. --- fuzz/src/chanmon_consistency.rs | 4 +- fuzz/src/full_stack.rs | 4 +- lightning/src/chain/channelmonitor.rs | 2 +- lightning/src/events/mod.rs | 9 +- lightning/src/ln/accountable_tests.rs | 2 +- lightning/src/ln/async_payments_tests.rs | 2 +- lightning/src/ln/async_signer_tests.rs | 12 +- lightning/src/ln/blinded_payment_tests.rs | 64 +++++----- lightning/src/ln/chanmon_update_fail_tests.rs | 56 ++++----- lightning/src/ln/channelmanager.rs | 70 ++++++++--- lightning/src/ln/functional_test_utils.rs | 4 +- lightning/src/ln/functional_tests.rs | 105 +++++++++-------- lightning/src/ln/htlc_reserve_unit_tests.rs | 76 ++++++------ lightning/src/ln/interception_tests.rs | 2 +- lightning/src/ln/invoice_utils.rs | 5 +- .../src/ln/max_payment_path_len_tests.rs | 13 ++- lightning/src/ln/monitor_tests.rs | 8 +- lightning/src/ln/offers_tests.rs | 2 +- lightning/src/ln/onion_payment.rs | 2 +- lightning/src/ln/onion_route_tests.rs | 46 ++++---- lightning/src/ln/onion_utils.rs | 27 ++++- lightning/src/ln/outbound_payment.rs | 98 ++++++++++++---- lightning/src/ln/payment_tests.rs | 109 ++++++++++-------- lightning/src/ln/priv_short_conf_tests.rs | 18 +-- lightning/src/ln/quiescence_tests.rs | 8 +- lightning/src/ln/reload_tests.rs | 14 +-- lightning/src/ln/shutdown_tests.rs | 8 +- lightning/src/ln/splicing_tests.rs | 2 +- lightning/src/ln/update_fee_tests.rs | 8 +- 29 files changed, 461 insertions(+), 319 deletions(-) diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 202488d9777..6d98301541f 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -585,7 +585,7 @@ fn send_payment( }], route_params: Some(route_params.clone()), }; - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt); let res = source.send_payment_with_route(route, payment_hash, onion, payment_id); match res { Err(err) => { @@ -642,7 +642,7 @@ fn send_hop_payment( }], route_params: Some(route_params.clone()), }; - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt); let res = source.send_payment_with_route(route, payment_hash, onion, payment_id); match res { Err(err) => { diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 8c887ed623a..4dda79dfe90 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -741,7 +741,7 @@ pub fn do_test(mut data: &[u8], logger: &Arc) { payments_sent += 1; let _ = channelmanager.send_payment( payment_hash, - RecipientOnionFields::spontaneous_empty(), + RecipientOnionFields::spontaneous_empty(final_value_msat), PaymentId(payment_hash.0), params, Retry::Attempts(2), @@ -763,7 +763,7 @@ pub fn do_test(mut data: &[u8], logger: &Arc) { payments_sent += 1; let _ = channelmanager.send_payment( payment_hash, - RecipientOnionFields::secret_only(payment_secret), + RecipientOnionFields::secret_only(payment_secret, final_value_msat), PaymentId(payment_hash.0), params, Retry::Attempts(2), diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index a537ff55874..741ac2bf4ba 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -6845,7 +6845,7 @@ mod tests { // the update through to the ChannelMonitor which will refuse it (as the channel is closed). let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 100_000); nodes[1].node.send_payment_with_route(route, payment_hash, - RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0) + RecipientOnionFields::secret_only(payment_secret, 100_000), PaymentId(payment_hash.0) ).unwrap(); check_added_monitors(&nodes[1], 1); diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index 3d860e9f363..74f2f34d4d8 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -41,8 +41,8 @@ use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; use crate::types::string::UntrustedString; use crate::util::errors::APIError; use crate::util::ser::{ - BigSize, FixedLengthReader, MaybeReadable, Readable, RequiredWrapper, UpgradableRequired, - WithoutLength, Writeable, Writer, + BigSize, FixedLengthReader, MaybeReadable, Readable, ReadableArgs, RequiredWrapper, + UpgradableRequired, WithoutLength, Writeable, Writer, }; use crate::io; @@ -2398,7 +2398,7 @@ impl MaybeReadable for Event { (6, _user_payment_id, option), (7, claim_deadline, option), (8, payment_preimage, option), - (9, onion_fields, option), + (9, onion_fields, (option: ReadableArgs, amount_msat)), (10, counterparty_skimmed_fee_msat_opt, option), (11, payment_context, option), (13, payment_id, option), @@ -2724,7 +2724,8 @@ impl MaybeReadable for Event { (4, amount_msat, required), (5, htlcs, optional_vec), (7, sender_intended_total_msat, option), - (9, onion_fields, option), + (9, onion_fields, (option: ReadableArgs, + sender_intended_total_msat.unwrap_or(amount_msat))), (11, payment_id, option), }); Ok(Some(Event::PaymentClaimed { diff --git a/lightning/src/ln/accountable_tests.rs b/lightning/src/ln/accountable_tests.rs index 16ca1425817..9b084423c24 100644 --- a/lightning/src/ln/accountable_tests.rs +++ b/lightning/src/ln/accountable_tests.rs @@ -31,7 +31,7 @@ fn test_accountable_forwarding_with_override( PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV), 100_000, ); - let onion_fields = RecipientOnionFields::secret_only(payment_secret); + let onion_fields = RecipientOnionFields::secret_only(payment_secret, 100_000); let payment_id = PaymentId(payment_hash.0); nodes[0] .node diff --git a/lightning/src/ln/async_payments_tests.rs b/lightning/src/ln/async_payments_tests.rs index 528cec44c00..264b8131596 100644 --- a/lightning/src/ln/async_payments_tests.rs +++ b/lightning/src/ln/async_payments_tests.rs @@ -615,7 +615,7 @@ fn invalid_keysend_payment_secret() { .node .send_spontaneous_payment( Some(keysend_preimage), - RecipientOnionFields::spontaneous_empty(), + RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(keysend_preimage.0), route_params, Retry::Attempts(0), diff --git a/lightning/src/ln/async_signer_tests.rs b/lightning/src/ln/async_signer_tests.rs index ddf17907718..be0eb968e49 100644 --- a/lightning/src/ln/async_signer_tests.rs +++ b/lightning/src/ln/async_signer_tests.rs @@ -301,7 +301,7 @@ fn do_test_async_commitment_signature_for_commitment_signed_revoke_and_ack( let (route, our_payment_hash, _our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(src, dst, 8000000); - let recipient_fields = RecipientOnionFields::secret_only(our_payment_secret); + let recipient_fields = RecipientOnionFields::secret_only(our_payment_secret, 8000000); let payment_id = PaymentId(our_payment_hash.0); src.node .send_payment_with_route(route, our_payment_hash, recipient_fields, payment_id) @@ -528,7 +528,7 @@ fn do_test_async_raa_peer_disconnect( let (route, our_payment_hash, _our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(src, dst, 8000000); - let recipient_fields = RecipientOnionFields::secret_only(our_payment_secret); + let recipient_fields = RecipientOnionFields::secret_only(our_payment_secret, 8000000); let payment_id = PaymentId(our_payment_hash.0); src.node .send_payment_with_route(route, our_payment_hash, recipient_fields, payment_id) @@ -677,7 +677,7 @@ fn do_test_async_commitment_signature_peer_disconnect( let (route, our_payment_hash, _our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(src, dst, 8000000); - let recipient_fields = RecipientOnionFields::secret_only(our_payment_secret); + let recipient_fields = RecipientOnionFields::secret_only(our_payment_secret, 8000000); let payment_id = PaymentId(our_payment_hash.0); src.node .send_payment_with_route(route, our_payment_hash, recipient_fields, payment_id) @@ -812,7 +812,7 @@ fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool) { // to the peer. let (route, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let recipient_fields = RecipientOnionFields::secret_only(payment_secret_2); + let recipient_fields = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let payment_id = PaymentId(payment_hash_2.0); nodes[0] .node @@ -1352,14 +1352,14 @@ fn test_no_disconnect_while_async_revoke_and_ack_expecting_remote_commitment_sig // We'll send a payment from both nodes to each other. let (route1, payment_hash1, _, payment_secret1) = get_route_and_payment_hash!(&nodes[0], &nodes[1], payment_amount); - let onion1 = RecipientOnionFields::secret_only(payment_secret1); + let onion1 = RecipientOnionFields::secret_only(payment_secret1, payment_amount); let payment_id1 = PaymentId(payment_hash1.0); nodes[0].node.send_payment_with_route(route1, payment_hash1, onion1, payment_id1).unwrap(); check_added_monitors(&nodes[0], 1); let (route2, payment_hash2, _, payment_secret2) = get_route_and_payment_hash!(&nodes[1], &nodes[0], payment_amount); - let onion2 = RecipientOnionFields::secret_only(payment_secret2); + let onion2 = RecipientOnionFields::secret_only(payment_secret2, payment_amount); let payment_id2 = PaymentId(payment_hash2.0); nodes[1].node.send_payment_with_route(route2, payment_hash2, onion2, payment_id2).unwrap(); check_added_monitors(&nodes[1], 1); diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index d78b9dfa4f2..b00a4f360d8 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -187,7 +187,7 @@ fn do_one_hop_blinded_path(success: bool) { PaymentParameters::blinded(vec![blinded_path]), amt_msat, ); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1]]], amt_msat, payment_hash, payment_secret); @@ -243,7 +243,7 @@ fn one_hop_blinded_path_with_dummy_hops() { .node .send_payment( payment_hash, - RecipientOnionFields::spontaneous_empty(), + RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0), @@ -307,7 +307,7 @@ fn mpp_to_one_hop_blinded_path() { PaymentParameters::blinded(vec![blinded_path]).with_bolt12_features(bolt12_features).unwrap(), amt_msat, ); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 2); let expected_route: &[&[&Node]] = &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]]; @@ -399,7 +399,7 @@ fn mpp_to_three_hop_blinded_paths() { RouteParameters::from_payment_params_and_value(pay_params, amt_msat) }; - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 2); @@ -464,7 +464,7 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) { let route = get_route(&nodes[0], &route_params).unwrap(); node_cfgs[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); macro_rules! cause_error { @@ -474,7 +474,7 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) { $update_add.cltv_expiry = 10; // causes outbound CLTV expiry to underflow }, ForwardCheckFail::ForwardPayloadEncodedAsReceive => { - let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(amt_msat); let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let mut onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv); let cur_height = nodes[0].best_block_info().1; @@ -594,7 +594,7 @@ fn failed_backwards_to_intro_node() { nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2], &chanmon_cfgs[2].keys_manager); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); @@ -680,7 +680,7 @@ fn do_forward_fail_in_process_pending_htlc_fwds(check: ProcessPendingHTLCsCheck, nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2, &chan_upd_2_3], &chanmon_cfgs[2].keys_manager); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); @@ -790,7 +790,7 @@ fn do_blinded_intercept_payment(intercept_node_fails: bool) { nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&intercept_chan_upd], &chanmon_cfgs[2].keys_manager); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); let payment_event = { @@ -865,7 +865,7 @@ fn two_hop_blinded_path_success() { nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2], &chanmon_cfgs[2].keys_manager); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], amt_msat, payment_hash, payment_secret); claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage); @@ -895,7 +895,7 @@ fn three_hop_blinded_path_success() { nodes.iter().skip(2).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_2_3, &chan_upd_3_4], &chanmon_cfgs[4].keys_manager); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3], &nodes[4]]], amt_msat, payment_hash, payment_secret); claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3], &nodes[4]], payment_preimage); @@ -920,7 +920,7 @@ fn three_hop_blinded_path_fail() { nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2, &chan_upd_2_3], &chanmon_cfgs[3].keys_manager); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3]]], amt_msat, payment_hash, payment_secret); @@ -1021,7 +1021,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { find_route(&nodes[0], &route_params).unwrap() }; node_cfgs[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); let mut payment_event_0_1 = { @@ -1064,7 +1064,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { let session_priv = SecretKey::from_slice(&session_priv).unwrap(); let mut onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv); let cur_height = nodes[0].best_block_info().1; - let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(amt_msat); let (mut onion_payloads, ..) = onion_utils::build_onion_payloads( &route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None, None, None).unwrap(); @@ -1210,7 +1210,7 @@ fn blinded_path_retries() { RouteParameters::from_payment_params_and_value(pay_params, amt_msat) }; - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(2)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(2)).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]]], amt_msat, payment_hash, payment_secret); @@ -1309,7 +1309,7 @@ fn min_htlc() { assert_eq!(min_htlc_msat, route_params.payment_params.payee.blinded_route_hints()[0].payinfo.htlc_minimum_msat); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(min_htlc_msat), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3]]], min_htlc_msat, payment_hash, payment_secret); claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3]], payment_preimage); @@ -1322,7 +1322,7 @@ fn min_htlc() { route_hints[0].payinfo.htlc_minimum_msat -= 1; } else { panic!() } route_params.final_value_msat -= 1; - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(route_params.final_value_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); let mut payment_event_0_1 = { @@ -1387,7 +1387,7 @@ fn conditionally_round_fwd_amt() { &chanmon_cfgs[4].keys_manager); route_params.max_total_routing_fee_msat = None; - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3], &nodes[4]]], amt_msat, payment_hash, payment_secret); nodes[4].node.claim_funds(payment_preimage); @@ -1432,7 +1432,7 @@ fn custom_tlvs_to_blinded_path() { amt_msat, ); - let recipient_onion_fields = RecipientOnionFields::spontaneous_empty() + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(amt_msat) .with_custom_tlvs(RecipientCustomTlvs::new(vec![((1 << 16) + 1, vec![42, 42])]).unwrap()); nodes[0].node.send_payment(payment_hash, recipient_onion_fields.clone(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); @@ -1487,7 +1487,7 @@ fn fails_receive_tlvs_authentication() { ); // Test authentication works normally. - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1]]], amt_msat, payment_hash, payment_secret); claim_payment(&nodes[0], &[&nodes[1]], payment_preimage); @@ -1517,7 +1517,7 @@ fn fails_receive_tlvs_authentication() { amt_msat, ); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); @@ -1574,7 +1574,7 @@ fn blinded_payment_path_padding() { let route_params = RouteParameters::from_payment_params_and_value(PaymentParameters::blinded(vec![blinded_path]), amt_msat); - nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3], &nodes[4]]], amt_msat, payment_hash, payment_secret); claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3], &nodes[4]], payment_preimage); @@ -1681,7 +1681,7 @@ fn route_blinding_spec_test_vector() { }), }; let cur_height = 747_000; - let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, amt_msat, &RecipientOnionFields::spontaneous_empty(), cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap(); + let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, amt_msat, &RecipientOnionFields::spontaneous_empty(amt_msat), cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap(); struct TestEcdhSigner { node_secret: SecretKey, @@ -1904,7 +1904,7 @@ fn test_combined_trampoline_onion_creation_vectors() { let amt_msat = 150_000_000; let cur_height = 800_000; - let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, amt_msat); let (bob_onion, htlc_msat, htlc_cltv) = onion_utils::create_payment_onion_internal(&secp_ctx, &path, &outer_session_key, amt_msat, &recipient_onion_fields, cur_height, &associated_data, &None, None, outer_onion_prng_seed, Some(session_priv), Some([0; 32])).unwrap(); let outer_onion_packet_hex = bob_onion.encode().to_lower_hex_string(); @@ -1995,7 +1995,7 @@ fn test_trampoline_inbound_payment_decoding() { let amt_msat = 150_000_001; let cur_height = 800_001; - let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, amt_msat); let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, amt_msat, &recipient_onion_fields, cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap(); struct TestEcdhSigner { @@ -2166,12 +2166,11 @@ fn test_trampoline_forward_payload_encoded_as_receive() { route_params: None, }; - nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap(); + nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0)).unwrap(); check_added_monitors(&nodes[0], 1); let replacement_onion = { // create a substitute onion where the last Trampoline hop is a forward - let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); let mut blinded_tail = route.paths[0].blinded_tail.clone().unwrap(); @@ -2181,6 +2180,7 @@ fn test_trampoline_forward_payload_encoded_as_receive() { encrypted_payload: vec![], }); + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(amt_msat); let (mut trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = onion_utils::build_trampoline_onion_payloads(&blinded_tail, amt_msat, &recipient_onion_fields, 32, &None).unwrap(); // pop the last dummy hop @@ -2195,6 +2195,7 @@ fn test_trampoline_forward_payload_encoded_as_receive() { None, ).unwrap(); + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(outer_total_msat); let (outer_payloads, _, _) = onion_utils::build_onion_payloads(&route.paths[0], outer_total_msat, &recipient_onion_fields, outer_starting_htlc_offset, &None, None, Some(trampoline_packet)).unwrap(); let outer_onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.clone().paths[0], &outer_session_priv); let outer_packet = onion_utils::construct_onion_packet( @@ -2331,7 +2332,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) { route_params: None, }; - nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap(); + nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0)).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], amt_msat, payment_hash, payment_secret); @@ -2477,7 +2478,7 @@ fn replacement_onion( ) -> msgs::OnionPacket { let outer_session_priv = SecretKey::from_slice(&override_random_bytes[..]).unwrap(); let trampoline_session_priv = onion_utils::compute_trampoline_session_priv(&outer_session_priv); - let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(original_amt_msat); let blinded_tail = route.paths[0].blinded_tail.clone().unwrap(); @@ -2525,6 +2526,7 @@ fn replacement_onion( // Use a different session key to construct the replacement onion packet. Note that the // sender isn't aware of this and won't be able to decode the fulfill hold times. + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(outer_total_msat); let (mut outer_payloads, _, _) = onion_utils::build_onion_payloads( &route.paths[0], outer_total_msat, @@ -2650,7 +2652,7 @@ fn do_test_trampoline_relay(blinded: bool, test_case: TrampolineTestCase) { .send_payment_with_route( route.clone(), payment_hash, - RecipientOnionFields::spontaneous_empty(), + RecipientOnionFields::spontaneous_empty(original_amt_msat), PaymentId(payment_hash.0), ) .unwrap(); @@ -2832,7 +2834,7 @@ fn test_trampoline_forward_rejection() { route_params: None, }; - nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap(); + nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0)).unwrap(); check_added_monitors(&nodes[0], 1); diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs index f3eda9b38ac..ef10258933a 100644 --- a/lightning/src/ln/chanmon_update_fail_tests.rs +++ b/lightning/src/ln/chanmon_update_fail_tests.rs @@ -187,7 +187,7 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool) { chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress); - let onion = RecipientOnionFields::secret_only(payment_secret_1); + let onion = RecipientOnionFields::secret_only(payment_secret_1, 1000000); let id = PaymentId(payment_hash_1.0); nodes[0].node.send_payment_with_route(route, payment_hash_1, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -254,7 +254,7 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool) { get_route_and_payment_hash!(&nodes[0], nodes[1], 1000000); chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress); - let onion = RecipientOnionFields::secret_only(payment_secret_2); + let onion = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let id = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route, payment_hash_2, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -330,7 +330,7 @@ fn do_test_monitor_temporary_update_fail(disconnect_count: usize) { let (route, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress); - let onion = RecipientOnionFields::secret_only(payment_secret_2); + let onion = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let id = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route, payment_hash_2, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -735,7 +735,7 @@ fn test_monitor_update_fail_cs() { let (route, our_payment_hash, payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 1000000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -843,7 +843,7 @@ fn test_monitor_update_fail_no_rebroadcast() { let (route, our_payment_hash, payment_preimage_1, payment_secret_1) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(payment_secret_1); + let onion = RecipientOnionFields::secret_only(payment_secret_1, 1000000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -897,7 +897,7 @@ fn test_monitor_update_raa_while_paused() { send_payment(&nodes[0], &[&nodes[1]], 5000000); let (route, our_payment_hash_1, payment_preimage_1, our_payment_secret_1) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(our_payment_secret_1); + let onion = RecipientOnionFields::secret_only(our_payment_secret_1, 1000000); let id = PaymentId(our_payment_hash_1.0); nodes[0].node.send_payment_with_route(route, our_payment_hash_1, onion, id).unwrap(); @@ -907,7 +907,7 @@ fn test_monitor_update_raa_while_paused() { let (route, our_payment_hash_2, payment_preimage_2, our_payment_secret_2) = get_route_and_payment_hash!(nodes[1], nodes[0], 1000000); - let onion_2 = RecipientOnionFields::secret_only(our_payment_secret_2); + let onion_2 = RecipientOnionFields::secret_only(our_payment_secret_2, 1000000); let id_2 = PaymentId(our_payment_hash_2.0); nodes[1].node.send_payment_with_route(route, our_payment_hash_2, onion_2, id_2).unwrap(); @@ -1008,7 +1008,7 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) { // holding cell. let (route, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], 1000000); - let onion_2 = RecipientOnionFields::secret_only(payment_secret_2); + let onion_2 = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let id_2 = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route, payment_hash_2, onion_2, id_2).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1034,7 +1034,7 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) { // being paused waiting a monitor update. let (route, payment_hash_3, _, payment_secret_3) = get_route_and_payment_hash!(nodes[0], nodes[2], 1000000); - let onion_3 = RecipientOnionFields::secret_only(payment_secret_3); + let onion_3 = RecipientOnionFields::secret_only(payment_secret_3, 1000000); let id_3 = PaymentId(payment_hash_3.0); nodes[0].node.send_payment_with_route(route, payment_hash_3, onion_3, id_3).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1055,7 +1055,7 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) { // Try to route another payment backwards from 2 to make sure 1 holds off on responding let (route, payment_hash_4, payment_preimage_4, payment_secret_4) = get_route_and_payment_hash!(nodes[2], nodes[0], 1000000); - let onion_4 = RecipientOnionFields::secret_only(payment_secret_4); + let onion_4 = RecipientOnionFields::secret_only(payment_secret_4, 1000000); let id_4 = PaymentId(payment_hash_4.0); nodes[2].node.send_payment_with_route(route, payment_hash_4, onion_4, id_4).unwrap(); check_added_monitors(&nodes[2], 1); @@ -1391,11 +1391,11 @@ fn raa_no_response_awaiting_raa_state() { // immediately after a CS. By setting failing the monitor update failure from the CS (which // requires only an RAA response due to AwaitingRAA) we can deliver the RAA and require the CS // generation during RAA while in monitor-update-failed state. - let onion_1 = RecipientOnionFields::secret_only(payment_secret_1); + let onion_1 = RecipientOnionFields::secret_only(payment_secret_1, 1000000); let id_1 = PaymentId(payment_hash_1.0); nodes[0].node.send_payment_with_route(route.clone(), payment_hash_1, onion_1, id_1).unwrap(); check_added_monitors(&nodes[0], 1); - let onion_2 = RecipientOnionFields::secret_only(payment_secret_2); + let onion_2 = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let id_2 = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route.clone(), payment_hash_2, onion_2, id_2).unwrap(); check_added_monitors(&nodes[0], 0); @@ -1444,7 +1444,7 @@ fn raa_no_response_awaiting_raa_state() { // We send a third payment here, which is somewhat of a redundant test, but the // chanmon_fail_consistency test required it to actually find the bug (by seeing out-of-sync // commitment transaction states) whereas here we can explicitly check for it. - let onion_3 = RecipientOnionFields::secret_only(payment_secret_3); + let onion_3 = RecipientOnionFields::secret_only(payment_secret_3, 1000000); let id_3 = PaymentId(payment_hash_3.0); nodes[0].node.send_payment_with_route(route, payment_hash_3, onion_3, id_3).unwrap(); check_added_monitors(&nodes[0], 0); @@ -1546,7 +1546,7 @@ fn claim_while_disconnected_monitor_update_fail() { // the monitor still failed let (route, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion_2 = RecipientOnionFields::secret_only(payment_secret_2); + let onion_2 = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let id_2 = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route, payment_hash_2, onion_2, id_2).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1653,7 +1653,7 @@ fn monitor_failed_no_reestablish_response() { // on receipt). let (route, payment_hash_1, payment_preimage_1, payment_secret_1) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(payment_secret_1); + let onion = RecipientOnionFields::secret_only(payment_secret_1, 1000000); let id = PaymentId(payment_hash_1.0); nodes[0].node.send_payment_with_route(route, payment_hash_1, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1737,7 +1737,7 @@ fn first_message_on_recv_ordering() { // can deliver it and fail the monitor update. let (route, payment_hash_1, payment_preimage_1, payment_secret_1) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion_1 = RecipientOnionFields::secret_only(payment_secret_1); + let onion_1 = RecipientOnionFields::secret_only(payment_secret_1, 1000000); let id_1 = PaymentId(payment_hash_1.0); nodes[0].node.send_payment_with_route(route, payment_hash_1, onion_1, id_1).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1761,7 +1761,7 @@ fn first_message_on_recv_ordering() { // Route the second payment, generating an update_add_htlc/commitment_signed let (route, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion_2 = RecipientOnionFields::secret_only(payment_secret_2); + let onion_2 = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let id_2 = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route, payment_hash_2, onion_2, id_2).unwrap(); @@ -1854,7 +1854,7 @@ fn test_monitor_update_fail_claim() { let (route, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[2], nodes[0], 1_000_000); - let onion_2 = RecipientOnionFields::secret_only(payment_secret_2); + let onion_2 = RecipientOnionFields::secret_only(payment_secret_2, 1_000_000); let id_2 = PaymentId(payment_hash_2.0); nodes[2].node.send_payment_with_route(route.clone(), payment_hash_2, onion_2, id_2).unwrap(); check_added_monitors(&nodes[2], 1); @@ -1874,7 +1874,7 @@ fn test_monitor_update_fail_claim() { let (_, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[0]); let id_3 = PaymentId(payment_hash_3.0); - let onion_3 = RecipientOnionFields::secret_only(payment_secret_3); + let onion_3 = RecipientOnionFields::secret_only(payment_secret_3, 1_000_000); nodes[2].node.send_payment_with_route(route, payment_hash_3, onion_3, id_3).unwrap(); check_added_monitors(&nodes[2], 1); @@ -1998,7 +1998,7 @@ fn test_monitor_update_on_pending_forwards() { let (route, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[2], nodes[0], 1000000); - let onion = RecipientOnionFields::secret_only(payment_secret_2); + let onion = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let id = PaymentId(payment_hash_2.0); nodes[2].node.send_payment_with_route(route, payment_hash_2, onion, id).unwrap(); check_added_monitors(&nodes[2], 1); @@ -2069,7 +2069,7 @@ fn monitor_update_claim_fail_no_response() { // Now start forwarding a second payment, skipping the last RAA so B is in AwaitingRAA let (route, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(payment_secret_2); + let onion = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let id = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route, payment_hash_2, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -2319,7 +2319,7 @@ fn test_path_paused_mpp() { chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress); // The first path should have succeeded with the second getting a MonitorUpdateInProgress err. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 200000); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 2); @@ -2375,7 +2375,7 @@ fn test_pending_update_fee_ack_on_reconnect() { let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(&nodes[1], nodes[0], 1_000_000); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 1_000_000); let id = PaymentId(payment_hash.0); nodes[1].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[1], 1); @@ -2690,14 +2690,14 @@ fn do_channel_holding_cell_serialize(disconnect: bool, reload_a: bool) { // (c) will not be freed from the holding cell. let (payment_preimage_0, payment_hash_0, ..) = route_payment(&nodes[1], &[&nodes[0]], 100_000); - let onion_1 = RecipientOnionFields::secret_only(payment_secret_1); + let onion_1 = RecipientOnionFields::secret_only(payment_secret_1, 100000); let id_1 = PaymentId(payment_hash_1.0); nodes[0].node.send_payment_with_route(route.clone(), payment_hash_1, onion_1, id_1).unwrap(); check_added_monitors(&nodes[0], 1); let send = SendEvent::from_node(&nodes[0]); assert_eq!(send.msgs.len(), 1); - let onion_2 = RecipientOnionFields::secret_only(payment_secret_2); + let onion_2 = RecipientOnionFields::secret_only(payment_secret_2, 100000); let id_2 = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route, payment_hash_2, onion_2, id_2).unwrap(); check_added_monitors(&nodes[0], 0); @@ -2874,7 +2874,7 @@ fn do_test_reconnect_dup_htlc_claims(htlc_status: HTLCStatusAtDupClaim, second_f // awaiting a remote revoke_and_ack from nodes[0]. let (route, second_payment_hash, _, second_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 100_000); - let onion_2 = RecipientOnionFields::secret_only(second_payment_secret); + let onion_2 = RecipientOnionFields::secret_only(second_payment_secret, 100_000); let id_2 = PaymentId(second_payment_hash.0); nodes[0].node.send_payment_with_route(route, second_payment_hash, onion_2, id_2).unwrap(); check_added_monitors(&nodes[0], 1); @@ -4147,7 +4147,7 @@ fn do_test_glacial_peer_cant_hang(hold_chan_a: bool) { // With the A<->B preimage persistence not yet complete, the B<->C channel is stuck // waiting. - let onion_2 = RecipientOnionFields::secret_only(payment_secret_2); + let onion_2 = RecipientOnionFields::secret_only(payment_secret_2, 1_000_000); let id_2 = PaymentId(payment_hash_2.0); nodes[1].node.send_payment_with_route(route, payment_hash_2, onion_2, id_2).unwrap(); check_added_monitors(&nodes[1], 0); @@ -5089,7 +5089,7 @@ fn test_mpp_claim_to_holding_cell() { // Put the C <-> D channel into AwaitingRaa let (preimage_2, paymnt_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[3]); - let onion = RecipientOnionFields::secret_only(payment_secret_2); + let onion = RecipientOnionFields::secret_only(payment_secret_2, 400_000); let id = PaymentId([42; 32]); let pay_params = PaymentParameters::from_node_id(node_d_id, TEST_FINAL_CLTV); let route_params = RouteParameters::from_payment_params_and_value(pay_params, 400_000); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index a733cae11d5..e3094234417 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -1078,7 +1078,7 @@ impl_writeable_tlv_based!(ClaimingPayment, { (4, receiver_node_id, required), (5, htlcs, optional_vec), (7, sender_intended_value, option), - (9, onion_fields, option), + (9, onion_fields, (option: ReadableArgs, amount_msat.0.unwrap())), (11, payment_id, option), }); @@ -1107,6 +1107,18 @@ impl ClaimablePayment { } } +/// We write the [`ClaimableHTLC`] [`RecipientOnionFields`] separately as they were added sometime +/// later. Because [`ClaimableHTLC`] only implements [`ReadableArgs`] and have to add a wrapper +/// which reads them without [`RecipientOnionFields::total_mpp_amount_msat`] and then fill them in +/// later. +struct AmountlessClaimablePaymentHTLCOnion(RecipientOnionFields); + +impl Readable for AmountlessClaimablePaymentHTLCOnion { + fn read(reader: &mut R) -> Result { + Ok(Self(ReadableArgs::read(reader, 0)?)) + } +} + /// Represent the channel funding transaction type. enum FundingType { /// This variant is useful when we want LDK to validate the funding transaction and @@ -7813,6 +7825,7 @@ impl< payment_secret: Some(payment_data.payment_secret), payment_metadata, custom_tlvs, + total_mpp_amount_msat: payment_data.total_msat, }; ( incoming_cltv_expiry, @@ -7841,6 +7854,10 @@ impl< payment_secret: payment_data .as_ref() .map(|data| data.payment_secret), + total_mpp_amount_msat: payment_data + .as_ref() + .map(|data| data.total_msat) + .unwrap_or(outgoing_amt_msat), payment_metadata, custom_tlvs, }; @@ -17472,8 +17489,10 @@ impl<'a, ES: EntropySource, NS: NodeSigner, SP: SignerProvider, L: Logger> let mut fake_scid_rand_bytes: Option<[u8; 32]> = None; let mut probing_cookie_secret: Option<[u8; 32]> = None; let mut claimable_htlc_purposes = None; - let mut claimable_htlc_onion_fields = None; - let mut pending_claiming_payments = None; + let mut amountless_claimable_htlc_onion_fields: Option< + Vec>, + > = None; + let mut pending_claiming_payments = Some(new_hash_map()); let mut monitor_update_blocked_actions_per_peer: Option>)>> = None; let mut events_override = None; @@ -17500,7 +17519,7 @@ impl<'a, ES: EntropySource, NS: NodeSigner, SP: SignerProvider, L: Logger> (9, claimable_htlc_purposes, optional_vec), (10, legacy_in_flight_monitor_updates, option), (11, probing_cookie_secret, option), - (13, claimable_htlc_onion_fields, optional_vec), + (13, amountless_claimable_htlc_onion_fields, optional_vec), (14, decode_update_add_htlcs_legacy, option), (15, inbound_payment_id_secret, option), (17, in_flight_monitor_updates, option), @@ -17565,7 +17584,7 @@ impl<'a, ES: EntropySource, NS: NodeSigner, SP: SignerProvider, L: Logger> if purposes.len() != claimable_htlcs_list.len() { return Err(DecodeError::InvalidValue); } - if let Some(onion_fields) = claimable_htlc_onion_fields { + if let Some(onion_fields) = amountless_claimable_htlc_onion_fields { if onion_fields.len() != claimable_htlcs_list.len() { return Err(DecodeError::InvalidValue); } @@ -17573,7 +17592,20 @@ impl<'a, ES: EntropySource, NS: NodeSigner, SP: SignerProvider, L: Logger> .into_iter() .zip(onion_fields.into_iter().zip(claimable_htlcs_list.into_iter())) { - let claimable = ClaimablePayment { purpose, htlcs, onion_fields: onion }; + let htlcs_total_msat = + htlcs.first().ok_or(DecodeError::InvalidValue)?.total_msat; + let onion_fields = if let Some(mut onion) = onion { + if onion.0.total_mpp_amount_msat != 0 + && onion.0.total_mpp_amount_msat != htlcs_total_msat + { + return Err(DecodeError::InvalidValue); + } + onion.0.total_mpp_amount_msat = htlcs_total_msat; + Some(onion.0) + } else { + None + }; + let claimable = ClaimablePayment { purpose, htlcs, onion_fields }; let existing_payment = claimable_payments.insert(payment_hash, claimable); if existing_payment.is_some() { return Err(DecodeError::InvalidValue); @@ -19635,9 +19667,9 @@ mod tests { // indicates there are more HTLCs coming. let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match. let session_privs = nodes[0].node.test_add_new_pending_payment(our_payment_hash, - RecipientOnionFields::secret_only(payment_secret), payment_id, &mpp_route).unwrap(); + RecipientOnionFields::secret_only(payment_secret, 200_000), payment_id, &mpp_route).unwrap(); nodes[0].node.test_send_payment_along_path(&mpp_route.paths[0], &our_payment_hash, - RecipientOnionFields::secret_only(payment_secret), 200_000, cur_height, payment_id, &None, session_privs[0]).unwrap(); + RecipientOnionFields::secret_only(payment_secret, 200_000), 200_000, cur_height, payment_id, &None, session_privs[0]).unwrap(); check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); @@ -19645,7 +19677,7 @@ mod tests { // Next, send a keysend payment with the same payment_hash and make sure it fails. nodes[0].node.send_spontaneous_payment( - Some(payment_preimage), RecipientOnionFields::spontaneous_empty(), + Some(payment_preimage), RecipientOnionFields::spontaneous_empty(100_000), PaymentId(payment_preimage.0), route.route_params.clone().unwrap(), Retry::Attempts(0) ).unwrap(); check_added_monitors(&nodes[0], 1); @@ -19673,7 +19705,7 @@ mod tests { // Send the second half of the original MPP payment. nodes[0].node.test_send_payment_along_path(&mpp_route.paths[1], &our_payment_hash, - RecipientOnionFields::secret_only(payment_secret), 200_000, cur_height, payment_id, &None, session_privs[1]).unwrap(); + RecipientOnionFields::secret_only(payment_secret, 200_000), 200_000, cur_height, payment_id, &None, session_privs[1]).unwrap(); check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); @@ -19763,7 +19795,7 @@ mod tests { PaymentParameters::for_keysend(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV, false), 100_000); nodes[0].node.send_spontaneous_payment( - Some(payment_preimage), RecipientOnionFields::spontaneous_empty(), + Some(payment_preimage), RecipientOnionFields::spontaneous_empty(100_000), PaymentId(payment_preimage.0), route_params.clone(), Retry::Attempts(0) ).unwrap(); check_added_monitors(&nodes[0], 1); @@ -19801,7 +19833,7 @@ mod tests { None, nodes[0].logger, &scorer, &Default::default(), &random_seed_bytes ).unwrap(); let payment_hash = nodes[0].node.send_spontaneous_payment( - Some(payment_preimage), RecipientOnionFields::spontaneous_empty(), + Some(payment_preimage), RecipientOnionFields::spontaneous_empty(100_000), PaymentId(payment_preimage.0), route.route_params.clone().unwrap(), Retry::Attempts(0) ).unwrap(); check_added_monitors(&nodes[0], 1); @@ -19814,7 +19846,7 @@ mod tests { // Next, attempt a regular payment and make sure it fails. let payment_secret = PaymentSecret([43; 32]); nodes[0].node.send_payment_with_route(route.clone(), payment_hash, - RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap(); + RecipientOnionFields::secret_only(payment_secret, 100_000), PaymentId(payment_hash.0)).unwrap(); check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); @@ -19844,7 +19876,7 @@ mod tests { // To start (3), send a keysend payment but don't claim it. let payment_id_1 = PaymentId([44; 32]); let payment_hash = nodes[0].node.send_spontaneous_payment( - Some(payment_preimage), RecipientOnionFields::spontaneous_empty(), payment_id_1, + Some(payment_preimage), RecipientOnionFields::spontaneous_empty(100_000), payment_id_1, route.route_params.clone().unwrap(), Retry::Attempts(0) ).unwrap(); check_added_monitors(&nodes[0], 1); @@ -19861,7 +19893,7 @@ mod tests { ); let payment_id_2 = PaymentId([45; 32]); nodes[0].node.send_spontaneous_payment( - Some(payment_preimage), RecipientOnionFields::spontaneous_empty(), payment_id_2, route_params, + Some(payment_preimage), RecipientOnionFields::spontaneous_empty(100_000), payment_id_2, route_params, Retry::Attempts(0) ).unwrap(); check_added_monitors(&nodes[0], 1); @@ -19919,9 +19951,9 @@ mod tests { let test_preimage = PaymentPreimage([42; 32]); let mismatch_payment_hash = PaymentHash([43; 32]); let session_privs = nodes[0].node.test_add_new_pending_payment(mismatch_payment_hash, - RecipientOnionFields::spontaneous_empty(), PaymentId(mismatch_payment_hash.0), &route).unwrap(); + RecipientOnionFields::spontaneous_empty(10_000), PaymentId(mismatch_payment_hash.0), &route).unwrap(); nodes[0].node.test_send_payment_internal(&route, mismatch_payment_hash, - RecipientOnionFields::spontaneous_empty(), Some(test_preimage), PaymentId(mismatch_payment_hash.0), None, session_privs).unwrap(); + RecipientOnionFields::spontaneous_empty(10_000), Some(test_preimage), PaymentId(mismatch_payment_hash.0), None, session_privs).unwrap(); check_added_monitors(&nodes[0], 1); let updates = get_htlc_update_msgs(&nodes[0], &nodes[1].node.get_our_node_id()); @@ -19966,7 +19998,7 @@ mod tests { route.route_params.as_mut().unwrap().final_value_msat *= 2; nodes[0].node.send_payment_with_route(route, payment_hash, - RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap(); + RecipientOnionFields::spontaneous_empty(200000), PaymentId(payment_hash.0)).unwrap(); let events = nodes[0].node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); match events[0] { @@ -20783,7 +20815,7 @@ pub mod bench { let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array()); let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None).unwrap(); - $node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret), + $node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret, 10_000), PaymentId(payment_hash.0), RouteParameters::from_payment_params_and_value(payment_params, 10_000), Retry::Attempts(0)).unwrap(); diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 218779123f6..0e1ada083c6 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -3412,7 +3412,7 @@ pub fn send_along_route_with_secret<'a, 'b, 'c>( .node .send_payment( our_payment_hash, - RecipientOnionFields::secret_only(our_payment_secret), + RecipientOnionFields::secret_only(our_payment_secret, recv_value), payment_id, route.route_params.unwrap(), Retry::Attempts(0), @@ -3551,7 +3551,7 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option if is_last_hop && is_probe { do_commitment_signed_dance(node, prev_node, &payment_event.commitment_msg, true, true); - node.node.process_pending_htlc_forwards(); + expect_and_process_pending_htlcs(node, true); check_added_monitors(node, 1); } else { let commitment = &payment_event.commitment_msg; diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 21c2af696c1..c2926681c73 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -1986,7 +1986,7 @@ fn do_test_commitment_revoked_fail_backward_exhaustive( // on nodes[2]'s RAA. let (route, fourth_payment_hash, _, fourth_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[2], 1000000); - let onion = RecipientOnionFields::secret_only(fourth_payment_secret); + let onion = RecipientOnionFields::secret_only(fourth_payment_secret, 1000000); let id = PaymentId(fourth_payment_hash.0); nodes[1].node.send_payment_with_route(route, fourth_payment_hash, onion, id).unwrap(); assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); @@ -2209,7 +2209,7 @@ pub fn fail_backward_pending_htlc_upon_channel_failure() { { let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 50_000); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 50_000); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -2227,7 +2227,7 @@ pub fn fail_backward_pending_htlc_upon_channel_failure() { let (route, failed_payment_hash, _, failed_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 50_000); { - let onion = RecipientOnionFields::secret_only(failed_payment_secret); + let onion = RecipientOnionFields::secret_only(failed_payment_secret, 50_000); let id = PaymentId(failed_payment_hash.0); nodes[0].node.send_payment_with_route(route, failed_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 0); @@ -2243,7 +2243,7 @@ pub fn fail_backward_pending_htlc_upon_channel_failure() { let secp_ctx = Secp256k1::new(); let session_priv = SecretKey::from_slice(&[42; 32]).unwrap(); let current_height = nodes[1].node.best_block.read().unwrap().height + 1; - let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, 50_000); let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads( &route.paths[0], 50_000, @@ -2372,7 +2372,7 @@ pub fn test_force_close_fail_back() { get_route_and_payment_hash!(nodes[0], nodes[2], 1000000); let mut payment_event = { - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 1000000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -2658,7 +2658,7 @@ fn do_test_drop_messages_peer_disconnect(messages_delivered: u8, simulate_broken get_route_and_payment_hash!(nodes[0], nodes[1], 1_000_000); let payment_event = { - let onion = RecipientOnionFields::secret_only(payment_secret_1); + let onion = RecipientOnionFields::secret_only(payment_secret_1, 1_000_000); let id = PaymentId(payment_hash_1.0); nodes[0].node.send_payment_with_route(route, payment_hash_1, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -3073,7 +3073,7 @@ pub fn test_drop_messages_peer_disconnect_dual_htlc() { // Now try to send a second payment which will fail to send let (route, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(payment_secret_2); + let onion = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let id = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route, payment_hash_2, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -3262,7 +3262,7 @@ fn do_test_htlc_timeout(send_partial_mpp: bool) { // indicates there are more HTLCs coming. let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match. let payment_id = PaymentId([42; 32]); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 100000); let session_privs = nodes[0] .node .test_add_new_pending_payment(our_payment_hash, onion, payment_id, &route) @@ -3273,7 +3273,7 @@ fn do_test_htlc_timeout(send_partial_mpp: bool) { .test_send_payment_along_path( &route.paths[0], &our_payment_hash, - RecipientOnionFields::secret_only(payment_secret), + RecipientOnionFields::secret_only(payment_secret, 200_000), 200_000, cur_height, payment_id, @@ -3362,7 +3362,7 @@ fn do_test_holding_cell_htlc_add_timeouts(forwarded_htlc: bool) { // Route a first payment to get the 1 -> 2 channel in awaiting_raa... let (route, first_payment_hash, _, first_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[2], 100000); - let onion = RecipientOnionFields::secret_only(first_payment_secret); + let onion = RecipientOnionFields::secret_only(first_payment_secret, 100000); let id = PaymentId(first_payment_hash.0); nodes[1].node.send_payment_with_route(route, first_payment_hash, onion, id).unwrap(); assert_eq!(nodes[1].node.get_and_clear_pending_msg_events().len(), 1); @@ -3372,7 +3372,7 @@ fn do_test_holding_cell_htlc_add_timeouts(forwarded_htlc: bool) { let sending_node = if forwarded_htlc { &nodes[0] } else { &nodes[1] }; let (route, second_payment_hash, _, second_payment_secret) = get_route_and_payment_hash!(sending_node, nodes[2], 100000); - let onion = RecipientOnionFields::secret_only(second_payment_secret); + let onion = RecipientOnionFields::secret_only(second_payment_secret, 100000); let id = PaymentId(second_payment_hash.0); sending_node.node.send_payment_with_route(route, second_payment_hash, onion, id).unwrap(); @@ -4986,7 +4986,8 @@ fn do_htlc_claim_current_remote_commitment_only(use_dust: bool) { let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], if use_dust { 50000 } else { 3000000 }); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = + RecipientOnionFields::secret_only(payment_secret, if use_dust { 50000 } else { 3000000 }); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -5149,7 +5150,7 @@ pub fn test_fail_holding_cell_htlc_upon_free() { get_route_and_payment_hash!(nodes[0], nodes[1], max_can_send); // Send a payment which passes reserve checks but gets stuck in the holding cell. - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, max_can_send); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route.clone(), our_payment_hash, onion, id).unwrap(); chan_stat = get_channel_value_stat!(nodes[0], nodes[1], chan.2); @@ -5253,14 +5254,14 @@ pub fn test_free_and_fail_holding_cell_htlcs() { get_route_and_payment_hash!(nodes[0], nodes[1], amt_2); // Send 2 payments which pass reserve checks but get stuck in the holding cell. - let onion = RecipientOnionFields::secret_only(payment_secret_1); + let onion = RecipientOnionFields::secret_only(payment_secret_1, amt_1); let id_1 = PaymentId(payment_hash_1.0); nodes[0].node.send_payment_with_route(route_1, payment_hash_1, onion, id_1).unwrap(); chan_stat = get_channel_value_stat!(nodes[0], nodes[1], chan.2); assert_eq!(chan_stat.holding_cell_outbound_amount_msat, amt_1); let id_2 = PaymentId(nodes[0].keys_manager.get_secure_random_bytes()); - let onion = RecipientOnionFields::secret_only(payment_secret_2); + let onion = RecipientOnionFields::secret_only(payment_secret_2, amt_2); nodes[0].node.send_payment_with_route(route_2.clone(), payment_hash_2, onion, id_2).unwrap(); chan_stat = get_channel_value_stat!(nodes[0], nodes[1], chan.2); assert_eq!(chan_stat.holding_cell_outbound_amount_msat, amt_1 + amt_2); @@ -5399,7 +5400,7 @@ pub fn test_fail_holding_cell_htlc_upon_free_multihop() { let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], max_can_send); let payment_event = { - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, max_can_send); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -5507,7 +5508,7 @@ pub fn test_update_fulfill_htlc_bolt2_after_malformed_htlc_message_must_forward_ //First hop let mut payment_event = { - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 100000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -5620,7 +5621,7 @@ pub fn test_channel_failed_after_message_with_badonion_node_perm_bits_set() { // First hop let mut payment_event = { - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 100_000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -5967,7 +5968,7 @@ pub fn test_check_htlc_underpaying() { .node .create_inbound_payment_for_hash(our_payment_hash, Some(100_000), 7200, None) .unwrap(); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, route.get_total_amount()); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -6906,7 +6907,7 @@ pub fn test_onion_value_mpp_set_calculation() { // Send payment let id = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes()); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, total_msat); let onion_session_privs = nodes[0].node.test_add_new_pending_payment(hash, onion.clone(), id, &route).unwrap(); let amt = Some(total_msat); @@ -6939,7 +6940,7 @@ pub fn test_onion_value_mpp_set_calculation() { &route.paths[0], &session_priv, ); - let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, 100_000); let (mut onion_payloads, _, _) = onion_utils::build_onion_payloads( &route.paths[0], 100_000, @@ -7044,10 +7045,10 @@ fn do_test_overshoot_mpp(msat_amounts: &[u64], total_msat: u64) { // Send payment with manually set total_msat let id = PaymentId(nodes[src_idx].keys_manager.backing.get_secure_random_bytes()); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, total_msat); let onion_session_privs = nodes[src_idx].node.test_add_new_pending_payment(hash, onion, id, &route).unwrap(); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, total_msat); let amt = Some(total_msat); nodes[src_idx] .node @@ -7135,7 +7136,7 @@ pub fn test_preimage_storage() { let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(100_000), 7200, None).unwrap(); let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[1], 100_000); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 100_000); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); @@ -7227,20 +7228,20 @@ pub fn test_bad_secret_hash() { let expected_err_data = [0, 0, 0, 0, 0, 1, 0x86, 0xa0, 0, 0, 0, CHAN_CONFIRM_DEPTH as u8]; // Send a payment with the right payment hash but the wrong payment secret - let onion = RecipientOnionFields::secret_only(random_secret); + let onion = RecipientOnionFields::secret_only(random_secret, 100_000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route.clone(), our_payment_hash, onion, id).unwrap(); handle_unknown_invalid_payment_data!(our_payment_hash); expect_payment_failed!(nodes[0], our_payment_hash, true, expected_err_code, expected_err_data); // Send a payment with a random payment hash, but the right payment secret - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 100_000); nodes[0].node.send_payment_with_route(route.clone(), random_hash, onion, id).unwrap(); handle_unknown_invalid_payment_data!(random_hash); expect_payment_failed!(nodes[0], random_hash, true, expected_err_code, expected_err_data); // Send a payment with a random payment hash and random payment secret - let onion = RecipientOnionFields::secret_only(random_secret); + let onion = RecipientOnionFields::secret_only(random_secret, 100_000); nodes[0].node.send_payment_with_route(route, random_hash, onion, id).unwrap(); handle_unknown_invalid_payment_data!(random_hash); expect_payment_failed!(nodes[0], random_hash, true, expected_err_code, expected_err_data); @@ -7467,7 +7468,7 @@ pub fn test_concurrent_monitor_claim() { // Route another payment to generate another update with still previous HTLC pending let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 3000000); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 3000000); let id = PaymentId(payment_hash.0); nodes[1].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[1], 1); @@ -8203,7 +8204,7 @@ fn do_test_dup_htlc_second_rejected(test_for_second_fail_panic: bool) { get_payment_preimage_hash!(&nodes[1]); { - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 10_000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route.clone(), our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -8219,7 +8220,7 @@ fn do_test_dup_htlc_second_rejected(test_for_second_fail_panic: bool) { { // Note that we use a different PaymentId here to allow us to duplicativly pay - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 10_000); let id = PaymentId(our_payment_secret.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -8359,10 +8360,10 @@ pub fn test_inconsistent_mpp_params() { // ultimately have, just not right away. let mut dup_route = route.clone(); dup_route.paths.push(route.paths[1].clone()); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 15_000_000); nodes[0].node.test_add_new_pending_payment(hash, onion, id, &dup_route).unwrap() }; - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 15_000_000); let path_a = &route.paths[0]; let real_amt = 15_000_000; let priv_a = session_privs[0]; @@ -8380,7 +8381,7 @@ pub fn test_inconsistent_mpp_params() { assert!(nodes[3].node.get_and_clear_pending_events().is_empty()); let path_b = &route.paths[1]; - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 14_000_000); let amt_b = 14_000_000; let priv_b = session_privs[1]; nodes[0] @@ -8440,7 +8441,7 @@ pub fn test_inconsistent_mpp_params() { let conditions = PaymentFailedConditions::new().mpp_parts_remain(); expect_payment_failed_conditions(&nodes[0], hash, true, conditions); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, real_amt); let path_b = &route.paths[1]; let priv_c = session_privs[2]; nodes[0] @@ -8508,7 +8509,7 @@ pub fn test_double_partial_claim() { pass_failed_payment_back(&nodes[0], paths, false, hash, reason); // nodes[1] now retries one of the two paths... - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 15_000_000); let id = PaymentId(hash.0); nodes[0].node.send_payment_with_route(route, hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 2); @@ -8740,12 +8741,18 @@ fn do_test_max_dust_htlc_exposure( }; // With default dust exposure: 5000 sats if on_holder_tx { - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only( + payment_secret, + dust_outbound_htlc_on_holder_tx_msat, + ); let id = PaymentId(payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, payment_hash, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { .. }, {}); } else { - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only( + payment_secret, + dust_htlc_on_counterparty_tx_msat + 1, + ); let id = PaymentId(payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, payment_hash, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -8759,7 +8766,7 @@ fn do_test_max_dust_htlc_exposure( let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], amount_msats); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amount_msats); let id = PaymentId(payment_hash.0); nodes[1].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[1], 1); @@ -8798,7 +8805,7 @@ fn do_test_max_dust_htlc_exposure( // to cross the threshold. for _ in 0..AT_FEE_OUTBOUND_HTLCS { let (_, hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(1_000), None); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, route.get_total_amount()); let id = PaymentId(hash.0); nodes[0].node.send_payment_with_route(route.clone(), hash, onion, id).unwrap(); } @@ -9028,7 +9035,7 @@ pub fn test_nondust_htlc_excess_fees_are_dust() { // Send an additional non-dust htlc from 1 to 0, and check the complaint let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], dust_limit * 2); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, route.get_total_amount()); let id = PaymentId(payment_hash.0); nodes[1].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[1], 1); @@ -9064,7 +9071,7 @@ pub fn test_nondust_htlc_excess_fees_are_dust() { assert_eq!(nodes[1].node.list_channels()[0].pending_outbound_htlcs.len(), 0); // Send an additional non-dust htlc from 0 to 1 using the pre-calculated route above, and check the immediate complaint - let onion = RecipientOnionFields::secret_only(payment_secret_0_1); + let onion = RecipientOnionFields::secret_only(payment_secret_0_1, route_0_1.get_total_amount()); let id = PaymentId(payment_hash_0_1.0); let res = nodes[0].node.send_payment_with_route(route_0_1, payment_hash_0_1, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -9082,7 +9089,7 @@ pub fn test_nondust_htlc_excess_fees_are_dust() { create_announced_chan_between_nodes(&nodes, 2, 0); let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[2], nodes[1], dust_limit * 2); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, route.get_total_amount()); nodes[2].node.send_payment_with_route(route, payment_hash, onion, PaymentId([0; 32])).unwrap(); check_added_monitors(&nodes[2], 1); let send = SendEvent::from_node(&nodes[2]); @@ -9204,7 +9211,7 @@ fn do_test_nondust_htlc_fees_dust_exposure_delta(features: ChannelTypeFeatures) // Send an additional non-dust htlc from 0 to 1, and check the complaint let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], NON_DUST_HTLC_MSAT); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, NON_DUST_HTLC_MSAT); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -9286,7 +9293,7 @@ fn do_test_nondust_htlc_fees_dust_exposure_delta(features: ChannelTypeFeatures) nodes[1].node.update_partial_channel_config(&node_a_id, &[chan_id], &update).unwrap(); // Send an additional non-dust htlc from 1 to 0 using the pre-calculated route above, and check the immediate complaint - let onion = RecipientOnionFields::secret_only(payment_secret_1_0); + let onion = RecipientOnionFields::secret_only(payment_secret_1_0, NON_DUST_HTLC_MSAT); let id = PaymentId(payment_hash_1_0.0); let res = nodes[1].node.send_payment_with_route(route_1_0, payment_hash_1_0, onion, id); unwrap_send_err!(nodes[1], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -9369,7 +9376,7 @@ fn do_payment_with_custom_min_final_cltv_expiry(valid_delta: bool, use_user_hash (hash, nodes[1].node.get_payment_preimage(hash, payment_secret).unwrap(), payment_secret) }; let route = get_route!(nodes[0], payment_parameters, recv_value).unwrap(); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, recv_value); nodes[0].node.send_payment_with_route(route, hash, onion, PaymentId(hash.0)).unwrap(); check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); @@ -9846,7 +9853,7 @@ fn do_test_multi_post_event_actions(do_reload: bool) { let (route, payment_hash_3, _, payment_secret_3) = get_route_and_payment_hash!(nodes[1], nodes[0], 100_000); let payment_id = PaymentId(payment_hash_3.0); - let onion = RecipientOnionFields::secret_only(payment_secret_3); + let onion = RecipientOnionFields::secret_only(payment_secret_3, 100_000); nodes[1].node.send_payment_with_route(route, payment_hash_3, onion, payment_id).unwrap(); check_added_monitors(&nodes[1], 1); @@ -9957,7 +9964,7 @@ pub fn test_dust_exposure_holding_cell_assertion() { // messages (leaving B waiting on C's RAA) the next HTLC will go into B's holding cell. let (route_bc, payment_hash_bc, _payment_preimage_bc, payment_secret_bc) = get_route_and_payment_hash!(nodes[1], nodes[2], DUST_HTLC_VALUE_MSAT); - let onion_bc = RecipientOnionFields::secret_only(payment_secret_bc); + let onion_bc = RecipientOnionFields::secret_only(payment_secret_bc, DUST_HTLC_VALUE_MSAT); let id = PaymentId(payment_hash_bc.0); nodes[1].node.send_payment_with_route(route_bc, payment_hash_bc, onion_bc, id).unwrap(); check_added_monitors(&nodes[1], 1); @@ -9977,7 +9984,7 @@ pub fn test_dust_exposure_holding_cell_assertion() { .unwrap(); let (route_ac, payment_hash_cell, _, payment_secret_ac) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params_ac, DUST_HTLC_VALUE_MSAT); - let onion_ac = RecipientOnionFields::secret_only(payment_secret_ac); + let onion_ac = RecipientOnionFields::secret_only(payment_secret_ac, DUST_HTLC_VALUE_MSAT); let id = PaymentId(payment_hash_cell.0); nodes[0].node.send_payment_with_route(route_ac, payment_hash_cell, onion_ac, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -10000,7 +10007,7 @@ pub fn test_dust_exposure_holding_cell_assertion() { // its holding cell as it would be over-exposed to dust. let (route_cb, payment_hash_cb, payment_preimage_cb, payment_secret_cb) = get_route_and_payment_hash!(nodes[2], nodes[1], DUST_HTLC_VALUE_MSAT); - let onion_cb = RecipientOnionFields::secret_only(payment_secret_cb); + let onion_cb = RecipientOnionFields::secret_only(payment_secret_cb, DUST_HTLC_VALUE_MSAT); let id = PaymentId(payment_hash_cb.0); nodes[2].node.send_payment_with_route(route_cb, payment_hash_cb, onion_cb, id).unwrap(); check_added_monitors(&nodes[2], 1); diff --git a/lightning/src/ln/htlc_reserve_unit_tests.rs b/lightning/src/ln/htlc_reserve_unit_tests.rs index 4c4fbada7dd..5a8b7eb522e 100644 --- a/lightning/src/ln/htlc_reserve_unit_tests.rs +++ b/lightning/src/ln/htlc_reserve_unit_tests.rs @@ -170,7 +170,7 @@ pub fn test_channel_reserve_holding_cell_htlcs() { route.paths[0].hops.last_mut().unwrap().fee_msat += 1; assert!(route.paths[0].hops.iter().rev().skip(1).all(|h| h.fee_msat == feemsat)); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, route.get_total_amount()); let id = PaymentId(our_payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -246,7 +246,7 @@ pub fn test_channel_reserve_holding_cell_htlcs() { get_route_and_payment_hash!(nodes[0], nodes[2], recv_value_1); let payment_event_1 = { let route = route_1.clone(); - let onion = RecipientOnionFields::secret_only(our_payment_secret_1); + let onion = RecipientOnionFields::secret_only(our_payment_secret_1, recv_value_1); let id = PaymentId(our_payment_hash_1.0); nodes[0].node.send_payment_with_route(route, our_payment_hash_1, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -267,7 +267,7 @@ pub fn test_channel_reserve_holding_cell_htlcs() { let mut route = route_1.clone(); route.paths[0].hops.last_mut().unwrap().fee_msat = recv_value_2 + 1; let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[2]); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, route.get_total_amount()); let id = PaymentId(our_payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -295,7 +295,7 @@ pub fn test_channel_reserve_holding_cell_htlcs() { let (route_21, our_payment_hash_21, our_payment_preimage_21, our_payment_secret_21) = get_route_and_payment_hash!(nodes[0], nodes[2], recv_value_21); // but this will stuck in the holding cell - let onion = RecipientOnionFields::secret_only(our_payment_secret_21); + let onion = RecipientOnionFields::secret_only(our_payment_secret_21, recv_value_21); let id = PaymentId(our_payment_hash_21.0); nodes[0].node.send_payment_with_route(route_21, our_payment_hash_21, onion, id).unwrap(); check_added_monitors(&nodes[0], 0); @@ -307,7 +307,7 @@ pub fn test_channel_reserve_holding_cell_htlcs() { let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], recv_value_22); route.paths[0].hops.last_mut().unwrap().fee_msat += 1; - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, route.get_total_amount()); let id = PaymentId(our_payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -317,7 +317,7 @@ pub fn test_channel_reserve_holding_cell_htlcs() { let (route_22, our_payment_hash_22, our_payment_preimage_22, our_payment_secret_22) = get_route_and_payment_hash!(nodes[0], nodes[2], recv_value_22); // this will also stuck in the holding cell - let onion = RecipientOnionFields::secret_only(our_payment_secret_22); + let onion = RecipientOnionFields::secret_only(our_payment_secret_22, recv_value_22); let id = PaymentId(our_payment_hash_22.0); nodes[0].node.send_payment_with_route(route_22, our_payment_hash_22, onion, id).unwrap(); check_added_monitors(&nodes[0], 0); @@ -493,7 +493,7 @@ pub fn channel_reserve_in_flight_removes() { let (route, payment_hash_3, payment_preimage_3, payment_secret_3) = get_route_and_payment_hash!(nodes[0], nodes[1], 100000); let send_1 = { - let onion = RecipientOnionFields::secret_only(payment_secret_3); + let onion = RecipientOnionFields::secret_only(payment_secret_3, 100000); let id = PaymentId(payment_hash_3.0); nodes[0].node.send_payment_with_route(route, payment_hash_3, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -570,7 +570,7 @@ pub fn channel_reserve_in_flight_removes() { let (route, payment_hash_4, payment_preimage_4, payment_secret_4) = get_route_and_payment_hash!(nodes[1], nodes[0], 10000); let send_2 = { - let onion = RecipientOnionFields::secret_only(payment_secret_4); + let onion = RecipientOnionFields::secret_only(payment_secret_4, 10000); let id = PaymentId(payment_hash_4.0); nodes[1].node.send_payment_with_route(route, payment_hash_4, onion, id).unwrap(); check_added_monitors(&nodes[1], 1); @@ -637,7 +637,7 @@ pub fn holding_cell_htlc_counting() { for _ in 0..50 { let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[2], 100000); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 100000); let id = PaymentId(payment_hash.0); nodes[1].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); payments.push((payment_preimage, payment_hash)); @@ -653,7 +653,7 @@ pub fn holding_cell_htlc_counting() { // the holding cell waiting on B's RAA to send. At this point we should not be able to add // another HTLC. { - let onion = RecipientOnionFields::secret_only(payment_secret_1); + let onion = RecipientOnionFields::secret_only(payment_secret_1, 100000); let id = PaymentId(payment_hash_1.0); let res = nodes[1].node.send_payment_with_route(route, payment_hash_1, onion, id); unwrap_send_err!(nodes[1], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -663,7 +663,7 @@ pub fn holding_cell_htlc_counting() { // This should also be true if we try to forward a payment. let (route, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], 100000); - let onion = RecipientOnionFields::secret_only(payment_secret_2); + let onion = RecipientOnionFields::secret_only(payment_secret_2, 100000); let id = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route, payment_hash_2, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -767,7 +767,7 @@ pub fn test_basic_channel_reserve() { let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], max_can_send); route.paths[0].hops.last_mut().unwrap().fee_msat += 1; - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, max_can_send + 1); let id = PaymentId(our_payment_hash.0); let err = nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[0], err, true, APIError::ChannelUnavailable { .. }, {}); @@ -815,7 +815,8 @@ pub fn do_test_fee_spike_buffer(cfg: Option, htlc_fails: bool) { let payment_amt_msat = 3460001; let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv); - let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion_fields = + RecipientOnionFields::secret_only(payment_secret, payment_amt_msat); let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( &route.paths[0], payment_amt_msat, @@ -1014,7 +1015,7 @@ pub fn test_chan_reserve_violation_outbound_htlc_inbound_chan() { } // However one more HTLC should be significantly over the reserve amount and fail. - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 1_000_000); let id = PaymentId(our_payment_hash.0); let res = nodes[1].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[1], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -1059,7 +1060,7 @@ pub fn test_chan_reserve_violation_inbound_htlc_outbound_channel() { let session_priv = SecretKey::from_slice(&[42; 32]).unwrap(); let cur_height = nodes[1].node.best_block.read().unwrap().height + 1; let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv); - let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, 700_000); let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( &route.paths[0], 700_000, @@ -1142,7 +1143,7 @@ pub fn test_chan_reserve_dust_inbound_htlcs_outbound_chan() { let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], dust_amt); route.paths[0].hops[0].fee_msat += 1; - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, dust_amt + 1); let id = PaymentId(our_payment_hash.0); let res = nodes[1].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[1], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -1211,7 +1212,7 @@ pub fn test_chan_reserve_violation_inbound_htlc_inbound_chan() { let (route_1, our_payment_hash_1, _, our_payment_secret_1) = get_route_and_payment_hash!(nodes[0], nodes[2], amt_msat_1); let payment_event_1 = { - let onion = RecipientOnionFields::secret_only(our_payment_secret_1); + let onion = RecipientOnionFields::secret_only(our_payment_secret_1, amt_msat_1); let id = PaymentId(our_payment_hash_1.0); let route = route_1.clone(); nodes[0].node.send_payment_with_route(route, our_payment_hash_1, onion, id).unwrap(); @@ -1240,7 +1241,7 @@ pub fn test_chan_reserve_violation_inbound_htlc_inbound_chan() { let session_priv = SecretKey::from_slice(&[42; 32]).unwrap(); let cur_height = nodes[0].node.best_block.read().unwrap().height + 1; let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route_2.paths[0], &session_priv); - let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(recv_value_2); let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( &route_2.paths[0], recv_value_2, @@ -1310,7 +1311,7 @@ pub fn test_payment_route_reaching_same_channel_twice() { route.paths[0].hops.extend_from_slice(&cloned_hops); unwrap_send_err!(nodes[0], nodes[0].node.send_payment_with_route(route, our_payment_hash, - RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0) + RecipientOnionFields::secret_only(our_payment_secret, 100000000), PaymentId(our_payment_hash.0) ), false, APIError::InvalidRoute { ref err }, assert_eq!(err, &"Path went through the same channel twice")); assert!(nodes[0].node.list_recent_payments().is_empty()); @@ -1334,7 +1335,7 @@ pub fn test_update_add_htlc_bolt2_sender_value_below_minimum_msat() { get_route_and_payment_hash!(nodes[0], nodes[1], 100000); route.paths[0].hops[0].fee_msat = 100; - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 100); let id = PaymentId(our_payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -1354,7 +1355,7 @@ pub fn test_update_add_htlc_bolt2_sender_zero_value_msat() { let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 100000); route.paths[0].hops[0].fee_msat = 0; - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 0); let id = PaymentId(our_payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[0], res, @@ -1384,7 +1385,7 @@ pub fn test_update_add_htlc_bolt2_receiver_zero_value_msat() { let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 100000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 100000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1425,7 +1426,7 @@ pub fn test_update_add_htlc_bolt2_sender_cltv_expiry_too_high() { get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, 100000000); route.paths[0].hops.last_mut().unwrap().cltv_expiry_delta = 500000001; - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 100000000); let id = PaymentId(our_payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::InvalidRoute { ref err }, @@ -1460,7 +1461,7 @@ pub fn test_update_add_htlc_bolt2_sender_exceed_max_htlc_num_and_htlc_id_increme let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 100000); let payment_event = { - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 100000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1486,7 +1487,7 @@ pub fn test_update_add_htlc_bolt2_sender_exceed_max_htlc_num_and_htlc_id_increme expect_and_process_pending_htlcs(&nodes[1], false); expect_payment_claimable!(nodes[1], our_payment_hash, our_payment_secret, 100000); } - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 100000); let id = PaymentId(our_payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -1514,7 +1515,7 @@ pub fn test_update_add_htlc_bolt2_sender_exceed_max_htlc_value_in_flight() { // Manually create a route over our max in flight (which our router normally automatically // limits us to. route.paths[0].hops[0].fee_msat = max_in_flight + 1; - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, max_in_flight + 1); let id = PaymentId(our_payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -1546,7 +1547,7 @@ pub fn test_update_add_htlc_bolt2_receiver_check_amount_received_more_than_min() let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], htlc_minimum_msat); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, htlc_minimum_msat); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1584,7 +1585,7 @@ pub fn test_update_add_htlc_bolt2_receiver_sender_can_afford_amount_sent() { let max_can_send = 5000000 - channel_reserve - commit_tx_fee_outbound; let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], max_can_send); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, max_can_send); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1628,7 +1629,7 @@ pub fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() { &route.paths[0], &session_priv, ); - let recipient_onion_fields = RecipientOnionFields::secret_only(our_payment_secret); + let recipient_onion_fields = RecipientOnionFields::secret_only(our_payment_secret, send_amt); let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( &route.paths[0], send_amt, @@ -1688,7 +1689,7 @@ pub fn test_update_add_htlc_bolt2_receiver_check_max_in_flight_msat() { let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 1000000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1722,7 +1723,7 @@ pub fn test_update_add_htlc_bolt2_receiver_check_cltv_expiry() { create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000); let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let reason = RecipientOnionFields::secret_only(our_payment_secret); + let reason = RecipientOnionFields::secret_only(our_payment_secret, 1000000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, reason, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1754,7 +1755,7 @@ pub fn test_update_add_htlc_bolt2_receiver_check_repeated_id_ignore() { create_announced_chan_between_nodes(&nodes, 0, 1); let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 1000000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); @@ -1819,7 +1820,7 @@ pub fn test_update_fulfill_htlc_bolt2_update_fulfill_htlc_before_commitment() { let chan = create_announced_chan_between_nodes(&nodes, 0, 1); let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 1000000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); @@ -1864,7 +1865,7 @@ pub fn test_update_fulfill_htlc_bolt2_update_fail_htlc_before_commitment() { let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 1000000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1908,7 +1909,7 @@ pub fn test_update_fulfill_htlc_bolt2_update_fail_malformed_htlc_before_commitme let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 1000000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -2071,7 +2072,7 @@ pub fn test_update_fulfill_htlc_bolt2_missing_badonion_bit_for_malformed_htlc_me let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 1000000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -2234,7 +2235,8 @@ pub fn do_test_dust_limit_fee_accounting(can_afford: bool) { let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route_0_1.paths[0], &session_priv); - let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret_0_1); + let recipient_onion_fields = + RecipientOnionFields::secret_only(payment_secret_0_1, HTLC_AMT_SAT * 1000); let (onion_payloads, amount_msat, cltv_expiry) = onion_utils::build_onion_payloads( &route_0_1.paths[0], HTLC_AMT_SAT * 1000, diff --git a/lightning/src/ln/interception_tests.rs b/lightning/src/ln/interception_tests.rs index c83ef177628..2f5692c2b16 100644 --- a/lightning/src/ln/interception_tests.rs +++ b/lightning/src/ln/interception_tests.rs @@ -123,7 +123,7 @@ fn do_test_htlc_interception_flags( None => {}, } - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let payment_id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap(); check_added_monitors(&nodes[0], 1); diff --git a/lightning/src/ln/invoice_utils.rs b/lightning/src/ln/invoice_utils.rs index 195eb73595a..b1097b6c274 100644 --- a/lightning/src/ln/invoice_utils.rs +++ b/lightning/src/ln/invoice_utils.rs @@ -1283,7 +1283,10 @@ mod test { let payment_hash = invoice.payment_hash(); let id = PaymentId(payment_hash.0); - let onion = RecipientOnionFields::secret_only(*invoice.payment_secret()); + let onion = RecipientOnionFields::secret_only( + *invoice.payment_secret(), + invoice.amount_milli_satoshis().unwrap(), + ); nodes[0].node.send_payment(payment_hash, onion, id, params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index b947273115e..ea78449316c 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -87,6 +87,7 @@ fn large_payment_metadata() { payment_secret: Some(payment_secret), payment_metadata: Some(payment_metadata.clone()), custom_tlvs: Vec::new(), + total_mpp_amount_msat: amt_msat, }; let route_params = route_0_1.route_params.clone().unwrap(); let id = PaymentId(payment_hash.0); @@ -128,6 +129,7 @@ fn large_payment_metadata() { // If our payment_metadata contains 1 additional byte, we'll fail prior to pathfinding. let mut too_large_onion = max_sized_onion.clone(); too_large_onion.payment_metadata.as_mut().map(|mut md| md.push(42)); + too_large_onion.total_mpp_amount_msat = MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY; // First confirm we'll fail to create the onion packet directly. let secp_ctx = Secp256k1::signing_only(); @@ -167,6 +169,7 @@ fn large_payment_metadata() { payment_secret: Some(payment_secret_2), payment_metadata: Some(two_hop_metadata.clone()), custom_tlvs: Vec::new(), + total_mpp_amount_msat: amt_msat, }; let mut route_params_0_2 = route_0_2.route_params.clone().unwrap(); route_params_0_2.payment_params.max_path_length = 2; @@ -261,7 +264,7 @@ fn one_hop_blinded_path_with_custom_tlv() { - final_payload_len_without_custom_tlv; // Check that we can send the maximum custom TLV with 1 blinded hop. - let max_sized_onion = RecipientOnionFields::spontaneous_empty().with_custom_tlvs( + let max_sized_onion = RecipientOnionFields::spontaneous_empty(amt_msat).with_custom_tlvs( RecipientCustomTlvs::new(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])]).unwrap(), ); let id = PaymentId(payment_hash.0); @@ -369,7 +372,7 @@ fn blinded_path_with_custom_tlv() { let reserved_packet_bytes_without_custom_tlv: usize = onion_utils::build_onion_payloads( &route.paths[0], MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, - &RecipientOnionFields::spontaneous_empty(), + &RecipientOnionFields::spontaneous_empty(MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY), nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &None, None, @@ -387,7 +390,7 @@ fn blinded_path_with_custom_tlv() { - reserved_packet_bytes_without_custom_tlv; // Check that we can send the maximum custom TLV size with 0 intermediate unblinded hops. - let max_sized_onion = RecipientOnionFields::spontaneous_empty().with_custom_tlvs( + let max_sized_onion = RecipientOnionFields::spontaneous_empty(amt_msat).with_custom_tlvs( RecipientCustomTlvs::new(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])]).unwrap(), ); let no_retry = Retry::Attempts(0); @@ -420,10 +423,12 @@ fn blinded_path_with_custom_tlv() { .unwrap_err(); assert_eq!(err, RetryableSendFailure::OnionPacketSizeExceeded); - // Confirm that we can't construct an onion packet given this too-large custom TLV. + // Confirm that we can't construct an onion packet given this too-large custom TLV (as long as + // we actually use the amount the payment logic uses when validating). let secp_ctx = Secp256k1::signing_only(); route.paths[0].hops[0].fee_msat = MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY; route.paths[0].hops[0].cltv_expiry_delta = DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA; + too_large_onion.total_mpp_amount_msat = MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY; let err = onion_utils::create_payment_onion( &secp_ctx, &route.paths[0], diff --git a/lightning/src/ln/monitor_tests.rs b/lightning/src/ln/monitor_tests.rs index 097266cf83f..686010bd228 100644 --- a/lightning/src/ln/monitor_tests.rs +++ b/lightning/src/ln/monitor_tests.rs @@ -68,7 +68,7 @@ fn chanmon_fail_from_stale_commitment() { let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000); nodes[0].node.send_payment_with_route(route, payment_hash, - RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap(); + RecipientOnionFields::secret_only(payment_secret, 1_000_000), PaymentId(payment_hash.0)).unwrap(); check_added_monitors(&nodes[0], 1); let bs_txn = get_local_commitment_txn!(nodes[1], chan_id_2); @@ -885,7 +885,7 @@ fn do_test_balances_on_local_commitment_htlcs(keyed_anchors: bool, p2a_anchor: b let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 10_000_000); let htlc_cltv_timeout = nodes[0].best_block_info().1 + TEST_FINAL_CLTV + 1; // Note ChannelManager adds one to CLTV timeouts for safety nodes[0].node.send_payment_with_route(route, payment_hash, - RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap(); + RecipientOnionFields::secret_only(payment_secret, 10_000_000), PaymentId(payment_hash.0)).unwrap(); check_added_monitors(&nodes[0], 1); let updates = get_htlc_update_msgs(&nodes[0], &nodes[1].node.get_our_node_id()); @@ -897,7 +897,7 @@ fn do_test_balances_on_local_commitment_htlcs(keyed_anchors: bool, p2a_anchor: b let (route_2, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[1], 20_000_000); nodes[0].node.send_payment_with_route(route_2, payment_hash_2, - RecipientOnionFields::secret_only(payment_secret_2), PaymentId(payment_hash_2.0)).unwrap(); + RecipientOnionFields::secret_only(payment_secret_2, 20_000_000), PaymentId(payment_hash_2.0)).unwrap(); check_added_monitors(&nodes[0], 1); let updates = get_htlc_update_msgs(&nodes[0], &nodes[1].node.get_our_node_id()); @@ -3643,7 +3643,7 @@ fn do_test_lost_timeout_monitor_events(confirm_tx: CommitmentType, dust_htlcs: b let (route, hash_b, _, payment_secret_b) = get_route_and_payment_hash!(nodes[1], nodes[2], amt); - let onion = RecipientOnionFields::secret_only(payment_secret_b); + let onion = RecipientOnionFields::secret_only(payment_secret_b, amt); nodes[1].node.send_payment_with_route(route, hash_b, onion, PaymentId(hash_b.0)).unwrap(); check_added_monitors(&nodes[1], 1); diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs index 12e631b4042..1c689686ccd 100644 --- a/lightning/src/ln/offers_tests.rs +++ b/lightning/src/ln/offers_tests.rs @@ -2463,7 +2463,7 @@ fn rejects_keysend_to_non_static_invoice_path() { let route_params = RouteParameters::from_payment_params_and_value(pay_params, amt_msat); let keysend_payment_id = PaymentId([2; 32]); let payment_hash = nodes[0].node.send_spontaneous_payment( - Some(payment_preimage), RecipientOnionFields::spontaneous_empty(), keysend_payment_id, + Some(payment_preimage), RecipientOnionFields::spontaneous_empty(amt_msat), keysend_payment_id, route_params, Retry::Attempts(0) ).unwrap(); check_added_monitors(&nodes[0], 1); diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index 555cc7a87af..d0d50c6a315 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -879,7 +879,7 @@ mod tests { let total_amt_msat = 1000; let cur_height = 1000; let pay_secret = PaymentSecret([99; 32]); - let recipient_onion = RecipientOnionFields::secret_only(pay_secret); + let recipient_onion = RecipientOnionFields::secret_only(pay_secret, total_amt_msat); let preimage_bytes = [43; 32]; let preimage = PaymentPreimage(preimage_bytes); let rhash_bytes = Sha256::hash(&preimage_bytes).to_byte_array(); diff --git a/lightning/src/ln/onion_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index 27e0cfafade..1ee0be2938b 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -128,7 +128,8 @@ fn run_onion_failure_test_with_fail_intercept( // 0 ~~> 2 send payment let payment_id = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes()); - let recipient_onion = RecipientOnionFields::secret_only(*payment_secret); + let recipient_onion = + RecipientOnionFields::secret_only(*payment_secret, route.get_total_amount()); nodes[0] .node .send_payment_with_route(route.clone(), *payment_hash, recipient_onion, payment_id) @@ -399,7 +400,7 @@ fn test_fee_failures() { // positive case let (route, payment_hash_success, payment_preimage_success, payment_secret_success) = get_route_and_payment_hash!(nodes[0], nodes[2], 40_000); - let recipient_onion = RecipientOnionFields::secret_only(payment_secret_success); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret_success, 40_000); let payment_id = PaymentId(payment_hash_success.0); nodes[0] .node @@ -450,7 +451,7 @@ fn test_fee_failures() { let (payment_preimage_success, payment_hash_success, payment_secret_success) = get_payment_preimage_hash!(nodes[2]); - let recipient_onion = RecipientOnionFields::secret_only(payment_secret_success); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret_success, 40_000); let payment_id = PaymentId(payment_hash_success.0); nodes[0] .node @@ -523,7 +524,7 @@ fn test_onion_failure() { let cur_height = nodes[0].best_block_info().1 + 1; let onion_keys = construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv); - let recipient_fields = RecipientOnionFields::spontaneous_empty(); + let recipient_fields = RecipientOnionFields::spontaneous_empty(40000); let path = &route.paths[0]; let (mut onion_payloads, _htlc_msat, _htlc_cltv) = build_onion_payloads(path, 40000, &recipient_fields, cur_height, &None, None, None) @@ -565,7 +566,7 @@ fn test_onion_failure() { let cur_height = nodes[0].best_block_info().1 + 1; let onion_keys = construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv); - let recipient_fields = RecipientOnionFields::spontaneous_empty(); + let recipient_fields = RecipientOnionFields::spontaneous_empty(40000); let path = &route.paths[0]; let (mut onion_payloads, _htlc_msat, _htlc_cltv) = build_onion_payloads(path, 40000, &recipient_fields, cur_height, &None, None, None) @@ -1284,7 +1285,7 @@ fn test_onion_failure() { CLTV_FAR_FAR_AWAY + route.paths[0].hops[0].cltv_expiry_delta + 1; let onion_keys = construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv); - let recipient_fields = RecipientOnionFields::spontaneous_empty(); + let recipient_fields = RecipientOnionFields::spontaneous_empty(40000); let path = &route.paths[0]; let (onion_payloads, _, htlc_cltv) = build_onion_payloads(path, 40000, &recipient_fields, height, &None, None, None) @@ -1542,7 +1543,7 @@ fn test_overshoot_final_cltv() { get_route_and_payment_hash!(nodes[0], nodes[2], 40000); let payment_id = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes()); - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret, 40000); nodes[0] .node .send_payment_with_route(route, payment_hash, recipient_onion, payment_id) @@ -1837,7 +1838,7 @@ fn test_always_create_tlv_format_onion_payloads() { assert!(!hops[1].node_features.supports_variable_length_onion()); let cur_height = nodes[0].best_block_info().1 + 1; - let recipient_fields = RecipientOnionFields::spontaneous_empty(); + let recipient_fields = RecipientOnionFields::spontaneous_empty(40000); let path = &route.paths[0]; let (onion_payloads, _htlc_msat, _htlc_cltv) = build_onion_payloads(path, 40000, &recipient_fields, cur_height, &None, None, None) @@ -1973,7 +1974,7 @@ fn test_trampoline_onion_payload_assembly_values() { let payment_secret = PaymentSecret( SecretKey::from_slice(&>::from_hex(SECRET_HEX).unwrap()).unwrap().secret_bytes(), ); - let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, amt_msat); let (trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = onion_utils::build_trampoline_onion_payloads( &path.blinded_tail.as_ref().unwrap(), @@ -2038,6 +2039,8 @@ fn test_trampoline_onion_payload_assembly_values() { ) .unwrap(); + let recipient_onion_fields = + RecipientOnionFields::secret_only(payment_secret, outer_total_msat); let (outer_payloads, total_msat, total_htlc_offset) = build_onion_payloads( &path, outer_total_msat, @@ -2072,6 +2075,7 @@ fn test_trampoline_onion_payload_assembly_values() { panic!("Bob payload must be Forward"); } + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, amt_msat); let (_, total_msat_combined, total_htlc_offset_combined) = onion_utils::create_payment_onion( &Secp256k1::new(), &path, @@ -2280,7 +2284,7 @@ fn do_test_fail_htlc_backwards_with_reason(failure_code: FailureCode) { let payment_amount = 100_000; let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_amount); - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret, payment_amount); nodes[0] .node .send_payment_with_route(route, payment_hash, recipient_onion, PaymentId(payment_hash.0)) @@ -2430,7 +2434,7 @@ fn test_phantom_onion_hmac_failure() { let (route, phantom_scid) = get_phantom_route!(nodes, recv_value_msat, channel); // Route the HTLC through to the destination. - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret, recv_value_msat); nodes[0] .node .send_payment_with_route(route, payment_hash, recipient_onion, PaymentId(payment_hash.0)) @@ -2502,7 +2506,7 @@ fn test_phantom_invalid_onion_payload() { // We'll use the session priv later when constructing an invalid onion packet. let session_priv = [3; 32]; *nodes[0].keys_manager.override_random_bytes.lock().unwrap() = Some(session_priv); - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret, recv_value_msat); let payment_id = PaymentId(payment_hash.0); nodes[0] .node @@ -2534,7 +2538,8 @@ fn test_phantom_invalid_onion_payload() { let session_priv = SecretKey::from_slice(&session_priv).unwrap(); let mut onion_keys = construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv); - let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion_fields = + RecipientOnionFields::secret_only(payment_secret, msgs::MAX_VALUE_MSAT + 1); let (mut onion_payloads, _, _) = build_onion_payloads( &route.paths[0], msgs::MAX_VALUE_MSAT + 1, @@ -2602,7 +2607,7 @@ fn test_phantom_final_incorrect_cltv_expiry() { let (route, phantom_scid) = get_phantom_route!(nodes, recv_value_msat, channel); // Route the HTLC through to the destination. - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret, recv_value_msat); nodes[0] .node .send_payment_with_route(route, payment_hash, recipient_onion, PaymentId(payment_hash.0)) @@ -2671,7 +2676,7 @@ fn test_phantom_failure_too_low_cltv() { route.paths[0].hops[1].cltv_expiry_delta = 5; // Route the HTLC through to the destination. - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret, recv_value_msat); nodes[0] .node .send_payment_with_route(route, payment_hash, recipient_onion, PaymentId(payment_hash.0)) @@ -2724,7 +2729,7 @@ fn test_phantom_failure_modified_cltv() { let (mut route, phantom_scid) = get_phantom_route!(nodes, recv_value_msat, channel); // Route the HTLC through to the destination. - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret, recv_value_msat); nodes[0] .node .send_payment_with_route(route, payment_hash, recipient_onion, PaymentId(payment_hash.0)) @@ -2779,7 +2784,7 @@ fn test_phantom_failure_expires_too_soon() { let (mut route, phantom_scid) = get_phantom_route!(nodes, recv_value_msat, channel); // Route the HTLC through to the destination. - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret, recv_value_msat); nodes[0] .node .send_payment_with_route(route, payment_hash, recipient_onion, PaymentId(payment_hash.0)) @@ -2829,7 +2834,8 @@ fn test_phantom_failure_too_low_recv_amt() { let (mut route, phantom_scid) = get_phantom_route!(nodes, bad_recv_amt_msat, channel); // Route the HTLC through to the destination. - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = + RecipientOnionFields::secret_only(payment_secret, route.get_total_amount()); nodes[0] .node .send_payment_with_route(route, payment_hash, recipient_onion, PaymentId(payment_hash.0)) @@ -2898,7 +2904,7 @@ fn do_test_phantom_dust_exposure_failure(multiplier_dust_limit: bool) { let (mut route, phantom_scid) = get_phantom_route!(nodes, max_dust_exposure + 1, channel); // Route the HTLC through to the destination. - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret, max_dust_exposure + 1); let payment_id = PaymentId(payment_hash.0); nodes[0] .node @@ -2948,7 +2954,7 @@ fn test_phantom_failure_reject_payment() { let (mut route, phantom_scid) = get_phantom_route!(nodes, recv_amt_msat, channel); // Route the HTLC through to the destination. - let recipient_onion = RecipientOnionFields::secret_only(payment_secret); + let recipient_onion = RecipientOnionFields::secret_only(payment_secret, recv_amt_msat); let payment_id = PaymentId(payment_hash.0); nodes[0] .node diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index d48fcb25179..b5c5088eecb 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -219,6 +219,7 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundOnionPayload<'a> { recipient_onion: &'a RecipientOnionFields, keysend_preimage: Option, sender_intended_htlc_amt_msat: u64, total_msat: u64, cltv_expiry_height: u32, ) -> Result { + debug_assert_eq!(total_msat, recipient_onion.total_mpp_amount_msat); Ok(Self::Receive { payment_data: recipient_onion .payment_secret @@ -257,6 +258,7 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundOnionPayload<'a> { total_msat: u64, amt_to_forward: u64, outgoing_cltv_value: u32, recipient_onion: &'a RecipientOnionFields, packet: msgs::TrampolineOnionPacket, ) -> Result { + debug_assert_eq!(total_msat, recipient_onion.total_mpp_amount_msat); Ok(Self::TrampolineEntrypoint { amt_to_forward, outgoing_cltv_value, @@ -443,6 +445,8 @@ pub(super) fn build_onion_payloads<'a>( invoice_request: Option<&'a InvoiceRequest>, trampoline_packet: Option, ) -> Result<(Vec>, u64, u32), APIError> { + debug_assert_eq!(total_msat, recipient_onion.total_mpp_amount_msat); + let mut res: Vec = Vec::with_capacity( path.hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len()), ); @@ -514,6 +518,8 @@ where let mut cur_cltv = starting_htlc_offset; let mut last_hop_id = None; + debug_assert_eq!(total_msat, recipient_onion.total_mpp_amount_msat); + for (idx, hop) in hops.rev().enumerate() { // First hop gets special values so that it can check, on receipt, that everything is // exactly as it should be (and the next hop isn't trying to probe to find out if we're @@ -661,11 +667,15 @@ pub(crate) fn set_max_path_length( maybe_announced_channel: false, }; let mut num_reserved_bytes: usize = 0; + // TODO: Find a way to avoid `clone`ing the whole recipient onion without re-adding the + // explicit amount parameter to build_onion_payloads_callback. + let mut recipient_onion_with_excess_value = recipient_onion.clone(); + recipient_onion_with_excess_value.total_mpp_amount_msat = final_value_msat_with_overpay_buffer; let build_payloads_res = build_onion_payloads_callback( core::iter::once(&unblinded_route_hop), blinded_tail_opt, final_value_msat_with_overpay_buffer, - &recipient_onion, + &recipient_onion_with_excess_value, best_block_height, &keysend_preimage, invoice_request, @@ -2623,10 +2633,19 @@ pub(crate) fn create_payment_onion_internal( prng_seed: [u8; 32], trampoline_session_priv_override: Option, trampoline_prng_seed_override: Option<[u8; 32]>, ) -> Result<(msgs::OnionPacket, u64, u32), APIError> { + debug_assert_eq!(total_msat, recipient_onion.total_mpp_amount_msat); + let mut outer_total_msat = total_msat; let mut outer_starting_htlc_offset = cur_block_height; let mut trampoline_packet_option = None; + let mut trampoline_outer_onion = RecipientOnionFields { + payment_secret: recipient_onion.payment_secret, + total_mpp_amount_msat: recipient_onion.total_mpp_amount_msat, + payment_metadata: None, + custom_tlvs: Vec::new(), + }; + let mut outer_onion = recipient_onion; if let Some(blinded_tail) = &path.blinded_tail { if !blinded_tail.trampoline_hops.is_empty() { let trampoline_payloads; @@ -2638,6 +2657,7 @@ pub(crate) fn create_payment_onion_internal( cur_block_height, keysend_preimage, )?; + trampoline_outer_onion.total_mpp_amount_msat = outer_total_msat; let trampoline_session_priv = trampoline_session_priv_override .unwrap_or_else(|| compute_trampoline_session_priv(session_priv)); @@ -2657,13 +2677,14 @@ pub(crate) fn create_payment_onion_internal( })?; trampoline_packet_option = Some(trampoline_packet); + outer_onion = &trampoline_outer_onion; } } let (onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads( &path, outer_total_msat, - recipient_onion, + outer_onion, outer_starting_htlc_offset, keysend_preimage, invoice_request, @@ -4029,7 +4050,7 @@ mod tests { max_total_routing_fee_msat: Some(u64::MAX), }; route_params.payment_params.max_total_cltv_expiry_delta = u32::MAX; - let recipient_onion = RecipientOnionFields::spontaneous_empty(); + let recipient_onion = RecipientOnionFields::spontaneous_empty(u64::MAX); set_max_path_length(&mut route_params, &recipient_onion, None, None, 42).unwrap(); } diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 3380e4f0120..6a72601d4a0 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -21,6 +21,7 @@ use crate::ln::channelmanager::{ EventCompletionAction, HTLCSource, OptionalBolt11PaymentParams, PaymentCompleteUpdate, PaymentId, }; +use crate::ln::msgs::DecodeError; use crate::ln::onion_utils; use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason}; use crate::offers::invoice::{Bolt12Invoice, DerivedSigningPubkey, InvoiceBuilder}; @@ -44,8 +45,10 @@ use core::fmt::{self, Display, Formatter}; use core::sync::atomic::{AtomicBool, Ordering}; use core::time::Duration; +use crate::io; use crate::prelude::*; use crate::sync::Mutex; +use crate::util::ser; /// The number of ticks of [`ChannelManager::timer_tick_occurred`] until we time-out the idempotency /// of payments by [`PaymentId`]. See [`OutboundPayments::remove_stale_payments`]. @@ -758,21 +761,60 @@ pub struct RecipientOnionFields { pub payment_metadata: Option>, /// See [`Self::custom_tlvs`] for more info. pub(super) custom_tlvs: Vec<(u64, Vec)>, + /// The total payment amount which is being sent. + /// + /// This is communicated to the recipient as an indication that they should delay claiming the + /// payment until they've received multiple payment parts totaling at least this amount. + /// + /// Note that in order to properly communicate this, the recipient must either be paid using + /// blinded paths or a [`Self::payment_secret`] must be set. + pub total_mpp_amount_msat: u64, } -impl_writeable_tlv_based!(RecipientOnionFields, { - (0, payment_secret, option), - (1, custom_tlvs, optional_vec), - (2, payment_metadata, option), -}); +impl ser::Writeable for RecipientOnionFields { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { + write_tlv_fields!(writer, { + (0, self.payment_secret, option), + (1, self.custom_tlvs, optional_vec), + (2, self.payment_metadata, option), + (3, self.total_mpp_amount_msat, required), + }); + Ok(()) + } +} + +impl ser::ReadableArgs for RecipientOnionFields { + fn read( + reader: &mut R, default_total_mpp_amount_msat: u64, + ) -> Result { + _init_and_read_len_prefixed_tlv_fields!(reader, { + (0, payment_secret, option), + (1, custom_tlvs, optional_vec), + (2, payment_metadata, option), + // Added and always written in LDK 0.3 + (3, total_mpp_amount_msat, option), + }); + Ok(Self { + payment_secret, + custom_tlvs: custom_tlvs.unwrap_or(Vec::new()), + payment_metadata, + total_mpp_amount_msat: total_mpp_amount_msat.unwrap_or(default_total_mpp_amount_msat), + }) + } +} impl RecipientOnionFields { /// Creates a [`RecipientOnionFields`] from only a [`PaymentSecret`]. This is the most common /// set of onion fields for today's BOLT11 invoices - most nodes require a [`PaymentSecret`] /// but do not require or provide any further data. #[rustfmt::skip] - pub fn secret_only(payment_secret: PaymentSecret) -> Self { - Self { payment_secret: Some(payment_secret), payment_metadata: None, custom_tlvs: Vec::new() } + pub fn secret_only(payment_secret: PaymentSecret, total_mpp_amount_msat: u64) -> Self { + Self { + payment_secret: Some(payment_secret), + payment_metadata: None, + custom_tlvs: Vec::new(), + total_mpp_amount_msat, + } } /// Creates a new [`RecipientOnionFields`] with no fields. This generally does not create @@ -783,8 +825,13 @@ impl RecipientOnionFields { /// /// [`ChannelManager::send_spontaneous_payment`]: super::channelmanager::ChannelManager::send_spontaneous_payment /// [`RecipientOnionFields::secret_only`]: RecipientOnionFields::secret_only - pub fn spontaneous_empty() -> Self { - Self { payment_secret: None, payment_metadata: None, custom_tlvs: Vec::new() } + pub fn spontaneous_empty(total_mpp_amount_msat: u64) -> Self { + Self { + payment_secret: None, + payment_metadata: None, + custom_tlvs: Vec::new(), + total_mpp_amount_msat, + } } /// Creates a new [`RecipientOnionFields`] from an existing one, adding validated custom TLVs. @@ -837,6 +884,9 @@ impl RecipientOnionFields { pub(super) fn check_merge(&mut self, further_htlc_fields: &mut Self) -> Result<(), ()> { if self.payment_secret != further_htlc_fields.payment_secret { return Err(()); } if self.payment_metadata != further_htlc_fields.payment_metadata { return Err(()); } + if self.total_mpp_amount_msat != further_htlc_fields.total_mpp_amount_msat { + return Err(()); + } let tlvs = &mut self.custom_tlvs; let further_tlvs = &mut further_htlc_fields.custom_tlvs; @@ -984,8 +1034,9 @@ impl OutboundPayments { (None, None) => return Err(Bolt11PaymentError::InvalidAmount), }; - let mut recipient_onion = RecipientOnionFields::secret_only(*invoice.payment_secret()) - .with_custom_tlvs(optional_params.custom_tlvs); + let mut recipient_onion = + RecipientOnionFields::secret_only(*invoice.payment_secret(), amount) + .with_custom_tlvs(optional_params.custom_tlvs); recipient_onion.payment_metadata = invoice.payment_metadata().map(|v| v.clone()); let payment_params = PaymentParameters::from_bolt11_invoice(invoice) @@ -1084,6 +1135,7 @@ impl OutboundPayments { payment_secret: None, payment_metadata: None, custom_tlvs: vec![], + total_mpp_amount_msat: route_params.final_value_msat, }; let route = match self.find_initial_route( payment_id, payment_hash, &recipient_onion, keysend_preimage, invoice_request, @@ -1224,7 +1276,7 @@ impl OutboundPayments { if let Err(()) = onion_utils::set_max_path_length( &mut route_params, - &RecipientOnionFields::spontaneous_empty(), + &RecipientOnionFields::spontaneous_empty(amount_msat), Some(keysend_preimage), Some(invreq), best_block_height, @@ -1620,6 +1672,7 @@ impl OutboundPayments { payment_secret: *payment_secret, payment_metadata: payment_metadata.clone(), custom_tlvs: custom_tlvs.clone(), + total_mpp_amount_msat: total_msat, }; let keysend_preimage = *keysend_preimage; let invoice_request = invoice_request.clone(); @@ -1824,15 +1877,16 @@ impl OutboundPayments { } let route = Route { paths: vec![path], route_params: None }; + let recipient_onion_fields = + RecipientOnionFields::secret_only(payment_secret, route.get_total_amount()); let onion_session_privs = self.add_new_pending_payment(payment_hash, - RecipientOnionFields::secret_only(payment_secret), payment_id, None, &route, None, None, + recipient_onion_fields.clone(), payment_id, None, &route, None, None, entropy_source, best_block_height, None ).map_err(|e| { debug_assert!(matches!(e, PaymentSendFailure::DuplicatePayment)); ProbeSendFailure::DuplicateProbe })?; - let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields, None, None, None, payment_id, None, &onion_session_privs, false, node_signer, best_block_height, &send_payment_along_path @@ -2847,7 +2901,7 @@ mod tests { #[test] #[rustfmt::skip] fn test_recipient_onion_fields_with_custom_tlvs() { - let onion_fields = RecipientOnionFields::spontaneous_empty(); + let onion_fields = RecipientOnionFields::spontaneous_empty(42); let bad_type_range_tlvs = RecipientCustomTlvs::new(vec![ (0, vec![42]), @@ -2895,7 +2949,7 @@ mod tests { let expired_route_params = RouteParameters::from_payment_params_and_value(payment_params, 0); let pending_events = Mutex::new(VecDeque::new()); if on_retry { - outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), + outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(0), PaymentId([0; 32]), None, &Route { paths: vec![], route_params: None }, Some(Retry::Attempts(1)), Some(expired_route_params.payment_params.clone()), &&keys_manager, 0, None).unwrap(); @@ -2910,7 +2964,7 @@ mod tests { } else { panic!("Unexpected event"); } } else { let err = outbound_payments.send_payment( - PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]), + PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(0), PaymentId([0; 32]), Retry::Attempts(0), expired_route_params, &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &pending_events, |_| Ok(()), &log).unwrap_err(); if let RetryableSendFailure::PaymentExpired = err { } else { panic!("Unexpected error"); } @@ -2941,7 +2995,7 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); if on_retry { - outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), + outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(0), PaymentId([0; 32]), None, &Route { paths: vec![], route_params: None }, Some(Retry::Attempts(1)), Some(route_params.payment_params.clone()), &&keys_manager, 0, None).unwrap(); @@ -2954,7 +3008,7 @@ mod tests { if let Event::PaymentFailed { .. } = events[0].0 { } else { panic!("Unexpected event"); } } else { let err = outbound_payments.send_payment( - PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]), + PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(0), PaymentId([0; 32]), Retry::Attempts(0), route_params, &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &pending_events, |_| Ok(()), &log).unwrap_err(); if let RetryableSendFailure::RouteNotFound = err { @@ -3005,7 +3059,7 @@ mod tests { // PaymentPathFailed event. let pending_events = Mutex::new(VecDeque::new()); outbound_payments.send_payment( - PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]), + PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(1), PaymentId([0; 32]), Retry::Attempts(0), route_params.clone(), &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &pending_events, |_| Err(APIError::ChannelUnavailable { err: "test".to_owned() }), &log).unwrap(); @@ -3023,7 +3077,7 @@ mod tests { // Ensure that a MonitorUpdateInProgress "error" will not result in a PaymentPathFailed event. outbound_payments.send_payment( - PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]), + PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(1), PaymentId([0; 32]), Retry::Attempts(0), route_params.clone(), &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &pending_events, |_| Err(APIError::MonitorUpdateInProgress), &log).unwrap(); @@ -3031,7 +3085,7 @@ mod tests { // Ensure that any other error will result in a PaymentPathFailed event but no blamed scid. outbound_payments.send_payment( - PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([1; 32]), + PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(1), PaymentId([1; 32]), Retry::Attempts(0), route_params.clone(), &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &pending_events, |_| Err(APIError::APIMisuseError { err: "test".to_owned() }), &log).unwrap(); diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index e6c3c6eb121..a4eaa1653ca 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -146,7 +146,7 @@ fn mpp_retry() { let mut route_params = route.route_params.clone().unwrap(); nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); - let onion = RecipientOnionFields::secret_only(pay_secret); + let onion = RecipientOnionFields::secret_only(pay_secret, amt_msat * 2); let retry = Retry::Attempts(1); nodes[0].node.send_payment(hash, onion, id, route_params.clone(), retry).unwrap(); check_added_monitors(&nodes[0], 2); // one monitor per path @@ -264,7 +264,7 @@ fn mpp_retry_overpay() { let mut route_params = route.route_params.clone().unwrap(); nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); - let onion = RecipientOnionFields::secret_only(pay_secret); + let onion = RecipientOnionFields::secret_only(pay_secret, amt_msat); let retry = Retry::Attempts(1); nodes[0].node.send_payment(hash, onion, id, route_params.clone(), retry).unwrap(); check_added_monitors(&nodes[0], 2); // one monitor per path @@ -366,7 +366,7 @@ fn do_mpp_receive_timeout(send_partial_mpp: bool) { route.route_params.as_mut().unwrap().final_value_msat *= 2; // Initiate the MPP payment. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 200_000); nodes[0].node.send_payment_with_route(route, hash, onion, PaymentId(hash.0)).unwrap(); check_added_monitors(&nodes[0], 2); // one monitor per path let mut events = nodes[0].node.get_and_clear_pending_msg_events(); @@ -461,7 +461,7 @@ fn do_test_keysend_payments(public_node: bool) { { let preimage = Some(PaymentPreimage([42; 32])); - let onion = RecipientOnionFields::spontaneous_empty(); + let onion = RecipientOnionFields::spontaneous_empty(10000); let retry = Retry::Attempts(1); let id = PaymentId([42; 32]); nodes[0].node.send_spontaneous_payment(preimage, onion, id, route_params, retry).unwrap(); @@ -511,7 +511,7 @@ fn test_mpp_keysend() { let preimage = Some(PaymentPreimage([42; 32])); let payment_secret = PaymentSecret([42; 32]); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, recv_value); let retry = Retry::Attempts(0); let id = PaymentId([42; 32]); let hash = @@ -554,7 +554,7 @@ fn test_fulfill_hold_times() { let preimage = Some(PaymentPreimage([42; 32])); let payment_secret = PaymentSecret([42; 32]); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, recv_value); let retry = Retry::Attempts(0); let id = PaymentId([42; 32]); let hash = @@ -624,7 +624,7 @@ fn test_reject_mpp_keysend_htlc_mismatching_secret() { let payment_id_0 = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes()); nodes[0].router.expect_find_route(route.route_params.clone().unwrap(), Ok(route.clone())); let params = route.route_params.clone().unwrap(); - let onion = RecipientOnionFields::spontaneous_empty(); + let onion = RecipientOnionFields::spontaneous_empty(amount); let retry = Retry::Attempts(0); nodes[0].node.send_spontaneous_payment(preimage, onion, payment_id_0, params, retry).unwrap(); check_added_monitors(&nodes[0], 1); @@ -672,7 +672,7 @@ fn test_reject_mpp_keysend_htlc_mismatching_secret() { let payment_id_1 = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes()); nodes[0].router.expect_find_route(route.route_params.clone().unwrap(), Ok(route.clone())); - let onion = RecipientOnionFields::spontaneous_empty(); + let onion = RecipientOnionFields::spontaneous_empty(amount); let params = route.route_params.clone().unwrap(); let retry = Retry::Attempts(0); nodes[0].node.send_spontaneous_payment(preimage, onion, payment_id_1, params, retry).unwrap(); @@ -761,7 +761,7 @@ fn no_pending_leak_on_initial_send_failure() { nodes[0].node.peer_disconnected(node_b_id); nodes[1].node.peer_disconnected(node_a_id); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 100_000); let payment_id = PaymentId(payment_hash.0); let res = nodes[0].node.send_payment_with_route(route, payment_hash, onion, payment_id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { ref err }, @@ -809,7 +809,7 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) { send_along_route(&nodes[0], route.clone(), &[&nodes[1], &nodes[2]], 1_000_000); let route_params = route.route_params.unwrap().clone(); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment(payment_hash, onion, id, route_params, Retry::Attempts(1)).unwrap(); check_added_monitors(&nodes[0], 1); @@ -986,7 +986,7 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) { nodes[1].node.timer_tick_occurred(); } - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 1_000_000); // Check that we cannot retry a fulfilled payment nodes[0] .node @@ -994,7 +994,7 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) { .unwrap_err(); // ...but if we send with a different PaymentId the payment should fly let id = PaymentId(payment_hash.0); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 1_000_000); nodes[0].node.send_payment_with_route(new_route.clone(), payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1164,7 +1164,7 @@ fn do_test_completed_payment_not_retryable_on_reload(use_dust: bool) { // If we attempt to retry prior to the HTLC-Timeout (or commitment transaction, for dust HTLCs) // confirming, we will fail as it's considered still-pending... let (new_route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[2], amt); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt); match nodes[0].node.send_payment_with_route(new_route.clone(), hash, onion, payment_id) { Err(RetryableSendFailure::DuplicatePayment) => {}, _ => panic!("Unexpected error"), @@ -1184,7 +1184,7 @@ fn do_test_completed_payment_not_retryable_on_reload(use_dust: bool) { node_a_ser = nodes[0].node.encode(); // After the payment failed, we're free to send it again. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt); nodes[0].node.send_payment_with_route(new_route.clone(), hash, onion, payment_id).unwrap(); assert!(!nodes[0].node.get_and_clear_pending_msg_events().is_empty()); @@ -1201,13 +1201,13 @@ fn do_test_completed_payment_not_retryable_on_reload(use_dust: bool) { // Now resend the payment, delivering the HTLC and actually claiming it this time. This ensures // the payment is not (spuriously) listed as still pending. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt); nodes[0].node.send_payment_with_route(new_route.clone(), hash, onion, payment_id).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], amt, hash, payment_secret); claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt); match nodes[0].node.send_payment_with_route(new_route.clone(), hash, onion, payment_id) { Err(RetryableSendFailure::DuplicatePayment) => {}, _ => panic!("Unexpected error"), @@ -1229,7 +1229,7 @@ fn do_test_completed_payment_not_retryable_on_reload(use_dust: bool) { reconnect_nodes(ReconnectArgs::new(&nodes[0], &nodes[1])); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt); match nodes[0].node.send_payment_with_route(new_route, hash, onion, payment_id) { Err(RetryableSendFailure::DuplicatePayment) => {}, _ => panic!("Unexpected error"), @@ -1520,7 +1520,7 @@ fn get_ldk_payment_preimage() { &Default::default(), &random_seed_bytes, ); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route.unwrap(), payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1873,7 +1873,7 @@ fn claimed_send_payment_idempotent() { () => { // If we try to resend a new payment with a different payment_hash but with the same // payment_id, it should be rejected. - let onion = RecipientOnionFields::secret_only(second_payment_secret); + let onion = RecipientOnionFields::secret_only(second_payment_secret, 100_000); let send_result = nodes[0].node.send_payment_with_route(route.clone(), hash_b, onion, payment_id); match send_result { @@ -1885,7 +1885,7 @@ fn claimed_send_payment_idempotent() { // also be rejected. let send_result = nodes[0].node.send_spontaneous_payment( None, - RecipientOnionFields::spontaneous_empty(), + RecipientOnionFields::spontaneous_empty(100_000), payment_id, route.route_params.clone().unwrap(), Retry::Attempts(0), @@ -1929,7 +1929,7 @@ fn claimed_send_payment_idempotent() { nodes[0].node.timer_tick_occurred(); } - let onion = RecipientOnionFields::secret_only(second_payment_secret); + let onion = RecipientOnionFields::secret_only(second_payment_secret, 100_000); nodes[0].node.send_payment_with_route(route, hash_b, onion, payment_id).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1]]], 100_000, hash_b, second_payment_secret); @@ -1956,7 +1956,7 @@ fn abandoned_send_payment_idempotent() { () => { // If we try to resend a new payment with a different payment_hash but with the same // payment_id, it should be rejected. - let onion = RecipientOnionFields::secret_only(second_payment_secret); + let onion = RecipientOnionFields::secret_only(second_payment_secret, 100_000); let send_result = nodes[0].node.send_payment_with_route(route.clone(), hash_b, onion, payment_id); match send_result { @@ -1968,7 +1968,7 @@ fn abandoned_send_payment_idempotent() { // also be rejected. let send_result = nodes[0].node.send_spontaneous_payment( None, - RecipientOnionFields::spontaneous_empty(), + RecipientOnionFields::spontaneous_empty(100_000), payment_id, route.route_params.clone().unwrap(), Retry::Attempts(0), @@ -1998,7 +1998,7 @@ fn abandoned_send_payment_idempotent() { // However, we can reuse the PaymentId immediately after we `abandon_payment` upon passing the // failed payment back. - let onion = RecipientOnionFields::secret_only(second_payment_secret); + let onion = RecipientOnionFields::secret_only(second_payment_secret, 100_000); nodes[0].node.send_payment_with_route(route, hash_b, onion, payment_id).unwrap(); check_added_monitors(&nodes[0], 1); pass_along_route(&nodes[0], &[&[&nodes[1]]], 100_000, hash_b, second_payment_secret); @@ -2166,12 +2166,12 @@ fn test_holding_cell_inflight_htlcs() { // Queue up two payments - one will be delivered right away, one immediately goes into the // holding cell as nodes[0] is AwaitingRAA. { - let onion = RecipientOnionFields::secret_only(payment_secret_1); + let onion = RecipientOnionFields::secret_only(payment_secret_1, 1000000); let id = PaymentId(payment_hash_1.0); nodes[0].node.send_payment_with_route(route.clone(), payment_hash_1, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); - let onion = RecipientOnionFields::secret_only(payment_secret_2); + let onion = RecipientOnionFields::secret_only(payment_secret_2, 1000000); let id = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route, payment_hash_2, onion, id).unwrap(); check_added_monitors(&nodes[0], 0); @@ -2262,7 +2262,7 @@ fn do_test_intercepted_payment(test: InterceptTest) { let (hash, payment_secret) = nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None).unwrap(); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(hash.0); nodes[0].node.send_payment_with_route(route.clone(), hash, onion, id).unwrap(); let payment_event = { @@ -2498,7 +2498,7 @@ fn do_accept_underpaying_htlcs_config(num_mpp_parts: usize) { let (payment_hash, payment_secret) = nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None).unwrap(); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment(payment_hash, onion, id, route_params, Retry::Attempts(0)).unwrap(); @@ -2710,7 +2710,7 @@ fn do_automatic_retries(test: AutoRetry) { if test == AutoRetry::Success { // Test that we can succeed on the first retry. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(hash.0); let retry = Retry::Attempts(1); nodes[0].node.send_payment(hash, onion, id, route_params, retry).unwrap(); @@ -2736,7 +2736,7 @@ fn do_automatic_retries(test: AutoRetry) { preimage, )); } else if test == AutoRetry::Spontaneous { - let onion = RecipientOnionFields::spontaneous_empty(); + let onion = RecipientOnionFields::spontaneous_empty(amt_msat); let id = PaymentId(hash.0); nodes[0] .node @@ -2761,7 +2761,7 @@ fn do_automatic_retries(test: AutoRetry) { claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], &[path], preimage)); } else if test == AutoRetry::FailAttempts { // Ensure ChannelManager will not retry a payment if it has run out of payment attempts. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(hash.0); nodes[0].node.send_payment(hash, onion, id, route_params, Retry::Attempts(1)).unwrap(); pass_failed_attempt_with_retry_along_path!(channel_id_2, true); @@ -2782,7 +2782,7 @@ fn do_automatic_retries(test: AutoRetry) { #[cfg(feature = "std")] { // Ensure ChannelManager will not retry a payment if it times out due to Retry::Timeout. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(hash.0); let retry = Retry::Timeout(Duration::from_secs(60)); nodes[0].node.send_payment(hash, onion, id, route_params, retry).unwrap(); @@ -2810,7 +2810,7 @@ fn do_automatic_retries(test: AutoRetry) { } else if test == AutoRetry::FailOnRestart { // Ensure ChannelManager will not retry a payment after restart, even if there were retry // attempts remaining prior to restart. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(hash.0); nodes[0].node.send_payment(hash, onion, id, route_params, Retry::Attempts(2)).unwrap(); pass_failed_attempt_with_retry_along_path!(channel_id_2, true); @@ -2844,7 +2844,7 @@ fn do_automatic_retries(test: AutoRetry) { _ => panic!("Unexpected event"), } } else if test == AutoRetry::FailOnRetry { - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(hash.0); nodes[0].node.send_payment(hash, onion, id, route_params, Retry::Attempts(1)).unwrap(); pass_failed_attempt_with_retry_along_path!(channel_id_2, true); @@ -3004,7 +3004,7 @@ fn auto_retry_partial_failure() { nodes[0].router.expect_find_route(retry_2_params, Ok(retry_2_route)); // Send a payment that will partially fail on send, then partially fail on retry, then succeed. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment(payment_hash, onion, id, route_params, Retry::Attempts(3)).unwrap(); @@ -3164,7 +3164,7 @@ fn auto_retry_zero_attempts_send_error() { }; nodes[0].router.expect_find_route(route_params.clone(), Ok(send_route)); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment(payment_hash, onion, id, route_params, Retry::Attempts(0)).unwrap(); @@ -3212,7 +3212,7 @@ fn fails_paying_after_rejected_by_payee() { .unwrap(); let route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment(payment_hash, onion, id, route_params, Retry::Attempts(1)).unwrap(); check_added_monitors(&nodes[0], 1); @@ -3328,7 +3328,9 @@ fn retry_multi_path_single_failed_payment() { scorer.expect_usage(chans[1].short_channel_id.unwrap(), usage); } - let onion = RecipientOnionFields::secret_only(payment_secret); + // Note that while we actaully pay amt_msat + 1, we should really set the onion amount to + // amt_msat as that's what we built a route for. + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat + 1); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment(payment_hash, onion, id, route_params, Retry::Attempts(1)).unwrap(); let events = nodes[0].node.get_and_clear_pending_events(); @@ -3409,7 +3411,7 @@ fn immediate_retry_on_failure() { route.route_params = Some(retry_params.clone()); nodes[0].router.expect_find_route(retry_params, Ok(route.clone())); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment(payment_hash, onion, id, route_params, Retry::Attempts(1)).unwrap(); let events = nodes[0].node.get_and_clear_pending_events(); @@ -3548,7 +3550,7 @@ fn no_extra_retries_on_back_to_back_fail() { // We can't use the commitment_signed_dance macro helper because in this test we'll be sending // two HTLCs back-to-back on the same channel, and the macro only expects to handle one at a // time. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment(payment_hash, onion, id, route_params, Retry::Attempts(1)).unwrap(); @@ -3793,7 +3795,7 @@ fn test_simple_partial_retry() { // We can't use the commitment_signed_dance macro helper because in this test we'll be sending // two HTLCs back-to-back on the same channel, and the macro only expects to handle one at a // time. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment(payment_hash, onion, id, route_params, Retry::Attempts(1)).unwrap(); let first_htlc = SendEvent::from_node(&nodes[0]); @@ -3995,7 +3997,7 @@ fn test_threaded_payment_retries() { }; nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); let retry = Retry::Attempts(0xdeadbeef); nodes[0].node.send_payment(payment_hash, onion, id, route_params.clone(), retry).unwrap(); @@ -4294,7 +4296,7 @@ fn do_claim_from_closed_chan(fail_payment: bool) { let final_cltv = nodes[0].best_block_info().1 + TEST_FINAL_CLTV + 8 + 1; nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(hash.0); nodes[0].node.send_payment(hash, onion, id, route_params, Retry::Attempts(1)).unwrap(); @@ -4463,6 +4465,7 @@ fn do_test_custom_tlvs(spontaneous: bool, even_tlvs: bool, known_tlvs: bool) { payment_secret: if spontaneous { None } else { Some(payment_secret) }, payment_metadata: None, custom_tlvs: custom_tlvs.clone(), + total_mpp_amount_msat: amt_msat, }; if spontaneous { let params = route.route_params.unwrap(); @@ -4543,7 +4546,7 @@ fn test_retry_custom_tlvs() { let mut route_params = route.route_params.clone().unwrap(); let custom_tlvs = vec![((1 << 16) + 1, vec![0x42u8; 16])]; - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let onion = onion.with_custom_tlvs(RecipientCustomTlvs::new(custom_tlvs.clone()).unwrap()); nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); @@ -4675,6 +4678,7 @@ fn do_test_custom_tlvs_consistency( payment_secret: Some(payment_secret), payment_metadata: None, custom_tlvs: first_tlvs, + total_mpp_amount_msat: amt_msat, }; let session_privs = nodes[0].node.test_add_new_pending_payment(hash, onion.clone(), id, &route).unwrap(); @@ -4700,6 +4704,7 @@ fn do_test_custom_tlvs_consistency( payment_secret: Some(payment_secret), payment_metadata: None, custom_tlvs: second_tlvs, + total_mpp_amount_msat: amt_msat, }; let path_b = &route.paths[1]; let priv_b = session_privs[1]; @@ -4824,6 +4829,7 @@ fn do_test_payment_metadata_consistency(do_reload: bool, do_modify: bool) { payment_secret: Some(payment_secret), payment_metadata: Some(payment_metadata), custom_tlvs: vec![], + total_mpp_amount_msat: amt_msat, }; let retry = Retry::Attempts(1); nodes[0].node.send_payment(payment_hash, onion, payment_id, route_params, retry).unwrap(); @@ -5018,7 +5024,10 @@ fn test_htlc_forward_considers_anchor_outputs_value() { nodes[2], sendable_balance_msat + anchor_outpus_value_msat ); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only( + payment_secret, + sendable_balance_msat + anchor_outpus_value_msat, + ); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -5083,7 +5092,7 @@ fn peel_payment_onion_custom_tlvs() { let payment_params = PaymentParameters::for_keysend(node_b_id, TEST_FINAL_CLTV, false); let route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat); let route = functional_test_utils::get_route(&nodes[0], &route_params).unwrap(); - let mut recipient_onion = RecipientOnionFields::spontaneous_empty() + let mut recipient_onion = RecipientOnionFields::spontaneous_empty(amt_msat) .with_custom_tlvs(RecipientCustomTlvs::new(vec![(414141, vec![42; 1200])]).unwrap()); let prng_seed = chanmon_cfgs[0].keys_manager.get_secure_random_bytes(); let session_priv = SecretKey::from_slice(&prng_seed[..]).expect("RNG is busted"); @@ -5178,7 +5187,7 @@ fn test_non_strict_forwarding() { for i in 0..4 { let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(payment_value), None); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, payment_value); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route.clone(), payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -5217,7 +5226,7 @@ fn test_non_strict_forwarding() { // Send a 5th payment which will fail. let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(payment_value), None); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, payment_value); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route.clone(), payment_hash, onion, id).unwrap(); @@ -5278,7 +5287,7 @@ fn remove_pending_outbounds_on_buggy_router() { nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); // Send the payment with one retry allowed, but the payment should still fail - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let retry = Retry::Attempts(1); nodes[0].node.send_payment(payment_hash, onion, payment_id, route_params, retry).unwrap(); let events = nodes[0].node.get_and_clear_pending_events(); @@ -5354,7 +5363,7 @@ fn pay_route_without_params() { get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, amt_msat); route.route_params.take(); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(hash.0); nodes[0].node.send_payment_with_route(route, hash, onion, id).unwrap(); diff --git a/lightning/src/ln/priv_short_conf_tests.rs b/lightning/src/ln/priv_short_conf_tests.rs index 2035af15046..664d52a1a33 100644 --- a/lightning/src/ln/priv_short_conf_tests.rs +++ b/lightning/src/ln/priv_short_conf_tests.rs @@ -81,7 +81,7 @@ fn test_priv_forwarding_rejection() { let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 10_000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route.clone(), our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -164,7 +164,7 @@ fn test_priv_forwarding_rejection() { get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, node_c_id); get_event_msg!(nodes[2], MessageSendEvent::SendChannelUpdate, node_b_id); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 10_000); let id = PaymentId(our_payment_hash.0); nodes[0].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -348,7 +348,7 @@ fn test_routed_scid_alias() { get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000); assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap()); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 100_000); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -578,7 +578,7 @@ fn test_inbound_scid_privacy() { get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000); assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap()); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 100_000); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -599,7 +599,7 @@ fn test_inbound_scid_privacy() { get_route_and_payment_hash!(nodes[0], nodes[2], payment_params_2, 100_000); assert_eq!(route_2.paths[0].hops[1].short_channel_id, last_hop[0].short_channel_id.unwrap()); - let onion = RecipientOnionFields::secret_only(payment_secret_2); + let onion = RecipientOnionFields::secret_only(payment_secret_2, 100_000); let id = PaymentId(payment_hash_2.0); nodes[0].node.send_payment_with_route(route_2, payment_hash_2, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -695,7 +695,7 @@ fn test_scid_alias_returned() { route.paths[0].hops[1].fee_msat = 10_000_000; // Overshoot the last channel's value // Route the HTLC through to the destination. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, route.get_total_amount()); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route.clone(), payment_hash, onion, id).unwrap(); @@ -732,7 +732,7 @@ fn test_scid_alias_returned() { route.paths[0].hops[0].fee_msat = 0; // But set fee paid to the middle hop to 0 // Route the HTLC through to the destination. - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 10_000); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); @@ -936,7 +936,7 @@ fn test_0conf_channel_with_async_monitor() { let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 1_000_000); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -1288,7 +1288,7 @@ fn test_0conf_channel_reorg() { ); claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 10_000); let id = PaymentId([0; 32]); nodes[1].node.send_payment_with_route(route, payment_hash, onion.clone(), id).unwrap(); let mut conditions = PaymentFailedConditions::new(); diff --git a/lightning/src/ln/quiescence_tests.rs b/lightning/src/ln/quiescence_tests.rs index d972fb6a5c5..3557b03697e 100644 --- a/lightning/src/ln/quiescence_tests.rs +++ b/lightning/src/ln/quiescence_tests.rs @@ -98,7 +98,7 @@ fn allow_shutdown_while_awaiting_quiescence(local_shutdown: bool) { let payment_amount = 1_000_000; let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(local_node, remote_node, payment_amount); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, payment_amount); let payment_id = PaymentId(payment_hash.0); local_node.node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap(); check_added_monitors(&local_node, 1); @@ -304,7 +304,7 @@ fn test_quiescence_on_final_revoke_and_ack_pending_monitor_update() { let payment_amount = 1_000_000; let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(&nodes[0], &nodes[1], payment_amount); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, payment_amount); let payment_id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap(); check_added_monitors(&nodes[0], 1); @@ -370,7 +370,7 @@ fn quiescence_updates_go_to_holding_cell(fail_htlc: bool) { let (route1, payment_hash1, payment_preimage1, payment_secret1) = get_route_and_payment_hash!(&nodes[1], &nodes[0], payment_amount); - let onion1 = RecipientOnionFields::secret_only(payment_secret1); + let onion1 = RecipientOnionFields::secret_only(payment_secret1, payment_amount); let payment_id1 = PaymentId(payment_hash1.0); nodes[1].node.send_payment_with_route(route1, payment_hash1, onion1, payment_id1).unwrap(); check_added_monitors(&nodes[1], 0); @@ -380,7 +380,7 @@ fn quiescence_updates_go_to_holding_cell(fail_htlc: bool) { // allowed to make updates. let (route2, payment_hash2, payment_preimage2, payment_secret2) = get_route_and_payment_hash!(&nodes[0], &nodes[1], payment_amount); - let onion2 = RecipientOnionFields::secret_only(payment_secret2); + let onion2 = RecipientOnionFields::secret_only(payment_secret2, payment_amount); let payment_id2 = PaymentId(payment_hash2.0); nodes[0].node.send_payment_with_route(route2, payment_hash2, onion2, payment_id2).unwrap(); check_added_monitors(&nodes[0], 1); diff --git a/lightning/src/ln/reload_tests.rs b/lightning/src/ln/reload_tests.rs index c0432051a62..2b1cc1f1395 100644 --- a/lightning/src/ln/reload_tests.rs +++ b/lightning/src/ln/reload_tests.rs @@ -541,7 +541,7 @@ fn do_test_data_loss_protect(reconnect_panicing: bool, substantially_old: bool, // `not_stale` to test the boundary condition. let pay_params = PaymentParameters::for_keysend(nodes[1].node.get_our_node_id(), 100, false); let route_params = RouteParameters::from_payment_params_and_value(pay_params, 40000); - nodes[0].node.send_spontaneous_payment(None, RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]), route_params, Retry::Attempts(0)).unwrap(); + nodes[0].node.send_spontaneous_payment(None, RecipientOnionFields::spontaneous_empty(40000), PaymentId([0; 32]), route_params, Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); let update_add_commit = SendEvent::from_node(&nodes[0]); @@ -754,7 +754,7 @@ fn do_test_partial_claim_before_restart(persist_both_monitors: bool, double_rest }); nodes[0].node.send_payment_with_route(route, payment_hash, - RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap(); + RecipientOnionFields::secret_only(payment_secret, 15_000_000), PaymentId(payment_hash.0)).unwrap(); check_added_monitors(&nodes[0], 2); // Send the payment through to nodes[3] *without* clearing the PaymentClaimable event @@ -952,7 +952,7 @@ fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_ht let payment_id = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes()); let htlc_expiry = nodes[0].best_block_info().1 + TEST_FINAL_CLTV; nodes[0].node.send_payment_with_route(route, payment_hash, - RecipientOnionFields::secret_only(payment_secret), payment_id).unwrap(); + RecipientOnionFields::secret_only(payment_secret, 1_000_000), payment_id).unwrap(); check_added_monitors(&nodes[0], 1); let payment_event = SendEvent::from_node(&nodes[0]); @@ -1206,7 +1206,7 @@ fn do_manager_persisted_pre_outbound_edge_forward(intercept_htlc: bool) { if intercept_htlc { route.paths[0].hops[1].short_channel_id = nodes[1].node.get_intercept_scid(); } - nodes[0].node.send_payment_with_route(route, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap(); + nodes[0].node.send_payment_with_route(route, payment_hash, RecipientOnionFields::secret_only(payment_secret, amt_msat), PaymentId(payment_hash.0)).unwrap(); check_added_monitors(&nodes[0], 1); let updates = get_htlc_update_msgs(&nodes[0], &nodes[1].node.get_our_node_id()); nodes[1].node.handle_update_add_htlc(nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]); @@ -1279,7 +1279,7 @@ fn test_manager_persisted_post_outbound_edge_forward() { // Lock in the HTLC from node_a <> node_b. let amt_msat = 5000; let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], amt_msat); - nodes[0].node.send_payment_with_route(route, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap(); + nodes[0].node.send_payment_with_route(route, payment_hash, RecipientOnionFields::secret_only(payment_secret, amt_msat), PaymentId(payment_hash.0)).unwrap(); check_added_monitors(&nodes[0], 1); let updates = get_htlc_update_msgs(&nodes[0], &nodes[1].node.get_our_node_id()); nodes[1].node.handle_update_add_htlc(nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]); @@ -1409,9 +1409,9 @@ fn test_htlc_localremoved_persistence() { let test_preimage = PaymentPreimage([42; 32]); let mismatch_payment_hash = PaymentHash([43; 32]); let session_privs = nodes[0].node.test_add_new_pending_payment(mismatch_payment_hash, - RecipientOnionFields::spontaneous_empty(), PaymentId(mismatch_payment_hash.0), &route).unwrap(); + RecipientOnionFields::spontaneous_empty(10_000), PaymentId(mismatch_payment_hash.0), &route).unwrap(); nodes[0].node.test_send_payment_internal(&route, mismatch_payment_hash, - RecipientOnionFields::spontaneous_empty(), Some(test_preimage), PaymentId(mismatch_payment_hash.0), None, session_privs).unwrap(); + RecipientOnionFields::spontaneous_empty(10_000), Some(test_preimage), PaymentId(mismatch_payment_hash.0), None, session_privs).unwrap(); check_added_monitors(&nodes[0], 1); let updates = get_htlc_update_msgs(&nodes[0], &nodes[1].node.get_our_node_id()); diff --git a/lightning/src/ln/shutdown_tests.rs b/lightning/src/ln/shutdown_tests.rs index ee037be428e..a0db05f1197 100644 --- a/lightning/src/ln/shutdown_tests.rs +++ b/lightning/src/ln/shutdown_tests.rs @@ -443,12 +443,12 @@ fn updates_shutdown_wait() { ) .unwrap(); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 100_000); let id = PaymentId(payment_hash.0); let res = nodes[0].node.send_payment_with_route(route_1, payment_hash, onion, id); unwrap_send_err!(nodes[0], res, true, APIError::ChannelUnavailable { .. }, {}); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 100_000); let res = nodes[1].node.send_payment_with_route(route_2, payment_hash, onion, id); unwrap_send_err!(nodes[1], res, true, APIError::ChannelUnavailable { .. }, {}); @@ -544,7 +544,7 @@ fn do_htlc_fail_async_shutdown(blinded_recipient: bool) { amt_msat, ) }; - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, amt_msat); let id = PaymentId(our_payment_hash.0); nodes[0] .node @@ -1877,7 +1877,7 @@ fn test_pending_htlcs_arent_lost_on_mon_delay() { // moment `cs_last_raa` is received by B. let (route_b, payment_hash_b, _preimage, payment_secret_b) = get_route_and_payment_hash!(&nodes[0], nodes[2], 900_000); - let onion = RecipientOnionFields::secret_only(payment_secret_b); + let onion = RecipientOnionFields::secret_only(payment_secret_b, 900_000); let id = PaymentId(payment_hash_b.0); nodes[0].node.send_payment_with_route(route_b, payment_hash_b, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); diff --git a/lightning/src/ln/splicing_tests.rs b/lightning/src/ln/splicing_tests.rs index 9c7c8b55eac..a810b8eba9d 100644 --- a/lightning/src/ln/splicing_tests.rs +++ b/lightning/src/ln/splicing_tests.rs @@ -2119,7 +2119,7 @@ fn do_test_splice_with_inflight_htlc_forward_and_resolution(expire_scid_pre_forw let route = get_route(&nodes[0], &route_params).unwrap(); let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(payment_amount), None); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, payment_amount); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment_with_route(route.clone(), payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[0], 1); diff --git a/lightning/src/ln/update_fee_tests.rs b/lightning/src/ln/update_fee_tests.rs index 67a07325ad6..4b69d5c3766 100644 --- a/lightning/src/ln/update_fee_tests.rs +++ b/lightning/src/ln/update_fee_tests.rs @@ -80,7 +80,7 @@ pub fn test_async_inbound_update_fee() { // ...but before it's delivered, nodes[1] starts to send a payment back to nodes[0]... let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 40000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 40000); let id = PaymentId(our_payment_hash.0); nodes[1].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[1], 1); @@ -181,7 +181,7 @@ pub fn test_update_fee_unordered_raa() { // ...but before it's delivered, nodes[1] starts to send a payment back to nodes[0]... let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 40000); - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 40000); let id = PaymentId(our_payment_hash.0); nodes[1].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[1], 1); @@ -672,7 +672,7 @@ pub fn test_update_fee_with_fundee_update_add_htlc() { get_route_and_payment_hash!(nodes[1], nodes[0], 800000); // nothing happens since node[1] is in AwaitingRemoteRevoke - let onion = RecipientOnionFields::secret_only(our_payment_secret); + let onion = RecipientOnionFields::secret_only(our_payment_secret, 800000); let id = PaymentId(our_payment_hash.0); nodes[1].node.send_payment_with_route(route, our_payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[1], 0); @@ -1094,7 +1094,7 @@ pub fn do_cannot_afford_on_holding_cell_release( let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 5000 * 1000); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 5000 * 1000); let id = PaymentId(payment_hash.0); nodes[1].node.send_payment_with_route(route, payment_hash, onion, id).unwrap(); check_added_monitors(&nodes[1], 1); From 4a17b4dbfd40f4004a44bfdf3adac6ba683a97a4 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 6 Feb 2026 17:34:13 +0000 Subject: [PATCH 6/9] f rebase --- lightning/src/ln/reload_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/ln/reload_tests.rs b/lightning/src/ln/reload_tests.rs index 2b1cc1f1395..6bc1eb3ad38 100644 --- a/lightning/src/ln/reload_tests.rs +++ b/lightning/src/ln/reload_tests.rs @@ -1595,7 +1595,7 @@ fn test_hold_completed_inflight_monitor_updates_upon_manager_reload() { let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 1_000_000); let payment_id = PaymentId(payment_hash.0); - let onion = RecipientOnionFields::secret_only(payment_secret); + let onion = RecipientOnionFields::secret_only(payment_secret, 1_000_000); nodes[0].node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap(); check_added_monitors(&nodes[0], 1); From 2c20efaa6e7ed6fd6519daf2b57b9ed3641e187a Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 2 Feb 2026 20:52:29 +0000 Subject: [PATCH 7/9] Replace existing MPP-total args with `RecipientOnionFields` In some uses of LDK we need the ability to send HTLCs for only a portion of some larger MPP payment. This allows payers to make single payments which spend funds from multiple wallets, which may be important for ecash wallets holding funds in multiple mints or graduated wallets which hold funds across a trusted wallet and a self-custodial wallet. In the previous commit we added a new field to `RecipientOnionFields` to describe the total value of an MPP payment. Here we start using this field when building onions, dropping existing arguments to onion-building methods. --- lightning/src/ln/blinded_payment_tests.rs | 22 ++--- lightning/src/ln/channelmanager.rs | 16 ++-- lightning/src/ln/functional_tests.rs | 20 ++-- lightning/src/ln/htlc_reserve_unit_tests.rs | 15 +-- .../src/ln/max_payment_path_len_tests.rs | 5 +- lightning/src/ln/onion_payment.rs | 8 +- lightning/src/ln/onion_route_tests.rs | 19 ++-- lightning/src/ln/onion_utils.rs | 91 ++++++++++--------- lightning/src/ln/outbound_payment.rs | 29 +++--- lightning/src/ln/payment_tests.rs | 9 +- lightning/src/ln/reload_tests.rs | 2 +- 11 files changed, 101 insertions(+), 135 deletions(-) diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index b00a4f360d8..3eb1f55d445 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -478,8 +478,8 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let mut onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv); let cur_height = nodes[0].best_block_info().1; - let (mut onion_payloads, ..) = onion_utils::build_onion_payloads( - &route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None, None, None).unwrap(); + let (mut onion_payloads, ..) = onion_utils::test_build_onion_payloads( + &route.paths[0], &recipient_onion_fields, cur_height, &None, None, None).unwrap(); // Remove the receive payload so the blinded forward payload is encoded as a final payload // (i.e. next_hop_hmac == [0; 32]) onion_payloads.pop(); @@ -1065,8 +1065,8 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { let mut onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv); let cur_height = nodes[0].best_block_info().1; let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(amt_msat); - let (mut onion_payloads, ..) = onion_utils::build_onion_payloads( - &route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None, None, None).unwrap(); + let (mut onion_payloads, ..) = onion_utils::test_build_onion_payloads( + &route.paths[0], &recipient_onion_fields, cur_height, &None, None, None).unwrap(); let update_add = &mut payment_event_1_2.msgs[0]; onion_payloads.last_mut().map(|p| { @@ -1681,7 +1681,7 @@ fn route_blinding_spec_test_vector() { }), }; let cur_height = 747_000; - let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, amt_msat, &RecipientOnionFields::spontaneous_empty(amt_msat), cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap(); + let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, &RecipientOnionFields::spontaneous_empty(amt_msat), cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap(); struct TestEcdhSigner { node_secret: SecretKey, @@ -1905,7 +1905,7 @@ fn test_combined_trampoline_onion_creation_vectors() { let amt_msat = 150_000_000; let cur_height = 800_000; let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, amt_msat); - let (bob_onion, htlc_msat, htlc_cltv) = onion_utils::create_payment_onion_internal(&secp_ctx, &path, &outer_session_key, amt_msat, &recipient_onion_fields, cur_height, &associated_data, &None, None, outer_onion_prng_seed, Some(session_priv), Some([0; 32])).unwrap(); + let (bob_onion, htlc_msat, htlc_cltv) = onion_utils::create_payment_onion_internal(&secp_ctx, &path, &outer_session_key, &recipient_onion_fields, cur_height, &associated_data, &None, None, outer_onion_prng_seed, Some(session_priv), Some([0; 32])).unwrap(); let outer_onion_packet_hex = bob_onion.encode().to_lower_hex_string(); assert_eq!(outer_onion_packet_hex, "00025fd60556c134ae97e4baedba220a644037754ee67c54fd05e93bf40c17cbb73362fb9dee96001ff229945595b6edb59437a6bc143406d3f90f749892a84d8d430c6890437d26d5bfc599d565316ef51347521075bbab87c59c57bcf20af7e63d7192b46cf171e4f73cb11f9f603915389105d91ad630224bea95d735e3988add1e24b5bf28f1d7128db64284d90a839ba340d088c74b1fb1bd21136b1809428ec5399c8649e9bdf92d2dcfc694deae5046fa5b2bdf646847aaad73f5e95275763091c90e71031cae1f9a770fdea559642c9c02f424a2a28163dd0957e3874bd28a97bec67d18c0321b0e68bc804aa8345b17cb626e2348ca06c8312a167c989521056b0f25c55559d446507d6c491d50605cb79fa87929ce64b0a9860926eeaec2c431d926a1cadb9a1186e4061cb01671a122fc1f57602cbef06d6c194ec4b715c2e3dd4120baca3172cd81900b49fef857fb6d6afd24c983b608108b0a5ac0c1c6c52011f23b8778059ffadd1bb7cd06e2525417365f485a7fd1d4a9ba3818ede7cdc9e71afee8532252d08e2531ca52538655b7e8d912f7ec6d37bbcce8d7ec690709dbf9321e92c565b78e7fe2c22edf23e0902153d1ca15a112ad32fb19695ec65ce11ddf670da7915f05ad4b86c154fb908cb567315d1124f303f75fa075ebde8ef7bb12e27737ad9e4924439097338ea6d7a6fc3721b88c9b830a34e8d55f4c582b74a3895cc848fe57f4fe29f115dabeb6b3175be15d94408ed6771109cfaf57067ae658201082eae7605d26b1449af4425ae8e8f58cdda5c6265f1fd7a386fc6cea3074e4f25b909b96175883676f7610a00fdf34df9eb6c7b9a4ae89b839c69fd1f285e38cdceb634d782cc6d81179759bc9fd47d7fd060470d0b048287764c6837963274e708314f017ac7dc26d0554d59bfcfd3136225798f65f0b0fea337c6b256ebbb63a90b994c0ab93fd8b1d6bd4c74aebe535d6110014cd3d525394027dfe8faa98b4e9b2bee7949eb1961f1b026791092f84deea63afab66603dbe9b6365a102a1fef2f6b9744bc1bb091a8da9130d34d4d39f25dbad191649cfb67e10246364b7ce0c6ec072f9690cabb459d9fda0c849e17535de4357e9907270c75953fca3c845bb613926ecf73205219c7057a4b6bb244c184362bb4e2f24279dc4e60b94a5b1ec11c34081a628428ba5646c995b9558821053ba9c84a05afbf00dabd60223723096516d2f5668f3ec7e11612b01eb7a3a0506189a2272b88e89807943adb34291a17f6cb5516ffd6f945a1c42a524b21f096d66f350b1dad4db455741ae3d0e023309fbda5ef55fb0dc74f3297041448b2be76c525141963934c6afc53d263fb7836626df502d7c2ee9e79cbbd87afd84bbb8dfbf45248af3cd61ad5fac827e7683ca4f91dfad507a8eb9c17b2c9ac5ec051fe645a4a6cb37136f6f19b611e0ea8da7960af2d779507e55f57305bc74b7568928c5dd5132990fe54c22117df91c257d8c7b61935a018a28c1c3b17bab8e4294fa699161ec21123c9fc4e71079df31f300c2822e1246561e04765d3aab333eafd026c7431ac7616debb0e022746f4538e1c6348b600c988eeb2d051fc60c468dca260a84c79ab3ab8342dc345a764672848ea234e17332bc124799daf7c5fcb2e2358514a7461357e1c19c802c5ee32deccf1776885dd825bedd5f781d459984370a6b7ae885d4483a76ddb19b30f47ed47cd56aa5a079a89793dbcad461c59f2e002067ac98dd5a534e525c9c46c2af730741bf1f8629357ec0bfc0bc9ecb31af96777e507648ff4260dc3673716e098d9111dfd245f1d7c55a6de340deb8bd7a053e5d62d760f184dc70ca8fa255b9023b9b9aedfb6e419a5b5951ba0f83b603793830ee68d442d7b88ee1bbf6bbd1bcd6f68cc1af"); @@ -1996,7 +1996,7 @@ fn test_trampoline_inbound_payment_decoding() { let amt_msat = 150_000_001; let cur_height = 800_001; let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, amt_msat); - let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, amt_msat, &recipient_onion_fields, cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap(); + let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, &recipient_onion_fields, cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap(); struct TestEcdhSigner { node_secret: SecretKey, @@ -2181,7 +2181,7 @@ fn test_trampoline_forward_payload_encoded_as_receive() { }); let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(amt_msat); - let (mut trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = onion_utils::build_trampoline_onion_payloads(&blinded_tail, amt_msat, &recipient_onion_fields, 32, &None).unwrap(); + let (mut trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = onion_utils::build_trampoline_onion_payloads(&blinded_tail, &recipient_onion_fields, 32, &None).unwrap(); // pop the last dummy hop trampoline_payloads.pop(); @@ -2196,7 +2196,7 @@ fn test_trampoline_forward_payload_encoded_as_receive() { ).unwrap(); let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(outer_total_msat); - let (outer_payloads, _, _) = onion_utils::build_onion_payloads(&route.paths[0], outer_total_msat, &recipient_onion_fields, outer_starting_htlc_offset, &None, None, Some(trampoline_packet)).unwrap(); + let (outer_payloads, _, _) = onion_utils::test_build_onion_payloads(&route.paths[0], &recipient_onion_fields, outer_starting_htlc_offset, &None, None, Some(trampoline_packet)).unwrap(); let outer_onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.clone().paths[0], &outer_session_priv); let outer_packet = onion_utils::construct_onion_packet( outer_payloads, @@ -2489,7 +2489,6 @@ fn replacement_onion( let (mut trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = onion_utils::build_trampoline_onion_payloads( &blinded_tail, - original_amt_msat, &recipient_onion_fields, starting_htlc_offset, &None, @@ -2527,9 +2526,8 @@ fn replacement_onion( // Use a different session key to construct the replacement onion packet. Note that the // sender isn't aware of this and won't be able to decode the fulfill hold times. let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(outer_total_msat); - let (mut outer_payloads, _, _) = onion_utils::build_onion_payloads( + let (mut outer_payloads, _, _) = onion_utils::test_build_onion_payloads( &route.paths[0], - outer_total_msat, &recipient_onion_fields, outer_starting_htlc_offset, &None, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index e3094234417..7afdf97c731 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -5131,15 +5131,14 @@ impl< #[cfg(any(test, feature = "_externalize_tests"))] pub(crate) fn test_send_payment_along_path( &self, path: &Path, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, - total_value: u64, cur_height: u32, payment_id: PaymentId, - keysend_preimage: &Option, session_priv_bytes: [u8; 32], + cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option, + session_priv_bytes: [u8; 32], ) -> Result<(), APIError> { let _lck = self.total_consistency_lock.read().unwrap(); self.send_payment_along_path(SendAlongPathArgs { path, payment_hash, recipient_onion: &recipient_onion, - total_value, cur_height, payment_id, keysend_preimage, @@ -5155,7 +5154,6 @@ impl< path, payment_hash, recipient_onion, - total_value, cur_height, payment_id, keysend_preimage, @@ -5180,7 +5178,6 @@ impl< &self.secp_ctx, &path, &session_priv, - total_value, recipient_onion, cur_height, payment_hash, @@ -5399,7 +5396,7 @@ impl< pub(super) fn test_send_payment_internal( &self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, keysend_preimage: Option, payment_id: PaymentId, - recv_value_msat: Option, onion_session_privs: Vec<[u8; 32]>, + onion_session_privs: Vec<[u8; 32]>, ) -> Result<(), PaymentSendFailure> { let best_block_height = self.best_block.read().unwrap().height; let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); @@ -5409,7 +5406,6 @@ impl< recipient_onion, keysend_preimage, payment_id, - recv_value_msat, onion_session_privs, &self.node_signer, best_block_height, @@ -19669,7 +19665,7 @@ mod tests { let session_privs = nodes[0].node.test_add_new_pending_payment(our_payment_hash, RecipientOnionFields::secret_only(payment_secret, 200_000), payment_id, &mpp_route).unwrap(); nodes[0].node.test_send_payment_along_path(&mpp_route.paths[0], &our_payment_hash, - RecipientOnionFields::secret_only(payment_secret, 200_000), 200_000, cur_height, payment_id, &None, session_privs[0]).unwrap(); + RecipientOnionFields::secret_only(payment_secret, 200_000), cur_height, payment_id, &None, session_privs[0]).unwrap(); check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); @@ -19705,7 +19701,7 @@ mod tests { // Send the second half of the original MPP payment. nodes[0].node.test_send_payment_along_path(&mpp_route.paths[1], &our_payment_hash, - RecipientOnionFields::secret_only(payment_secret, 200_000), 200_000, cur_height, payment_id, &None, session_privs[1]).unwrap(); + RecipientOnionFields::secret_only(payment_secret, 200_000), cur_height, payment_id, &None, session_privs[1]).unwrap(); check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); @@ -19953,7 +19949,7 @@ mod tests { let session_privs = nodes[0].node.test_add_new_pending_payment(mismatch_payment_hash, RecipientOnionFields::spontaneous_empty(10_000), PaymentId(mismatch_payment_hash.0), &route).unwrap(); nodes[0].node.test_send_payment_internal(&route, mismatch_payment_hash, - RecipientOnionFields::spontaneous_empty(10_000), Some(test_preimage), PaymentId(mismatch_payment_hash.0), None, session_privs).unwrap(); + RecipientOnionFields::spontaneous_empty(10_000), Some(test_preimage), PaymentId(mismatch_payment_hash.0), session_privs).unwrap(); check_added_monitors(&nodes[0], 1); let updates = get_htlc_update_msgs(&nodes[0], &nodes[1].node.get_our_node_id()); diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index c2926681c73..ed2665665c7 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -2244,9 +2244,8 @@ pub fn fail_backward_pending_htlc_upon_channel_failure() { let session_priv = SecretKey::from_slice(&[42; 32]).unwrap(); let current_height = nodes[1].node.best_block.read().unwrap().height + 1; let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, 50_000); - let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads( + let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::test_build_onion_payloads( &route.paths[0], - 50_000, &recipient_onion_fields, current_height, &None, @@ -3274,7 +3273,6 @@ fn do_test_htlc_timeout(send_partial_mpp: bool) { &route.paths[0], &our_payment_hash, RecipientOnionFields::secret_only(payment_secret, 200_000), - 200_000, cur_height, payment_id, &None, @@ -6910,10 +6908,9 @@ pub fn test_onion_value_mpp_set_calculation() { let onion = RecipientOnionFields::secret_only(payment_secret, total_msat); let onion_session_privs = nodes[0].node.test_add_new_pending_payment(hash, onion.clone(), id, &route).unwrap(); - let amt = Some(total_msat); nodes[0] .node - .test_send_payment_internal(&route, hash, onion, None, id, amt, onion_session_privs) + .test_send_payment_internal(&route, hash, onion, None, id, onion_session_privs) .unwrap(); check_added_monitors(&nodes[0], expected_paths.len()); @@ -6941,9 +6938,8 @@ pub fn test_onion_value_mpp_set_calculation() { &session_priv, ); let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, 100_000); - let (mut onion_payloads, _, _) = onion_utils::build_onion_payloads( + let (mut onion_payloads, _, _) = onion_utils::test_build_onion_payloads( &route.paths[0], - 100_000, &recipient_onion_fields, height + 1, &None, @@ -7049,10 +7045,9 @@ fn do_test_overshoot_mpp(msat_amounts: &[u64], total_msat: u64) { let onion_session_privs = nodes[src_idx].node.test_add_new_pending_payment(hash, onion, id, &route).unwrap(); let onion = RecipientOnionFields::secret_only(payment_secret, total_msat); - let amt = Some(total_msat); nodes[src_idx] .node - .test_send_payment_internal(&route, hash, onion, None, id, amt, onion_session_privs) + .test_send_payment_internal(&route, hash, onion, None, id, onion_session_privs) .unwrap(); check_added_monitors(&nodes[src_idx], expected_paths.len()); @@ -8369,7 +8364,7 @@ pub fn test_inconsistent_mpp_params() { let priv_a = session_privs[0]; nodes[0] .node - .test_send_payment_along_path(path_a, &hash, onion, real_amt, cur_height, id, &None, priv_a) + .test_send_payment_along_path(path_a, &hash, onion, cur_height, id, &None, priv_a) .unwrap(); check_added_monitors(&nodes[0], 1); @@ -8382,11 +8377,10 @@ pub fn test_inconsistent_mpp_params() { let path_b = &route.paths[1]; let onion = RecipientOnionFields::secret_only(payment_secret, 14_000_000); - let amt_b = 14_000_000; let priv_b = session_privs[1]; nodes[0] .node - .test_send_payment_along_path(path_b, &hash, onion, amt_b, cur_height, id, &None, priv_b) + .test_send_payment_along_path(path_b, &hash, onion, cur_height, id, &None, priv_b) .unwrap(); check_added_monitors(&nodes[0], 1); @@ -8446,7 +8440,7 @@ pub fn test_inconsistent_mpp_params() { let priv_c = session_privs[2]; nodes[0] .node - .test_send_payment_along_path(path_b, &hash, onion, real_amt, cur_height, id, &None, priv_c) + .test_send_payment_along_path(path_b, &hash, onion, cur_height, id, &None, priv_c) .unwrap(); check_added_monitors(&nodes[0], 1); diff --git a/lightning/src/ln/htlc_reserve_unit_tests.rs b/lightning/src/ln/htlc_reserve_unit_tests.rs index 5a8b7eb522e..2a1ceadaf5a 100644 --- a/lightning/src/ln/htlc_reserve_unit_tests.rs +++ b/lightning/src/ln/htlc_reserve_unit_tests.rs @@ -817,9 +817,8 @@ pub fn do_test_fee_spike_buffer(cfg: Option, htlc_fails: bool) { let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv); let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, payment_amt_msat); - let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( + let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::test_build_onion_payloads( &route.paths[0], - payment_amt_msat, &recipient_onion_fields, cur_height, &None, @@ -1061,9 +1060,8 @@ pub fn test_chan_reserve_violation_inbound_htlc_outbound_channel() { let cur_height = nodes[1].node.best_block.read().unwrap().height + 1; let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv); let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, 700_000); - let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( + let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::test_build_onion_payloads( &route.paths[0], - 700_000, &recipient_onion_fields, cur_height, &None, @@ -1242,9 +1240,8 @@ pub fn test_chan_reserve_violation_inbound_htlc_inbound_chan() { let cur_height = nodes[0].node.best_block.read().unwrap().height + 1; let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route_2.paths[0], &session_priv); let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(recv_value_2); - let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( + let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::test_build_onion_payloads( &route_2.paths[0], - recv_value_2, &recipient_onion_fields, cur_height, &None, @@ -1630,9 +1627,8 @@ pub fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() { &session_priv, ); let recipient_onion_fields = RecipientOnionFields::secret_only(our_payment_secret, send_amt); - let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( + let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::test_build_onion_payloads( &route.paths[0], - send_amt, &recipient_onion_fields, cur_height, &None, @@ -2237,9 +2233,8 @@ pub fn do_test_dust_limit_fee_accounting(can_afford: bool) { onion_utils::construct_onion_keys(&secp_ctx, &route_0_1.paths[0], &session_priv); let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret_0_1, HTLC_AMT_SAT * 1000); - let (onion_payloads, amount_msat, cltv_expiry) = onion_utils::build_onion_payloads( + let (onion_payloads, amount_msat, cltv_expiry) = onion_utils::test_build_onion_payloads( &route_0_1.paths[0], - HTLC_AMT_SAT * 1000, &recipient_onion_fields, cur_height, &None, diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index ea78449316c..45640d3486d 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -139,7 +139,6 @@ fn large_payment_metadata() { &secp_ctx, &route_0_1.paths[0], &test_utils::privkey(42), - MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &too_large_onion, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, @@ -369,9 +368,8 @@ fn blinded_path_with_custom_tlv() { // Calculate the maximum custom TLV value size where a valid onion packet is still possible. const CUSTOM_TLV_TYPE: u64 = 65537; let mut route = get_route(&nodes[1], &route_params).unwrap(); - let reserved_packet_bytes_without_custom_tlv: usize = onion_utils::build_onion_payloads( + let reserved_packet_bytes_without_custom_tlv: usize = onion_utils::test_build_onion_payloads( &route.paths[0], - MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &RecipientOnionFields::spontaneous_empty(MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY), nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &None, @@ -433,7 +431,6 @@ fn blinded_path_with_custom_tlv() { &secp_ctx, &route.paths[0], &test_utils::privkey(42), - MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &too_large_onion, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index d0d50c6a315..def4a1861c4 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -779,7 +779,7 @@ mod tests { let charlie_pk = PublicKey::from_secret_key(&secp_ctx, &charlie.get_node_secret_key()); let ( - session_priv, total_amt_msat, cur_height, mut recipient_onion, keysend_preimage, payment_hash, + session_priv, _total_amt_msat, cur_height, mut recipient_onion, keysend_preimage, payment_hash, prng_seed, hops, .. ) = payment_onion_args(bob_pk, charlie_pk); @@ -788,8 +788,8 @@ mod tests { let path = Path { hops, blinded_tail: None, }; let onion_keys = super::onion_utils::construct_onion_keys(&secp_ctx, &path, &session_priv); - let (onion_payloads, ..) = super::onion_utils::build_onion_payloads( - &path, total_amt_msat, &recipient_onion, cur_height + 1, &Some(keysend_preimage), None, None + let (onion_payloads, ..) = super::onion_utils::test_build_onion_payloads( + &path, &recipient_onion, cur_height + 1, &Some(keysend_preimage), None, None ).unwrap(); assert!(super::onion_utils::construct_onion_packet( @@ -817,7 +817,7 @@ mod tests { }; let (onion, amount_msat, cltv_expiry) = create_payment_onion( - &secp_ctx, &path, &session_priv, total_amt_msat, &recipient_onion, + &secp_ctx, &path, &session_priv, &recipient_onion, cur_height, &payment_hash, &Some(preimage), None, prng_seed ).unwrap(); diff --git a/lightning/src/ln/onion_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index 1ee0be2938b..f18eead8759 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -25,7 +25,7 @@ use crate::ln::msgs::{ OutboundOnionPayload, OutboundTrampolinePayload, }; use crate::ln::onion_utils::{ - self, build_onion_payloads, construct_onion_keys, LocalHTLCFailureReason, + self, construct_onion_keys, test_build_onion_payloads, LocalHTLCFailureReason, }; use crate::ln::outbound_payment::RecipientOnionFields; use crate::ln::wire::Encode; @@ -527,7 +527,7 @@ fn test_onion_failure() { let recipient_fields = RecipientOnionFields::spontaneous_empty(40000); let path = &route.paths[0]; let (mut onion_payloads, _htlc_msat, _htlc_cltv) = - build_onion_payloads(path, 40000, &recipient_fields, cur_height, &None, None, None) + test_build_onion_payloads(path, &recipient_fields, cur_height, &None, None, None) .unwrap(); let mut new_payloads = Vec::new(); for payload in onion_payloads.drain(..) { @@ -569,7 +569,7 @@ fn test_onion_failure() { let recipient_fields = RecipientOnionFields::spontaneous_empty(40000); let path = &route.paths[0]; let (mut onion_payloads, _htlc_msat, _htlc_cltv) = - build_onion_payloads(path, 40000, &recipient_fields, cur_height, &None, None, None) + test_build_onion_payloads(path, &recipient_fields, cur_height, &None, None, None) .unwrap(); let mut new_payloads = Vec::new(); for payload in onion_payloads.drain(..) { @@ -1288,7 +1288,7 @@ fn test_onion_failure() { let recipient_fields = RecipientOnionFields::spontaneous_empty(40000); let path = &route.paths[0]; let (onion_payloads, _, htlc_cltv) = - build_onion_payloads(path, 40000, &recipient_fields, height, &None, None, None) + test_build_onion_payloads(path, &recipient_fields, height, &None, None, None) .unwrap(); let onion_packet = onion_utils::construct_onion_packet( onion_payloads, @@ -1841,8 +1841,7 @@ fn test_always_create_tlv_format_onion_payloads() { let recipient_fields = RecipientOnionFields::spontaneous_empty(40000); let path = &route.paths[0]; let (onion_payloads, _htlc_msat, _htlc_cltv) = - build_onion_payloads(path, 40000, &recipient_fields, cur_height, &None, None, None) - .unwrap(); + test_build_onion_payloads(path, &recipient_fields, cur_height, &None, None, None).unwrap(); match onion_payloads[0] { msgs::OutboundOnionPayload::Forward { .. } => {}, @@ -1978,7 +1977,6 @@ fn test_trampoline_onion_payload_assembly_values() { let (trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = onion_utils::build_trampoline_onion_payloads( &path.blinded_tail.as_ref().unwrap(), - amt_msat, &recipient_onion_fields, cur_height, &None, @@ -2041,9 +2039,8 @@ fn test_trampoline_onion_payload_assembly_values() { let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, outer_total_msat); - let (outer_payloads, total_msat, total_htlc_offset) = build_onion_payloads( + let (outer_payloads, total_msat, total_htlc_offset) = test_build_onion_payloads( &path, - outer_total_msat, &recipient_onion_fields, outer_starting_htlc_offset, &None, @@ -2080,7 +2077,6 @@ fn test_trampoline_onion_payload_assembly_values() { &Secp256k1::new(), &path, &session_priv, - amt_msat, &recipient_onion_fields, cur_height, &payment_hash, @@ -2540,9 +2536,8 @@ fn test_phantom_invalid_onion_payload() { construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv); let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, msgs::MAX_VALUE_MSAT + 1); - let (mut onion_payloads, _, _) = build_onion_payloads( + let (mut onion_payloads, _, _) = test_build_onion_payloads( &route.paths[0], - msgs::MAX_VALUE_MSAT + 1, &recipient_onion_fields, height + 1, &None, diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index b5c5088eecb..f9bcbea7e34 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -193,7 +193,7 @@ trait OnionPayload<'a, 'b> { ) -> Self; fn new_receive( recipient_onion: &'a RecipientOnionFields, keysend_preimage: Option, - sender_intended_htlc_amt_msat: u64, total_msat: u64, cltv_expiry_height: u32, + sender_intended_htlc_amt_msat: u64, cltv_expiry_height: u32, ) -> Result; fn new_blinded_forward( encrypted_tlvs: &'a Vec, intro_node_blinding_point: Option, @@ -205,8 +205,8 @@ trait OnionPayload<'a, 'b> { custom_tlvs: &'a Vec<(u64, Vec)>, ) -> Self; fn new_trampoline_entry( - total_msat: u64, amt_to_forward: u64, outgoing_cltv_value: u32, - recipient_onion: &'a RecipientOnionFields, packet: msgs::TrampolineOnionPacket, + amt_to_forward: u64, outgoing_cltv_value: u32, recipient_onion: &'a RecipientOnionFields, + packet: msgs::TrampolineOnionPacket, ) -> Result; } impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundOnionPayload<'a> { @@ -217,13 +217,15 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundOnionPayload<'a> { } fn new_receive( recipient_onion: &'a RecipientOnionFields, keysend_preimage: Option, - sender_intended_htlc_amt_msat: u64, total_msat: u64, cltv_expiry_height: u32, + sender_intended_htlc_amt_msat: u64, cltv_expiry_height: u32, ) -> Result { - debug_assert_eq!(total_msat, recipient_onion.total_mpp_amount_msat); Ok(Self::Receive { - payment_data: recipient_onion - .payment_secret - .map(|payment_secret| msgs::FinalOnionHopData { payment_secret, total_msat }), + payment_data: recipient_onion.payment_secret.map(|payment_secret| { + msgs::FinalOnionHopData { + payment_secret, + total_msat: recipient_onion.total_mpp_amount_msat, + } + }), payment_metadata: recipient_onion.payment_metadata.as_ref(), keysend_preimage, custom_tlvs: &recipient_onion.custom_tlvs, @@ -255,16 +257,18 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundOnionPayload<'a> { } fn new_trampoline_entry( - total_msat: u64, amt_to_forward: u64, outgoing_cltv_value: u32, - recipient_onion: &'a RecipientOnionFields, packet: msgs::TrampolineOnionPacket, + amt_to_forward: u64, outgoing_cltv_value: u32, recipient_onion: &'a RecipientOnionFields, + packet: msgs::TrampolineOnionPacket, ) -> Result { - debug_assert_eq!(total_msat, recipient_onion.total_mpp_amount_msat); Ok(Self::TrampolineEntrypoint { amt_to_forward, outgoing_cltv_value, - multipath_trampoline_data: recipient_onion - .payment_secret - .map(|payment_secret| msgs::FinalOnionHopData { payment_secret, total_msat }), + multipath_trampoline_data: recipient_onion.payment_secret.map(|payment_secret| { + msgs::FinalOnionHopData { + payment_secret, + total_msat: recipient_onion.total_mpp_amount_msat, + } + }), trampoline_packet: packet, }) } @@ -279,7 +283,7 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundTrampolinePayload<'a> { } fn new_receive( _recipient_onion: &'a RecipientOnionFields, _keysend_preimage: Option, - _sender_intended_htlc_amt_msat: u64, _total_msat: u64, _cltv_expiry_height: u32, + _sender_intended_htlc_amt_msat: u64, _cltv_expiry_height: u32, ) -> Result { Err(APIError::InvalidRoute { err: "Unblinded receiving is not supported for Trampoline!".to_string(), @@ -308,7 +312,7 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundTrampolinePayload<'a> { } fn new_trampoline_entry( - _total_msat: u64, _amt_to_forward: u64, _outgoing_cltv_value: u32, + _amt_to_forward: u64, _outgoing_cltv_value: u32, _recipient_onion: &'a RecipientOnionFields, _packet: msgs::TrampolineOnionPacket, ) -> Result { Err(APIError::InvalidRoute { @@ -410,7 +414,7 @@ pub(super) fn construct_trampoline_onion_keys( } pub(super) fn build_trampoline_onion_payloads<'a>( - blinded_tail: &'a BlindedTail, total_msat: u64, recipient_onion: &'a RecipientOnionFields, + blinded_tail: &'a BlindedTail, recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32, keysend_preimage: &Option, ) -> Result<(Vec>, u64, u32), APIError> { let mut res: Vec = @@ -425,7 +429,6 @@ pub(super) fn build_trampoline_onion_payloads<'a>( let (value_msat, cltv) = build_onion_payloads_callback( blinded_tail.trampoline_hops.iter(), Some(blinded_tail_with_hop_iter), - total_msat, recipient_onion, starting_htlc_offset, keysend_preimage, @@ -439,14 +442,28 @@ pub(super) fn build_trampoline_onion_payloads<'a>( } /// returns the hop data, as well as the first-hop value_msat and CLTV value we should send. -pub(super) fn build_onion_payloads<'a>( - path: &'a Path, total_msat: u64, recipient_onion: &'a RecipientOnionFields, - starting_htlc_offset: u32, keysend_preimage: &Option, - invoice_request: Option<&'a InvoiceRequest>, +#[cfg(any(test, feature = "_externalize_tests"))] +pub(crate) fn test_build_onion_payloads<'a>( + path: &'a Path, recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32, + keysend_preimage: &Option, invoice_request: Option<&'a InvoiceRequest>, trampoline_packet: Option, ) -> Result<(Vec>, u64, u32), APIError> { - debug_assert_eq!(total_msat, recipient_onion.total_mpp_amount_msat); + build_onion_payloads( + path, + recipient_onion, + starting_htlc_offset, + keysend_preimage, + invoice_request, + trampoline_packet, + ) +} +/// returns the hop data, as well as the first-hop value_msat and CLTV value we should send. +fn build_onion_payloads<'a>( + path: &'a Path, recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32, + keysend_preimage: &Option, invoice_request: Option<&'a InvoiceRequest>, + trampoline_packet: Option, +) -> Result<(Vec>, u64, u32), APIError> { let mut res: Vec = Vec::with_capacity( path.hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len()), ); @@ -472,7 +489,6 @@ pub(super) fn build_onion_payloads<'a>( let (value_msat, cltv) = build_onion_payloads_callback( path.hops.iter(), blinded_tail_with_hop_iter, - total_msat, recipient_onion, starting_htlc_offset, keysend_preimage, @@ -503,7 +519,7 @@ enum PayloadCallbackAction { PushFront, } fn build_onion_payloads_callback<'a, 'b, H, B, F, OP>( - hops: H, mut blinded_tail: Option>, total_msat: u64, + hops: H, mut blinded_tail: Option>, recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32, keysend_preimage: &Option, invoice_request: Option<&'a InvoiceRequest>, mut callback: F, @@ -518,8 +534,6 @@ where let mut cur_cltv = starting_htlc_offset; let mut last_hop_id = None; - debug_assert_eq!(total_msat, recipient_onion.total_mpp_amount_msat); - for (idx, hop) in hops.rev().enumerate() { // First hop gets special values so that it can check, on receipt, that everything is // exactly as it should be (and the next hop isn't trying to probe to find out if we're @@ -548,7 +562,7 @@ where PayloadCallbackAction::PushBack, OP::new_blinded_receive( final_value_msat, - total_msat, + recipient_onion.total_mpp_amount_msat, cur_cltv + excess_final_cltv_expiry_delta, &blinded_hop.encrypted_payload, blinding_point.take(), @@ -576,7 +590,6 @@ where callback( PayloadCallbackAction::PushBack, OP::new_trampoline_entry( - total_msat, final_value_msat + hop.fee_msat(), cur_cltv, &recipient_onion, @@ -587,13 +600,7 @@ where None => { callback( PayloadCallbackAction::PushBack, - OP::new_receive( - &recipient_onion, - *keysend_preimage, - value_msat, - total_msat, - cltv, - )?, + OP::new_receive(&recipient_onion, *keysend_preimage, value_msat, cltv)?, ); }, } @@ -674,7 +681,6 @@ pub(crate) fn set_max_path_length( let build_payloads_res = build_onion_payloads_callback( core::iter::once(&unblinded_route_hop), blinded_tail_opt, - final_value_msat_with_overpay_buffer, &recipient_onion_with_excess_value, best_block_height, &keysend_preimage, @@ -2596,7 +2602,7 @@ pub(super) fn peel_dummy_hop_update_add_htlc( - secp_ctx: &Secp256k1, path: &Path, session_priv: &SecretKey, total_msat: u64, + secp_ctx: &Secp256k1, path: &Path, session_priv: &SecretKey, recipient_onion: &RecipientOnionFields, cur_block_height: u32, payment_hash: &PaymentHash, keysend_preimage: &Option, invoice_request: Option<&InvoiceRequest>, prng_seed: [u8; 32], @@ -2605,7 +2611,6 @@ pub fn create_payment_onion( secp_ctx, path, session_priv, - total_msat, recipient_onion, cur_block_height, payment_hash, @@ -2627,15 +2632,12 @@ pub(super) fn compute_trampoline_session_priv(outer_onion_session_priv: &SecretK /// Build a payment onion, returning the first hop msat and cltv values as well. /// `cur_block_height` should be set to the best known block height + 1. pub(crate) fn create_payment_onion_internal( - secp_ctx: &Secp256k1, path: &Path, session_priv: &SecretKey, total_msat: u64, + secp_ctx: &Secp256k1, path: &Path, session_priv: &SecretKey, recipient_onion: &RecipientOnionFields, cur_block_height: u32, payment_hash: &PaymentHash, keysend_preimage: &Option, invoice_request: Option<&InvoiceRequest>, prng_seed: [u8; 32], trampoline_session_priv_override: Option, trampoline_prng_seed_override: Option<[u8; 32]>, ) -> Result<(msgs::OnionPacket, u64, u32), APIError> { - debug_assert_eq!(total_msat, recipient_onion.total_mpp_amount_msat); - - let mut outer_total_msat = total_msat; let mut outer_starting_htlc_offset = cur_block_height; let mut trampoline_packet_option = None; @@ -2649,10 +2651,10 @@ pub(crate) fn create_payment_onion_internal( if let Some(blinded_tail) = &path.blinded_tail { if !blinded_tail.trampoline_hops.is_empty() { let trampoline_payloads; + let outer_total_msat; (trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = build_trampoline_onion_payloads( &blinded_tail, - total_msat, recipient_onion, cur_block_height, keysend_preimage, @@ -2683,7 +2685,6 @@ pub(crate) fn create_payment_onion_internal( let (onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads( &path, - outer_total_msat, outer_onion, outer_starting_htlc_offset, keysend_preimage, diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 6a72601d4a0..f677d811894 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -907,7 +907,6 @@ pub(super) struct SendAlongPathArgs<'a> { pub path: &'a Path, pub payment_hash: &'a PaymentHash, pub recipient_onion: &'a RecipientOnionFields, - pub total_value: u64, pub cur_height: u32, pub payment_id: PaymentId, pub keysend_preimage: &'a Option, @@ -1186,7 +1185,7 @@ impl OutboundPayments { let result = self.pay_route_internal( &route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id, - Some(route_params.final_value_msat), &onion_session_privs, hold_htlcs_at_next_hop, node_signer, + &onion_session_privs, hold_htlcs_at_next_hop, node_signer, best_block_height, &send_payment_along_path ); log_info!( @@ -1577,7 +1576,7 @@ impl OutboundPayments { })?; let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, - keysend_preimage, None, None, payment_id, None, &onion_session_privs, false, node_signer, + keysend_preimage, None, None, payment_id, &onion_session_privs, false, node_signer, best_block_height, &send_payment_along_path); log_info!(logger, "Sending payment with id {} and hash {} returned {:?}", payment_id, payment_hash, res); @@ -1644,7 +1643,7 @@ impl OutboundPayments { } } } - let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = { + let (recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = { let mut outbounds = self.pending_outbound_payments.lock().unwrap(); match outbounds.entry(payment_id) { hash_map::Entry::Occupied(mut payment) => { @@ -1667,12 +1666,11 @@ impl OutboundPayments { return } - let total_msat = *total_msat; let recipient_onion = RecipientOnionFields { payment_secret: *payment_secret, payment_metadata: payment_metadata.clone(), custom_tlvs: custom_tlvs.clone(), - total_mpp_amount_msat: total_msat, + total_mpp_amount_msat: *total_msat, }; let keysend_preimage = *keysend_preimage; let invoice_request = invoice_request.clone(); @@ -1689,7 +1687,7 @@ impl OutboundPayments { payment.get_mut().increment_attempts(); let bolt12_invoice = payment.get().bolt12_invoice(); - (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned()) + (recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned()) }, PendingOutboundPayment::Legacy { .. } => { log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102"); @@ -1729,7 +1727,7 @@ impl OutboundPayments { } }; let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage, - invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, Some(total_msat), + invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, &onion_session_privs, false, node_signer, best_block_height, &send_payment_along_path); log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res); if let Err(e) = res { @@ -1888,7 +1886,7 @@ impl OutboundPayments { })?; match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields, - None, None, None, payment_id, None, &onion_session_privs, false, node_signer, + None, None, None, payment_id, &onion_session_privs, false, node_signer, best_block_height, &send_payment_along_path ) { Ok(()) => Ok((payment_hash, payment_id)), @@ -2133,7 +2131,7 @@ impl OutboundPayments { fn pay_route_internal( &self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields, keysend_preimage: Option, invoice_request: Option<&InvoiceRequest>, bolt12_invoice: Option<&PaidBolt12Invoice>, - payment_id: PaymentId, recv_value_msat: Option, onion_session_privs: &Vec<[u8; 32]>, + payment_id: PaymentId, onion_session_privs: &Vec<[u8; 32]>, hold_htlcs_at_next_hop: bool, node_signer: &NS, best_block_height: u32, send_payment_along_path: &F ) -> Result<(), PaymentSendFailure> where @@ -2147,7 +2145,6 @@ impl OutboundPayments { { return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_owned()})); } - let mut total_value = 0; let our_node_id = node_signer.get_node_id(Recipient::Node).unwrap(); // TODO no unwrap let mut path_errs = Vec::with_capacity(route.paths.len()); 'path_check: for path in route.paths.iter() { @@ -2170,22 +2167,18 @@ impl OutboundPayments { continue 'path_check; } } - total_value += path.final_value_msat(); path_errs.push(Ok(())); } if path_errs.iter().any(|e| e.is_err()) { return Err(PaymentSendFailure::PathParameterError(path_errs)); } - if let Some(amt_msat) = recv_value_msat { - total_value = amt_msat; - } let cur_height = best_block_height + 1; let mut results = Vec::new(); debug_assert_eq!(route.paths.len(), onion_session_privs.len()); for (path, session_priv_bytes) in route.paths.iter().zip(onion_session_privs.iter()) { let path_res = send_payment_along_path(SendAlongPathArgs { - path: &path, payment_hash: &payment_hash, recipient_onion, total_value, + path: &path, payment_hash: &payment_hash, recipient_onion, cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request, bolt12_invoice, hold_htlc_at_next_hop: hold_htlcs_at_next_hop, session_priv_bytes: *session_priv_bytes @@ -2246,7 +2239,7 @@ impl OutboundPayments { #[rustfmt::skip] pub(super) fn test_send_payment_internal( &self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, - keysend_preimage: Option, payment_id: PaymentId, recv_value_msat: Option, + keysend_preimage: Option, payment_id: PaymentId, onion_session_privs: Vec<[u8; 32]>, node_signer: &NS, best_block_height: u32, send_payment_along_path: F ) -> Result<(), PaymentSendFailure> @@ -2254,7 +2247,7 @@ impl OutboundPayments { F: Fn(SendAlongPathArgs) -> Result<(), APIError>, { self.pay_route_internal(route, payment_hash, &recipient_onion, - keysend_preimage, None, None, payment_id, recv_value_msat, &onion_session_privs, + keysend_preimage, None, None, payment_id, &onion_session_privs, false, node_signer, best_block_height, &send_payment_along_path) .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e }) } diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index a4eaa1653ca..662d3543f8f 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -3328,9 +3328,7 @@ fn retry_multi_path_single_failed_payment() { scorer.expect_usage(chans[1].short_channel_id.unwrap(), usage); } - // Note that while we actaully pay amt_msat + 1, we should really set the onion amount to - // amt_msat as that's what we built a route for. - let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat + 1); + let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); nodes[0].node.send_payment(payment_hash, onion, id, route_params, Retry::Attempts(1)).unwrap(); let events = nodes[0].node.get_and_clear_pending_events(); @@ -4687,7 +4685,7 @@ fn do_test_custom_tlvs_consistency( let priv_a = session_privs[0]; nodes[0] .node - .test_send_payment_along_path(path_a, &hash, onion, amt_msat, cur_height, id, &None, priv_a) + .test_send_payment_along_path(path_a, &hash, onion, cur_height, id, &None, priv_a) .unwrap(); check_added_monitors(&nodes[0], 1); @@ -4710,7 +4708,7 @@ fn do_test_custom_tlvs_consistency( let priv_b = session_privs[1]; nodes[0] .node - .test_send_payment_along_path(path_b, &hash, onion, amt_msat, cur_height, id, &None, priv_b) + .test_send_payment_along_path(path_b, &hash, onion, cur_height, id, &None, priv_b) .unwrap(); check_added_monitors(&nodes[0], 1); @@ -5103,7 +5101,6 @@ fn peel_payment_onion_custom_tlvs() { &secp_ctx, &route.paths[0], &session_priv, - amt_msat, &recipient_onion, nodes[0].best_block_info().1, &payment_hash, diff --git a/lightning/src/ln/reload_tests.rs b/lightning/src/ln/reload_tests.rs index 6bc1eb3ad38..61d11d664b0 100644 --- a/lightning/src/ln/reload_tests.rs +++ b/lightning/src/ln/reload_tests.rs @@ -1411,7 +1411,7 @@ fn test_htlc_localremoved_persistence() { let session_privs = nodes[0].node.test_add_new_pending_payment(mismatch_payment_hash, RecipientOnionFields::spontaneous_empty(10_000), PaymentId(mismatch_payment_hash.0), &route).unwrap(); nodes[0].node.test_send_payment_internal(&route, mismatch_payment_hash, - RecipientOnionFields::spontaneous_empty(10_000), Some(test_preimage), PaymentId(mismatch_payment_hash.0), None, session_privs).unwrap(); + RecipientOnionFields::spontaneous_empty(10_000), Some(test_preimage), PaymentId(mismatch_payment_hash.0), session_privs).unwrap(); check_added_monitors(&nodes[0], 1); let updates = get_htlc_update_msgs(&nodes[0], &nodes[1].node.get_our_node_id()); From e01cb472db99d76645c30f66d8c293c1abef7096 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 2 Feb 2026 01:09:57 +0000 Subject: [PATCH 8/9] Add total-MPP-value storage in pending payments In some uses of LDK we need the ability to send HTLCs for only a portion of some larger MPP payment. This allows payers to make single payments which spend funds from multiple wallets, which may be important for ecash wallets holding funds in multiple mints or graduated wallets which hold funds across a trusted wallet and a self-custodial wallet. In the previous commits we moved the total-MPP-value we set in onions from being manually passed through onion-building to passing it via `RecipientOnionFields`. This introduced a subtle bug, though - payments which are retried will get a fresh `RecipientOnionFields` built from the data in `PendingOutboundPayment::Retryable`, losing any custom total-MPP-value settings and causing retries to fail. Here we fix this by storing the total-MPP-value directly in `PendingOutboundPayment::Retryable`. --- lightning/src/ln/outbound_payment.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index f677d811894..a10f1dc9ce1 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -133,6 +133,11 @@ pub(crate) enum PendingOutboundPayment { pending_fee_msat: Option, /// The total payment amount across all paths, used to verify that a retry is not overpaying. total_msat: u64, + /// The total payment amount which is set in the onion. + /// + /// This is generally equal to [`Self::Retryable::total_msat`] but may differ when making + /// payments which are sent MPP from different sources. + onion_total_msat: u64, /// Our best known block height at the time this payment was initiated. starting_block_height: u32, remaining_max_total_routing_fee_msat: Option, @@ -1650,7 +1655,7 @@ impl OutboundPayments { match payment.get() { PendingOutboundPayment::Retryable { total_msat, keysend_preimage, payment_secret, payment_metadata, - custom_tlvs, pending_amt_msat, invoice_request, .. + custom_tlvs, pending_amt_msat, invoice_request, onion_total_msat, .. } => { const RETRY_OVERFLOW_PERCENTAGE: u64 = 10; let retry_amt_msat = route.get_total_amount(); @@ -1670,7 +1675,7 @@ impl OutboundPayments { payment_secret: *payment_secret, payment_metadata: payment_metadata.clone(), custom_tlvs: custom_tlvs.clone(), - total_mpp_amount_msat: *total_msat, + total_mpp_amount_msat: *onion_total_msat, }; let keysend_preimage = *keysend_preimage; let invoice_request = invoice_request.clone(); @@ -1986,6 +1991,7 @@ impl OutboundPayments { custom_tlvs: recipient_onion.custom_tlvs, starting_block_height: best_block_height, total_msat: route.get_total_amount(), + onion_total_msat: recipient_onion.total_mpp_amount_msat, remaining_max_total_routing_fee_msat: route.route_params.as_ref().and_then(|p| p.max_total_routing_fee_msat), }; @@ -2693,6 +2699,7 @@ impl OutboundPayments { pending_amt_msat: path_amt, pending_fee_msat: Some(path_fee), total_msat: path_amt, + onion_total_msat: path_amt, starting_block_height: best_block_height, remaining_max_total_routing_fee_msat: None, // only used for retries, and we'll never retry on startup } @@ -2775,6 +2782,21 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (9, custom_tlvs, optional_vec), (10, starting_block_height, required), (11, remaining_max_total_routing_fee_msat, option), + (12, onion_total_msat, (custom, u64, + // Once we get here, `total_msat` will have been read (or we'll fail to read) + |read_val: Option| Ok(read_val.unwrap_or(total_msat.0.unwrap())), + |us: &PendingOutboundPayment| { + match us { + PendingOutboundPayment::Retryable { total_msat, onion_total_msat, .. } => { + if total_msat != onion_total_msat { + Some(*onion_total_msat) + } else { + None + } + }, + _ => unreachable!(), + } + })), (13, invoice_request, option), (15, bolt12_invoice, option), (not_written, retry_strategy, (static_value, None)), From db6aa28878af6ca37c2e977ffa7a50c99e26d250 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 2 Feb 2026 01:49:34 +0000 Subject: [PATCH 9/9] Allow BOLT 11 payments to be a part of a larger MPP payment In some uses of LDK we need the ability to send HTLCs for only a portion of some larger MPP payment. This allows payers to make single payments which spend funds from multiple wallets, which may be important for ecash wallets holding funds in multiple mints or graduated wallets which hold funds across a trusted wallet and a self-custodial wallet. In the previous few commits we added support for making these kinds of payments when using the payment methods which explicitly accepted a `RecipientOnionFields`. Here we also add support for such payments made via the `pay_for_bolt11_invoice` method, utilizing the new `OptionalBolt11PaymentParams` to hide the parameter from most calls. Test mostly by Claude --- lightning/src/ln/channelmanager.rs | 31 +++++- lightning/src/ln/invoice_utils.rs | 1 + lightning/src/ln/outbound_payment.rs | 22 +++- lightning/src/ln/payment_tests.rs | 157 +++++++++++++++++++++++++++ 4 files changed, 205 insertions(+), 6 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 7afdf97c731..94776f15655 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -686,6 +686,20 @@ pub struct OptionalBolt11PaymentParams { /// will ultimately fail once all pending paths have failed (generating an /// [`Event::PaymentFailed`]). pub retry_strategy: Retry, + /// If the payment being made from this node is part of a larger MPP payment from multiple + /// nodes (i.e. because a single payment is being made from multiple wallets), you can specify + /// the total amount being paid here. + /// + /// If this is set, it must be at least the [`Bolt11Invoice::amount_milli_satoshis`] for the + /// invoice provided to [`ChannelManager::pay_for_bolt11_invoice`]. Further, if this is set, + /// the `amount_msats` provided to [`ChannelManager::pay_for_bolt11_invoice`] is allowed to be + /// lower than [`Bolt11Invoice::amount_milli_satoshis`] (as the payment we're making may be a + /// small part of the amount needed to meet the invoice's minimum). + /// + /// If this is lower than the `amount_msats` passed to + /// [`ChannelManager::pay_for_bolt11_invoice`] the call will fail with + /// [`Bolt11PaymentError::InvalidAmount`]. + pub declared_total_mpp_value_override: Option, } impl Default for OptionalBolt11PaymentParams { @@ -697,6 +711,7 @@ impl Default for OptionalBolt11PaymentParams { retry_strategy: Retry::Timeout(core::time::Duration::from_secs(2)), #[cfg(not(feature = "std"))] retry_strategy: Retry::Attempts(3), + declared_total_mpp_value_override: None, } } } @@ -5456,10 +5471,18 @@ impl< /// The invoice's `payment_hash().0` serves as a reliable choice for the `payment_id`. /// /// # Handling Invoice Amounts - /// Some invoices include a specific amount, while others require you to specify one. - /// - If the invoice **includes** an amount, user may provide an amount greater or equal to it - /// to allow for overpayments. - /// - If the invoice **doesn't include** an amount, you'll need to specify `amount_msats`. + /// Some invoices require a specific amount (which can be fetched with + /// [`Bolt11Invoice::amount_milli_satoshis`]) while others allow you to pay amount amount. + /// + /// - If the invoice **includes** an amount, `amount_msats` may be `None` to pay exactly + /// [`Bolt11Invoice::amount_milli_satoshis`] or may be `Some` with a value greater than or + /// equal to the [`Bolt11Invoice::amount_milli_satoshis`] to allow for deliberate overpayment + /// (e.g. for "tips"). + /// - If the invoice **doesn't include** an amount, `amount_msats` must be `Some`. + /// + /// In the special case that [`OptionalBolt11PaymentParams::declared_total_mpp_value_override`] + /// is set, `amount_msats` may be `Some` and lower than + /// [`Bolt11Invoice::amount_milli_satoshis`]. See the parameter for more details. /// /// If these conditions aren’t met, the function will return [`Bolt11PaymentError::InvalidAmount`]. /// diff --git a/lightning/src/ln/invoice_utils.rs b/lightning/src/ln/invoice_utils.rs index b1097b6c274..e97d2066d83 100644 --- a/lightning/src/ln/invoice_utils.rs +++ b/lightning/src/ln/invoice_utils.rs @@ -690,6 +690,7 @@ mod test { custom_tlvs: custom_tlvs.clone(), route_params_config: RouteParametersConfig::default(), retry_strategy: Retry::Attempts(0), + declared_total_mpp_value_override: None, }; nodes[0] diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index a10f1dc9ce1..38000b40e74 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -627,7 +627,11 @@ pub(crate) enum PaymentSendFailure { #[derive(Debug)] pub enum Bolt11PaymentError { /// Incorrect amount was provided to [`ChannelManager::pay_for_bolt11_invoice`]. - /// This happens when the user-provided amount is less than an amount specified in the [`Bolt11Invoice`]. + /// + /// This happens when the user-provided amount is less than an amount specified in the + /// [`Bolt11Invoice`] or the amount set at + /// [`OptionalBolt11PaymentParams::declared_total_mpp_value_override`] was lower than the + /// explicit amount provided to [`ChannelManager::pay_for_bolt11_invoice`]. /// /// [`Bolt11Invoice`]: lightning_invoice::Bolt11Invoice /// [`ChannelManager::pay_for_bolt11_invoice`]: crate::ln::channelmanager::ChannelManager::pay_for_bolt11_invoice @@ -1031,9 +1035,11 @@ impl OutboundPayments { { let payment_hash = invoice.payment_hash(); + let partial_payment = optional_params.declared_total_mpp_value_override.is_some(); let amount = match (invoice.amount_milli_satoshis(), amount_msats) { (Some(amt), None) | (None, Some(amt)) => amt, - (Some(inv_amt), Some(user_amt)) if user_amt < inv_amt => return Err(Bolt11PaymentError::InvalidAmount), + (Some(inv_amt), Some(user_amt)) if user_amt < inv_amt && !partial_payment => + return Err(Bolt11PaymentError::InvalidAmount), (Some(_), Some(user_amt)) => user_amt, (None, None) => return Err(Bolt11PaymentError::InvalidAmount), }; @@ -1043,6 +1049,18 @@ impl OutboundPayments { .with_custom_tlvs(optional_params.custom_tlvs); recipient_onion.payment_metadata = invoice.payment_metadata().map(|v| v.clone()); + if let Some(mpp_amt) = optional_params.declared_total_mpp_value_override { + if mpp_amt < amount { + return Err(Bolt11PaymentError::InvalidAmount); + } + if let Some(invoice_amount) = invoice.amount_milli_satoshis() { + if mpp_amt < invoice_amount { + return Err(Bolt11PaymentError::InvalidAmount); + } + } + recipient_onion.total_mpp_amount_msat = mpp_amt; + } + let payment_params = PaymentParameters::from_bolt11_invoice(invoice) .with_user_config_ignoring_fee_limit(optional_params.route_params_config); diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index 662d3543f8f..6d819bb0fe2 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -5423,3 +5423,160 @@ fn max_out_mpp_path() { check_added_monitors(&nodes[0], 2); // one monitor update per MPP part nodes[0].node.get_and_clear_pending_msg_events(); } + +#[test] +fn bolt11_multi_node_mpp() { + // Test that multiple nodes can collaborate to pay a single BOLT 11 invoice, with each node + // paying a portion of the total invoice amount. This is useful for scenarios like: + // - Paying from multiple wallets (e.g., ecash wallets with funds in multiple mints) + // - Graduated wallets (funds split between trusted and self-custodial wallets) + + let chanmon_cfgs = create_chanmon_cfgs(3); + let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]); + let nodes = create_network(3, &node_cfgs, &node_chanmgrs); + + // Create channels: A<>C and B<>C + create_announced_chan_between_nodes(&nodes, 0, 2); + create_announced_chan_between_nodes(&nodes, 1, 2); + + // Node C creates a BOLT 11 invoice for 100_000 msat + let invoice_amt_msat = 100_000; + let invoice_params = crate::ln::channelmanager::Bolt11InvoiceParameters { + amount_msats: Some(invoice_amt_msat), + ..Default::default() + }; + let invoice = nodes[2].node.create_bolt11_invoice(invoice_params).unwrap(); + + // Node A pays 60_000 msat (part of the total) + let node_a_payment_amt = 60_000; + let payment_id_a = PaymentId([1; 32]); + let optional_params_a = crate::ln::channelmanager::OptionalBolt11PaymentParams { + declared_total_mpp_value_override: Some(invoice_amt_msat), + ..Default::default() + }; + nodes[0] + .node + .pay_for_bolt11_invoice(&invoice, payment_id_a, Some(node_a_payment_amt), optional_params_a) + .unwrap(); + check_added_monitors(&nodes[0], 1); + + // Node B pays 40_000 msat (the remaining part) + let node_b_payment_amt = 40_000; + let payment_id_b = PaymentId([2; 32]); + let optional_params_b = crate::ln::channelmanager::OptionalBolt11PaymentParams { + declared_total_mpp_value_override: Some(invoice_amt_msat), + ..Default::default() + }; + nodes[1] + .node + .pay_for_bolt11_invoice(&invoice, payment_id_b, Some(node_b_payment_amt), optional_params_b) + .unwrap(); + check_added_monitors(&nodes[1], 1); + + let payment_event_a = SendEvent::from_node(&nodes[0]); + nodes[2].node.handle_update_add_htlc(nodes[0].node.get_our_node_id(), &payment_event_a.msgs[0]); + do_commitment_signed_dance(&nodes[2], &nodes[0], &payment_event_a.commitment_msg, false, false); + + let payment_event_b = SendEvent::from_node(&nodes[1]); + nodes[2].node.handle_update_add_htlc(nodes[1].node.get_our_node_id(), &payment_event_b.msgs[0]); + do_commitment_signed_dance(&nodes[2], &nodes[1], &payment_event_b.commitment_msg, false, false); + + // Process the pending HTLCs on node C and generate the PaymentClaimable event + assert!(nodes[2].node.get_and_clear_pending_events().is_empty()); + expect_and_process_pending_htlcs(&nodes[2], false); + let events = nodes[2].node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + let payment_preimage = match &events[0] { + Event::PaymentClaimable { + payment_hash, + amount_msat, + purpose: PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. }, + .. + } => { + assert_eq!(*payment_hash, invoice.payment_hash()); + assert_eq!(*amount_msat, invoice_amt_msat); + payment_preimage.unwrap() + }, + _ => panic!("Unexpected event: {:?}", events[0]), + }; + + nodes[2].node.claim_funds(payment_preimage); + + expect_payment_claimed!(nodes[2], invoice.payment_hash(), invoice_amt_msat); + check_added_monitors(&nodes[2], 2); + + // Get the fulfill messages from C to both A and B + let mut events_c = nodes[2].node.get_and_clear_pending_msg_events(); + assert_eq!(events_c.len(), 2); + + // Handle fulfill message from C to A + let fulfill_idx_a = events_c + .iter() + .position(|ev| { + if let MessageSendEvent::UpdateHTLCs { node_id, .. } = ev { + *node_id == nodes[0].node.get_our_node_id() + } else { + false + } + }) + .unwrap(); + let fulfill_idx_b = 1 - fulfill_idx_a; + + if let MessageSendEvent::UpdateHTLCs { ref updates, .. } = events_c[fulfill_idx_a] { + nodes[0].node.handle_update_fulfill_htlc( + nodes[2].node.get_our_node_id(), + updates.update_fulfill_htlcs[0].clone(), + ); + do_commitment_signed_dance(&nodes[0], &nodes[2], &updates.commitment_signed, false, false); + } + + let payment_sent = nodes[0].node.get_and_clear_pending_events(); + check_added_monitors(&nodes[0], 1); + + assert_eq!(payment_sent.len(), 2, "{payment_sent:?}"); + if let Event::PaymentSent { payment_id, payment_hash, amount_msat, fee_paid_msat, .. } = + &payment_sent[0] + { + assert_eq!(*payment_id, Some(payment_id_a)); + assert_eq!(*payment_hash, invoice.payment_hash()); + assert_eq!(*amount_msat, Some(node_a_payment_amt)); + assert_eq!(*fee_paid_msat, Some(0)); + } else { + panic!("{payment_sent:?}"); + } + if let Event::PaymentPathSuccessful { payment_id, .. } = &payment_sent[1] { + assert_eq!(*payment_id, payment_id_a); + } else { + panic!("{payment_sent:?}"); + } + + // Handle fulfill message from C to B + if let MessageSendEvent::UpdateHTLCs { ref updates, .. } = events_c[fulfill_idx_b] { + nodes[1].node.handle_update_fulfill_htlc( + nodes[2].node.get_our_node_id(), + updates.update_fulfill_htlcs[0].clone(), + ); + do_commitment_signed_dance(&nodes[1], &nodes[2], &updates.commitment_signed, false, false); + } + + let payment_sent = nodes[1].node.get_and_clear_pending_events(); + check_added_monitors(&nodes[1], 1); + + assert_eq!(payment_sent.len(), 2, "{payment_sent:?}"); + if let Event::PaymentSent { payment_id, payment_hash, amount_msat, fee_paid_msat, .. } = + &payment_sent[0] + { + assert_eq!(*payment_id, Some(payment_id_b)); + assert_eq!(*payment_hash, invoice.payment_hash()); + assert_eq!(*amount_msat, Some(node_b_payment_amt)); + assert_eq!(*fee_paid_msat, Some(0)); + } else { + panic!("{payment_sent:?}"); + } + if let Event::PaymentPathSuccessful { payment_id, .. } = &payment_sent[1] { + assert_eq!(*payment_id, payment_id_b); + } else { + panic!("{payment_sent:?}"); + } +}