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-liquidity/tests/lsps2_integration_tests.rs b/lightning-liquidity/tests/lsps2_integration_tests.rs index 312199e19ec..7d4bcfb7830 100644 --- a/lightning-liquidity/tests/lsps2_integration_tests.rs +++ b/lightning-liquidity/tests/lsps2_integration_tests.rs @@ -1332,14 +1332,14 @@ fn client_trusts_lsp_end_to_end_test() { let total_fee_msat = match service_events[0].clone() { Event::PaymentForwarded { - prev_node_id, - next_node_id, + ref prev_htlcs, + ref next_htlcs, skimmed_fee_msat, total_fee_earned_msat, .. } => { - assert_eq!(prev_node_id, Some(payer_node_id)); - assert_eq!(next_node_id, Some(client_node_id)); + assert_eq!(prev_htlcs[0].node_id, Some(payer_node_id)); + assert_eq!(next_htlcs[0].node_id, Some(client_node_id)); service_handler.payment_forwarded(channel_id, skimmed_fee_msat.unwrap_or(0)).unwrap(); Some(total_fee_earned_msat.unwrap() - skimmed_fee_msat.unwrap()) }, 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/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index a537ff55874..847860ad295 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -2794,6 +2794,7 @@ impl ChannelMonitorImpl { let outbound_payment = match source { None => panic!("Outbound HTLCs should have a source"), Some(&HTLCSource::PreviousHopData(_)) => false, + Some(&HTLCSource::TrampolineForward { .. }) => false, Some(&HTLCSource::OutboundRoute { .. }) => true, }; return Some(Balance::MaybeTimeoutClaimableHTLC { @@ -3006,6 +3007,7 @@ impl ChannelMonitor { let outbound_payment = match source { None => panic!("Outbound HTLCs should have a source"), Some(HTLCSource::PreviousHopData(_)) => false, + Some(HTLCSource::TrampolineForward { .. }) => false, Some(HTLCSource::OutboundRoute { .. }) => true, }; if outbound_payment { @@ -6845,7 +6847,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..2ba6eb1ed3a 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -41,11 +41,11 @@ 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; +use crate::io::{self, ErrorKind::InvalidData as IOInvalidData}; use crate::sync::Arc; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash; @@ -163,6 +163,9 @@ pub enum PaymentPurpose { /// Because this is a spontaneous payment, the payer generated their own preimage rather than us /// (the payee) providing a preimage. SpontaneousPayment(PaymentPreimage), + /// HTLCs terminating at our node are intended for forwarding onwards as a trampoline + /// forward. + Trampoline {}, } impl PaymentPurpose { @@ -173,6 +176,7 @@ impl PaymentPurpose { PaymentPurpose::Bolt12OfferPayment { payment_preimage, .. } => *payment_preimage, PaymentPurpose::Bolt12RefundPayment { payment_preimage, .. } => *payment_preimage, PaymentPurpose::SpontaneousPayment(preimage) => Some(*preimage), + PaymentPurpose::Trampoline {} => None, } } @@ -182,6 +186,7 @@ impl PaymentPurpose { PaymentPurpose::Bolt12OfferPayment { .. } => false, PaymentPurpose::Bolt12RefundPayment { .. } => false, PaymentPurpose::SpontaneousPayment(..) => true, + PaymentPurpose::Trampoline {} => false, } } @@ -229,8 +234,9 @@ impl_writeable_tlv_based_enum_legacy!(PaymentPurpose, (2, payment_secret, required), (4, payment_context, required), }, + (3, Trampoline) => {}, ; - (2, SpontaneousPayment) + (2, SpontaneousPayment), ); /// Information about an HTLC that is part of a payment that can be claimed. @@ -576,6 +582,10 @@ pub enum HTLCHandlingFailureType { /// The payment hash of the payment we attempted to process. payment_hash: PaymentHash, }, + /// We were responsible for pathfinding and forwarding of a trampoline payment, but failed to + /// do so. An example of such an instance is when we can't find a route to the specified + /// trampoline destination. + TrampolineForward {}, } impl_writeable_tlv_based_enum_upgradable!(HTLCHandlingFailureType, @@ -593,6 +603,7 @@ impl_writeable_tlv_based_enum_upgradable!(HTLCHandlingFailureType, (4, Receive) => { (0, payment_hash, required), }, + (5, TrampolineForward) => {}, ); /// The reason for HTLC failures in [`Event::HTLCHandlingFailed`]. @@ -730,6 +741,25 @@ pub enum InboundChannelFunds { DualFunded, } +/// Identifies the channel and peer committed to a HTLC, used for both incoming and outgoing HTLCs. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct HTLCLocator { + /// The channel that the HTLC was sent or received on. + pub channel_id: ChannelId, + + /// The `user_channel_id` for `channel_id`. + pub user_channel_id: Option, + + /// The public key identity of the node that the HTLC was sent to or received from. + pub node_id: Option, +} + +impl_writeable_tlv_based!(HTLCLocator, { + (1, channel_id, required), + (3, user_channel_id, option), + (5, node_id, option), +}); + /// An Event which you should probably take some action in response to. /// /// Note that while Writeable and Readable are implemented for Event, you probably shouldn't use @@ -1322,38 +1352,22 @@ pub enum Event { /// This event is generated when a payment has been successfully forwarded through us and a /// forwarding fee earned. /// + /// Note that downgrading from 0.3 with pending trampoline forwards that use multipart payments + /// will produce an event that only provides information about the first htlc that was + /// received/dispatched. + /// /// # Failure Behavior and Persistence /// This event will eventually be replayed after failures-to-handle (i.e., the event handler /// returning `Err(ReplayEvent ())`) and will be persisted across restarts. PaymentForwarded { - /// The channel id of the incoming channel between the previous node and us. - /// - /// This is only `None` for events generated or serialized by versions prior to 0.0.107. - prev_channel_id: Option, - /// The channel id of the outgoing channel between the next node and us. - /// - /// This is only `None` for events generated or serialized by versions prior to 0.0.107. - next_channel_id: Option, - /// The `user_channel_id` of the incoming channel between the previous node and us. - /// - /// This is only `None` for events generated or serialized by versions prior to 0.0.122. - prev_user_channel_id: Option, - /// The `user_channel_id` of the outgoing channel between the next node and us. - /// - /// This will be `None` if the payment was settled via an on-chain transaction. See the - /// caveat described for the `total_fee_earned_msat` field. Moreover it will be `None` for - /// events generated or serialized by versions prior to 0.0.122. - next_user_channel_id: Option, - /// The node id of the previous node. - /// - /// This is only `None` for HTLCs received prior to 0.1 or for events serialized by - /// versions prior to 0.1 - prev_node_id: Option, - /// The node id of the next node. - /// - /// This is only `None` for HTLCs received prior to 0.1 or for events serialized by - /// versions prior to 0.1 - next_node_id: Option, + /// The set of HTLCs forwarded to our node that will be claimed by this forward. Contains a + /// single HTLC for source-routed payments, and may contain multiple HTLCs when we acted as + /// a trampoline router, responsible for pathfinding within the route. + prev_htlcs: Vec, + /// The set of HTLCs forwarded by our node that have been claimed by this forward. Contains + /// a single HTLC for regular source-routed payments, and may contain multiple HTLCs when + /// we acted as a trampoline router, responsible for pathfinding within the route. + next_htlcs: Vec, /// The total fee, in milli-satoshis, which was earned as a result of the payment. /// /// Note that if we force-closed the channel over which we forwarded an HTLC while the HTLC @@ -1670,12 +1684,17 @@ pub enum Event { /// Indicates that the HTLC was accepted, but could not be processed when or after attempting to /// forward it. /// + /// Note that downgrading from 0.3 with pending trampoline forwards that have incoming multipart + /// payments will produce an event that only provides information about the first htlc that was + /// received/dispatched. + /// /// # Failure Behavior and Persistence /// This event will eventually be replayed after failures-to-handle (i.e., the event handler /// returning `Err(ReplayEvent ())`) and will be persisted across restarts. HTLCHandlingFailed { - /// The channel over which the HTLC was received. - prev_channel_id: ChannelId, + /// The channel(s) over which the HTLC(s) was received. May contain multiple entries for + /// trampoline forwards. + prev_channel_ids: Vec, /// The type of HTLC handling that failed. failure_type: HTLCHandlingFailureType, /// The reason that the HTLC failed. @@ -1927,6 +1946,11 @@ impl Writeable for Event { PaymentPurpose::SpontaneousPayment(preimage) => { payment_preimage = Some(*preimage); }, + PaymentPurpose::Trampoline {} => { + payment_secret = None; + payment_preimage = None; + payment_context = None; + }, } let skimmed_fee_opt = if counterparty_skimmed_fee_msat == 0 { None @@ -2039,29 +2063,33 @@ impl Writeable for Event { }); }, &Event::PaymentForwarded { - prev_channel_id, - next_channel_id, - prev_user_channel_id, - next_user_channel_id, - prev_node_id, - next_node_id, + ref prev_htlcs, + ref next_htlcs, total_fee_earned_msat, skimmed_fee_msat, claim_from_onchain_tx, outbound_amount_forwarded_msat, } => { 7u8.write(writer)?; + // Fields 1, 3, 9, 11, 13 and 15 are written for backwards compatibility. + let legacy_prev = prev_htlcs.first().ok_or(io::Error::from(IOInvalidData))?; + let legacy_next = next_htlcs.first().ok_or(io::Error::from(IOInvalidData))?; write_tlv_fields!(writer, { (0, total_fee_earned_msat, option), - (1, prev_channel_id, option), + (1, Some(legacy_prev.channel_id), option), (2, claim_from_onchain_tx, required), - (3, next_channel_id, option), + (3, Some(legacy_next.channel_id), option), (5, outbound_amount_forwarded_msat, option), (7, skimmed_fee_msat, option), - (9, prev_user_channel_id, option), - (11, next_user_channel_id, option), - (13, prev_node_id, option), - (15, next_node_id, option), + (9, legacy_prev.user_channel_id, option), + (11, legacy_next.user_channel_id, option), + (13, legacy_prev.node_id, option), + (15, legacy_next.node_id, option), + // HTLCs are written as required, rather than required_vec, so that they can be + // deserialized using default_value to fill in legacy fields which expects + // LengthReadable (required_vec is WithoutLength). + (17, *prev_htlcs, required), + (19, *next_htlcs, required), }); }, &Event::ChannelClosed { @@ -2209,15 +2237,19 @@ impl Writeable for Event { }) }, &Event::HTLCHandlingFailed { - ref prev_channel_id, + ref prev_channel_ids, ref failure_type, ref failure_reason, } => { 25u8.write(writer)?; + let legacy_chan_id = + prev_channel_ids.first().ok_or(io::Error::from(IOInvalidData))?; write_tlv_fields!(writer, { - (0, prev_channel_id, required), + // Write legacy field to remain backwards compatible. + (0, legacy_chan_id, required), (1, failure_reason, option), (2, failure_type, required), + (3, *prev_channel_ids, required), }) }, &Event::BumpTransaction(ref event) => { @@ -2398,7 +2430,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), @@ -2559,35 +2591,51 @@ impl MaybeReadable for Event { }, 7u8 => { let mut f = || { - let mut prev_channel_id = None; - let mut next_channel_id = None; - let mut prev_user_channel_id = None; - let mut next_user_channel_id = None; - let mut prev_node_id = None; - let mut next_node_id = None; + // Legacy values that have been replaced by prev_htlcs and next_htlcs. + let mut prev_channel_id_legacy = None; + let mut next_channel_id_legacy = None; + let mut prev_user_channel_id_legacy = None; + let mut next_user_channel_id_legacy = None; + let mut prev_node_id_legacy = None; + let mut next_node_id_legacy = None; + let mut total_fee_earned_msat = None; let mut skimmed_fee_msat = None; let mut claim_from_onchain_tx = false; let mut outbound_amount_forwarded_msat = None; + let mut prev_htlcs = vec![]; + let mut next_htlcs = vec![]; read_tlv_fields!(reader, { (0, total_fee_earned_msat, option), - (1, prev_channel_id, option), + (1, prev_channel_id_legacy, option), (2, claim_from_onchain_tx, required), - (3, next_channel_id, option), + (3, next_channel_id_legacy, option), (5, outbound_amount_forwarded_msat, option), (7, skimmed_fee_msat, option), - (9, prev_user_channel_id, option), - (11, next_user_channel_id, option), - (13, prev_node_id, option), - (15, next_node_id, option), + (9, prev_user_channel_id_legacy, option), + (11, next_user_channel_id_legacy, option), + (13, prev_node_id_legacy, option), + (15, next_node_id_legacy, option), + // We can unwrap in the eagerly-evaluated default_value code because we + // always write legacy fields to be backwards compatible, and expect + // this field to be set because the legacy field was only None for versions + // before 0.0.107 and we do not allow upgrades with pending forwards to 0.1 + // for any version before 0.0.123. + (17, prev_htlcs, (default_value, vec![HTLCLocator{ + channel_id: prev_channel_id_legacy.unwrap(), + user_channel_id: prev_user_channel_id_legacy, + node_id: prev_node_id_legacy, + }])), + (19, next_htlcs, (default_value, vec![HTLCLocator{ + channel_id: next_channel_id_legacy.unwrap(), + user_channel_id: next_user_channel_id_legacy, + node_id: next_node_id_legacy, + }])), }); + Ok(Some(Event::PaymentForwarded { - prev_channel_id, - next_channel_id, - prev_user_channel_id, - next_user_channel_id, - prev_node_id, - next_node_id, + prev_htlcs, + next_htlcs, total_fee_earned_msat, skimmed_fee_msat, claim_from_onchain_tx, @@ -2724,7 +2772,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 { @@ -2776,13 +2825,17 @@ impl MaybeReadable for Event { }, 25u8 => { let mut f = || { - let mut prev_channel_id = ChannelId::new_zero(); + let mut prev_channel_id_legacy = ChannelId::new_zero(); let mut failure_reason = None; let mut failure_type_opt = UpgradableRequired(None); + let mut prev_channel_ids = vec![]; read_tlv_fields!(reader, { - (0, prev_channel_id, required), + (0, prev_channel_id_legacy, required), (1, failure_reason, option), (2, failure_type_opt, upgradable_required), + (3, prev_channel_ids, (default_value, vec![ + prev_channel_id_legacy, + ])), }); // If a legacy HTLCHandlingFailureType::UnknownNextHop was written, upgrade @@ -2797,7 +2850,7 @@ impl MaybeReadable for Event { failure_reason = Some(LocalHTLCFailureReason::UnknownNextPeer.into()); } Ok(Some(Event::HTLCHandlingFailed { - prev_channel_id, + prev_channel_ids, failure_type: _init_tlv_based_struct_field!( failure_type_opt, upgradable_required 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..90cd70ae85f 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -8,8 +8,9 @@ // licenses. use crate::blinded_path::payment::{ - BlindedPaymentPath, Bolt12RefundContext, DummyTlvs, ForwardTlvs, PaymentConstraints, - PaymentContext, PaymentForwardNode, PaymentRelay, ReceiveTlvs, PAYMENT_PADDING_ROUND_OFF, + compute_aggregated_base_prop_fee, BlindedPaymentPath, Bolt12RefundContext, DummyTlvs, + ForwardTlvs, PaymentConstraints, PaymentContext, PaymentForwardNode, PaymentRelay, ReceiveTlvs, + TrampolineForwardTlvs, PAYMENT_PADDING_ROUND_OFF, }; use crate::blinded_path::utils::is_padded; use crate::blinded_path::{self, BlindedHop}; @@ -29,7 +30,8 @@ use crate::ln::types::ChannelId; use crate::offers::invoice::UnsignedBolt12Invoice; use crate::prelude::*; use crate::routing::router::{ - BlindedTail, Path, Payee, PaymentParameters, Route, RouteHop, RouteParameters, TrampolineHop, + compute_fees_saturating, BlindedTail, Path, Payee, PaymentParameters, Route, RouteHop, + RouteParameters, TrampolineHop, }; use crate::sign::{NodeSigner, PeerStorageKey, ReceiveAuthKey, Recipient}; use crate::types::features::{BlindedHopFeatures, ChannelFeatures, NodeFeatures}; @@ -41,6 +43,7 @@ use bitcoin::hex::DisplayHex; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::{schnorr, All, PublicKey, Scalar, Secp256k1, SecretKey}; +use bolt11_invoice::RoutingFees; use lightning_invoice::RawBolt11Invoice; use types::features::Features; @@ -187,7 +190,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 +246,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 +310,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 +402,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 +467,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,12 +477,12 @@ 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; - 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(); @@ -594,7 +597,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 +683,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 +793,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 +868,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 +898,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 +923,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); @@ -981,11 +984,11 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { }; let amt_msat = 5000; - let excess_final_cltv_delta_opt = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck { - // Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards. - Some(TEST_FINAL_CLTV as u16 - 2) + let required_final_cltv = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck { + // Set the final CLTV required much too high to trigger the failure in process_pending_htlc_forwards. + Some((TEST_FINAL_CLTV as u16) * 10) } else { None }; - let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), excess_final_cltv_delta_opt); + let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), required_final_cltv); let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000, nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2], &chanmon_cfgs[2].keys_manager); @@ -993,11 +996,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { route_params.payment_params.max_path_length = 17; let route = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck { - let mut route = get_route(&nodes[0], &route_params).unwrap(); - // Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards. - route.paths[0].hops.last_mut().map(|h| h.cltv_expiry_delta += excess_final_cltv_delta_opt.unwrap() as u32); - route.paths[0].blinded_tail.as_mut().map(|bt| bt.excess_final_cltv_expiry_delta = excess_final_cltv_delta_opt.unwrap() as u32); - route + get_route(&nodes[0], &route_params).unwrap() } else if check == ReceiveCheckFail::PaymentConstraints { // Create a blinded path where the receiver's encrypted payload has an htlc_minimum_msat that is // violated by `amt_msat`, and stick it in the route_params without changing the corresponding @@ -1021,7 +1020,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,9 +1063,9 @@ 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 (mut onion_payloads, ..) = onion_utils::build_onion_payloads( - &route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None, None, None).unwrap(); + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(amt_msat); + 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| { @@ -1115,7 +1114,6 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { check_added_monitors(&nodes[2], 1); }, ReceiveCheckFail::ProcessPendingHTLCsCheck => { - assert_eq!(payment_event_1_2.msgs[0].cltv_expiry, nodes[0].best_block_info().1 + 1 + excess_final_cltv_delta_opt.unwrap() as u32 + TEST_FINAL_CLTV); nodes[2].node.handle_update_add_htlc(nodes[1].node.get_our_node_id(), &payment_event_1_2.msgs[0]); check_added_monitors(&nodes[2], 0); do_commitment_signed_dance(&nodes[2], &nodes[1], &payment_event_1_2.commitment_msg, true, true); @@ -1210,7 +1208,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 +1307,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 +1320,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 +1385,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 +1430,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 +1485,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 +1515,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 +1572,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 +1679,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, &RecipientOnionFields::spontaneous_empty(amt_msat), cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap(); struct TestEcdhSigner { node_secret: SecretKey, @@ -1857,7 +1855,7 @@ fn test_combined_trampoline_onion_creation_vectors() { short_channel_id: (572330 << 40) + (42 << 16) + 2821, channel_features: ChannelFeatures::empty(), fee_msat: 153_000, - cltv_expiry_delta: 0, + cltv_expiry_delta: 24 + 36, maybe_announced_channel: false, }, ], @@ -1904,8 +1902,8 @@ 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 (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 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, &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"); @@ -1952,7 +1950,7 @@ fn test_trampoline_inbound_payment_decoding() { short_channel_id: (572330 << 40) + (42 << 16) + 2821, channel_features: ChannelFeatures::empty(), fee_msat: 150_153_000, - cltv_expiry_delta: 0, + cltv_expiry_delta: 24 + 36, maybe_announced_channel: false, }, ], @@ -1995,8 +1993,8 @@ 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 (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 recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, amt_msat); + 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, @@ -2120,7 +2118,7 @@ fn test_trampoline_forward_payload_encoded_as_receive() { blinded_path::utils::construct_blinded_hops( &secp_ctx, path.into_iter(), &trampoline_session_priv, ) - }; + }; let route = Route { paths: vec![Path { @@ -2143,7 +2141,7 @@ fn test_trampoline_forward_payload_encoded_as_receive() { short_channel_id: bob_carol_scid, channel_features: ChannelFeatures::empty(), fee_msat: 0, - cltv_expiry_delta: 48, + cltv_expiry_delta: 24 + 39, maybe_announced_channel: false, } ], @@ -2166,12 +2164,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,7 +2178,8 @@ fn test_trampoline_forward_payload_encoded_as_receive() { encrypted_payload: vec![], }); - 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 recipient_onion_fields = RecipientOnionFields::spontaneous_empty(amt_msat); + let (mut trampoline_payloads, outer_total_msat) = onion_utils::build_trampoline_onion_payloads(&blinded_tail, &recipient_onion_fields, 32, &None).unwrap(); // pop the last dummy hop trampoline_payloads.pop(); @@ -2195,7 +2193,8 @@ fn test_trampoline_forward_payload_encoded_as_receive() { None, ).unwrap(); - 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 recipient_onion_fields = RecipientOnionFields::spontaneous_empty(outer_total_msat); + let (outer_payloads, _, _) = onion_utils::test_build_onion_payloads(&route.paths[0], &recipient_onion_fields, 32, &None, None, Some((trampoline_packet, None))).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, @@ -2308,7 +2307,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) { short_channel_id: bob_carol_scid, channel_features: ChannelFeatures::empty(), fee_msat: 0, - cltv_expiry_delta: 48, + cltv_expiry_delta: 104 + 39, maybe_announced_channel: false, } ], @@ -2331,7 +2330,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); @@ -2395,16 +2394,16 @@ impl<'a> TrampolineTestCase { } } - fn outer_onion_cltv(&self, outer_cltv: u32) -> u32 { + fn inner_onion_cltv(&self, outer_cltv: u32) -> u32 { if *self == TrampolineTestCase::OuterCLTVLessThanTrampoline { - return outer_cltv / 2; + return outer_cltv * 10; } outer_cltv } - fn outer_onion_amt(&self, original_amt: u64) -> u64 { + fn inner_onion_amt(&self, original_amt: u64) -> u64 { if *self == TrampolineTestCase::Underpayment { - return original_amt / 2; + return original_amt * 10; } original_amt } @@ -2424,28 +2423,53 @@ fn test_trampoline_blinded_receive() { do_test_trampoline_relay(true, TrampolineTestCase::OuterCLTVLessThanTrampoline); } -/// Creates a blinded tail where Carol receives via a blinded path. +/// Creates a blinded tail where Carol is the introduction point, Eve is a blinded trampoline +/// relay and Fred is the final recipient. fn create_blinded_tail( - secp_ctx: &Secp256k1, override_random_bytes: [u8; 32], carol_node_id: PublicKey, - carol_auth_key: ReceiveAuthKey, trampoline_cltv_expiry_delta: u32, final_value_msat: u64, + secp_ctx: &Secp256k1, override_random_bytes: [u8; 32], carol: (PublicKey, &PaymentRelay), + eve: (PublicKey, &PaymentRelay), fred_node_id: PublicKey, fred_auth_key: ReceiveAuthKey, + fred_cltv_final: u32, excess_final_cltv_delta: u32, final_value_msat: u64, payment_secret: PaymentSecret, ) -> BlindedTail { 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 carol_blinding_point = PublicKey::from_secret_key(&secp_ctx, &trampoline_session_priv); - let carol_blinded_hops = { - let payee_tlvs = ReceiveTlvs { + let blinded_hops = { + let no_payment_constraints = PaymentConstraints { + max_cltv_expiry: u32::max_value(), + htlc_minimum_msat: final_value_msat, + }; + let carol_tlvs = TrampolineForwardTlvs { + next_trampoline: eve.0, + payment_relay: carol.1.clone(), + payment_constraints: no_payment_constraints.clone(), + features: BlindedHopFeatures::empty(), + next_blinding_override: None, + } + .encode(); + + let eve_tlvs = TrampolineForwardTlvs { + next_trampoline: fred_node_id, + payment_relay: eve.1.clone(), + payment_constraints: no_payment_constraints.clone(), + features: BlindedHopFeatures::empty(), + next_blinding_override: None, + } + .encode(); + + let fred_tlvs = ReceiveTlvs { payment_secret, - payment_constraints: PaymentConstraints { - max_cltv_expiry: u32::max_value(), - htlc_minimum_msat: final_value_msat, - }, + payment_constraints: no_payment_constraints, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), } .encode(); - let path = [((carol_node_id, Some(carol_auth_key)), WithoutLength(&payee_tlvs))]; + let path = [ + ((carol.0, None), WithoutLength(&carol_tlvs)), + ((eve.0, None), WithoutLength(&eve_tlvs)), + ((fred_node_id, Some(fred_auth_key)), WithoutLength(&fred_tlvs)), + ]; blinded_path::utils::construct_blinded_hops( &secp_ctx, @@ -2454,16 +2478,34 @@ fn create_blinded_tail( ) }; + // We have to report the total fees for the blinded path to report to the sender. + let (base_msat, proportional_millionths) = + compute_aggregated_base_prop_fee([&carol.1, &eve.1].iter().map(|relay| RoutingFees { + base_msat: relay.fee_base_msat, + proportional_millionths: relay.fee_proportional_millionths, + })) + .unwrap(); + let total_fees = compute_fees_saturating( + final_value_msat, + RoutingFees { + base_msat: base_msat as u32, + proportional_millionths: proportional_millionths as u32, + }, + ); + BlindedTail { trampoline_hops: vec![TrampolineHop { - pubkey: carol_node_id, + pubkey: carol.0, node_features: Features::empty(), - fee_msat: final_value_msat, - cltv_expiry_delta: trampoline_cltv_expiry_delta, + fee_msat: total_fees, + cltv_expiry_delta: carol.1.cltv_expiry_delta as u32 + + eve.1.cltv_expiry_delta as u32 + + fred_cltv_final + + excess_final_cltv_delta, }], - hops: carol_blinded_hops, + hops: blinded_hops, blinding_point: carol_blinding_point, - excess_final_cltv_expiry_delta: 39, + excess_final_cltv_expiry_delta: excess_final_cltv_delta, final_value_msat, } } @@ -2472,23 +2514,25 @@ fn create_blinded_tail( // payloads that send to unblinded receives and invalid payloads. fn replacement_onion( test_case: TrampolineTestCase, secp_ctx: &Secp256k1, override_random_bytes: [u8; 32], - route: Route, original_amt_msat: u64, starting_htlc_offset: u32, original_trampoline_cltv: u32, + route: Route, fred_amt_msat: u64, fred_final_cltv: u32, excess_final_cltv_delta: u32, payment_hash: PaymentHash, payment_secret: PaymentSecret, blinded: bool, + starting_htlc_offset: u32, carol: PublicKey, eve: (PublicKey, &PaymentRelay), fred: PublicKey, ) -> msgs::OnionPacket { + assert!(!blinded || !matches!(test_case, TrampolineTestCase::Success)); 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(fred_amt_msat); let blinded_tail = route.paths[0].blinded_tail.clone().unwrap(); - // Rebuild our trampoline packet from the original route. If we want to test Carol receiving - // as an unblinded trampoline hop, we switch out her inner trampoline onion with a direct + // Rebuild our trampoline packet from the original route. If we want to test Fred receiving + // as an unblinded trampoline hop, we switch out the trampoline packets with unblinded ones. + // her inner trampoline onion with a direct // receive payload because LDK doesn't support unblinded trampoline receives. - let (trampoline_packet, outer_total_msat, outer_starting_htlc_offset) = { - let (mut trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = + let (trampoline_packet, outer_total_msat) = { + let (mut trampoline_payloads, outer_total_msat) = onion_utils::build_trampoline_onion_payloads( &blinded_tail, - original_amt_msat, &recipient_onion_fields, starting_htlc_offset, &None, @@ -2496,19 +2540,105 @@ fn replacement_onion( .unwrap(); if !blinded { - trampoline_payloads = vec![msgs::OutboundTrampolinePayload::Receive { - payment_data: Some(msgs::FinalOnionHopData { - payment_secret, - total_msat: original_amt_msat, - }), - sender_intended_htlc_amt_msat: original_amt_msat, - cltv_expiry_height: original_trampoline_cltv + starting_htlc_offset, - }]; + let eve_trampoline_fees = compute_fees_saturating( + fred_amt_msat, + RoutingFees { + base_msat: eve.1.fee_base_msat, + proportional_millionths: eve.1.fee_proportional_millionths, + }, + ); + + trampoline_payloads = vec![ + // Carol must forward to Eve with enough fees + CLTV to cover her policy. + msgs::OutboundTrampolinePayload::Forward { + amt_to_forward: fred_amt_msat + eve_trampoline_fees, + outgoing_cltv_value: starting_htlc_offset + + fred_final_cltv + excess_final_cltv_delta + + eve.1.cltv_expiry_delta as u32, + outgoing_node_id: eve.0, + }, + // Eve should forward the final amount to fred, allowing enough CLTV to cover his + // final expiry delta and the excess that the sender added. + msgs::OutboundTrampolinePayload::Forward { + amt_to_forward: fred_amt_msat, + outgoing_cltv_value: starting_htlc_offset + + fred_final_cltv + excess_final_cltv_delta, + outgoing_node_id: fred, + }, + // Fred just needs to receive the amount he's expecting, and since this is an + // unblinded route he'll expect an outgoing cltv that accounts for his final + // expiry delta and excess that the sender added. + msgs::OutboundTrampolinePayload::Receive { + payment_data: Some(msgs::FinalOnionHopData { + payment_secret, + total_msat: fred_amt_msat, + }), + sender_intended_htlc_amt_msat: fred_amt_msat, + cltv_expiry_height: starting_htlc_offset + + fred_final_cltv + excess_final_cltv_delta, + }, + ]; } + match trampoline_payloads.last_mut().unwrap() { + msgs::OutboundTrampolinePayload::Receive { + sender_intended_htlc_amt_msat, + cltv_expiry_height, + .. + } => { + *sender_intended_htlc_amt_msat = + test_case.inner_onion_amt(*sender_intended_htlc_amt_msat); + *cltv_expiry_height = test_case.inner_onion_cltv(*cltv_expiry_height); + }, + msgs::OutboundTrampolinePayload::BlindedReceive { + sender_intended_htlc_amt_msat, + cltv_expiry_height, + .. + } => { + *sender_intended_htlc_amt_msat = + test_case.inner_onion_amt(*sender_intended_htlc_amt_msat); + *cltv_expiry_height = test_case.inner_onion_cltv(*cltv_expiry_height); + }, + _ => panic!("unexpected final trampoline payload type"), + } + + // TODO: clean this up + let key_derivation_tail = if !blinded { + BlindedTail { + // Note: this tail isn't *actually* used in our trampoline key derivation, we just + // have to have one to be able to use the helper function. + trampoline_hops: vec![ + TrampolineHop { + pubkey: carol, + node_features: Features::empty(), + fee_msat: 0, + cltv_expiry_delta: 0, + }, + TrampolineHop { + pubkey: eve.0, + node_features: Features::empty(), + fee_msat: 0, + cltv_expiry_delta: 0, + }, + TrampolineHop { + pubkey: fred, + node_features: Features::empty(), + fee_msat: 0, + cltv_expiry_delta: 0, + }, + ], + hops: vec![], + blinding_point: blinded_tail.blinding_point, + excess_final_cltv_expiry_delta: excess_final_cltv_delta, + final_value_msat: fred_amt_msat, + } + } else { + blinded_tail.clone() + }; + let trampoline_onion_keys = onion_utils::construct_trampoline_onion_keys( &secp_ctx, - &blinded_tail, + &key_derivation_tail, &trampoline_session_priv, ); let trampoline_packet = onion_utils::construct_trampoline_onion_packet( @@ -2520,39 +2650,23 @@ fn replacement_onion( ) .unwrap(); - (trampoline_packet, outer_total_msat, outer_starting_htlc_offset) + (trampoline_packet, outer_total_msat) }; // 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 (mut outer_payloads, _, _) = onion_utils::build_onion_payloads( + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(outer_total_msat); + let (mut outer_payloads, _, _) = onion_utils::test_build_onion_payloads( &route.paths[0], - outer_total_msat, &recipient_onion_fields, - outer_starting_htlc_offset, + starting_htlc_offset, &None, None, - Some(trampoline_packet), + Some((trampoline_packet, None)), ) .unwrap(); assert_eq!(outer_payloads.len(), 2); - // If we're trying to test invalid payloads, we modify Carol's *outer* onion to have values - // that are inconsistent with her inner onion. We need to do this manually because we - // (obviously) can't construct an invalid onion with LDK's built in functions. - match &mut outer_payloads[1] { - msgs::OutboundOnionPayload::TrampolineEntrypoint { - amt_to_forward, - outgoing_cltv_value, - .. - } => { - *amt_to_forward = test_case.outer_onion_amt(original_amt_msat); - let outer_cltv = original_trampoline_cltv + starting_htlc_offset; - *outgoing_cltv_value = test_case.outer_onion_cltv(outer_cltv); - }, - _ => panic!("final payload is not trampoline entrypoint"), - } - let outer_onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.clone().paths[0], &outer_session_priv); onion_utils::construct_onion_packet( @@ -2570,7 +2684,7 @@ fn replacement_onion( // - To hit validation errors by manipulating the trampoline's outer packet. Without this, we would // have to manually construct the onion. fn do_test_trampoline_relay(blinded: bool, test_case: TrampolineTestCase) { - const TOTAL_NODE_COUNT: usize = 3; + const TOTAL_NODE_COUNT: usize = 6; let secp_ctx = Secp256k1::new(); let chanmon_cfgs = create_chanmon_cfgs(TOTAL_NODE_COUNT); @@ -2581,32 +2695,82 @@ fn do_test_trampoline_relay(blinded: bool, test_case: TrampolineTestCase) { let alice_bob_chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); let bob_carol_chan = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0); + let carol_dave_chan = + create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0); + let dave_eve_chan = create_announced_chan_between_nodes_with_value(&nodes, 3, 4, 1_000_000, 0); + let eve_fred_chan = create_announced_chan_between_nodes_with_value(&nodes, 4, 5, 1_000_000, 0); + let starting_htlc_offset = (TOTAL_NODE_COUNT as u32) * CHAN_CONFIRM_DEPTH + 1; for i in 0..TOTAL_NODE_COUNT { - connect_blocks( - &nodes[i], - (TOTAL_NODE_COUNT as u32) * CHAN_CONFIRM_DEPTH + 1 - nodes[i].best_block_info().1, - ); + connect_blocks(&nodes[i], starting_htlc_offset - nodes[i].best_block_info().1); } - let alice_node_id = nodes[0].node.get_our_node_id(); let bob_node_id = nodes[1].node().get_our_node_id(); let carol_node_id = nodes[2].node().get_our_node_id(); + let dave_node_id = nodes[3].node().get_our_node_id(); + let eve_node_id = nodes[4].node().get_our_node_id(); + let fred_node_id = nodes[5].node().get_our_node_id(); let alice_bob_scid = get_scid_from_channel_id(&nodes[0], alice_bob_chan.2); let bob_carol_scid = get_scid_from_channel_id(&nodes[1], bob_carol_chan.2); - let original_amt_msat = 1000; - let original_trampoline_cltv = 72; - let starting_htlc_offset = 32; - + let fred_recv_amt = 1000; + let fred_cltv_final = 72; + let excess_final_cltv = 70; + + let carol_dave_policy = carol_dave_chan.1.contents; + let dave_eve_policy = dave_eve_chan.1.contents; + let eve_fred_policy = eve_fred_chan.1.contents; + + let carol_trampoline_cltv_delta = + carol_dave_policy.cltv_expiry_delta + dave_eve_policy.cltv_expiry_delta; + let carol_trampoline_fee_prop = + carol_dave_policy.fee_proportional_millionths + dave_eve_policy.fee_proportional_millionths; + let carol_trampoline_fee_base = carol_dave_policy.fee_base_msat + dave_eve_policy.fee_base_msat; + + let eve_trampoline_relay = PaymentRelay { + // Note that we add 1 to eve's required CLTV so that she has a non-zero CLTV budget, because + // our pathfinding doesn't support a zero cltv detla. In reality, we'd include a larger + // margin than a single node's delta for trampoline payments, so we don't worry about it. + cltv_expiry_delta: eve_fred_policy.cltv_expiry_delta + 1, + fee_proportional_millionths: eve_fred_policy.fee_proportional_millionths, + fee_base_msat: eve_fred_policy.fee_base_msat, + }; let (payment_preimage, payment_hash, payment_secret) = - get_payment_preimage_hash(&nodes[2], Some(original_amt_msat), None); + get_payment_preimage_hash(&nodes[5], Some(fred_recv_amt), None); // We need the session priv to replace the onion packet later. let override_random_bytes = [42; 32]; *nodes[0].keys_manager.override_random_bytes.lock().unwrap() = Some(override_random_bytes); + // Create a blinded tail where Carol and Eve are trampoline hops, sending to Fred. In our + // unblinded test cases, we'll override this anyway (with a tail sending to an unblinded + // receive, which LDK doesn't allow). + let blinded_tail = create_blinded_tail( + &secp_ctx, + override_random_bytes, + ( + carol_node_id, + // The policy for a blinded trampoline hop needs to cover all the fees for the path to + // the next trampoline. Here we're using the exact values, but IRL the receiving node + // would probably set more general values. + &PaymentRelay { + cltv_expiry_delta: carol_trampoline_cltv_delta, + fee_proportional_millionths: carol_trampoline_fee_prop, + fee_base_msat: carol_trampoline_fee_base, + }, + ), + (eve_node_id, &eve_trampoline_relay), + fred_node_id, + nodes[5].keys_manager.get_receive_auth_key(), + fred_cltv_final, + excess_final_cltv, + fred_recv_amt, + payment_secret, + ); + assert_eq!(blinded_tail.trampoline_hops.len(), 1); + assert_eq!(blinded_tail.hops.len(), 3); + let route = Route { paths: vec![Path { hops: vec![ @@ -2624,23 +2788,12 @@ fn do_test_trampoline_relay(blinded: bool, test_case: TrampolineTestCase) { node_features: NodeFeatures::empty(), short_channel_id: bob_carol_scid, channel_features: ChannelFeatures::empty(), - fee_msat: 0, - cltv_expiry_delta: 48, + fee_msat: blinded_tail.trampoline_hops[0].fee_msat, + cltv_expiry_delta: blinded_tail.trampoline_hops[0].cltv_expiry_delta, maybe_announced_channel: false, }, ], - // Create a blinded tail where Carol is receiving. In our unblinded test cases, we'll - // override this anyway (with a tail sending to an unblinded receive, which LDK doesn't - // allow). - blinded_tail: Some(create_blinded_tail( - &secp_ctx, - override_random_bytes, - carol_node_id, - nodes[2].keys_manager.get_receive_auth_key(), - original_trampoline_cltv, - original_amt_msat, - payment_secret, - )), + blinded_tail: Some(blinded_tail), }], route_params: None, }; @@ -2650,7 +2803,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(fred_recv_amt), PaymentId(payment_hash.0), ) .unwrap(); @@ -2671,33 +2824,36 @@ fn do_test_trampoline_relay(blinded: bool, test_case: TrampolineTestCase) { // Replace the onion to test different scenarios: // - If !blinded: Creates a payload sending to an unblinded trampoline // - If blinded: Modifies outer onion to create outer/inner mismatches if testing failures - update_message.map(|msg| { - msg.onion_routing_packet = replacement_onion( - test_case, - &secp_ctx, - override_random_bytes, - route, - original_amt_msat, - starting_htlc_offset, - original_trampoline_cltv, - payment_hash, - payment_secret, - blinded, - ) - }); - - let route: &[&Node] = &[&nodes[1], &nodes[2]]; - let args = PassAlongPathArgs::new( - &nodes[0], - route, - original_amt_msat, - payment_hash, - first_message_event, - ); + if !blinded || !matches!(test_case, TrampolineTestCase::Success) { + update_message.map(|msg| { + msg.onion_routing_packet = replacement_onion( + test_case, + &secp_ctx, + override_random_bytes, + route, + fred_recv_amt, + fred_cltv_final, + excess_final_cltv, + payment_hash, + payment_secret, + blinded, + // Our internal send payment helpers add one block to the current height to + // create our payments. Do the same here so that our replacement onion will have + // the right cltv. + starting_htlc_offset + 1, + carol_node_id, + (eve_node_id, &eve_trampoline_relay), + fred_node_id, + ) + }); + } - let amt_bytes = test_case.outer_onion_amt(original_amt_msat).to_be_bytes(); - let cltv_bytes = - test_case.outer_onion_cltv(original_trampoline_cltv + starting_htlc_offset).to_be_bytes(); + // We add two blocks to the minimum height that fred will accept because we added one block + // extra CLTV for Eve's forwarding CLTV "budget" and our dispatch adds one block to the + // current height. + let final_cltv_height = fred_cltv_final + starting_htlc_offset + excess_final_cltv + 2; + let amt_bytes = fred_recv_amt.to_be_bytes(); + let cltv_bytes = final_cltv_height.to_be_bytes(); let payment_failure = test_case.payment_failed_conditions(&amt_bytes, &cltv_bytes).map(|p| { if blinded { PaymentFailedConditions::new() @@ -2706,33 +2862,117 @@ fn do_test_trampoline_relay(blinded: bool, test_case: TrampolineTestCase) { p } }); + let route: &[&Node] = &[&nodes[1], &nodes[2], &nodes[3], &nodes[4], &nodes[5]]; + + let args = + PassAlongPathArgs::new(&nodes[0], route, fred_recv_amt, payment_hash, first_message_event); + let args = if payment_failure.is_some() { args.with_payment_preimage(payment_preimage) .without_claimable_event() .expect_failure(HTLCHandlingFailureType::Receive { payment_hash }) } else { - args.with_payment_secret(payment_secret) + args.with_payment_secret(payment_secret).with_payment_claimable_cltv(final_cltv_height) }; do_pass_along_path(args); if let Some(failure) = payment_failure { - let node_updates = get_htlc_update_msgs(&nodes[2], &bob_node_id); - nodes[1].node.handle_update_fail_htlc(carol_node_id, &node_updates.update_fail_htlcs[0]); + let alice_node_id = nodes[0].node.get_our_node_id(); + + // Fred is a blinded introduction node recipient, so will fail back with fail htlc. + let updates_fred = get_htlc_update_msgs(&nodes[5], &eve_node_id); + assert_eq!(updates_fred.update_fail_htlcs.len(), 1); + nodes[4].node.handle_update_fail_htlc(fred_node_id, &updates_fred.update_fail_htlcs[0]); + do_commitment_signed_dance( + &nodes[4], + &nodes[5], + &updates_fred.commitment_signed, + false, + false, + ); + + // Eve is a relaying blinded trampoline, so will fail back with malformed htlc. + expect_and_process_pending_htlcs_and_htlc_handling_failed( + &nodes[4], + &[HTLCHandlingFailureType::TrampolineForward {}], + ); + check_added_monitors(&nodes[4], 1); + + let updates_eve = get_htlc_update_msgs(&nodes[4], &dave_node_id); + if blinded { + assert_eq!(updates_eve.update_fail_malformed_htlcs.len(), 1); + nodes[3].node.handle_update_fail_malformed_htlc( + eve_node_id, + &updates_eve.update_fail_malformed_htlcs[0], + ); + } else { + assert_eq!(updates_eve.update_fail_htlcs.len(), 1); + nodes[3].node.handle_update_fail_htlc(eve_node_id, &updates_eve.update_fail_htlcs[0]); + } + + do_commitment_signed_dance( + &nodes[3], + &nodes[4], + &updates_eve.commitment_signed, + true, + false, + ); + + // Dave is a regular forwarding node, so will fail back with fail htlc. + let updates_dave = get_htlc_update_msgs(&nodes[3], &carol_node_id); + assert_eq!(updates_dave.update_fail_htlcs.len(), 1); + nodes[2].node.handle_update_fail_htlc(dave_node_id, &updates_dave.update_fail_htlcs[0]); + do_commitment_signed_dance( + &nodes[2], + &nodes[3], + &updates_dave.commitment_signed, + false, + false, + ); + + // Carol is a blinded trampoline introduction node, so will fail back with htlc fail. + expect_and_process_pending_htlcs_and_htlc_handling_failed( + &nodes[2], + &[HTLCHandlingFailureType::TrampolineForward {}], + ); + + check_added_monitors(&nodes[2], 1); + + let updates_carol = get_htlc_update_msgs(&nodes[2], &bob_node_id); + assert_eq!(updates_carol.update_fail_htlcs.len(), 1); + nodes[1].node.handle_update_fail_htlc(carol_node_id, &updates_carol.update_fail_htlcs[0]); + let bob_carol_chan = nodes[1] + .node + .list_channels() + .iter() + .find(|c| c.counterparty.node_id == carol_node_id) + .unwrap() + .channel_id; do_commitment_signed_dance( &nodes[1], &nodes[2], - &node_updates.commitment_signed, - true, + &updates_carol.commitment_signed, + false, false, ); - let node_updates = get_htlc_update_msgs(&nodes[1], &alice_node_id); - nodes[0].node.handle_update_fail_htlc(bob_node_id, &node_updates.update_fail_htlcs[0]); + // Bob is a regular forwarding node, so will fail back with htlc fail. + expect_and_process_pending_htlcs_and_htlc_handling_failed( + &nodes[1], + &[HTLCHandlingFailureType::Forward { + node_id: Some(carol_node_id), + channel_id: bob_carol_chan, + }], + ); + check_added_monitors(&nodes[1], 1); + let updates_bob = get_htlc_update_msgs(&nodes[1], &alice_node_id); + assert_eq!(updates_bob.update_fail_htlcs.len(), 1); + nodes[0].node.handle_update_fail_htlc(bob_node_id, &updates_bob.update_fail_htlcs[0]); do_commitment_signed_dance( &nodes[0], &nodes[1], - &node_updates.commitment_signed, + &updates_bob.commitment_signed, false, false, ); @@ -2742,129 +2982,9 @@ fn do_test_trampoline_relay(blinded: bool, test_case: TrampolineTestCase) { // Because we support blinded paths, we also assert on our expected logs to make sure // that the failure reason hidden by obfuscated blinded errors is as expected. if let Some((module, line, count)) = test_case.expected_log() { - nodes[2].logger.assert_log_contains(module, line, count); + nodes[5].logger.assert_log_contains(module, line, count); } } else { - claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage); - } -} - -#[test] -#[rustfmt::skip] -fn test_trampoline_forward_rejection() { - const TOTAL_NODE_COUNT: usize = 3; - - let chanmon_cfgs = create_chanmon_cfgs(TOTAL_NODE_COUNT); - let node_cfgs = create_node_cfgs(TOTAL_NODE_COUNT, &chanmon_cfgs); - let node_chanmgrs = create_node_chanmgrs(TOTAL_NODE_COUNT, &node_cfgs, &vec![None; TOTAL_NODE_COUNT]); - let mut nodes = create_network(TOTAL_NODE_COUNT, &node_cfgs, &node_chanmgrs); - - let (_, _, chan_id_alice_bob, _) = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); - let (_, _, chan_id_bob_carol, _) = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0); - - for i in 0..TOTAL_NODE_COUNT { // connect all nodes' blocks - connect_blocks(&nodes[i], (TOTAL_NODE_COUNT as u32) * CHAN_CONFIRM_DEPTH + 1 - nodes[i].best_block_info().1); - } - - let alice_node_id = nodes[0].node().get_our_node_id(); - let bob_node_id = nodes[1].node().get_our_node_id(); - let carol_node_id = nodes[2].node().get_our_node_id(); - - let alice_bob_scid = nodes[0].node().list_channels().iter().find(|c| c.channel_id == chan_id_alice_bob).unwrap().short_channel_id.unwrap(); - let bob_carol_scid = nodes[1].node().list_channels().iter().find(|c| c.channel_id == chan_id_bob_carol).unwrap().short_channel_id.unwrap(); - - let amt_msat = 1000; - let (payment_preimage, payment_hash, _) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None); - - let route = Route { - paths: vec![Path { - hops: vec![ - // Bob - RouteHop { - pubkey: bob_node_id, - node_features: NodeFeatures::empty(), - short_channel_id: alice_bob_scid, - channel_features: ChannelFeatures::empty(), - fee_msat: 1000, - cltv_expiry_delta: 48, - maybe_announced_channel: false, - }, - - // Carol - RouteHop { - pubkey: carol_node_id, - node_features: NodeFeatures::empty(), - short_channel_id: bob_carol_scid, - channel_features: ChannelFeatures::empty(), - fee_msat: 0, - cltv_expiry_delta: 48, - maybe_announced_channel: false, - } - ], - blinded_tail: Some(BlindedTail { - trampoline_hops: vec![ - // Carol - TrampolineHop { - pubkey: carol_node_id, - node_features: Features::empty(), - fee_msat: amt_msat, - cltv_expiry_delta: 24, - }, - - // Alice (unreachable) - TrampolineHop { - pubkey: alice_node_id, - node_features: Features::empty(), - fee_msat: amt_msat, - cltv_expiry_delta: 24, - }, - ], - hops: vec![BlindedHop{ - // Fake public key - blinded_node_id: alice_node_id, - encrypted_payload: vec![], - }], - blinding_point: alice_node_id, - excess_final_cltv_expiry_delta: 39, - final_value_msat: amt_msat, - }) - }], - route_params: None, - }; - - nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(), 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); - let first_message_event = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events); - - let route: &[&Node] = &[&nodes[1], &nodes[2]]; - let args = PassAlongPathArgs::new(&nodes[0], route, amt_msat, payment_hash, first_message_event) - .with_payment_preimage(payment_preimage) - .without_claimable_event() - .expect_failure(HTLCHandlingFailureType::Receive { payment_hash }); - do_pass_along_path(args); - - { - let unblinded_node_updates = get_htlc_update_msgs(&nodes[2], &nodes[1].node.get_our_node_id()); - nodes[1].node.handle_update_fail_htlc( - nodes[2].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0] - ); - do_commitment_signed_dance(&nodes[1], &nodes[2], &unblinded_node_updates.commitment_signed, true, false); - } - { - let unblinded_node_updates = get_htlc_update_msgs(&nodes[1], &nodes[0].node.get_our_node_id()); - nodes[0].node.handle_update_fail_htlc( - nodes[1].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0] - ); - do_commitment_signed_dance(&nodes[0], &nodes[1], &unblinded_node_updates.commitment_signed, false, false); - } - { - // Expect UnknownNextPeer error while we are unable to route forwarding Trampoline payments. - let payment_failed_conditions = PaymentFailedConditions::new() - .expected_htlc_error_data(LocalHTLCFailureReason::UnknownNextPeer, &[0; 0]); - expect_payment_failed_conditions(&nodes[0], payment_hash, false, payment_failed_conditions); + claim_payment(&nodes[0], route, payment_preimage); } } diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs index 6dc237aeddb..ef10258933a 100644 --- a/lightning/src/ln/chanmon_update_fail_tests.rs +++ b/lightning/src/ln/chanmon_update_fail_tests.rs @@ -49,13 +49,6 @@ use crate::prelude::*; use crate::sync::{Arc, Mutex}; use bitcoin::hashes::Hash; -fn get_latest_mon_update_id<'a, 'b, 'c>( - node: &Node<'a, 'b, 'c>, channel_id: ChannelId, -) -> (u64, u64) { - let monitor_id_state = node.chain_monitor.latest_monitor_update_id.lock().unwrap(); - monitor_id_state.get(&channel_id).unwrap().clone() -} - #[test] fn test_monitor_and_persister_update_fail() { // Test that if both updating the `ChannelMonitor` and persisting the updated @@ -194,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); @@ -213,7 +206,7 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool) { } chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[0], channel_id); + let (latest_update, _) = nodes[0].chain_monitor.get_latest_mon_update_id(channel_id); nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); check_added_monitors(&nodes[0], 0); @@ -261,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); @@ -337,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); @@ -405,7 +398,7 @@ fn do_test_monitor_temporary_update_fail(disconnect_count: usize) { // Now fix monitor updating... chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[0], channel_id); + let (latest_update, _) = nodes[0].chain_monitor.get_latest_mon_update_id(channel_id); nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); check_added_monitors(&nodes[0], 0); @@ -742,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); @@ -758,7 +751,7 @@ fn test_monitor_update_fail_cs() { assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); check_added_monitors(&nodes[1], 0); let responses = nodes[1].node.get_and_clear_pending_msg_events(); @@ -793,7 +786,7 @@ fn test_monitor_update_fail_cs() { } chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[0], channel_id); + let (latest_update, _) = nodes[0].chain_monitor.get_latest_mon_update_id(channel_id); nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); check_added_monitors(&nodes[0], 0); @@ -850,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); @@ -869,7 +862,7 @@ fn test_monitor_update_fail_no_rebroadcast() { check_added_monitors(&nodes[1], 1); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); check_added_monitors(&nodes[1], 0); @@ -904,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(); @@ -914,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(); @@ -939,7 +932,7 @@ fn test_monitor_update_raa_while_paused() { assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); check_added_monitors(&nodes[0], 1); - let (latest_update, _) = get_latest_mon_update_id(&nodes[0], channel_id); + let (latest_update, _) = nodes[0].chain_monitor.get_latest_mon_update_id(channel_id); nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); check_added_monitors(&nodes[0], 0); @@ -1015,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); @@ -1041,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); @@ -1062,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); @@ -1081,7 +1074,7 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) { // Restore monitor updating, ensuring we immediately get a fail-back update and a // update_add update. chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], chan_2.2); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(chan_2.2); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(chan_2.2, latest_update); check_added_monitors(&nodes[1], 0); expect_and_process_pending_htlcs_and_htlc_handling_failed( @@ -1355,7 +1348,7 @@ fn test_monitor_update_fail_reestablish() { assert_eq!(bs_channel_upd.contents.channel_flags & 2, 0); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], chan_1.2); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(chan_1.2); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(chan_1.2, latest_update); check_added_monitors(&nodes[1], 0); @@ -1398,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); @@ -1440,7 +1433,7 @@ fn raa_no_response_awaiting_raa_state() { assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); check_added_monitors(&nodes[1], 1); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); // nodes[1] should be AwaitingRAA here! check_added_monitors(&nodes[1], 0); @@ -1451,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); @@ -1553,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); @@ -1569,7 +1562,7 @@ fn claim_while_disconnected_monitor_update_fail() { // Now un-fail the monitor, which will result in B sending its original commitment update, // receiving the commitment update from A, and the resulting commitment dances. chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); check_added_monitors(&nodes[1], 0); @@ -1660,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); @@ -1698,7 +1691,7 @@ fn monitor_failed_no_reestablish_response() { get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, node_b_id); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); check_added_monitors(&nodes[1], 0); let bs_responses = get_revoke_commit_msgs(&nodes[1], &node_a_id); @@ -1744,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); @@ -1768,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(); @@ -1796,7 +1789,7 @@ fn first_message_on_recv_ordering() { assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); check_added_monitors(&nodes[1], 0); @@ -1861,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); @@ -1881,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); @@ -1895,7 +1888,7 @@ fn test_monitor_update_fail_claim() { // Now restore monitor updating on the 0<->1 channel and claim the funds on B. let channel_id = chan_1.2; - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); expect_payment_claimed!(nodes[1], payment_hash_1, 1_000_000); check_added_monitors(&nodes[1], 0); @@ -2005,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); @@ -2024,7 +2017,7 @@ fn test_monitor_update_on_pending_forwards() { check_added_monitors(&nodes[1], 1); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], chan_1.2); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(chan_1.2); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(chan_1.2, latest_update); check_added_monitors(&nodes[1], 0); @@ -2076,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); @@ -2095,7 +2088,7 @@ fn monitor_update_claim_fail_no_response() { assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); expect_payment_claimed!(nodes[1], payment_hash_1, 1_000_000); check_added_monitors(&nodes[1], 0); @@ -2167,7 +2160,7 @@ fn do_during_funding_monitor_fail( assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); assert!(nodes[0].node.get_and_clear_pending_events().is_empty()); chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[0], channel_id); + let (latest_update, _) = nodes[0].chain_monitor.get_latest_mon_update_id(channel_id); nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); check_added_monitors(&nodes[0], 0); expect_channel_pending_event(&nodes[0], &node_b_id); @@ -2222,7 +2215,7 @@ fn do_during_funding_monitor_fail( } chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); check_added_monitors(&nodes[1], 0); @@ -2318,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. @@ -2325,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); @@ -2340,7 +2334,7 @@ fn test_path_paused_mpp() { // And check that, after we successfully update the monitor for chan_2 we can pass the second // HTLC along to nodes[3] and claim the whole payment back to nodes[0]. - let (latest_update, _) = get_latest_mon_update_id(&nodes[0], chan_2_id); + let (latest_update, _) = nodes[0].chain_monitor.get_latest_mon_update_id(chan_2_id); nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(chan_2_id, latest_update); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); @@ -2381,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); @@ -2696,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); @@ -2788,7 +2782,7 @@ fn do_channel_holding_cell_serialize(disconnect: bool, reload_a: bool) { // If we finish updating the monitor, we should free the holding cell right away (this did // not occur prior to #756). This should result in a new monitor update. chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (mon_id, _) = get_latest_mon_update_id(&nodes[0], chan_id); + let (mon_id, _) = nodes[0].chain_monitor.get_latest_mon_update_id(chan_id); nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(chan_id, mon_id); expect_payment_claimed!(nodes[0], payment_hash_0, 100_000); check_added_monitors(&nodes[0], 1); @@ -2880,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); @@ -3040,7 +3034,7 @@ fn test_temporary_error_during_shutdown() { chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[0], channel_id); + let (latest_update, _) = nodes[0].chain_monitor.get_latest_mon_update_id(channel_id); nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); nodes[1].node.handle_closing_signed( node_a_id, @@ -3050,7 +3044,7 @@ fn test_temporary_error_during_shutdown() { assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update); nodes[0].node.handle_closing_signed( @@ -3096,7 +3090,7 @@ fn double_temp_error() { // `claim_funds` results in a ChannelMonitorUpdate. nodes[1].node.claim_funds(payment_preimage_1); check_added_monitors(&nodes[1], 1); - let (latest_update_1, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update_1, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress); // Previously, this would've panicked due to a double-call to `Channel::monitor_update_failed`, @@ -3105,7 +3099,7 @@ fn double_temp_error() { check_added_monitors(&nodes[1], 1); chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); - let (latest_update_2, _) = get_latest_mon_update_id(&nodes[1], channel_id); + let (latest_update_2, _) = nodes[1].chain_monitor.get_latest_mon_update_id(channel_id); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(channel_id, latest_update_1); assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); check_added_monitors(&nodes[1], 0); @@ -3512,7 +3506,7 @@ fn do_test_blocked_chan_preimage_release(completion_mode: BlockedUpdateComplMode reconnect_nodes(a_b_reconnect); reconnect_nodes(ReconnectArgs::new(&nodes[2], &nodes[1])); } else if completion_mode == BlockedUpdateComplMode::Async { - let (latest_update, _) = get_latest_mon_update_id(&nodes[1], chan_id_2); + let (latest_update, _) = nodes[1].chain_monitor.get_latest_mon_update_id(chan_id_2); nodes[1] .chain_monitor .chain_monitor @@ -3690,7 +3684,7 @@ fn do_test_inverted_mon_completion_order( // (Finally) complete the A <-> B ChannelMonitorUpdate, ensuring the preimage is durably on // disk in the proper ChannelMonitor, unblocking the B <-> C ChannelMonitor updating // process. - let (_, ab_update_id) = get_latest_mon_update_id(&nodes[1], chan_id_ab); + let (_, ab_update_id) = nodes[1].chain_monitor.get_latest_mon_update_id(chan_id_ab); nodes[1] .chain_monitor .chain_monitor @@ -3723,7 +3717,7 @@ fn do_test_inverted_mon_completion_order( // ChannelMonitorUpdate hasn't yet completed. reconnect_nodes(ReconnectArgs::new(&nodes[0], &nodes[1])); - let (_, ab_update_id) = get_latest_mon_update_id(&nodes[1], chan_id_ab); + let (_, ab_update_id) = nodes[1].chain_monitor.get_latest_mon_update_id(chan_id_ab); nodes[1] .chain_monitor .chain_monitor @@ -3936,7 +3930,7 @@ fn do_test_durable_preimages_on_closed_channel( // Once the blocked `ChannelMonitorUpdate` *finally* completes, the pending // `PaymentForwarded` event will finally be released. - let (_, ab_update_id) = get_latest_mon_update_id(&nodes[1], chan_id_ab); + let (_, ab_update_id) = nodes[1].chain_monitor.get_latest_mon_update_id(chan_id_ab); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(chan_id_ab, ab_update_id); // If the A<->B channel was closed before we reload, we'll replay the claim against it on @@ -4048,7 +4042,7 @@ fn do_test_reload_mon_update_completion_actions(close_during_reload: bool) { mine_transaction_without_consistency_checks(&nodes[1], &as_closing_tx[0]); } - let (_, bc_update_id) = get_latest_mon_update_id(&nodes[1], chan_id_bc); + let (_, bc_update_id) = nodes[1].chain_monitor.get_latest_mon_update_id(chan_id_bc); let mut events = nodes[1].node.get_and_clear_pending_events(); assert_eq!(events.len(), if close_during_reload { 2 } else { 1 }); expect_payment_forwarded( @@ -4073,7 +4067,7 @@ fn do_test_reload_mon_update_completion_actions(close_during_reload: bool) { // Once we run event processing the monitor should free, check that it was indeed the B<->C // channel which was updated. check_added_monitors(&nodes[1], if close_during_reload { 2 } else { 1 }); - let (_, post_ev_bc_update_id) = get_latest_mon_update_id(&nodes[1], chan_id_bc); + let (_, post_ev_bc_update_id) = nodes[1].chain_monitor.get_latest_mon_update_id(chan_id_bc); assert!(bc_update_id != post_ev_bc_update_id); // Finally, check that there's nothing left to do on B<->C reconnect and the channel operates @@ -4153,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); @@ -4163,7 +4157,7 @@ fn do_test_glacial_peer_cant_hang(hold_chan_a: bool) { // ...but once we complete the A<->B channel preimage persistence, the B<->C channel // unlocks and we send both peers commitment updates. - let (ab_update_id, _) = get_latest_mon_update_id(&nodes[1], chan_id_ab); + let (ab_update_id, _) = nodes[1].chain_monitor.get_latest_mon_update_id(chan_id_ab); assert!(nodes[1] .chain_monitor .chain_monitor @@ -4251,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; @@ -4260,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); @@ -5093,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); @@ -5123,7 +5119,7 @@ fn test_mpp_claim_to_holding_cell() { check_added_monitors(&nodes[3], 2); // Complete the B <-> D monitor update, freeing the first fulfill. - let (latest_id, _) = get_latest_mon_update_id(&nodes[3], chan_3_id); + let (latest_id, _) = nodes[3].chain_monitor.get_latest_mon_update_id(chan_3_id); nodes[3].chain_monitor.chain_monitor.channel_monitor_updated(chan_3_id, latest_id).unwrap(); let mut b_claim = get_htlc_update_msgs(&nodes[3], &node_b_id); @@ -5134,7 +5130,7 @@ fn test_mpp_claim_to_holding_cell() { // Finally, complete the C <-> D monitor update. Previously, this unlock failed to be processed // due to the existence of the blocked RAA update above. - let (latest_id, _) = get_latest_mon_update_id(&nodes[3], chan_4_id); + let (latest_id, _) = nodes[3].chain_monitor.get_latest_mon_update_id(chan_4_id); nodes[3].chain_monitor.chain_monitor.channel_monitor_updated(chan_4_id, latest_id).unwrap(); // Once we process monitor events (in this case by checking for the `PaymentClaimed` event, the // RAA monitor update blocked above will be released. diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 665a79a9610..18669e9771a 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -81,16 +81,19 @@ use crate::ln::onion_utils::{self}; use crate::ln::onion_utils::{ decode_fulfill_attribution_data, HTLCFailReason, LocalHTLCFailureReason, }; -use crate::ln::onion_utils::{process_fulfill_attribution_data, AttributionData}; +use crate::ln::onion_utils::{ + process_fulfill_attribution_data, AttributionData, DecodedOnionFailure, +}; use crate::ln::our_peer_storage::{EncryptedOurPeerStorage, PeerStorageMonitorHolder}; #[cfg(test)] use crate::ln::outbound_payment; #[cfg(any(test, feature = "_externalize_tests"))] use crate::ln::outbound_payment::PaymentSendFailure; use crate::ln::outbound_payment::{ - Bolt11PaymentError, Bolt12PaymentError, OutboundPayments, PendingOutboundPayment, - ProbeSendFailure, RecipientCustomTlvs, RecipientOnionFields, Retry, RetryableInvoiceRequest, - RetryableSendFailure, SendAlongPathArgs, StaleExpiration, + Bolt11PaymentError, Bolt12PaymentError, NextTrampolineHopInfo, OutboundPayments, + PendingOutboundPayment, ProbeSendFailure, RecipientCustomTlvs, RecipientOnionFields, Retry, + RetryableInvoiceRequest, RetryableSendFailure, SendAlongPathArgs, StaleExpiration, + TrampolineForwardInfo, }; use crate::ln::types::ChannelId; use crate::offers::async_receive_offer_cache::AsyncReceiveOfferCache; @@ -115,14 +118,14 @@ use crate::onion_message::offers::{OffersMessage, OffersMessageHandler}; use crate::routing::gossip::NodeId; use crate::routing::router::{ BlindedTail, FixedRouter, InFlightHtlcs, Path, Payee, PaymentParameters, Route, - RouteParameters, RouteParametersConfig, Router, + RouteParameters, RouteParametersConfig, Router, DEFAULT_MAX_PATH_COUNT, + MAX_PATH_LENGTH_ESTIMATE, }; use crate::sign::ecdsa::EcdsaChannelSigner; use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider}; -#[cfg(any(feature = "_test_utils", test))] -use crate::types::features::Bolt11InvoiceFeatures; use crate::types::features::{ - Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures, + Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, + InitFeatures, NodeFeatures, }; use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; use crate::types::string::UntrustedString; @@ -244,6 +247,12 @@ pub enum PendingHTLCRouting { blinded: Option, /// The absolute CLTV of the inbound HTLC incoming_cltv_expiry: u32, + /// MPP data for accumulating incoming HTLCs before dispatching an outbound payment. + incoming_multipath_data: Option, + /// The amount that the next trampoline is expecting to receive. + next_trampoline_amt_msat: u64, + /// The CLTV expiry height that the next trampoline is expecting to receive. + next_trampoline_cltv_expiry: u32, }, /// The onion indicates that this is a payment for an invoice (supposedly) generated by us. /// @@ -522,6 +531,8 @@ enum OnionPayload { }, /// Contains the payer-provided preimage. Spontaneous(PaymentPreimage), + /// Indicates that the incoming onion payload is for a trampoline forward. + Trampoline { next_hop_info: NextTrampolineHopInfo, next_trampoline: PublicKey }, } /// HTLCs that are to us and can be failed/claimed by the user @@ -545,6 +556,20 @@ struct ClaimableHTLC { counterparty_skimmed_fee_msat: Option, } +impl ClaimableHTLC { + // Increments timer ticks and returns a boolean indicating whether HLTC is timed out. + fn mpp_timer_tick(&mut self) -> bool { + self.timer_ticks += 1; + self.timer_ticks >= MPP_TIMEOUT_TICKS + } + + /// Returns a boolean indicating whether the HTLC has timed out on chain, accounting for a buffer + /// that gives us time to resolve it. + fn check_onchain_timeout(&self, height: u32, buffer: u32) -> bool { + height >= self.cltv_expiry - buffer + } +} + impl From<&ClaimableHTLC> for events::ClaimedHTLC { fn from(val: &ClaimableHTLC) -> Self { events::ClaimedHTLC { @@ -686,6 +711,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 +736,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, } } } @@ -742,6 +782,7 @@ impl Default for OptionalOfferPaymentParams { pub(crate) enum SentHTLCId { PreviousHopData { prev_outbound_scid_alias: u64, htlc_id: u64 }, OutboundRoute { session_priv: [u8; SECRET_KEY_SIZE] }, + TrampolineForward { session_priv: [u8; SECRET_KEY_SIZE] }, } impl SentHTLCId { pub(crate) fn from_source(source: &HTLCSource) -> Self { @@ -750,6 +791,15 @@ impl SentHTLCId { prev_outbound_scid_alias: hop_data.prev_outbound_scid_alias, htlc_id: hop_data.htlc_id, }, + HTLCSource::TrampolineForward { + ref outbound_payment, + .. + } => Self::TrampolineForward { + session_priv: outbound_payment + .as_ref() + .map(|o| o.session_priv.secret_bytes()) + .expect("trying to identify a trampoline payment that we have no outbound_payment tracked for"), + }, HTLCSource::OutboundRoute { session_priv, .. } => { Self::OutboundRoute { session_priv: session_priv.secret_bytes() } }, @@ -764,6 +814,9 @@ impl_writeable_tlv_based_enum!(SentHTLCId, (2, OutboundRoute) => { (0, session_priv, required), }, + (4, TrampolineForward) => { + (0, session_priv, required), + }, ); // (src_outbound_scid_alias, src_counterparty_node_id, src_funding_outpoint, src_chan_id, src_user_chan_id) @@ -775,11 +828,31 @@ type FailedHTLCForward = (HTLCSource, PaymentHash, HTLCFailReason, HTLCHandlingF mod fuzzy_channelmanager { use super::*; + /// Information about the outgoing payment dispatched to forward to the next trampoline. + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct TrampolineDispatch { + /// The payment ID used for the outbound payment. + pub payment_id: PaymentId, + /// The path used for the outbound payment. + pub path: Path, + /// The session private key used for inter-trampoline outer onions. + pub session_priv: SecretKey, + } + /// Tracks the inbound corresponding to an outbound HTLC #[allow(clippy::derive_hash_xor_eq)] // Our Hash is faithful to the data, we just don't have SecretKey::hash #[derive(Clone, Debug, PartialEq, Eq)] pub enum HTLCSource { PreviousHopData(HTLCPreviousHopData), + TrampolineForward { + /// We might be forwarding an incoming payment that was received over MPP, and therefore + /// need to store the vector of corresponding `HTLCPreviousHopData` values. + previous_hop_data: Vec, + incoming_trampoline_shared_secret: [u8; 32], + /// Track outbound payment details once the payment has been dispatched, will be `None` + /// when waiting for incoming MPP to accumulate. + outbound_payment: Option, + }, OutboundRoute { path: Path, session_priv: SecretKey, @@ -794,6 +867,26 @@ mod fuzzy_channelmanager { }, } + impl HTLCSource { + pub fn failure_type( + &self, counterparty_node: PublicKey, channel_id: ChannelId, + ) -> HTLCHandlingFailureType { + match self { + // We won't actually emit an event with HTLCHandlingFailure if our source is an + // OutboundRoute, but `fail_htlc_backwards_internal` requires that we provide it. + HTLCSource::PreviousHopData(_) | HTLCSource::OutboundRoute { .. } => { + HTLCHandlingFailureType::Forward { + node_id: Some(counterparty_node), + channel_id, + } + }, + HTLCSource::TrampolineForward { .. } => { + HTLCHandlingFailureType::TrampolineForward {} + }, + } + } + } + /// Tracks the inbound corresponding to an outbound HTLC #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct HTLCPreviousHopData { @@ -814,6 +907,16 @@ mod fuzzy_channelmanager { /// channel remains unconfirmed for too long. pub cltv_expiry: Option, } + + impl From<&HTLCPreviousHopData> for events::HTLCLocator { + fn from(value: &HTLCPreviousHopData) -> Self { + events::HTLCLocator { + channel_id: value.channel_id, + user_channel_id: value.user_channel_id, + node_id: value.counterparty_node_id, + } + } + } } #[cfg(fuzzing)] pub use self::fuzzy_channelmanager::*; @@ -842,6 +945,20 @@ impl core::hash::Hash for HTLCSource { first_hop_htlc_msat.hash(hasher); bolt12_invoice.hash(hasher); }, + HTLCSource::TrampolineForward { + previous_hop_data, + incoming_trampoline_shared_secret, + outbound_payment, + } => { + 2u8.hash(hasher); + previous_hop_data.hash(hasher); + incoming_trampoline_shared_secret.hash(hasher); + if let Some(payment) = outbound_payment { + payment.payment_id.hash(hasher); + payment.path.hash(hasher); + payment.session_priv[..].hash(hasher); + } + }, } } } @@ -1078,7 +1195,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), }); @@ -1105,6 +1222,38 @@ impl ClaimablePayment { .map(|htlc| (htlc.prev_hop.channel_id, htlc.prev_hop.user_channel_id)) .collect() } + + /// Returns the total counterparty skimmed fee across all HTLCs. + fn total_counterparty_skimmed_msat(&self) -> u64 { + self.htlcs.iter().map(|htlc| htlc.counterparty_skimmed_fee_msat.unwrap_or(0)).sum() + } +} + +/// Increments MPP timeout tick for all HTLCs and returns a boolean indicating whether the HTLC +/// set has hit its MPP timeout. Will return false if the set have reached the sender's intended +/// total, as the MPP has completed in this case. +fn check_mpp_timeout(sender_intended_total: u64, htlcs: &mut Vec) -> bool { + if htlcs.is_empty() { + debug_assert!(false, "check_mpp_timeout called with no HTLCs"); + return false; + } + if htlcs[0].total_msat <= sender_intended_total { + return false; + } + + htlcs.iter_mut().any(|htlc| htlc.mpp_timer_tick()) +} + +/// 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. @@ -1278,7 +1427,11 @@ enum BackgroundEvent { /// Some [`ChannelMonitorUpdate`] (s) completed before we were serialized but we still have /// them marked pending, thus we need to run any [`MonitorUpdateCompletionAction`] (s) pending /// on a channel. - MonitorUpdatesComplete { counterparty_node_id: PublicKey, channel_id: ChannelId }, + MonitorUpdatesComplete { + counterparty_node_id: PublicKey, + channel_id: ChannelId, + highest_update_id_completed: u64, + }, } /// A pointer to a channel that is unblocked when an event is surfaced @@ -1342,8 +1495,8 @@ pub(crate) enum MonitorUpdateCompletionAction { /// completes a monitor update containing the payment preimage. In that case, after the inbound /// edge completes, we will surface an [`Event::PaymentForwarded`] as well as unblock the /// outbound edge. - EmitEventAndFreeOtherChannel { - event: events::Event, + EmitEventOptionAndFreeOtherChannel { + event: Option, downstream_counterparty_and_funding_outpoint: Option, }, /// Indicates we should immediately resume the operation of another channel, unless there is @@ -1353,8 +1506,8 @@ pub(crate) enum MonitorUpdateCompletionAction { /// This is usually generated when we've forwarded an HTLC and want to block the outbound edge /// from completing a monitor update which removes the payment preimage until the inbound edge /// completes a monitor update containing the payment preimage. However, we use this variant - /// instead of [`Self::EmitEventAndFreeOtherChannel`] when we discover that the claim was in - /// fact duplicative and we simply want to resume the outbound edge channel immediately. + /// instead of [`Self::EmitEventOptionAndFreeOtherChannel`] when we discover that the claim was + /// in fact duplicative and we simply want to resume the outbound edge channel immediately. /// /// This variant should thus never be written to disk, as it is processed inline rather than /// stored for later processing. @@ -1377,8 +1530,11 @@ impl_writeable_tlv_based_enum_upgradable!(MonitorUpdateCompletionAction, (4, blocking_action, upgradable_required), (5, downstream_channel_id, required), }, - (2, EmitEventAndFreeOtherChannel) => { - (0, event, upgradable_required), + (2, EmitEventOptionAndFreeOtherChannel) => { + // LDK prior to 0.3 required this field. It will not be present for trampoline payments + // with multiple incoming HTLCS, so nodes cannot downgrade while trampoline payments + // are in the process of being resolved. + (0, event, upgradable_option), // LDK prior to 0.0.116 did not have this field as the monitor update application order was // required by clients. If we downgrade to something prior to 0.0.116 this may result in // monitor updates which aren't properly blocked or resumed, however that's fine - we don't @@ -2681,6 +2837,12 @@ pub struct ChannelManager< /// [`ClaimablePayments`]' individual field docs for more info. claimable_payments: Mutex, + /// The sets of trampoline payments which are in the process of being accumulated on inbound + /// channel(s). + /// + /// Note: Not adding ChannelMangaer struct level docs because 4300 removes it. + awaiting_trampoline_forwards: Mutex>, + /// The set of outbound SCID aliases across all our channels, including unconfirmed channels /// and some closed channels which reached a usable state prior to being closed. This is used /// only to avoid duplicates, and is not persisted explicitly to disk, but rebuilt from the @@ -3453,6 +3615,7 @@ impl< forward_htlcs: Mutex::new(new_hash_map()), decode_update_add_htlcs: Mutex::new(new_hash_map()), claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: new_hash_map(), pending_claiming_payments: new_hash_map() }), + awaiting_trampoline_forwards: Mutex::new(new_hash_map()), pending_intercepted_htlcs: Mutex::new(new_hash_map()), short_to_chan_info: FairRwLock::new(new_hash_map()), @@ -3903,11 +4066,8 @@ impl< for htlc_source in failed_htlcs.drain(..) { let failure_reason = LocalHTLCFailureReason::ChannelClosed; let reason = HTLCFailReason::from_failure_code(failure_reason); - let receiver = HTLCHandlingFailureType::Forward { - node_id: Some(*counterparty_node_id), - channel_id: *chan_id, - }; let (source, hash) = htlc_source; + let receiver = source.failure_type(*counterparty_node_id, *chan_id); self.fail_htlc_backwards_internal(&source, &hash, &reason, receiver, None); } @@ -4070,10 +4230,7 @@ impl< let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source; let failure_reason = LocalHTLCFailureReason::ChannelClosed; let reason = HTLCFailReason::from_failure_code(failure_reason); - let receiver = HTLCHandlingFailureType::Forward { - node_id: Some(counterparty_node_id), - channel_id, - }; + let receiver = source.failure_type(counterparty_node_id, channel_id); self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver, None); } if let Some((_, funding_txo, _channel_id, monitor_update)) = shutdown_res.monitor_update { @@ -4844,6 +5001,7 @@ impl< fn can_forward_htlc_should_intercept( &self, msg: &msgs::UpdateAddHTLC, next_hop: &NextPacketDetails, ) -> Result { + let cur_height = self.best_block.read().unwrap().height + 1; let outgoing_scid = match next_hop.outgoing_connector { HopConnector::ShortChannelId(scid) => scid, HopConnector::Dummy => { @@ -4851,8 +5009,24 @@ impl< debug_assert!(false, "Dummy hop reached HTLC handling."); return Err(LocalHTLCFailureReason::InvalidOnionPayload); }, + // We can't make forwarding checks on trampoline forwards where we don't know the + // outgoing channel on receipt of the incoming htlc. Our trampoline logic will check + // our required delta and fee later on, so here we just check that the forwarding node + // did not "skim" off some of the sender's intended fee/cltv. HopConnector::Trampoline(_) => { - return Err(LocalHTLCFailureReason::InvalidTrampolineForward); + if msg.amount_msat < next_hop.outgoing_amt_msat { + return Err(LocalHTLCFailureReason::FeeInsufficient); + } + + check_incoming_htlc_cltv( + cur_height, + next_hop.outgoing_cltv_value, + msg.cltv_expiry, + 0, + )?; + + // TODO: what do we do about interception for trampoline? + return Ok(false); }, }; // TODO: We do the fake SCID namespace check a bunch of times here (and indirectly via @@ -4891,9 +5065,12 @@ impl< }, }; - let cur_height = self.best_block.read().unwrap().height + 1; - check_incoming_htlc_cltv(cur_height, next_hop.outgoing_cltv_value, msg.cltv_expiry)?; - + check_incoming_htlc_cltv( + cur_height, + next_hop.outgoing_cltv_value, + msg.cltv_expiry, + MIN_CLTV_EXPIRY_DELTA.into(), + )?; Ok(intercept) } @@ -5115,20 +5292,20 @@ 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, invoice_request: None, bolt12_invoice: None, + trampoline_forward_info: None, session_priv_bytes, hold_htlc_at_next_hop: false, }) @@ -5139,12 +5316,12 @@ impl< path, payment_hash, recipient_onion, - total_value, cur_height, payment_id, keysend_preimage, invoice_request, bolt12_invoice, + trampoline_forward_info, session_priv_bytes, hold_htlc_at_next_hop, } = args; @@ -5160,19 +5337,32 @@ impl< Some(*payment_hash), payment_id, ); - let (onion_packet, htlc_msat, htlc_cltv) = onion_utils::create_payment_onion( - &self.secp_ctx, - &path, - &session_priv, - total_value, - recipient_onion, - cur_height, - payment_hash, - keysend_preimage, - invoice_request, - prng_seed, - ) - .map_err(|e| { + let onion_result = if let Some(trampoline_forward_info) = trampoline_forward_info { + onion_utils::create_trampoline_forward_onion( + &self.secp_ctx, + &path, + &session_priv, + payment_hash, + recipient_onion, + keysend_preimage, + &trampoline_forward_info.next_hop_info, + prng_seed, + ) + } else { + onion_utils::create_payment_onion( + &self.secp_ctx, + &path, + &session_priv, + recipient_onion, + cur_height, + payment_hash, + keysend_preimage, + invoice_request, + prng_seed, + ) + }; + + let (onion_packet, htlc_msat, htlc_cltv) = onion_result.map_err(|e| { log_error!(logger, "Failed to build an onion for path"); e })?; @@ -5216,12 +5406,26 @@ impl< }); } let funding_txo = chan.funding.get_funding_txo().unwrap(); - let htlc_source = HTLCSource::OutboundRoute { - path: path.clone(), - session_priv: session_priv.clone(), - first_hop_htlc_msat: htlc_msat, - payment_id, - bolt12_invoice: bolt12_invoice.cloned(), + let htlc_source = match trampoline_forward_info { + None => HTLCSource::OutboundRoute { + path: path.clone(), + session_priv: session_priv.clone(), + first_hop_htlc_msat: htlc_msat, + payment_id, + bolt12_invoice: bolt12_invoice.cloned(), + }, + Some(trampoline_forward_info) => HTLCSource::TrampolineForward { + previous_hop_data: trampoline_forward_info + .previous_hop_data + .clone(), + incoming_trampoline_shared_secret: trampoline_forward_info + .incoming_trampoline_shared_secret, + outbound_payment: Some(TrampolineDispatch { + payment_id, + path: path.clone(), + session_priv, + }), + }, }; let send_res = chan.send_htlc_and_commit( htlc_msat, @@ -5346,7 +5550,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`]. /// @@ -5383,7 +5587,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); @@ -5393,7 +5597,6 @@ impl< recipient_onion, keysend_preimage, payment_id, - recv_value_msat, onion_session_privs, &self.node_signer, best_block_height, @@ -5444,10 +5647,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`]. /// @@ -7181,7 +7392,7 @@ impl< .push(failure); self.pending_events.lock().unwrap().push_back(( events::Event::HTLCHandlingFailed { - prev_channel_id: incoming_channel_id, + prev_channel_ids: vec![incoming_channel_id], failure_type, failure_reason: Some(failure_reason), }, @@ -7366,6 +7577,8 @@ impl< }; failed_forwards.push(( + // This can't be a trampoline payment because we don't process them + // as forwards (we're the last/"receiving" onion node). HTLCSource::PreviousHopData(prev_hop), payment_hash, HTLCFailReason::reason(reason, err_data), @@ -7759,6 +7972,324 @@ impl< } } + // Checks whether an incoming htlc can be added to our [`claimable_payments`], and handles + // MPP accumulation. On successful add, returns Ok() with a boolean indicating whether all + // MPP parts have arrrived. Callers *MUST NOT* fail htlcs if Ok(..) is returned. + fn check_claimable_incoming_htlc( + &self, claimable_payment: &mut ClaimablePayment, claimable_htlc: ClaimableHTLC, + mut onion_fields: RecipientOnionFields, payment_hash: PaymentHash, + ) -> Result { + if let Some(earlier_fields) = &mut claimable_payment.onion_fields { + if earlier_fields.check_merge(&mut onion_fields).is_err() { + return Err(()); + } + } else { + claimable_payment.onion_fields = Some(onion_fields); + } + let mut total_value = claimable_htlc.sender_intended_value; + for htlc in claimable_payment.htlcs.iter() { + total_value += htlc.sender_intended_value; + if htlc.total_msat != claimable_htlc.total_msat { + log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})", + &payment_hash, claimable_htlc.total_msat, htlc.total_msat); + total_value = msgs::MAX_VALUE_MSAT; + } + if total_value >= msgs::MAX_VALUE_MSAT { + break; + } + } + // The condition determining whether an MPP is complete must + // match exactly the condition used in `timer_tick_occurred` + if total_value >= msgs::MAX_VALUE_MSAT { + return Err(()); + } else if total_value - claimable_htlc.sender_intended_value >= claimable_htlc.total_msat { + log_trace!( + self.logger, + "Failing HTLC with payment_hash {} as payment is already claimable", + &payment_hash + ); + return Err(()); + } else if total_value >= claimable_htlc.total_msat { + claimable_payment.htlcs.push(claimable_htlc); + let amount_msat = claimable_payment.htlcs.iter().map(|htlc| htlc.value).sum(); + claimable_payment + .htlcs + .iter_mut() + .for_each(|htlc| htlc.total_value_received = Some(amount_msat)); + let counterparty_skimmed_fee_msat = claimable_payment + .htlcs + .iter() + .map(|htlc| htlc.counterparty_skimmed_fee_msat.unwrap_or(0)) + .sum(); + debug_assert!(total_value.saturating_sub(amount_msat) <= counterparty_skimmed_fee_msat); + claimable_payment.htlcs.sort(); + + Ok(true) + } else { + // Nothing to do - we haven't reached the total + // payment value yet, wait until we receive more + // MPP parts. + claimable_payment.htlcs.push(claimable_htlc); + Ok(false) + } + } + + // Handles the addition of a HTLC associated with a payment we're receiving. Err(bool) indicates + // whether we have failed after adding committing to the HTLC - callers should assert that this + // value is false. + fn handle_claimable_htlc( + &self, purpose: events::PaymentPurpose, claimable_htlc: ClaimableHTLC, + onion_fields: RecipientOnionFields, payment_hash: PaymentHash, receiver_node_id: PublicKey, + new_events: &mut VecDeque<(Event, Option)>, + ) -> Result<(), bool> { + let mut committed_to_claimable = false; + + let mut claimable_payments = self.claimable_payments.lock().unwrap(); + if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) { + return Err(committed_to_claimable); + } + + let ref mut claimable_payment = claimable_payments + .claimable_payments + .entry(payment_hash) + // Note that if we insert here we MUST NOT fail_htlc!() + .or_insert_with(|| { + committed_to_claimable = true; + ClaimablePayment { purpose: purpose.clone(), htlcs: Vec::new(), onion_fields: None } + }); + + let is_keysend = purpose.is_keysend(); + if purpose != claimable_payment.purpose { + let log_keysend = |keysend| if keysend { "keysend" } else { "non-keysend" }; + log_trace!(self.logger, "Failing new {} HTLC with payment_hash {} as we already had an existing {} HTLC with the same payment hash", log_keysend(is_keysend), &payment_hash, log_keysend(!is_keysend)); + return Err(committed_to_claimable); + } + + if self + .check_claimable_incoming_htlc( + claimable_payment, + claimable_htlc, + onion_fields, + payment_hash, + ) + .map_err(|_| committed_to_claimable)? + { + new_events.push_back(( + events::Event::PaymentClaimable { + receiver_node_id: Some(receiver_node_id), + payment_hash, + purpose, + amount_msat: claimable_payment.htlcs.iter().map(|htlc| htlc.value).sum(), + counterparty_skimmed_fee_msat: claimable_payment + .total_counterparty_skimmed_msat(), + receiving_channel_ids: claimable_payment.receiving_channel_ids(), + claim_deadline: Some( + claimable_payment.htlcs.iter().map(|h| h.cltv_expiry).min().unwrap() // TODO: don't unwrap + - HTLC_FAIL_BACK_BUFFER, + ), + onion_fields: claimable_payment.onion_fields.clone(), + payment_id: Some( + claimable_payment.inbound_payment_id(&self.inbound_payment_id_secret), + ), + }, + None, + )); + } + Ok(()) + } + + // Handles the addition of a HTLC associated with a trampoline forward that we need to accumulate + // on the incoming link before forwarding onwards. If the HTLC is failed, it returns the source + // and error that should be used to fail the HTLC(s) back. + fn handle_trampoline_htlc( + &self, claimable_htlc: ClaimableHTLC, onion_fields: RecipientOnionFields, + payment_hash: PaymentHash, incoming_trampoline_shared_secret: [u8; 32], + next_hop_info: NextTrampolineHopInfo, next_node_id: PublicKey, + ) -> Result<(), (HTLCSource, HTLCFailReason)> { + let mut trampoline_payments = self.awaiting_trampoline_forwards.lock().unwrap(); + + let mut committed_to_claimable = false; + let claimable_payment = trampoline_payments.entry(payment_hash).or_insert_with(|| { + committed_to_claimable = true; + ClaimablePayment { + purpose: events::PaymentPurpose::Trampoline {}, + htlcs: Vec::new(), + onion_fields: None, + } + }); + + // If MPP hasn't fully arrived yet, return early (saving indentation below). + let prev_hop = claimable_htlc.prev_hop.clone(); + if !self + .check_claimable_incoming_htlc( + claimable_payment, + claimable_htlc, + onion_fields, + payment_hash, + ) + .map_err(|_| { + debug_assert!(!committed_to_claimable); + ( + // When we couldn't add a new HTLC, we just fail back our last received htlc, + // allowing others to wait for more MPP parts to arrive. If this was the first + // htlc we'll eventually clean up the awaiting_trampoline_forwards entry in + // our MPP timeout logic. + HTLCSource::TrampolineForward { + previous_hop_data: vec![prev_hop], + incoming_trampoline_shared_secret, + outbound_payment: None, + }, + HTLCFailReason::reason( + LocalHTLCFailureReason::InvalidTrampolineForward, + vec![], + ), + ) + })? { + return Ok(()); + } + + let incoming_amt_msat: u64 = claimable_payment.htlcs.iter().map(|h| h.value).sum(); + let incoming_cltv_expiry = + claimable_payment.htlcs.iter().map(|h| h.cltv_expiry).min().unwrap(); + + let (forwarding_fee_proportional_millionths, forwarding_fee_base_msat, cltv_delta) = { + let config = self.config.read().unwrap(); + ( + config.channel_config.forwarding_fee_proportional_millionths, + config.channel_config.forwarding_fee_base_msat, + config.channel_config.cltv_expiry_delta as u32, + ) + }; + let proportional_fee = + forwarding_fee_proportional_millionths as u64 * next_hop_info.amount_msat / 1_000_000; + let our_forwarding_fee_msat = proportional_fee + forwarding_fee_base_msat as u64; + + let trampoline_source = || -> HTLCSource { + HTLCSource::TrampolineForward { + previous_hop_data: claimable_payment + .htlcs + .iter() + .map(|htlc| htlc.prev_hop.clone()) + .collect(), + incoming_trampoline_shared_secret, + outbound_payment: None, + } + }; + let trampoline_failure = || -> HTLCFailReason { + let mut err_data = Vec::with_capacity(10); + err_data.extend_from_slice(&forwarding_fee_base_msat.to_be_bytes()); + err_data.extend_from_slice(&forwarding_fee_proportional_millionths.to_be_bytes()); + err_data.extend_from_slice(&(cltv_delta as u16).to_be_bytes()); + HTLCFailReason::reason( + LocalHTLCFailureReason::TrampolineFeeOrExpiryInsufficient, + err_data, + ) + }; + + let max_total_routing_fee_msat = match incoming_amt_msat + .checked_sub(our_forwarding_fee_msat + next_hop_info.amount_msat) + { + Some(amount) => amount, + None => { + return Err((trampoline_source(), trampoline_failure())); + }, + }; + + let max_total_cltv_expiry_delta = + match incoming_cltv_expiry.checked_sub(next_hop_info.cltv_expiry_height + cltv_delta) { + Some(cltv_delta) => cltv_delta, + None => { + return Err((trampoline_source(), trampoline_failure())); + }, + }; + + // Assume any Trampoline node supports MPP + let mut recipient_features = Bolt11InvoiceFeatures::empty(); + recipient_features.set_basic_mpp_optional(); + + let route_parameters = RouteParameters { + payment_params: PaymentParameters { + payee: Payee::Clear { + node_id: next_node_id, // TODO: this can be threaded through from above + route_hints: vec![], + features: Some(recipient_features), + // When sending a trampoline payment, we assume that the original sender has + // baked a final cltv into our instructions. + final_cltv_expiry_delta: 0, + }, + expiry_time: None, + max_total_cltv_expiry_delta, + max_path_count: DEFAULT_MAX_PATH_COUNT, + max_path_length: MAX_PATH_LENGTH_ESTIMATE / 2, + max_channel_saturation_power_of_half: 2, + previously_failed_channels: vec![], + previously_failed_blinded_path_idxs: vec![], + }, + final_value_msat: next_hop_info.amount_msat, + max_total_routing_fee_msat: Some(max_total_routing_fee_msat), + }; + + #[cfg(not(any(test, feature = "_test_utils")))] + let retry_strategy = Retry::Attempts(3); + #[cfg(any(test, feature = "_test_utils"))] + let retry_strategy = Retry::Attempts(0); + + log_debug!( + self.logger, + "Attempting to forward trampoline payment that pays us {} with {} fee budget ({} total, {} cltv max)", + our_forwarding_fee_msat, + max_total_routing_fee_msat, + next_hop_info.amount_msat, + max_total_cltv_expiry_delta, + ); + let result = self.pending_outbound_payments.send_payment_for_trampoline_forward( + PaymentId(payment_hash.0), + payment_hash, + TrampolineForwardInfo { + next_hop_info, + previous_hop_data: claimable_payment + .htlcs + .iter() + .map(|htlc| htlc.prev_hop.clone()) + .collect(), + incoming_trampoline_shared_secret, + forwading_fee_msat: our_forwarding_fee_msat, + }, + retry_strategy, + route_parameters.clone(), + &self.router, + self.list_usable_channels(), + || self.compute_inflight_htlcs(), + &self.entropy_source, + &self.node_signer, + self.current_best_block().height, + &self.pending_events, + |args| self.send_payment_along_path(args), + &WithContext::from(&self.logger, None, None, Some(payment_hash)), + ); + + let source = trampoline_source(); + if trampoline_payments.remove(&payment_hash).is_none() { + log_error!( + &self.logger, + "Dispatched trampoline payment: {} was not present in awaiting inbound", + payment_hash + ); + return Err(( + source, + HTLCFailReason::reason(LocalHTLCFailureReason::TemporaryTrampolineFailure, vec![]), + )); + } + + if let Err(_retryable_send_failure) = result { + return Err(( + source, + HTLCFailReason::reason(LocalHTLCFailureReason::TemporaryTrampolineFailure, vec![]), + )); + }; + Ok(()) + } + fn process_receive_htlcs( &self, pending_forwards: &mut Vec, new_events: &mut VecDeque<(Event, Option)>, @@ -7789,7 +8320,7 @@ impl< payment_data, payment_context, phantom_shared_secret, - mut onion_fields, + onion_fields, has_recipient_created_payment_secret, invoice_request_opt, trampoline_shared_secret, @@ -7809,6 +8340,7 @@ impl< payment_secret: Some(payment_data.payment_secret), payment_metadata, custom_tlvs, + total_mpp_amount_msat: payment_data.total_msat, }; ( incoming_cltv_expiry, @@ -7837,6 +8369,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, }; @@ -7852,16 +8388,80 @@ impl< None, ) }, + PendingHTLCRouting::TrampolineForward { + incoming_shared_secret: incoming_trampoline_shared_secret, + onion_packet, + node_id: next_trampoline, + blinded, + incoming_cltv_expiry, + incoming_multipath_data, + next_trampoline_amt_msat, + next_trampoline_cltv_expiry, + } => { + // Trampoline forwards only *need* to have MPP data if they're + // multi-part. + let onion_fields = match incoming_multipath_data { + Some(ref final_mpp) => RecipientOnionFields::secret_only( + final_mpp.payment_secret, + final_mpp.total_msat, + ), + None => RecipientOnionFields::spontaneous_empty(outgoing_amt_msat), + }; + ( + incoming_cltv_expiry, + OnionPayload::Trampoline { + next_hop_info: NextTrampolineHopInfo { + onion_packet, + blinding_point: blinded.and_then(|b| { + b.next_blinding_override.or_else(|| { + let encrypted_tlvs_ss = self + .node_signer + .ecdh( + Recipient::Node, + &b.inbound_blinding_point, + None, + ) + .unwrap() + .secret_bytes(); + onion_utils::next_hop_pubkey( + &self.secp_ctx, + b.inbound_blinding_point, + &encrypted_tlvs_ss, + ) + .ok() + }) + }), + amount_msat: next_trampoline_amt_msat, + cltv_expiry_height: next_trampoline_cltv_expiry, + }, + next_trampoline, + }, + incoming_multipath_data, + None, + None, + onion_fields, + false, + None, + Some(incoming_trampoline_shared_secret), + ) + }, _ => { panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive"); }, }; + let htlc_value = incoming_amt_msat.unwrap_or(outgoing_amt_msat); + let prev_outbound_scid_alias = prev_hop.prev_outbound_scid_alias; + let user_channel_id = prev_hop.user_channel_id; + let counterparty_node_id = prev_hop.counterparty_node_id; + let htlc_id = prev_hop.htlc_id; + let incoming_packet_shared_secret = prev_hop.incoming_packet_shared_secret; + let claimable_htlc = ClaimableHTLC { prev_hop, // We differentiate the received value from the sender intended value // if possible so that we don't prematurely mark MPP payments complete // if routing nodes overpay - value: incoming_amt_msat.unwrap_or(outgoing_amt_msat), + value: htlc_value, sender_intended_value: outgoing_amt_msat, timer_ticks: 0, total_value_received: None, @@ -7875,39 +8475,34 @@ impl< counterparty_skimmed_fee_msat: skimmed_fee_msat, }; - let mut committed_to_claimable = false; - - macro_rules! fail_htlc { - ($htlc: expr, $payment_hash: expr) => { - debug_assert!(!committed_to_claimable); + macro_rules! fail_receive_htlc { + ($committed_to_claimable: expr) => { + let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData { + prev_outbound_scid_alias, + user_channel_id, + counterparty_node_id, + channel_id: prev_channel_id, + outpoint: prev_funding_outpoint, + htlc_id, + incoming_packet_shared_secret, + phantom_shared_secret, + trampoline_shared_secret, + blinded_failure, + cltv_expiry: Some(cltv_expiry), + }); + debug_assert!(!$committed_to_claimable); let err_data = invalid_payment_err_data( - $htlc.value, + htlc_value, self.best_block.read().unwrap().height, ); - let counterparty_node_id = $htlc.prev_hop.counterparty_node_id; - let incoming_packet_shared_secret = - $htlc.prev_hop.incoming_packet_shared_secret; - let prev_outbound_scid_alias = $htlc.prev_hop.prev_outbound_scid_alias; failed_forwards.push(( - HTLCSource::PreviousHopData(HTLCPreviousHopData { - prev_outbound_scid_alias, - user_channel_id: $htlc.prev_hop.user_channel_id, - counterparty_node_id, - channel_id: prev_channel_id, - outpoint: prev_funding_outpoint, - htlc_id: $htlc.prev_hop.htlc_id, - incoming_packet_shared_secret, - phantom_shared_secret, - trampoline_shared_secret, - blinded_failure, - cltv_expiry: Some(cltv_expiry), - }), + htlc_source, payment_hash, HTLCFailReason::reason( LocalHTLCFailureReason::IncorrectPaymentDetails, err_data, ), - HTLCHandlingFailureType::Receive { payment_hash: $payment_hash }, + HTLCHandlingFailureType::Receive { payment_hash }, )); continue 'next_forwardable_htlc; }; @@ -7921,96 +8516,6 @@ impl< .expect("Failed to get node_id for phantom node recipient"); } - macro_rules! check_total_value { - ($purpose: expr) => {{ - let mut payment_claimable_generated = false; - let is_keysend = $purpose.is_keysend(); - let mut claimable_payments = self.claimable_payments.lock().unwrap(); - if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) { - fail_htlc!(claimable_htlc, payment_hash); - } - let ref mut claimable_payment = claimable_payments.claimable_payments - .entry(payment_hash) - // Note that if we insert here we MUST NOT fail_htlc!() - .or_insert_with(|| { - committed_to_claimable = true; - ClaimablePayment { - purpose: $purpose.clone(), htlcs: Vec::new(), onion_fields: None, - } - }); - if $purpose != claimable_payment.purpose { - let log_keysend = |keysend| if keysend { "keysend" } else { "non-keysend" }; - log_trace!(self.logger, "Failing new {} HTLC with payment_hash {} as we already had an existing {} HTLC with the same payment hash", log_keysend(is_keysend), &payment_hash, log_keysend(!is_keysend)); - fail_htlc!(claimable_htlc, payment_hash); - } - if let Some(earlier_fields) = &mut claimable_payment.onion_fields { - if earlier_fields.check_merge(&mut onion_fields).is_err() { - fail_htlc!(claimable_htlc, payment_hash); - } - } else { - claimable_payment.onion_fields = Some(onion_fields); - } - let mut total_value = claimable_htlc.sender_intended_value; - let mut earliest_expiry = claimable_htlc.cltv_expiry; - for htlc in claimable_payment.htlcs.iter() { - total_value += htlc.sender_intended_value; - earliest_expiry = cmp::min(earliest_expiry, htlc.cltv_expiry); - if htlc.total_msat != claimable_htlc.total_msat { - log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})", - &payment_hash, claimable_htlc.total_msat, htlc.total_msat); - total_value = msgs::MAX_VALUE_MSAT; - } - if total_value >= msgs::MAX_VALUE_MSAT { break; } - } - // The condition determining whether an MPP is complete must - // match exactly the condition used in `timer_tick_occurred` - if total_value >= msgs::MAX_VALUE_MSAT { - fail_htlc!(claimable_htlc, payment_hash); - } else if total_value - claimable_htlc.sender_intended_value >= claimable_htlc.total_msat { - log_trace!(self.logger, "Failing HTLC with payment_hash {} as payment is already claimable", - &payment_hash); - fail_htlc!(claimable_htlc, payment_hash); - } else if total_value >= claimable_htlc.total_msat { - #[allow(unused_assignments)] { - committed_to_claimable = true; - } - claimable_payment.htlcs.push(claimable_htlc); - let amount_msat = - claimable_payment.htlcs.iter().map(|htlc| htlc.value).sum(); - claimable_payment.htlcs.iter_mut() - .for_each(|htlc| htlc.total_value_received = Some(amount_msat)); - let counterparty_skimmed_fee_msat = claimable_payment.htlcs.iter() - .map(|htlc| htlc.counterparty_skimmed_fee_msat.unwrap_or(0)).sum(); - debug_assert!(total_value.saturating_sub(amount_msat) <= - counterparty_skimmed_fee_msat); - claimable_payment.htlcs.sort(); - let payment_id = - claimable_payment.inbound_payment_id(&self.inbound_payment_id_secret); - new_events.push_back((events::Event::PaymentClaimable { - receiver_node_id: Some(receiver_node_id), - payment_hash, - purpose: $purpose, - amount_msat, - counterparty_skimmed_fee_msat, - receiving_channel_ids: claimable_payment.receiving_channel_ids(), - claim_deadline: Some(earliest_expiry - HTLC_FAIL_BACK_BUFFER), - onion_fields: claimable_payment.onion_fields.clone(), - payment_id: Some(payment_id), - }, None)); - payment_claimable_generated = true; - } else { - // Nothing to do - we haven't reached the total - // payment value yet, wait until we receive more - // MPP parts. - claimable_payment.htlcs.push(claimable_htlc); - #[allow(unused_assignments)] { - committed_to_claimable = true; - } - } - payment_claimable_generated - }} - } - // Check that the payment hash and secret are known. Note that we // MUST take care to handle the "unknown payment hash" and // "incorrect payment secret" cases here identically or we'd expose @@ -8030,7 +8535,7 @@ impl< Ok(result) => result, Err(()) => { log_trace!(self.logger, "Failing new HTLC with payment_hash {} as payment verification failed", &payment_hash); - fail_htlc!(claimable_htlc, payment_hash); + fail_receive_htlc!(false); }, }; if let Some(min_final_cltv_expiry_delta) = min_final_cltv_expiry_delta { @@ -8040,12 +8545,12 @@ impl< if (cltv_expiry as u64) < expected_min_expiry_height { log_trace!(self.logger, "Failing new HTLC with payment_hash {} as its CLTV expiry was too soon (had {}, earliest expected {})", &payment_hash, cltv_expiry, expected_min_expiry_height); - fail_htlc!(claimable_htlc, payment_hash); + fail_receive_htlc!(false); } } payment_preimage } else { - fail_htlc!(claimable_htlc, payment_hash); + fail_receive_htlc!(false); } } else { None @@ -8061,10 +8566,20 @@ impl< let purpose = match from_parts_res { Ok(purpose) => purpose, Err(()) => { - fail_htlc!(claimable_htlc, payment_hash); + fail_receive_htlc!(false); }, }; - check_total_value!(purpose); + + if let Err(committed_to_claimable) = self.handle_claimable_htlc( + purpose, + claimable_htlc, + onion_fields, + payment_hash, + receiver_node_id, + new_events, + ) { + fail_receive_htlc!(committed_to_claimable); + } }, OnionPayload::Spontaneous(keysend_preimage) => { let purpose = if let Some(PaymentContext::AsyncBolt12Offer( @@ -8078,7 +8593,7 @@ impl< false, "We checked that payment_data is Some above" ); - fail_htlc!(claimable_htlc, payment_hash); + fail_receive_htlc!(false); }, }; @@ -8097,13 +8612,13 @@ impl< verified_invreq.amount_msats() { if payment_data.total_msat < invreq_amt_msat { - fail_htlc!(claimable_htlc, payment_hash); + fail_receive_htlc!(false); } } verified_invreq }, None => { - fail_htlc!(claimable_htlc, payment_hash); + fail_receive_htlc!(false); }, }; let payment_purpose_context = @@ -8119,16 +8634,45 @@ impl< match from_parts_res { Ok(purpose) => purpose, Err(()) => { - fail_htlc!(claimable_htlc, payment_hash); + fail_receive_htlc!(false); }, } } else if payment_context.is_some() { log_trace!(self.logger, "Failing new HTLC with payment_hash {}: received a keysend payment to a non-async payments context {:#?}", payment_hash, payment_context); - fail_htlc!(claimable_htlc, payment_hash); + fail_receive_htlc!(false); } else { events::PaymentPurpose::SpontaneousPayment(keysend_preimage) }; - check_total_value!(purpose); + if let Err(committed_to_claimable) = self.handle_claimable_htlc( + purpose, + claimable_htlc, + onion_fields, + payment_hash, + receiver_node_id, + new_events, + ) { + fail_receive_htlc!(committed_to_claimable); + } + }, + OnionPayload::Trampoline { ref next_hop_info, next_trampoline } => { + let next_hop_info = next_hop_info.clone(); + if let Err((htlc_source, failure_reason)) = self.handle_trampoline_htlc( + claimable_htlc, + onion_fields, + payment_hash, + // Safe to unwrap because we set to Some above. + trampoline_shared_secret.unwrap(), + next_hop_info, + next_trampoline, + ) { + failed_forwards.push(( + htlc_source, + payment_hash, + failure_reason, + HTLCHandlingFailureType::TrampolineForward {}, + )); + continue 'next_forwardable_htlc; + } }, } }, @@ -8142,9 +8686,11 @@ impl< /// Free the background events, generally called from [`PersistenceNotifierGuard`] constructors. /// /// Expects the caller to have a total_consistency_lock read lock. - #[rustfmt::skip] fn process_background_events(&self) -> NotifyOption { - debug_assert_ne!(self.total_consistency_lock.held_by_thread(), LockHeldState::NotHeldByThread); + debug_assert_ne!( + self.total_consistency_lock.held_by_thread(), + LockHeldState::NotHeldByThread + ); self.background_events_processed_since_startup.store(true, Ordering::Release); @@ -8156,11 +8702,34 @@ impl< for event in background_events.drain(..) { match event { - BackgroundEvent::MonitorUpdateRegeneratedOnStartup { counterparty_node_id, funding_txo, channel_id, update } => { - self.apply_post_close_monitor_update(counterparty_node_id, channel_id, funding_txo, update); + BackgroundEvent::MonitorUpdateRegeneratedOnStartup { + counterparty_node_id, + funding_txo, + channel_id, + update, + } => { + self.apply_post_close_monitor_update( + counterparty_node_id, + channel_id, + funding_txo, + update, + ); }, - BackgroundEvent::MonitorUpdatesComplete { counterparty_node_id, channel_id } => { - self.channel_monitor_updated(&channel_id, None, &counterparty_node_id); + BackgroundEvent::MonitorUpdatesComplete { + counterparty_node_id, + channel_id, + highest_update_id_completed, + } => { + // Now that we can finally handle the background event, remove all in-flight + // monitor updates for this channel that we've known to complete, as they have + // already been persisted to the monitor and can be applied to our internal + // state such that the channel resumes operation if no new updates have been + // made since. + self.channel_monitor_updated( + &channel_id, + Some(highest_update_id_completed), + &counterparty_node_id, + ); }, } } @@ -8442,42 +9011,68 @@ impl< self.claimable_payments.lock().unwrap().claimable_payments.retain( |payment_hash, payment| { - if payment.htlcs.is_empty() { - // This should be unreachable - debug_assert!(false); - return false; - } if let OnionPayload::Invoice { .. } = payment.htlcs[0].onion_payload { - // Check if we've received all the parts we need for an MPP (the value of the parts adds to total_msat). - // In this case we're not going to handle any timeouts of the parts here. - // This condition determining whether the MPP is complete here must match - // exactly the condition used in `process_pending_htlc_forwards`. - let htlc_total_msat = + let htlc_total_msat: u64 = payment.htlcs.iter().map(|h| h.sender_intended_value).sum(); - if payment.htlcs[0].total_msat <= htlc_total_msat { - return true; - } else if payment.htlcs.iter_mut().any(|htlc| { - htlc.timer_ticks += 1; - return htlc.timer_ticks >= MPP_TIMEOUT_TICKS; - }) { - let htlcs = payment - .htlcs - .drain(..) - .map(|htlc: ClaimableHTLC| (htlc.prev_hop, *payment_hash)); - timed_out_mpp_htlcs.extend(htlcs); - return false; + let mpp_timeout = check_mpp_timeout(htlc_total_msat, &mut payment.htlcs); + if mpp_timeout { + timed_out_mpp_htlcs.extend(payment.htlcs.drain(..).map(|h| { + ( + HTLCSource::PreviousHopData(h.prev_hop), + *payment_hash, + HTLCHandlingFailureType::Receive { + payment_hash: *payment_hash, + }, + ) + })); } + return !mpp_timeout; } true }, ); - for htlc_source in timed_out_mpp_htlcs.drain(..) { - let source = HTLCSource::PreviousHopData(htlc_source.0.clone()); + self.awaiting_trampoline_forwards.lock().unwrap().retain(|payment_hash, payment| { + if let OnionPayload::Trampoline { .. } = payment.htlcs[0].onion_payload { + let htlc_total_msat: u64 = + payment.htlcs.iter().map(|h| h.sender_intended_value).sum(); + let mpp_timeout = check_mpp_timeout(htlc_total_msat, &mut payment.htlcs); + if mpp_timeout { + let previous_hop_data = + payment.htlcs.drain(..).map(|claimable| claimable.prev_hop).collect(); + + timed_out_mpp_htlcs.push(( + HTLCSource::TrampolineForward { + previous_hop_data, + incoming_trampoline_shared_secret: payment.htlcs[1] + .prev_hop + .incoming_packet_shared_secret, + outbound_payment: None, + }, + *payment_hash, + HTLCHandlingFailureType::TrampolineForward {}, + )); + } + !mpp_timeout + } else { + debug_assert!( + false, + "awaiting_trampoline_forwards should only contain trampolines" + ); + true + } + }); + + for (htlc_source, payment_hash, failure_type) in timed_out_mpp_htlcs.drain(..) { let failure_reason = LocalHTLCFailureReason::MPPTimeout; let reason = HTLCFailReason::from_failure_code(failure_reason); - let receiver = HTLCHandlingFailureType::Receive { payment_hash: htlc_source.1 }; - self.fail_htlc_backwards_internal(&source, &htlc_source.1, &reason, receiver, None); + self.fail_htlc_backwards_internal( + &htlc_source, + &payment_hash, + &reason, + failure_type, + None, + ); } for (err, counterparty_node_id) in handle_errors { @@ -8643,11 +9238,14 @@ impl< for (htlc_src, payment_hash) in htlcs_to_fail.drain(..) { let reason = HTLCFailReason::reason(failure_reason, onion_failure_data.clone()); - let receiver = HTLCHandlingFailureType::Forward { - node_id: Some(counterparty_node_id.clone()), - channel_id, - }; - self.fail_htlc_backwards_internal(&htlc_src, &payment_hash, &reason, receiver, None); + let failure_type = htlc_src.failure_type(*counterparty_node_id, channel_id); + self.fail_htlc_backwards_internal( + &htlc_src, + &payment_hash, + &reason, + failure_type, + None, + ); } } @@ -8667,6 +9265,19 @@ impl< debug_assert_ne!(peer.held_by_thread(), LockHeldState::HeldByThread); } + let push_forward_htlcs_failure = + |prev_outbound_scid_alias: u64, failure: HTLCForwardInfo| { + let mut forward_htlcs = self.forward_htlcs.lock().unwrap(); + match forward_htlcs.entry(prev_outbound_scid_alias) { + hash_map::Entry::Occupied(mut entry) => { + entry.get_mut().push(failure); + }, + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![failure]); + }, + } + }; + //TODO: There is a timing attack here where if a node fails an HTLC back to us they can //identify whether we sent it or not based on the (I presume) very different runtime //between the branches here. We should make this async and move it into the forward HTLCs @@ -8733,49 +9344,86 @@ impl< if blinded_failure.is_some() { "blinded " } else { "" }, onion_error ); - // In case of trampoline + phantom we prioritize the trampoline failure over the phantom failure. - // TODO: Correctly wrap the error packet twice if failing back a trampoline + phantom HTLC. - let secondary_shared_secret = trampoline_shared_secret.or(*phantom_shared_secret); - let failure = match blinded_failure { - Some(BlindedFailure::FromIntroductionNode) => { - let blinded_onion_error = HTLCFailReason::reason( - LocalHTLCFailureReason::InvalidOnionBlinding, - vec![0; 32], - ); - let err_packet = blinded_onion_error.get_encrypted_failure_packet( - incoming_packet_shared_secret, - &secondary_shared_secret, - ); - HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet } - }, - Some(BlindedFailure::FromBlindedNode) => HTLCForwardInfo::FailMalformedHTLC { - htlc_id: *htlc_id, - failure_code: LocalHTLCFailureReason::InvalidOnionBlinding.failure_code(), - sha256_of_onion: [0; 32], - }, - None => { - let err_packet = onion_error.get_encrypted_failure_packet( - incoming_packet_shared_secret, - &secondary_shared_secret, - ); - HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet } - }, - }; - let mut forward_htlcs = self.forward_htlcs.lock().unwrap(); - match forward_htlcs.entry(*prev_outbound_scid_alias) { - hash_map::Entry::Occupied(mut entry) => { - entry.get_mut().push(failure); - }, - hash_map::Entry::Vacant(entry) => { - entry.insert(vec![failure]); + push_forward_htlcs_failure( + *prev_outbound_scid_alias, + get_htlc_forward_failure( + blinded_failure, + onion_error, + incoming_packet_shared_secret, + trampoline_shared_secret, + phantom_shared_secret, + *htlc_id, + ), + ); + + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push_back(( + events::Event::HTLCHandlingFailed { + prev_channel_ids: vec![*channel_id], + failure_type, + failure_reason: Some(onion_error.into()), }, + None, + )); + }, + HTLCSource::TrampolineForward { previous_hop_data, .. } => { + if let Some(decoded_error) = self.pending_outbound_payments.trampoline_htlc_failed( + source, + payment_hash, + onion_error, + &self.secp_ctx, + &WithContext::from(&self.logger, None, None, Some(*payment_hash)), + ) { + let onion_error = match decoded_error { + DecodedOnionFailure { + onion_error_code: Some(error_code), + onion_error_data: Some(error_data), + .. + } if error_code.is_recipient_failure() => HTLCFailReason::reason(error_code, error_data), + _ => HTLCFailReason::reason( + LocalHTLCFailureReason::TemporaryTrampolineFailure, + Vec::new(), + ), + }; + + for current_hop_data in previous_hop_data { + let incoming_packet_shared_secret = + ¤t_hop_data.incoming_packet_shared_secret; + let channel_id = ¤t_hop_data.channel_id; + let short_channel_id = ¤t_hop_data.prev_outbound_scid_alias; + let htlc_id = ¤t_hop_data.htlc_id; + let blinded_failure = ¤t_hop_data.blinded_failure; + log_trace!( + WithContext::from(&self.logger, None, Some(*channel_id), Some(*payment_hash)), + "Failing {}HTLC with payment_hash {} backwards from us following Trampoline forwarding failure at {}: {:?}", + if blinded_failure.is_some() { "blinded " } else { "" }, &payment_hash, + if decoded_error.payment_failed_permanently { "final node" } else {"intermediate hop"}, + decoded_error.onion_error_code, + ); + push_forward_htlcs_failure( + *short_channel_id, + get_htlc_forward_failure( + blinded_failure, + &onion_error, + incoming_packet_shared_secret, + &None, + &None, + *htlc_id, + ), + ); + } } - mem::drop(forward_htlcs); + + // We only want to emit a single event for trampoline failures, so we do it once + // we've failed back all of our incoming HTLCs. let mut pending_events = self.pending_events.lock().unwrap(); pending_events.push_back(( events::Event::HTLCHandlingFailed { - prev_channel_id: *channel_id, + prev_channel_ids: previous_hop_data + .iter() + .map(|prev| prev.channel_id) + .collect(), failure_type, failure_reason: Some(onion_error.into()), }, @@ -9014,6 +9662,133 @@ impl< } } + /// Claims funds for a forwarded HTLC where we are an intermediate hop. + /// + /// Processes attribution data, calculates fees earned, and emits a [`Event::PaymentForwarded`] + /// event upon successful claim. `make_payment_forwarded_event` is responsible for creating a + /// single [`Event::PaymentForwarded`] event that represents the forward. + fn claim_funds_from_htlc_forward_hop( + &self, payment_preimage: PaymentPreimage, + make_payment_forwarded_event: impl Fn(Option) -> Option, + startup_replay: bool, next_channel_counterparty_node_id: PublicKey, + next_channel_outpoint: OutPoint, next_channel_id: ChannelId, hop_data: HTLCPreviousHopData, + attribution_data: Option, send_timestamp: Option, + ) { + let _prev_channel_id = hop_data.channel_id; + let completed_blocker = RAAMonitorUpdateBlockingAction::from_prev_hop_data(&hop_data); + + // Obtain hold time, if available. + let hold_time = hold_time_since(send_timestamp).unwrap_or(0); + + // If attribution data was received from downstream, we shift it and get it ready for adding our hold + // time. Note that fulfilled HTLCs take a fast path to the incoming side. We don't need to wait for RAA + // to record the hold time like we do for failed HTLCs. + let attribution_data = process_fulfill_attribution_data( + attribution_data, + &hop_data.incoming_packet_shared_secret, + hold_time, + ); + + #[cfg(test)] + let claiming_chan_funding_outpoint = hop_data.outpoint; + self.claim_funds_from_hop( + hop_data, + payment_preimage, + None, + Some(attribution_data), + |htlc_claim_value_msat, definitely_duplicate| { + let chan_to_release = Some(EventUnblockedChannel { + counterparty_node_id: next_channel_counterparty_node_id, + funding_txo: next_channel_outpoint, + channel_id: next_channel_id, + blocking_action: completed_blocker, + }); + + if definitely_duplicate && startup_replay { + // On startup we may get redundant claims which are related to + // monitor updates still in flight. In that case, we shouldn't + // immediately free, but instead let that monitor update complete + // in the background. + #[cfg(test)] + { + let per_peer_state = self.per_peer_state.deadlocking_read(); + // The channel we'd unblock should already be closed, or... + let channel_closed = per_peer_state + .get(&next_channel_counterparty_node_id) + .map(|lck| lck.deadlocking_lock()) + .map(|peer| !peer.channel_by_id.contains_key(&next_channel_id)) + .unwrap_or(true); + let background_events = self.pending_background_events.lock().unwrap(); + // there should be a `BackgroundEvent` pending... + let matching_bg_event = + background_events.iter().any(|ev| { + match ev { + // to apply a monitor update that blocked the claiming channel, + BackgroundEvent::MonitorUpdateRegeneratedOnStartup { + funding_txo, + update, + .. + } => { + if *funding_txo == claiming_chan_funding_outpoint { + assert!( + update.updates.iter().any(|upd| { + if let ChannelMonitorUpdateStep::PaymentPreimage { + payment_preimage: update_preimage, .. + } = upd { + payment_preimage == *update_preimage + } else { false } + }), + "{:?}", + update + ); + true + } else { + false + } + }, + // or the monitor update has completed and will unblock + // immediately once we get going. + BackgroundEvent::MonitorUpdatesComplete { + channel_id, .. + } => *channel_id == _prev_channel_id, + } + }); + assert!(channel_closed || matching_bg_event, "{:?}", *background_events); + } + (None, None) + } else if definitely_duplicate { + if let Some(other_chan) = chan_to_release { + ( + Some(MonitorUpdateCompletionAction::FreeOtherChannelImmediately { + downstream_counterparty_node_id: other_chan.counterparty_node_id, + downstream_channel_id: other_chan.channel_id, + blocking_action: other_chan.blocking_action, + }), + None, + ) + } else { + (None, None) + } + } else { + let event = make_payment_forwarded_event(htlc_claim_value_msat); + if let Some(ref payment_forwarded) = event { + debug_assert!(matches!( + payment_forwarded, + &events::Event::PaymentForwarded { .. } + )); + } + ( + Some(MonitorUpdateCompletionAction::EmitEventOptionAndFreeOtherChannel { + event, + downstream_counterparty_and_funding_outpoint: chan_to_release, + }), + None, + ) + } + }, + ); + } + fn claim_funds_from_hop< ComplFunc: FnOnce( Option, @@ -9409,136 +10184,105 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } }, HTLCSource::PreviousHopData(hop_data) => { - let prev_channel_id = hop_data.channel_id; - let prev_user_channel_id = hop_data.user_channel_id; - let prev_node_id = hop_data.counterparty_node_id; - let completed_blocker = - RAAMonitorUpdateBlockingAction::from_prev_hop_data(&hop_data); - - // Obtain hold time, if available. - let hold_time = hold_time_since(send_timestamp).unwrap_or(0); - - // If attribution data was received from downstream, we shift it and get it ready for adding our hold - // time. Note that fulfilled HTLCs take a fast path to the incoming side. We don't need to wait for RAA - // to record the hold time like we do for failed HTLCs. - let attribution_data = process_fulfill_attribution_data( - attribution_data, - &hop_data.incoming_packet_shared_secret, - hold_time, - ); - - #[cfg(test)] - let claiming_chan_funding_outpoint = hop_data.outpoint; - self.claim_funds_from_hop( - hop_data, + let prev_htlcs = vec![events::HTLCLocator::from(&hop_data)]; + self.claim_funds_from_htlc_forward_hop( payment_preimage, - None, - Some(attribution_data), - |htlc_claim_value_msat, definitely_duplicate| { - let chan_to_release = Some(EventUnblockedChannel { - counterparty_node_id: next_channel_counterparty_node_id, - funding_txo: next_channel_outpoint, - channel_id: next_channel_id, - blocking_action: completed_blocker, - }); - - if definitely_duplicate && startup_replay { - // On startup we may get redundant claims which are related to - // monitor updates still in flight. In that case, we shouldn't - // immediately free, but instead let that monitor update complete - // in the background. - #[cfg(test)] - { - let per_peer_state = self.per_peer_state.deadlocking_read(); - // The channel we'd unblock should already be closed, or... - let channel_closed = per_peer_state - .get(&next_channel_counterparty_node_id) - .map(|lck| lck.deadlocking_lock()) - .map(|peer| !peer.channel_by_id.contains_key(&next_channel_id)) - .unwrap_or(true); - let background_events = - self.pending_background_events.lock().unwrap(); - // there should be a `BackgroundEvent` pending... - let matching_bg_event = - background_events.iter().any(|ev| { - match ev { - // to apply a monitor update that blocked the claiming channel, - BackgroundEvent::MonitorUpdateRegeneratedOnStartup { - funding_txo, update, .. - } => { - if *funding_txo == claiming_chan_funding_outpoint { - assert!(update.updates.iter().any(|upd| - if let ChannelMonitorUpdateStep::PaymentPreimage { - payment_preimage: update_preimage, .. - } = upd { - payment_preimage == *update_preimage - } else { false } - ), "{:?}", update); - true - } else { false } - }, - // or the monitor update has completed and will unblock - // immediately once we get going. - BackgroundEvent::MonitorUpdatesComplete { - channel_id, .. - } => - *channel_id == prev_channel_id, - } - }); - assert!( - channel_closed || matching_bg_event, - "{:?}", - *background_events - ); - } - (None, None) - } else if definitely_duplicate { - if let Some(other_chan) = chan_to_release { - (Some(MonitorUpdateCompletionAction::FreeOtherChannelImmediately { - downstream_counterparty_node_id: other_chan.counterparty_node_id, - downstream_channel_id: other_chan.channel_id, - blocking_action: other_chan.blocking_action, - }), None) - } else { - (None, None) - } - } else { - let total_fee_earned_msat = - if let Some(forwarded_htlc_value) = forwarded_htlc_value_msat { - if let Some(claimed_htlc_value) = htlc_claim_value_msat { - Some(claimed_htlc_value - forwarded_htlc_value) - } else { - None - } + |htlc_claim_value_msat: Option| -> Option { + let total_fee_earned_msat = + if let Some(forwarded_htlc_value) = forwarded_htlc_value_msat { + if let Some(claimed_htlc_value) = htlc_claim_value_msat { + Some(claimed_htlc_value - forwarded_htlc_value) } else { None - }; - debug_assert!( - skimmed_fee_msat <= total_fee_earned_msat, - "skimmed_fee_msat must always be included in total_fee_earned_msat" - ); - ( - Some(MonitorUpdateCompletionAction::EmitEventAndFreeOtherChannel { - event: events::Event::PaymentForwarded { - prev_channel_id: Some(prev_channel_id), - next_channel_id: Some(next_channel_id), - prev_user_channel_id, - next_user_channel_id, - prev_node_id, - next_node_id: Some(next_channel_counterparty_node_id), - total_fee_earned_msat, - skimmed_fee_msat, - claim_from_onchain_tx: from_onchain, - outbound_amount_forwarded_msat: forwarded_htlc_value_msat, - }, - downstream_counterparty_and_funding_outpoint: chan_to_release, - }), - None, - ) - } + } + } else { + None + }; + debug_assert!( + skimmed_fee_msat <= total_fee_earned_msat, + "skimmed_fee_msat must always be included in total_fee_earned_msat" + ); + + Some(events::Event::PaymentForwarded { + prev_htlcs: prev_htlcs.clone(), + next_htlcs: vec![events::HTLCLocator { + channel_id: next_channel_id, + user_channel_id: next_user_channel_id, + node_id: Some(next_channel_counterparty_node_id), + }], + total_fee_earned_msat, + skimmed_fee_msat, + claim_from_onchain_tx: from_onchain, + outbound_amount_forwarded_msat: forwarded_htlc_value_msat, + }) }, + startup_replay, + next_channel_counterparty_node_id, + next_channel_outpoint, + next_channel_id, + hop_data, + attribution_data, + send_timestamp, ); }, + HTLCSource::TrampolineForward { previous_hop_data, outbound_payment, .. } => { + let total_fee_earned_msat = match outbound_payment { + Some(trampoline_dispatch) => { + let fee = self + .pending_outbound_payments + .get_trampoline_forwarding_fee(&trampoline_dispatch.payment_id); + debug_assert!( + fee.is_some(), + "Trampoline payment with unknown payment_id: {} settled", + trampoline_dispatch.payment_id + ); + fee + }, + None => { + debug_assert!( + false, + "Trampoline payment settled with no outbound payment dispatched" + ); + None + }, + }; + // Only emit a single event for trampoline claims. + let prev_htlcs: Vec = + previous_hop_data.iter().map(Into::into).collect(); + for (i, current_previous_hop_data) in previous_hop_data.into_iter().enumerate() { + self.claim_funds_from_htlc_forward_hop( + payment_preimage, + |_: Option| -> Option { + if i == 0 { + Some(events::Event::PaymentForwarded { + prev_htlcs: prev_htlcs.clone(), + // TODO: When trampoline payments are tracked in our + // pending_outbound_payments, we'll be able to provide all the + // outgoing htlcs for this forward. + next_htlcs: vec![events::HTLCLocator { + channel_id: next_channel_id, + user_channel_id: next_user_channel_id, + node_id: Some(next_channel_counterparty_node_id), + }], + total_fee_earned_msat, + skimmed_fee_msat, + claim_from_onchain_tx: from_onchain, + outbound_amount_forwarded_msat: forwarded_htlc_value_msat, + }) + } else { + None + } + }, + startup_replay, + next_channel_counterparty_node_id, + next_channel_outpoint, + next_channel_id, + current_previous_hop_data, + attribution_data.clone(), + send_timestamp, + ); + } + }, } } @@ -9621,11 +10365,14 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } self.finalize_claims(finalized_claimed_htlcs); for failure in failed_htlcs { - let receiver = HTLCHandlingFailureType::Forward { - node_id: Some(counterparty_node_id), - channel_id, - }; - self.fail_htlc_backwards_internal(&failure.0, &failure.1, &failure.2, receiver, None); + let failure_type = failure.0.failure_type(counterparty_node_id, channel_id); + self.fail_htlc_backwards_internal( + &failure.0, + &failure.1, + &failure.2, + failure_type, + None, + ); } } @@ -9751,11 +10498,13 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } } }, - MonitorUpdateCompletionAction::EmitEventAndFreeOtherChannel { + MonitorUpdateCompletionAction::EmitEventOptionAndFreeOtherChannel { event, downstream_counterparty_and_funding_outpoint, } => { - self.pending_events.lock().unwrap().push_back((event, None)); + if let Some(event) = event { + self.pending_events.lock().unwrap().push_back((event, None)); + } if let Some(unblocked) = downstream_counterparty_and_funding_outpoint { self.handle_monitor_update_release( unblocked.counterparty_node_id, @@ -11646,13 +12395,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } } for htlc_source in dropped_htlcs.drain(..) { - let receiver = HTLCHandlingFailureType::Forward { - node_id: Some(counterparty_node_id.clone()), - channel_id: msg.channel_id, - }; - let reason = HTLCFailReason::from_failure_code(LocalHTLCFailureReason::ChannelClosed); let (source, hash) = htlc_source; - self.fail_htlc_backwards_internal(&source, &hash, &reason, receiver, None); + let failure_type = source.failure_type(*counterparty_node_id, msg.channel_id); + let reason = HTLCFailReason::from_failure_code(LocalHTLCFailureReason::ChannelClosed); + self.fail_htlc_backwards_internal(&source, &hash, &reason, failure_type, None); } Ok(()) @@ -11879,14 +12625,14 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ match peer_state.channel_by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan_entry) => { if (msg.failure_code & 0x8000) == 0 { - let chan_err = ChannelError::close("Got update_fail_malformed_htlc with BADONION not set".to_owned()); - try_channel_entry!(self, peer_state, Err(chan_err), chan_entry); + let chan_err = ChannelError::close("Got update_fail_malformed_htlc with BADONION not set".to_owned()); + try_channel_entry!(self, peer_state, Err(chan_err), chan_entry); } if let Some(chan) = chan_entry.get_mut().as_funded_mut() { - try_channel_entry!(self, peer_state, chan.update_fail_malformed_htlc(&msg, HTLCFailReason::reason(msg.failure_code.into(), msg.sha256_of_onion.to_vec())), chan_entry); + try_channel_entry!(self, peer_state, chan.update_fail_malformed_htlc(&msg, HTLCFailReason::reason(msg.failure_code.into(), msg.sha256_of_onion.to_vec())), chan_entry); } else { - return try_channel_entry!(self, peer_state, Err(ChannelError::close( - "Got an update_fail_malformed_htlc message for an unfunded channel!".into())), chan_entry); + return try_channel_entry!(self, peer_state, Err(ChannelError::close( + "Got an update_fail_malformed_htlc message for an unfunded channel!".into())), chan_entry); } Ok(()) }, @@ -12690,10 +13436,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } else { log_trace!(logger, "Failing HTLC from our monitor"); let failure_reason = LocalHTLCFailureReason::OnChainTimeout; - let receiver = HTLCHandlingFailureType::Forward { - node_id: Some(counterparty_node_id), - channel_id, - }; + let failure_type = + htlc_update.source.failure_type(counterparty_node_id, channel_id); let reason = HTLCFailReason::from_failure_code(failure_reason); let completion_update = Some(PaymentCompleteUpdate { counterparty_node_id, @@ -12705,7 +13449,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ &htlc_update.source, &htlc_update.payment_hash, &reason, - receiver, + failure_type, completion_update, ); } @@ -13344,6 +14088,43 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } } +/// Constructs an HTLC forward failure for sending back to the previous hop, converting to a blinded +/// failure where appropriate. +/// +/// When both trampoline and phantom secrets are present, the trampoline secret takes priority +/// for error encryption. +fn get_htlc_forward_failure( + blinded_failure: &Option, onion_error: &HTLCFailReason, + incoming_packet_shared_secret: &[u8; 32], trampoline_shared_secret: &Option<[u8; 32]>, + phantom_shared_secret: &Option<[u8; 32]>, htlc_id: u64, +) -> HTLCForwardInfo { + // TODO: Correctly wrap the error packet twice if failing back a trampoline + phantom HTLC. + let secondary_shared_secret = trampoline_shared_secret.or(*phantom_shared_secret); + match blinded_failure { + Some(BlindedFailure::FromIntroductionNode) => { + let blinded_onion_error = + HTLCFailReason::reason(LocalHTLCFailureReason::InvalidOnionBlinding, vec![0; 32]); + let err_packet = blinded_onion_error.get_encrypted_failure_packet( + incoming_packet_shared_secret, + &secondary_shared_secret, + ); + HTLCForwardInfo::FailHTLC { htlc_id, err_packet } + }, + Some(BlindedFailure::FromBlindedNode) => HTLCForwardInfo::FailMalformedHTLC { + htlc_id, + failure_code: LocalHTLCFailureReason::InvalidOnionBlinding.failure_code(), + sha256_of_onion: [0; 32], + }, + None => { + let err_packet = onion_error.get_encrypted_failure_packet( + incoming_packet_shared_secret, + &secondary_shared_secret, + ); + HTLCForwardInfo::FailHTLC { htlc_id, err_packet } + }, + } +} + /// Parameters used with [`create_bolt11_invoice`]. /// /// [`create_bolt11_invoice`]: ChannelManager::create_bolt11_invoice @@ -15071,8 +15852,8 @@ impl< for (source, payment_hash) in timed_out_pending_htlcs.drain(..) { let reason = LocalHTLCFailureReason::CLTVExpiryTooSoon; let data = self.get_htlc_inbound_temp_fail_data(reason); - timed_out_htlcs.push((source, payment_hash, HTLCFailReason::reason(reason, data), - HTLCHandlingFailureType::Forward { node_id: Some(funded_channel.context.get_counterparty_node_id()), channel_id: *channel_id })); + let failure_type = source.failure_type(funded_channel.context.get_counterparty_node_id(), *channel_id); + timed_out_htlcs.push((source, payment_hash, HTLCFailReason::reason(reason, data), failure_type)); } let logger = WithChannelContext::from(&self.logger, &funded_channel.context, None); match funding_confirmed_opt { @@ -15226,14 +16007,16 @@ impl< } if let Some(height) = height_opt { + // If height is approaching the number of blocks we think it takes us to get our + // commitment transaction confirmed before the HTLC expires, plus the number of blocks + // we generally consider it to take to do a commitment update, just give up on it and + // fail the HTLC. self.claimable_payments.lock().unwrap().claimable_payments.retain( |payment_hash, payment| { payment.htlcs.retain(|htlc| { - // If height is approaching the number of blocks we think it takes us to get - // our commitment transaction confirmed before the HTLC expires, plus the - // number of blocks we generally consider it to take to do a commitment update, - // just give up on it and fail the HTLC. - if height >= htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER { + let htlc_timed_out = + htlc.check_onchain_timeout(height, HTLC_FAIL_BACK_BUFFER); + if htlc_timed_out { let reason = LocalHTLCFailureReason::PaymentClaimBuffer; timed_out_htlcs.push(( HTLCSource::PreviousHopData(htlc.prev_hop.clone()), @@ -15246,15 +16029,49 @@ impl< payment_hash: payment_hash.clone(), }, )); - false - } else { - true } + !htlc_timed_out }); !payment.htlcs.is_empty() // Only retain this entry if htlcs has at least one entry. }, ); + self.awaiting_trampoline_forwards.lock().unwrap().retain(|payment_hash, payment| { + if let OnionPayload::Trampoline { .. } = payment.htlcs[0].onion_payload { + let htlc_timed_out = payment + .htlcs + .iter() + .any(|htlc| htlc.check_onchain_timeout(height, HTLC_FAIL_BACK_BUFFER)); + if htlc_timed_out { + let previous_hop_data = + payment.htlcs.drain(..).map(|claimable| claimable.prev_hop).collect(); + + timed_out_htlcs.push(( + HTLCSource::TrampolineForward { + previous_hop_data, + incoming_trampoline_shared_secret: payment.htlcs[0] + .prev_hop + .incoming_packet_shared_secret, + outbound_payment: None, + }, + payment_hash.clone(), + HTLCFailReason::reason( + LocalHTLCFailureReason::CLTVExpiryTooSoon, + vec![], + ), + HTLCHandlingFailureType::TrampolineForward {}, + )); + } + !htlc_timed_out + } else { + debug_assert!( + false, + "awaiting_trampoline_forwards should only contain trampolines" + ); + true + } + }); + let mut intercepted_htlcs = self.pending_intercepted_htlcs.lock().unwrap(); intercepted_htlcs.retain(|_, htlc| { if height >= htlc.forward_info.outgoing_cltv_value - HTLC_FAIL_BACK_BUFFER { @@ -16582,6 +17399,9 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting, (4, blinded, option), (6, node_id, required), (8, incoming_cltv_expiry, required), + (10, incoming_multipath_data, option), + (12, next_trampoline_amt_msat, required), + (14, next_trampoline_cltv_expiry, required), } ); @@ -16696,12 +17516,25 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, { (13, trampoline_shared_secret, option), }); +impl_writeable_tlv_based!(TrampolineDispatch, { + (1, payment_id, required), + (3, path, required), + (5, session_priv, required), +}); + impl Writeable for ClaimableHTLC { fn write(&self, writer: &mut W) -> Result<(), io::Error> { - let (payment_data, keysend_preimage) = match &self.onion_payload { - OnionPayload::Invoice { _legacy_hop_data } => (_legacy_hop_data.as_ref(), None), - OnionPayload::Spontaneous(preimage) => (None, Some(preimage)), - }; + let (payment_data, keysend_preimage, trampoline_next_hop, trampoline_next_node) = + match &self.onion_payload { + OnionPayload::Invoice { _legacy_hop_data } => { + (_legacy_hop_data.as_ref(), None, None, None) + }, + OnionPayload::Spontaneous(preimage) => (None, Some(preimage), None, None), + OnionPayload::Trampoline { next_hop_info, next_trampoline } => { + (None, None, Some(next_hop_info), Some(next_trampoline)) + }, + }; + write_tlv_fields!(writer, { (0, self.prev_hop, required), (1, self.total_msat, required), @@ -16712,6 +17545,8 @@ impl Writeable for ClaimableHTLC { (6, self.cltv_expiry, required), (8, keysend_preimage, option), (10, self.counterparty_skimmed_fee_msat, option), + (12, trampoline_next_hop, option), + (14, trampoline_next_node, option) }); Ok(()) } @@ -16730,11 +17565,14 @@ impl Readable for ClaimableHTLC { (6, cltv_expiry, required), (8, keysend_preimage, option), (10, counterparty_skimmed_fee_msat, option), + (12, trampoline_next_hop, option), + (14, trampoline_next_node, option) + }); let payment_data: Option = payment_data_opt; let value = value_ser.0.unwrap(); - let onion_payload = match keysend_preimage { - Some(p) => { + let onion_payload = match (keysend_preimage, trampoline_next_hop) { + (Some(p), None) => { if payment_data.is_some() { return Err(DecodeError::InvalidValue) } @@ -16743,7 +17581,7 @@ impl Readable for ClaimableHTLC { } OnionPayload::Spontaneous(p) }, - None => { + (None, None) => { if total_msat.is_none() { if payment_data.is_none() { return Err(DecodeError::InvalidValue) @@ -16752,6 +17590,21 @@ impl Readable for ClaimableHTLC { } OnionPayload::Invoice { _legacy_hop_data: payment_data } }, + (None, Some(trampoline_next_hop)) => { + // TODO: check which values we'll have for trampoline! + if total_msat.is_none() { + match payment_data { + Some(data) => total_msat = Some(data.total_msat), + None => return Err(DecodeError::InvalidValue), + } + } + + OnionPayload::Trampoline { + next_hop_info: trampoline_next_hop, + next_trampoline: trampoline_next_node.ok_or(DecodeError::InvalidValue)?, + } + }, + _ => return Err(DecodeError::InvalidValue) }; Ok(Self { prev_hop: prev_hop.0.unwrap(), @@ -16814,6 +17667,18 @@ impl Readable for HTLCSource { }) } 1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)), + 2 => { + _init_and_read_len_prefixed_tlv_fields!(reader, { + (1, previous_hop_data, required_vec), + (3, incoming_trampoline_shared_secret, required), + (5, outbound_payment, option), + }); + Ok(HTLCSource::TrampolineForward { + previous_hop_data: _init_tlv_based_struct_field!(previous_hop_data, required_vec), + incoming_trampoline_shared_secret: _init_tlv_based_struct_field!(incoming_trampoline_shared_secret, required), + outbound_payment, + }) + }, _ => Err(DecodeError::UnknownRequiredFeature), } } @@ -16846,6 +17711,18 @@ impl Writeable for HTLCSource { 1u8.write(writer)?; field.write(writer)?; }, + HTLCSource::TrampolineForward { + ref previous_hop_data, + incoming_trampoline_shared_secret, + ref outbound_payment, + } => { + 2u8.write(writer)?; + write_tlv_fields!(writer, { + (1, *previous_hop_data, required_vec), + (3, incoming_trampoline_shared_secret, required), + (5, outbound_payment, option), + }); + }, } Ok(()) } @@ -17035,6 +17912,8 @@ impl< htlc_onion_fields.push(&payment.onion_fields); } + // TODO: write pending_trampoline_forwards + let mut monitor_update_blocked_actions_per_peer = None; let mut peer_states = Vec::new(); for (_, peer_state_mutex) in per_peer_state.iter() { @@ -17443,8 +18322,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; @@ -17471,7 +18352,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), @@ -17536,7 +18417,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); } @@ -17544,7 +18425,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); @@ -17594,6 +18488,7 @@ impl<'a, ES: EntropySource, NS: NodeSigner, SP: SignerProvider, L: Logger> OnionPayload::Spontaneous(payment_preimage) => { events::PaymentPurpose::SpontaneousPayment(*payment_preimage) }, + OnionPayload::Trampoline { .. } => todo!(), }; claimable_payments .insert(payment_hash, ClaimablePayment { purpose, htlcs, onion_fields: None }); @@ -17811,6 +18706,88 @@ fn dedup_decode_update_add_htlcs( } } +/// Checks if a forwarded HTLC claim needs to be replayed on startup, returning None if it doesn't +/// need to be replayed. When the HTLC needs to be claimed, it returns a bool indicating whether +/// deserialization of should be failed due to missing information. +fn prev_hop_needs_claim_replay( + prev_hop: &HTLCPreviousHopData, payment_preimage: PaymentPreimage, + inbound_edge_monitor: Option<&ChannelMonitor>, + short_to_chan_info: &HashMap, logger: &L, +) -> Option { + // Note that for channels which have gone to chain, `get_all_current_outbound_htlcs` is never + // pruned and always returns a constant set until the monitor is removed/archived. Thus, we + // want to skip replaying claims that have definitely been resolved on-chain. + + // If the inbound monitor is not present, we assume it was fully resolved and properly archived, + // implying this payment had plenty of time to get claimed and we can safely skip any further + // attempts to claim it (they wouldn't succeed anyway as we don't have a monitor against which + // to do so). + let inbound_edge_monitor = inbound_edge_monitor?; + + // Second, if the inbound edge of the payment's monitor has been fully claimed we've had at + // least `ANTI_REORG_DELAY` blocks to get any PaymentForwarded event(s) to the user and assume + // that there's no need to try to replay the claim just for that. + let inbound_edge_balances = inbound_edge_monitor.get_claimable_balances(); + if inbound_edge_balances.is_empty() { + return None; + } + + let mut fail_read = false; + if prev_hop.counterparty_node_id.is_none() { + // We no longer support claiming an HTLC where we don't have the counterparty_node_id + // available if the claim has to go to a closed channel. Its possible we can get away with + // it if the channel is not yet closed, but its by no means a guarantee. + + // Thus, in this case we are a bit more aggressive with our pruning - if we have no use for + // the claim (because the inbound edge of the payment's monitor has already claimed the + // HTLC) we skip trying to replay the claim. + let htlc_payment_hash: PaymentHash = payment_preimage.into(); + let balance_could_incl_htlc = |bal| match bal { + &Balance::ClaimableOnChannelClose { .. } => { + // The channel is still open, assume we can still claim against it + true + }, + &Balance::MaybePreimageClaimableHTLC { payment_hash, .. } => { + payment_hash == htlc_payment_hash + }, + _ => false, + }; + let htlc_may_be_in_balances = inbound_edge_balances.iter().any(balance_could_incl_htlc); + if !htlc_may_be_in_balances { + return None; + } + + // First check if we're absolutely going to fail - if we need to replay this claim to get + // the preimage into the inbound edge monitor but the channel is closed (and thus we'll + // immediately panic if we call claim_funds_from_hop). + if short_to_chan_info.get(&prev_hop.prev_outbound_scid_alias).is_none() { + log_error!(logger, + "We need to replay the HTLC claim for payment_hash {} (preimage {}) but cannot do so as the HTLC was forwarded prior to LDK 0.0.124. \ + All HTLCs that were forwarded by LDK 0.0.123 and prior must be resolved prior to upgrading to LDK 0.1", + htlc_payment_hash, + payment_preimage, + ); + fail_read = true; + } + + // At this point we're confident we need the claim, but the inbound edge channel is still + // live. As long as this remains the case, we can conceivably proceed, but we run some risk + // of panicking at runtime. The user ideally should have read the release notes and we + // wouldn't be here, but we go ahead and let things run in the hope that it'll all just + // work out. + log_error!(logger, + "We need to replay the HTLC claim for payment_hash {} (preimage {}) but don't have all the required information to do so reliably. \ + As long as the channel for the inbound edge of the forward remains open, this may work okay, but we may panic at runtime! \ + All HTLCs that were forwarded by LDK 0.0.123 and prior must be resolved prior to upgrading to LDK 0.1. \ + Continuing anyway, though panics may occur!", + htlc_payment_hash, + payment_preimage, + ); + } + + Some(fail_read) +} + // Implement ReadableArgs for an Arc'd ChannelManager to make it a bit easier to work with the // SipmleArcChannelManager type: impl< @@ -18197,6 +19174,7 @@ impl< peer_state.get_mut().unwrap().latest_features = latest_features; } } + // TODO: pending trampoline forwards? // Post-deserialization processing let mut decode_update_add_htlcs = new_hash_map(); @@ -18240,39 +19218,58 @@ impl< ($counterparty_node_id: expr, $chan_in_flight_upds: expr, $monitor: expr, $peer_state: expr, $logger: expr, $channel_info_log: expr ) => { { + // When all in-flight updates have completed after we were last serialized, we + // need to remove them. However, we can't guarantee that the next serialization + // will have happened after processing the + // `BackgroundEvent::MonitorUpdatesComplete`, so removing them now could lead to the + // channel never being resumed as the event would not be regenerated after another + // reload. At the same time, we don't want to resume the channel now because there + // may be post-update actions to handle. Therefore, we're forced to keep tracking + // the completed in-flight updates (but only when they have all completed) until we + // are processing the `BackgroundEvent::MonitorUpdatesComplete`. let mut max_in_flight_update_id = 0; - let starting_len = $chan_in_flight_upds.len(); - $chan_in_flight_upds.retain(|upd| upd.update_id > $monitor.get_latest_update_id()); - if $chan_in_flight_upds.len() < starting_len { + let num_updates_completed = $chan_in_flight_upds + .iter() + .filter(|update| { + max_in_flight_update_id = cmp::max(max_in_flight_update_id, update.update_id); + update.update_id <= $monitor.get_latest_update_id() + }) + .count(); + if num_updates_completed > 0 { log_debug!( $logger, "{} ChannelMonitorUpdates completed after ChannelManager was last serialized", - starting_len - $chan_in_flight_upds.len() + num_updates_completed, ); } + let all_updates_completed = num_updates_completed == $chan_in_flight_upds.len(); + let funding_txo = $monitor.get_funding_txo(); - for update in $chan_in_flight_upds.iter() { - log_debug!($logger, "Replaying ChannelMonitorUpdate {} for {}channel {}", - update.update_id, $channel_info_log, &$monitor.channel_id()); - max_in_flight_update_id = cmp::max(max_in_flight_update_id, update.update_id); - pending_background_events.push( - BackgroundEvent::MonitorUpdateRegeneratedOnStartup { - counterparty_node_id: $counterparty_node_id, - funding_txo: funding_txo, - channel_id: $monitor.channel_id(), - update: update.clone(), - }); - } - if $chan_in_flight_upds.is_empty() { - // We had some updates to apply, but it turns out they had completed before we - // were serialized, we just weren't notified of that. Thus, we may have to run - // the completion actions for any monitor updates, but otherwise are done. + if all_updates_completed { + log_debug!($logger, "All monitor updates completed since the ChannelManager was last serialized"); pending_background_events.push( BackgroundEvent::MonitorUpdatesComplete { counterparty_node_id: $counterparty_node_id, channel_id: $monitor.channel_id(), + highest_update_id_completed: max_in_flight_update_id, }); } else { + $chan_in_flight_upds.retain(|update| { + let replay = update.update_id > $monitor.get_latest_update_id(); + if replay { + log_debug!($logger, "Replaying ChannelMonitorUpdate {} for {}channel {}", + update.update_id, $channel_info_log, &$monitor.channel_id()); + pending_background_events.push( + BackgroundEvent::MonitorUpdateRegeneratedOnStartup { + counterparty_node_id: $counterparty_node_id, + funding_txo: funding_txo, + channel_id: $monitor.channel_id(), + update: update.clone(), + } + ); + } + replay + }); $peer_state.closed_channel_monitor_update_ids.entry($monitor.channel_id()) .and_modify(|v| *v = cmp::max(max_in_flight_update_id, *v)) .or_insert(max_in_flight_update_id); @@ -18561,10 +19558,6 @@ impl< let htlc_id = SentHTLCId::from_source(&htlc_source); match htlc_source { HTLCSource::PreviousHopData(prev_hop_data) => { - let pending_forward_matches_htlc = |info: &PendingAddHTLCInfo| { - info.prev_funding_outpoint == prev_hop_data.outpoint - && info.prev_htlc_id == prev_hop_data.htlc_id - }; // If `reconstruct_manager_from_monitors` is set, we always add all inbound committed // HTLCs to `decode_update_add_htlcs` in the above loop, but we need to prune from // those added HTLCs if they were already forwarded to the outbound edge. Otherwise, @@ -18581,42 +19574,50 @@ impl< if !is_channel_closed || reconstruct_manager_from_monitors { continue; } - // The ChannelMonitor is now responsible for this HTLC's - // failure/success and will let us know what its outcome is. If we - // still have an entry for this HTLC in `forward_htlcs_legacy`, - // `pending_intercepted_htlcs_legacy`, or - // `decode_update_add_htlcs_legacy`, we were apparently not persisted - // after the monitor was when forwarding the payment. - dedup_decode_update_add_htlcs( + + reconcile_pending_htlcs_with_monitor( + &mut forward_htlcs_legacy, + &mut pending_events_read, + &mut pending_intercepted_htlcs_legacy, &mut decode_update_add_htlcs_legacy, - &prev_hop_data, - "HTLC was forwarded to the closed channel", - &&logger, - ); - forward_htlcs_legacy.retain(|_, forwards| { - forwards.retain(|forward| { - if let HTLCForwardInfo::AddHTLC(htlc_info) = forward { - if pending_forward_matches_htlc(&htlc_info) { - log_info!(logger, "Removing pending to-forward HTLC with hash {} as it was forwarded to the closed channel {}", - &htlc.payment_hash, &monitor.channel_id()); - false - } else { true } - } else { true } - }); - !forwards.is_empty() - }); - pending_intercepted_htlcs_legacy.retain(|intercepted_id, htlc_info| { - if pending_forward_matches_htlc(&htlc_info) { - log_info!(logger, "Removing pending intercepted HTLC with hash {} as it was forwarded to the closed channel {}", - &htlc.payment_hash, &monitor.channel_id()); - pending_events_read.retain(|(event, _)| { - if let Event::HTLCIntercepted { intercept_id: ev_id, .. } = event { - intercepted_id != ev_id - } else { true } - }); - false - } else { true } - }); + prev_hop_data, + &logger, + htlc.payment_hash, + monitor.channel_id(), + ) + }, + HTLCSource::TrampolineForward { previous_hop_data, .. } => { + // If `reconstruct_manager_from_monitors` is set, we always add all inbound committed + // HTLCs to `decode_update_add_htlcs` in the above loop, but we need to prune from + // those added HTLCs if they were already forwarded to the outbound edge. Otherwise, + // we'll double-forward. + for prev_hop_data in previous_hop_data.iter() { + if reconstruct_manager_from_monitors { + dedup_decode_update_add_htlcs( + &mut decode_update_add_htlcs, + prev_hop_data, + "HTLC already forwarded to the outbound edge", + &&logger, + ); + } + } + + if !is_channel_closed || reconstruct_manager_from_monitors { + continue; + } + + for prev_hop_data in previous_hop_data { + reconcile_pending_htlcs_with_monitor( + &mut forward_htlcs_legacy, + &mut pending_events_read, + &mut pending_intercepted_htlcs_legacy, + &mut decode_update_add_htlcs_legacy, + prev_hop_data, + &logger, + htlc.payment_hash, + monitor.channel_id(), + ); + } }, HTLCSource::OutboundRoute { payment_id, @@ -18677,7 +19678,6 @@ impl< // Note that for channels closed pre-0.1, the latest // update_id is `u64::MAX`. *update_id = update_id.saturating_add(1); - pending_background_events.push( BackgroundEvent::MonitorUpdateRegeneratedOnStartup { counterparty_node_id: monitor @@ -18730,112 +19730,71 @@ impl< // preimages from it which may be needed in upstream channels for forwarded // payments. let mut fail_read = false; - let outbound_claimed_htlcs_iter = monitor.get_all_current_outbound_htlcs() + let outbound_claimed_htlcs_iter = monitor + .get_all_current_outbound_htlcs() .into_iter() .filter_map(|(htlc_source, (htlc, preimage_opt))| { - if let HTLCSource::PreviousHopData(prev_hop) = &htlc_source { - if let Some(payment_preimage) = preimage_opt { - let inbound_edge_monitor = args.channel_monitors.get(&prev_hop.channel_id); - // Note that for channels which have gone to chain, - // `get_all_current_outbound_htlcs` is never pruned and always returns - // a constant set until the monitor is removed/archived. Thus, we - // want to skip replaying claims that have definitely been resolved - // on-chain. - - // If the inbound monitor is not present, we assume it was fully - // resolved and properly archived, implying this payment had plenty - // of time to get claimed and we can safely skip any further - // attempts to claim it (they wouldn't succeed anyway as we don't - // have a monitor against which to do so). - let inbound_edge_monitor = if let Some(monitor) = inbound_edge_monitor { - monitor - } else { - return None; - }; - // Second, if the inbound edge of the payment's monitor has been - // fully claimed we've had at least `ANTI_REORG_DELAY` blocks to - // get any PaymentForwarded event(s) to the user and assume that - // there's no need to try to replay the claim just for that. - let inbound_edge_balances = inbound_edge_monitor.get_claimable_balances(); - if inbound_edge_balances.is_empty() { - return None; - } + let payment_preimage = preimage_opt?; - if prev_hop.counterparty_node_id.is_none() { - // We no longer support claiming an HTLC where we don't have - // the counterparty_node_id available if the claim has to go to - // a closed channel. Its possible we can get away with it if - // the channel is not yet closed, but its by no means a - // guarantee. - - // Thus, in this case we are a bit more aggressive with our - // pruning - if we have no use for the claim (because the - // inbound edge of the payment's monitor has already claimed - // the HTLC) we skip trying to replay the claim. - let htlc_payment_hash: PaymentHash = payment_preimage.into(); - let logger = WithChannelMonitor::from( - &args.logger, - monitor, - Some(htlc_payment_hash), - ); - let balance_could_incl_htlc = |bal| match bal { - &Balance::ClaimableOnChannelClose { .. } => { - // The channel is still open, assume we can still - // claim against it - true - }, - &Balance::MaybePreimageClaimableHTLC { payment_hash, .. } => { - payment_hash == htlc_payment_hash - }, - _ => false, - }; - let htlc_may_be_in_balances = - inbound_edge_balances.iter().any(balance_could_incl_htlc); - if !htlc_may_be_in_balances { - return None; - } + let prev_htlcs = match &htlc_source { + HTLCSource::PreviousHopData(prev_hop) => vec![prev_hop], + HTLCSource::TrampolineForward { previous_hop_data, .. } => { + previous_hop_data.iter().collect() + }, + // If it was an outbound payment, we've handled it above - if a preimage + // came in and we persisted the `ChannelManager` we either handled it + // and are good to go or the channel force-closed - we don't have to + // handle the channel still live case here. + _ => vec![], + }; - // First check if we're absolutely going to fail - if we need - // to replay this claim to get the preimage into the inbound - // edge monitor but the channel is closed (and thus we'll - // immediately panic if we call claim_funds_from_hop). - if short_to_chan_info.get(&prev_hop.prev_outbound_scid_alias).is_none() { - log_error!(logger, - "We need to replay the HTLC claim for payment_hash {} (preimage {}) but cannot do so as the HTLC was forwarded prior to LDK 0.0.124.\ - All HTLCs that were forwarded by LDK 0.0.123 and prior must be resolved prior to upgrading to LDK 0.1", - htlc_payment_hash, - payment_preimage, - ); - fail_read = true; - } + let prev_htlcs_count = prev_htlcs.len(); + if prev_htlcs_count == 0 { + return None; + } - // At this point we're confident we need the claim, but the - // inbound edge channel is still live. As long as this remains - // the case, we can conceivably proceed, but we run some risk - // of panicking at runtime. The user ideally should have read - // the release notes and we wouldn't be here, but we go ahead - // and let things run in the hope that it'll all just work out. - log_error!(logger, - "We need to replay the HTLC claim for payment_hash {} (preimage {}) but don't have all the required information to do so reliably.\ - As long as the channel for the inbound edge of the forward remains open, this may work okay, but we may panic at runtime!\ - All HTLCs that were forwarded by LDK 0.0.123 and prior must be resolved prior to upgrading to LDK 0.1\ - Continuing anyway, though panics may occur!", - htlc_payment_hash, - payment_preimage, - ); + for prev_hop in prev_htlcs { + let inbound_edge_monitor = + args.channel_monitors.get(&prev_hop.channel_id).copied(); + let logger = WithChannelMonitor::from( + &args.logger, + monitor, + Some(payment_preimage.into()), + ); + if let Some(fail_claim_read) = prev_hop_needs_claim_replay( + prev_hop, + payment_preimage, + inbound_edge_monitor, + &short_to_chan_info, + &logger, + ) { + // We can only fail to read from disk for legacy HTLCs that have + // a single prev_htlc. If we could fail_claim_read for multiple + // prev_htlcs, it wouldn't be correct to exit early on our first + // claimable prev_hop (because a subsequent one may + // fail_claim_read). + if fail_claim_read { + debug_assert!(prev_htlcs_count == 1); } - Some((htlc_source, payment_preimage, htlc.amount_msat, - is_channel_closed, monitor.get_counterparty_node_id(), - monitor.get_funding_txo(), monitor.channel_id())) - } else { None } - } else { - // If it was an outbound payment, we've handled it above - if a preimage - // came in and we persisted the `ChannelManager` we either handled it and - // are good to go or the channel force-closed - we don't have to handle the - // channel still live case here. - None + fail_read |= fail_claim_read; + return Some(( + // When we have multiple prev_htlcs we assume that they all + // share the same htlc_source which contains all previous hops, + // so we can exit on the first claimable prev_hop because this + // will result in all prev_hops being claimed. + htlc_source, + payment_preimage, + htlc.amount_msat, + is_channel_closed, + monitor.get_counterparty_node_id(), + monitor.get_funding_txo(), + monitor.channel_id(), + )); + } } + + None }); for tuple in outbound_claimed_htlcs_iter { pending_claims_to_replay.push(tuple); @@ -18954,7 +19913,7 @@ impl< let logger = WithContext::from(&args.logger, Some(node_id), Some(*channel_id), None); for action in actions.iter() { - if let MonitorUpdateCompletionAction::EmitEventAndFreeOtherChannel { + if let MonitorUpdateCompletionAction::EmitEventOptionAndFreeOtherChannel { downstream_counterparty_and_funding_outpoint: Some(EventUnblockedChannel { counterparty_node_id: blocked_node_id, @@ -19136,6 +20095,7 @@ impl< claimable_payments, pending_claiming_payments, }), + awaiting_trampoline_forwards: Mutex::new(new_hash_map()), outbound_scid_aliases: Mutex::new(outbound_scid_aliases), short_to_chan_info: FairRwLock::new(short_to_chan_info), fake_scid_rand_bytes: fake_scid_rand_bytes.unwrap(), @@ -19419,11 +20379,15 @@ impl< for htlc_source in failed_htlcs { let (source, hash, counterparty_id, channel_id, failure_reason, ev_action) = htlc_source; - let receiver = - HTLCHandlingFailureType::Forward { node_id: Some(counterparty_id), channel_id }; + let failure_type = source.failure_type(counterparty_id, channel_id); let reason = HTLCFailReason::from_failure_code(failure_reason); - channel_manager - .fail_htlc_backwards_internal(&source, &hash, &reason, receiver, ev_action); + channel_manager.fail_htlc_backwards_internal( + &source, + &hash, + &reason, + failure_type, + ev_action, + ); } for ( @@ -19462,6 +20426,57 @@ impl< } } +/// Removes pending HTLC entries that the ChannelMonitor has already taken responsibility for, +/// cleaning up state mismatches that can occur during restart. +fn reconcile_pending_htlcs_with_monitor( + forward_htlcs_legacy: &mut HashMap>, + pending_events_read: &mut VecDeque<(Event, Option)>, + pending_intercepted_htlcs_legacy: &mut HashMap, + decode_update_add_htlcs_legacy: &mut HashMap>, + prev_hop_data: HTLCPreviousHopData, logger: &impl Logger, payment_hash: PaymentHash, + channel_id: ChannelId, +) { + let pending_forward_matches_htlc = |info: &PendingAddHTLCInfo| { + info.prev_funding_outpoint == prev_hop_data.outpoint + && info.prev_htlc_id == prev_hop_data.htlc_id + }; + + // The ChannelMonitor is now responsible for this HTLC's failure/success and will let us know + // what its outcome is. If we still have an entry for this HTLC in `forward_htlcs_legacy`, + // `pending_intercepted_htlcs_legacy`, or `decode_update_add_htlcs_legacy`, we were apparently + // not persisted after the monitor was when forwarding the payment. + dedup_decode_update_add_htlcs( + decode_update_add_htlcs_legacy, + &prev_hop_data, + "HTLC was forwarded to the closed channel", + &&logger, + ); + forward_htlcs_legacy.retain(|_, forwards| { + forwards.retain(|forward| { + if let HTLCForwardInfo::AddHTLC(htlc_info) = forward { + if pending_forward_matches_htlc(&htlc_info) { + log_info!(logger, "Removing pending to-forward HTLC with hash {} as it was forwarded to the closed channel {}", + &payment_hash, &channel_id); + false + } else { true } + } else { true } + }); + !forwards.is_empty() + }); + pending_intercepted_htlcs_legacy.retain(|intercepted_id, htlc_info| { + if pending_forward_matches_htlc(&htlc_info) { + log_info!(logger, "Removing pending intercepted HTLC with hash {} as it was forwarded to the closed channel {}", + &payment_hash, &channel_id); + pending_events_read.retain(|(event, _)| { + if let Event::HTLCIntercepted { intercept_id: ev_id, .. } = event { + intercepted_id != ev_id + } else { true } + }); + false + } else { true } + }); +} + #[cfg(test)] mod tests { use crate::events::{ClosureReason, Event, HTLCHandlingFailureType}; @@ -19587,9 +20602,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), 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); @@ -19597,7 +20612,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); @@ -19625,7 +20640,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), 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); @@ -19715,7 +20730,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); @@ -19753,7 +20768,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); @@ -19766,7 +20781,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); @@ -19796,7 +20811,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); @@ -19813,7 +20828,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); @@ -19871,9 +20886,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), session_privs).unwrap(); check_added_monitors(&nodes[0], 1); let updates = get_htlc_update_msgs(&nodes[0], &nodes[1].node.get_our_node_id()); @@ -19915,9 +20930,10 @@ 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(); + 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] { @@ -20734,7 +21750,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..1b7a44cb34e 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -11,7 +11,7 @@ //! nodes for functional tests. use crate::blinded_path::payment::DummyTlvs; -use crate::chain::channelmonitor::ChannelMonitor; +use crate::chain::channelmonitor::{ChannelMonitor, HTLC_FAIL_BACK_BUFFER}; use crate::chain::transaction::OutPoint; use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch}; use crate::events::bump_transaction::sync::{ @@ -3060,17 +3060,16 @@ pub fn expect_payment_forwarded>( ) -> Option { match event { Event::PaymentForwarded { - prev_channel_id, - next_channel_id, - prev_user_channel_id, - next_user_channel_id, - prev_node_id, - next_node_id, + prev_htlcs, + next_htlcs, total_fee_earned_msat, skimmed_fee_msat, claim_from_onchain_tx, .. } => { + assert_eq!(prev_htlcs.len(), 1); + assert_eq!(next_htlcs.len(), 1); + if allow_1_msat_fee_overpay { // Aggregating fees for blinded paths may result in a rounding error, causing slight // overpayment in fees. @@ -3085,33 +3084,36 @@ pub fn expect_payment_forwarded>( // overpaid amount. assert!(skimmed_fee_msat == expected_extra_fees_msat); if !upstream_force_closed { - assert_eq!(prev_node.node().get_our_node_id(), prev_node_id.unwrap()); + let prev_node_id = prev_htlcs[0].node_id.unwrap(); + let prev_channel_id = prev_htlcs[0].channel_id; + let prev_user_channel_id = prev_htlcs[0].user_channel_id.unwrap(); + + assert_eq!(prev_node.node().get_our_node_id(), prev_node_id); // Is the event prev_channel_id in one of the channels between the two nodes? let node_chans = node.node().list_channels(); - assert!(node_chans.iter().any(|x| x.counterparty.node_id == prev_node_id.unwrap() - && x.channel_id == prev_channel_id.unwrap() - && x.user_channel_id == prev_user_channel_id.unwrap())); + assert!(node_chans.iter().any(|x| x.counterparty.node_id == prev_node_id + && x.channel_id == prev_channel_id + && x.user_channel_id == prev_user_channel_id)); } // We check for force closures since a force closed channel is removed from the // node's channel list if !downstream_force_closed { + let next_node_id = next_htlcs[0].node_id.unwrap(); + let next_channel_id = next_htlcs[0].channel_id; + let next_user_channel_id = next_htlcs[0].user_channel_id.unwrap(); // As documented, `next_user_channel_id` will only be `Some` if we didn't settle via an // onchain transaction, just as the `total_fee_earned_msat` field. Rather than // introducing yet another variable, we use the latter's state as a flag to detect // this and only check if it's `Some`. - assert_eq!(next_node.node().get_our_node_id(), next_node_id.unwrap()); + assert_eq!(next_node.node().get_our_node_id(), next_node_id); let node_chans = node.node().list_channels(); if total_fee_earned_msat.is_none() { - assert!(node_chans - .iter() - .any(|x| x.counterparty.node_id == next_node_id.unwrap() - && x.channel_id == next_channel_id.unwrap())); + assert!(node_chans.iter().any(|x| x.counterparty.node_id == next_node_id + && x.channel_id == next_channel_id)); } else { - assert!(node_chans - .iter() - .any(|x| x.counterparty.node_id == next_node_id.unwrap() - && x.channel_id == next_channel_id.unwrap() - && x.user_channel_id == next_user_channel_id.unwrap())); + assert!(node_chans.iter().any(|x| x.counterparty.node_id == next_node_id + && x.channel_id == next_channel_id + && x.user_channel_id == next_user_channel_id)); } } assert_eq!(claim_from_onchain_tx, downstream_force_closed); @@ -3412,7 +3414,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), @@ -3456,6 +3458,7 @@ pub struct PassAlongPathArgs<'a, 'b, 'c, 'd> { pub custom_tlvs: Vec<(u64, Vec)>, pub payment_metadata: Option>, pub expected_failure: Option, + pub payment_claimable_cltv: Option, } impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { @@ -3478,6 +3481,7 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { custom_tlvs: Vec::new(), payment_metadata: None, expected_failure: None, + payment_claimable_cltv: None, } } pub fn without_clearing_recipient_events(mut self) -> Self { @@ -3518,6 +3522,10 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { self.dummy_tlvs = dummy_tlvs.to_vec(); self } + pub fn with_payment_claimable_cltv(mut self, cltv: u32) -> Self { + self.payment_claimable_cltv = Some(cltv); + self + } } pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option { @@ -3536,6 +3544,7 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option custom_tlvs, payment_metadata, expected_failure, + payment_claimable_cltv, } = args; let mut payment_event = SendEvent::from_event(ev); @@ -3551,7 +3560,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; @@ -3640,6 +3649,9 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option onion_fields.as_ref().unwrap().payment_secret ); }, + PaymentPurpose::Trampoline {} => { + panic!("Trampoline should not emit PaymentClaimable"); + }, } assert_eq!(*amount_msat, recv_value); let channels = node.node.list_channels(); @@ -3651,6 +3663,12 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option assert_eq!(*user_chan_id, Some(chan.user_channel_id)); } assert!(claim_deadline.unwrap() > node.best_block_info().1); + if let Some(expected_cltv) = payment_claimable_cltv { + assert_eq!( + claim_deadline.unwrap(), + expected_cltv - HTLC_FAIL_BACK_BUFFER, + ); + } }, _ => panic!("Unexpected event"), } diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 8e854b31150..5b9074d5347 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -1460,37 +1460,37 @@ pub fn test_htlc_on_chain_success() { connect_blocks(&nodes[1], TEST_FINAL_CLTV); // Confirm blocks until the HTLC expires let forwarded_events = nodes[1].node.get_and_clear_pending_events(); assert_eq!(forwarded_events.len(), 3); - let chan_id = Some(chan_1.2); + let chan_id = chan_1.2; match forwarded_events[0] { Event::PaymentForwarded { + ref prev_htlcs, + ref next_htlcs, total_fee_earned_msat, - prev_channel_id, claim_from_onchain_tx, - next_channel_id, outbound_amount_forwarded_msat, .. } => { assert_eq!(total_fee_earned_msat, Some(1000)); - assert_eq!(prev_channel_id, chan_id); + assert_eq!(prev_htlcs[0].channel_id, chan_id); assert_eq!(claim_from_onchain_tx, true); - assert_eq!(next_channel_id, Some(chan_2.2)); + assert_eq!(next_htlcs[0].channel_id, chan_2.2); assert_eq!(outbound_amount_forwarded_msat, Some(3000000)); }, _ => panic!(), } match forwarded_events[1] { Event::PaymentForwarded { + ref prev_htlcs, + ref next_htlcs, total_fee_earned_msat, - prev_channel_id, claim_from_onchain_tx, - next_channel_id, outbound_amount_forwarded_msat, .. } => { assert_eq!(total_fee_earned_msat, Some(1000)); - assert_eq!(prev_channel_id, chan_id); + assert_eq!(prev_htlcs[0].channel_id, chan_id); assert_eq!(claim_from_onchain_tx, true); - assert_eq!(next_channel_id, Some(chan_2.2)); + assert_eq!(next_htlcs[0].channel_id, chan_2.2); assert_eq!(outbound_amount_forwarded_msat, Some(3000000)); }, _ => panic!(), @@ -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,10 +2243,9 @@ 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 (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads( + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, 50_000); + let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::test_build_onion_payloads( &route.paths[0], - 50_000, &recipient_onion_fields, current_height, &None, @@ -2372,7 +2371,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 +2657,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 +3072,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 +3261,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,8 +3272,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), - 200_000, + RecipientOnionFields::secret_only(payment_secret, 200_000), cur_height, payment_id, &None, @@ -3362,7 +3360,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 +3370,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(); @@ -3967,17 +3965,17 @@ pub fn test_onchain_to_onchain_claim() { assert_eq!(events.len(), 2); match events[0] { Event::PaymentForwarded { + ref prev_htlcs, + ref next_htlcs, total_fee_earned_msat, - prev_channel_id, claim_from_onchain_tx, - next_channel_id, outbound_amount_forwarded_msat, .. } => { assert_eq!(total_fee_earned_msat, Some(1000)); - assert_eq!(prev_channel_id, Some(chan_1.2)); + assert_eq!(prev_htlcs[0].channel_id, chan_1.2); assert_eq!(claim_from_onchain_tx, true); - assert_eq!(next_channel_id, Some(chan_2.2)); + assert_eq!(next_htlcs[0].channel_id, chan_2.2); assert_eq!(outbound_amount_forwarded_msat, Some(3000000)); }, _ => panic!("Unexpected event"), @@ -4986,7 +4984,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 +5148,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 +5252,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 +5398,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 +5506,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 +5619,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 +5966,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,13 +6905,12 @@ 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); 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()); @@ -6939,10 +6937,9 @@ pub fn test_onion_value_mpp_set_calculation() { &route.paths[0], &session_priv, ); - let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); - let (mut onion_payloads, _, _) = onion_utils::build_onion_payloads( + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, 100_000); + let (mut onion_payloads, _, _) = onion_utils::test_build_onion_payloads( &route.paths[0], - 100_000, &recipient_onion_fields, height + 1, &None, @@ -7044,14 +7041,13 @@ 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 amt = Some(total_msat); + let onion = RecipientOnionFields::secret_only(payment_secret, 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()); @@ -7104,7 +7100,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 +7109,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)); @@ -7134,7 +7131,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(); @@ -7226,20 +7223,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); @@ -7466,7 +7463,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); @@ -8202,7 +8199,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); @@ -8218,7 +8215,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); @@ -8358,16 +8355,16 @@ 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]; 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); @@ -8379,12 +8376,11 @@ 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 amt_b = 14_000_000; + let onion = RecipientOnionFields::secret_only(payment_secret, 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); @@ -8439,12 +8435,12 @@ 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] .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); @@ -8507,7 +8503,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); @@ -8739,12 +8735,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 { .. }, {}); @@ -8758,7 +8760,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); @@ -8797,7 +8799,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(); } @@ -9027,7 +9029,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); @@ -9063,7 +9065,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 { .. }, {}); @@ -9081,7 +9083,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]); @@ -9203,7 +9205,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); @@ -9285,7 +9287,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 { .. }, {}); @@ -9368,7 +9370,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(); @@ -9845,7 +9847,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); @@ -9956,7 +9958,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); @@ -9976,7 +9978,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); @@ -9999,7 +10001,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..cff438aa8b5 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,10 +815,10 @@ 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 (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( + let recipient_onion_fields = + RecipientOnionFields::secret_only(payment_secret, payment_amt_msat); + 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, @@ -1014,7 +1014,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,10 +1059,9 @@ 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 (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, 700_000); + let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::test_build_onion_payloads( &route.paths[0], - 700_000, &recipient_onion_fields, cur_height, &None, @@ -1142,7 +1141,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 +1210,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,10 +1239,9 @@ 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 (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(recv_value_2); + 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, @@ -1310,7 +1308,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 +1332,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 +1352,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 +1382,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); @@ -1418,14 +1416,15 @@ pub fn test_update_add_htlc_bolt2_sender_cltv_expiry_too_high() { let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 0); - let payment_params = PaymentParameters::from_node_id(node_b_id, 0) + let mut payment_params = PaymentParameters::from_node_id(node_b_id, 0) .with_bolt11_features(nodes[1].node.bolt11_invoice_features()) .unwrap(); + payment_params.max_total_cltv_expiry_delta = 500000001; let (mut route, our_payment_hash, _, our_payment_secret) = 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 +1459,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 +1485,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 +1513,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 +1545,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 +1583,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,10 +1627,9 @@ 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 (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( + let recipient_onion_fields = RecipientOnionFields::secret_only(our_payment_secret, send_amt); + let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::test_build_onion_payloads( &route.paths[0], - send_amt, &recipient_onion_fields, cur_height, &None, @@ -1688,7 +1686,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 +1720,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 +1752,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 +1817,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 +1862,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 +1906,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 +2069,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,10 +2232,10 @@ 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 (onion_payloads, amount_msat, cltv_expiry) = onion_utils::build_onion_payloads( + let recipient_onion_fields = + RecipientOnionFields::secret_only(payment_secret_0_1, HTLC_AMT_SAT * 1000); + 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/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..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] @@ -1283,7 +1284,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..45640d3486d 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(); @@ -137,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, @@ -167,6 +168,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 +263,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); @@ -366,10 +368,9 @@ 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(), + &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 +388,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,15 +421,16 @@ 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], &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/monitor_tests.rs b/lightning/src/ln/monitor_tests.rs index 097266cf83f..c065dcf2f28 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); @@ -3793,8 +3793,8 @@ fn do_test_lost_timeout_monitor_events(confirm_tx: CommitmentType, dust_htlcs: b Event::PaymentFailed { payment_hash, .. } => { assert_eq!(payment_hash, Some(hash_b)); }, - Event::HTLCHandlingFailed { prev_channel_id, .. } => { - assert_eq!(prev_channel_id, chan_a); + Event::HTLCHandlingFailed { prev_channel_ids, .. } => { + assert_eq!(prev_channel_ids[0], chan_a); }, _ => panic!("Wrong event {ev:?}"), } diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 67f7807a487..26be79915ef 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -2659,7 +2659,6 @@ mod fuzzy_internal_msgs { /// This is used for Trampoline hops that are not the blinded path intro hop. /// We would only ever construct this variant when we are a Trampoline node forwarding a /// payment along a blinded path. - #[allow(unused)] BlindedTrampolineEntrypoint { amt_to_forward: u64, outgoing_cltv_value: u32, 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..7e052d216fb 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -111,6 +111,9 @@ enum RoutingInfo { next_hop_hmac: [u8; 32], shared_secret: SharedSecret, current_path_key: Option, + incoming_multipath_data: Option, + next_trampoline_amt_msat: u64, + next_trampoline_cltv: u32, }, } @@ -167,23 +170,26 @@ pub(super) fn create_fwd_pending_htlc_info( reason: LocalHTLCFailureReason::InvalidOnionPayload, err_data: Vec::new(), }), - onion_utils::Hop::TrampolineForward { next_trampoline_hop_data, next_trampoline_hop_hmac, new_trampoline_packet_bytes, trampoline_shared_secret, .. } => { + onion_utils::Hop::TrampolineForward { outer_hop_data, next_trampoline_hop_data, next_trampoline_hop_hmac, new_trampoline_packet_bytes, trampoline_shared_secret, .. } => { ( RoutingInfo::Trampoline { next_trampoline: next_trampoline_hop_data.next_trampoline, new_packet_bytes: new_trampoline_packet_bytes, next_hop_hmac: next_trampoline_hop_hmac, shared_secret: trampoline_shared_secret, - current_path_key: None + current_path_key: None, + incoming_multipath_data: outer_hop_data.multipath_trampoline_data, + next_trampoline_amt_msat: next_trampoline_hop_data.amt_to_forward, + next_trampoline_cltv: next_trampoline_hop_data.outgoing_cltv_value, }, - next_trampoline_hop_data.amt_to_forward, - next_trampoline_hop_data.outgoing_cltv_value, + outer_hop_data.amt_to_forward, + outer_hop_data.outgoing_cltv_value, None, None ) }, onion_utils::Hop::TrampolineBlindedForward { outer_hop_data, next_trampoline_hop_data, next_trampoline_hop_hmac, new_trampoline_packet_bytes, trampoline_shared_secret, .. } => { - let (amt_to_forward, outgoing_cltv_value) = check_blinded_forward( + let (next_hop_amount, next_hop_cltv) = check_blinded_forward( msg.amount_msat, msg.cltv_expiry, &next_trampoline_hop_data.payment_relay, &next_trampoline_hop_data.payment_constraints, &next_trampoline_hop_data.features ).map_err(|()| { // We should be returning malformed here if `msg.blinding_point` is set, but this is @@ -200,10 +206,13 @@ pub(super) fn create_fwd_pending_htlc_info( new_packet_bytes: new_trampoline_packet_bytes, next_hop_hmac: next_trampoline_hop_hmac, shared_secret: trampoline_shared_secret, - current_path_key: outer_hop_data.current_path_key + current_path_key: outer_hop_data.current_path_key, + incoming_multipath_data: outer_hop_data.multipath_trampoline_data, + next_trampoline_amt_msat: next_hop_amount, + next_trampoline_cltv: next_hop_cltv, }, - amt_to_forward, - outgoing_cltv_value, + outer_hop_data.amt_to_forward, + outer_hop_data.outgoing_cltv_value, next_trampoline_hop_data.intro_node_blinding_point, next_trampoline_hop_data.next_blinding_override ) @@ -233,7 +242,7 @@ pub(super) fn create_fwd_pending_htlc_info( }), } } - RoutingInfo::Trampoline { next_trampoline, new_packet_bytes, next_hop_hmac, shared_secret, current_path_key } => { + RoutingInfo::Trampoline { next_trampoline, new_packet_bytes, next_hop_hmac, shared_secret, current_path_key, incoming_multipath_data: multipath_trampoline_data, next_trampoline_amt_msat: next_hop_amount, next_trampoline_cltv: next_hop_cltv} => { let next_trampoline_packet_pubkey = match next_packet_pubkey_opt { Some(Ok(pubkey)) => pubkey, _ => return Err(InboundHTLCErr { @@ -260,7 +269,10 @@ pub(super) fn create_fwd_pending_htlc_info( failure: intro_node_blinding_point .map(|_| BlindedFailure::FromIntroductionNode) .unwrap_or(BlindedFailure::FromBlindedNode), - }) + }), + incoming_multipath_data: multipath_trampoline_data, + next_trampoline_amt_msat: next_hop_amount, + next_trampoline_cltv_expiry: next_hop_cltv, } } }; @@ -438,7 +450,7 @@ pub(super) fn create_recv_pending_htlc_info( payment_data, payment_preimage, payment_metadata, - incoming_cltv_expiry: onion_cltv_expiry, + incoming_cltv_expiry: cltv_expiry, custom_tlvs, requires_blinded_error, has_recipient_created_payment_secret, @@ -450,7 +462,7 @@ pub(super) fn create_recv_pending_htlc_info( payment_data: data, payment_metadata, payment_context, - incoming_cltv_expiry: onion_cltv_expiry, + incoming_cltv_expiry: cltv_expiry, phantom_shared_secret, trampoline_shared_secret, custom_tlvs, @@ -515,7 +527,7 @@ pub fn peel_payment_onion }; if let Err(reason) = check_incoming_htlc_cltv( - cur_height, outgoing_cltv_value, msg.cltv_expiry, + cur_height, outgoing_cltv_value, msg.cltv_expiry, MIN_CLTV_EXPIRY_DELTA.into(), ) { return Err(InboundHTLCErr { msg: "incoming cltv check failed", @@ -683,33 +695,24 @@ pub(super) fn decode_incoming_update_add_htlc_onion { + onion_utils::Hop::TrampolineForward { next_trampoline_hop_data: msgs::InboundTrampolineForwardPayload { next_trampoline, .. }, ref outer_hop_data, trampoline_shared_secret, incoming_trampoline_public_key, .. } => { let next_trampoline_packet_pubkey = onion_utils::next_hop_pubkey(secp_ctx, incoming_trampoline_public_key, &trampoline_shared_secret.secret_bytes()); Some(NextPacketDetails { next_packet_pubkey: next_trampoline_packet_pubkey, outgoing_connector: HopConnector::Trampoline(next_trampoline), - outgoing_amt_msat: amt_to_forward, - outgoing_cltv_value, + outgoing_amt_msat: outer_hop_data.amt_to_forward, + outgoing_cltv_value: outer_hop_data.outgoing_cltv_value, }) } - onion_utils::Hop::TrampolineBlindedForward { next_trampoline_hop_data: msgs::InboundTrampolineBlindedForwardPayload { next_trampoline, ref payment_relay, ref payment_constraints, ref features, .. }, outer_shared_secret, trampoline_shared_secret, incoming_trampoline_public_key, .. } => { - let (amt_to_forward, outgoing_cltv_value) = match check_blinded_forward( - msg.amount_msat, msg.cltv_expiry, &payment_relay, &payment_constraints, &features - ) { - Ok((amt, cltv)) => (amt, cltv), - Err(()) => { - return encode_relay_error("Underflow calculating outbound amount or cltv value for blinded trampoline forward", - LocalHTLCFailureReason::InvalidOnionBlinding, outer_shared_secret.secret_bytes(), Some(trampoline_shared_secret.secret_bytes()), &[0; 32]); - } - }; + onion_utils::Hop::TrampolineBlindedForward { next_trampoline_hop_data: msgs::InboundTrampolineBlindedForwardPayload { next_trampoline, .. }, ref outer_hop_data, trampoline_shared_secret, incoming_trampoline_public_key, .. } => { let next_trampoline_packet_pubkey = onion_utils::next_hop_pubkey(secp_ctx, incoming_trampoline_public_key, &trampoline_shared_secret.secret_bytes()); Some(NextPacketDetails { next_packet_pubkey: next_trampoline_packet_pubkey, outgoing_connector: HopConnector::Trampoline(next_trampoline), - outgoing_amt_msat: amt_to_forward, - outgoing_cltv_value, + outgoing_amt_msat: outer_hop_data.amt_to_forward, + outgoing_cltv_value: outer_hop_data.outgoing_cltv_value, }) } _ => None @@ -719,9 +722,9 @@ pub(super) fn decode_incoming_update_add_htlc_onion Result<(), LocalHTLCFailureReason> { - if (cltv_expiry as u64) < (outgoing_cltv_value) as u64 + MIN_CLTV_EXPIRY_DELTA as u64 { + if (cltv_expiry as u64) < (outgoing_cltv_value) as u64 + min_cltv_expiry_delta { return Err(LocalHTLCFailureReason::IncorrectCLTVExpiry); } // Theoretically, channel counterparty shouldn't send us a HTLC expiring now, @@ -779,7 +782,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 +791,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 +820,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(); @@ -842,7 +845,7 @@ mod tests { PendingHTLCRouting::ReceiveKeysend { payment_preimage, payment_data, incoming_cltv_expiry, .. } => { assert_eq!(payment_preimage, preimage); assert_eq!(peeled2.outgoing_amt_msat, recipient_amount); - assert_eq!(incoming_cltv_expiry, peeled2.outgoing_cltv_value); + assert_eq!(incoming_cltv_expiry, msg.cltv_expiry); let msgs::FinalOnionHopData{total_msat, payment_secret} = payment_data.unwrap(); assert_eq!(total_msat, total_amt_msat); assert_eq!(payment_secret, pay_secret); @@ -879,7 +882,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..e15bd8b74df 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; @@ -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,10 +524,10 @@ 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) + 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(..) { @@ -565,10 +566,10 @@ 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) + 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(..) { @@ -1284,10 +1285,10 @@ 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) + test_build_onion_payloads(path, &recipient_fields, height, &None, None, None) .unwrap(); let onion_packet = onion_utils::construct_onion_packet( onion_payloads, @@ -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,11 +1838,10 @@ 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) - .unwrap(); + test_build_onion_payloads(path, &recipient_fields, cur_height, &None, None, None).unwrap(); match onion_payloads[0] { msgs::OutboundOnionPayload::Forward { .. } => {}, @@ -1918,7 +1918,7 @@ fn test_trampoline_onion_payload_assembly_values() { short_channel_id: (572330 << 40) + (42 << 16) + 2821, channel_features: ChannelFeatures::empty(), fee_msat: 153_000, - cltv_expiry_delta: 0, + cltv_expiry_delta: 36 + 24, // Last hop should include the CLTV of the trampoline hops maybe_announced_channel: false, }, ], @@ -1973,19 +1973,16 @@ 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 (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, - ) - .unwrap(); + let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret, amt_msat); + let (trampoline_payloads, outer_total_msat) = onion_utils::build_trampoline_onion_payloads( + &path.blinded_tail.as_ref().unwrap(), + &recipient_onion_fields, + cur_height, + &None, + ) + .unwrap(); assert_eq!(trampoline_payloads.len(), 3); assert_eq!(outer_total_msat, 150_153_000); - assert_eq!(outer_starting_htlc_offset, 800_060); let trampoline_carol_payload = &trampoline_payloads[0]; let trampoline_dave_payload = &trampoline_payloads[1]; @@ -2038,14 +2035,15 @@ fn test_trampoline_onion_payload_assembly_values() { ) .unwrap(); - let (outer_payloads, total_msat, total_htlc_offset) = build_onion_payloads( + let recipient_onion_fields = + RecipientOnionFields::secret_only(payment_secret, outer_total_msat); + let (outer_payloads, total_msat, total_htlc_offset) = test_build_onion_payloads( &path, - outer_total_msat, &recipient_onion_fields, - outer_starting_htlc_offset, + cur_height, &None, None, - Some(trampoline_packet), + Some((trampoline_packet, None)), ) .unwrap(); assert_eq!(outer_payloads.len(), 2); @@ -2067,16 +2065,16 @@ fn test_trampoline_onion_payload_assembly_values() { outer_bob_payload { assert_eq!(amt_to_forward, &150_153_000); - assert_eq!(outgoing_cltv_value, &800_084); + assert_eq!(outgoing_cltv_value, &800_060); } else { 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, &session_priv, - amt_msat, &recipient_onion_fields, cur_height, &payment_hash, @@ -2280,7 +2278,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 +2428,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 +2500,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,10 +2532,10 @@ 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 (mut onion_payloads, _, _) = build_onion_payloads( + let recipient_onion_fields = + RecipientOnionFields::secret_only(payment_secret, msgs::MAX_VALUE_MSAT + 1); + let (mut onion_payloads, _, _) = test_build_onion_payloads( &route.paths[0], - msgs::MAX_VALUE_MSAT + 1, &recipient_onion_fields, height + 1, &None, @@ -2602,7 +2600,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 +2669,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 +2722,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 +2777,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 +2827,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 +2897,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 +2947,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..d9b23fb9409 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -18,7 +18,7 @@ use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS; use crate::ln::channelmanager::HTLCSource; use crate::ln::msgs::{self, DecodeError, InboundOnionDummyPayload, OnionPacket, UpdateAddHTLC}; use crate::ln::onion_payment::{HopConnector, NextPacketDetails}; -use crate::ln::outbound_payment::RecipientOnionFields; +use crate::ln::outbound_payment::{NextTrampolineHopInfo, RecipientOnionFields}; use crate::offers::invoice_request::InvoiceRequest; use crate::routing::gossip::NetworkUpdate; use crate::routing::router::{BlindedTail, Path, RouteHop, RouteParameters, TrampolineHop}; @@ -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, blinding_point: Option, ) -> Result; } impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundOnionPayload<'a> { @@ -217,12 +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 { 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, @@ -254,17 +257,30 @@ 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, blinding_point: Option, ) -> Result { - 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 }), - trampoline_packet: packet, - }) + let total_msat = recipient_onion.total_mpp_amount_msat; + let multipath_trampoline_data = recipient_onion + .payment_secret + .map(|payment_secret| msgs::FinalOnionHopData { payment_secret, total_msat }); + + if let Some(blinding_point) = blinding_point { + Ok(Self::BlindedTrampolineEntrypoint { + amt_to_forward, + outgoing_cltv_value, + multipath_trampoline_data, + trampoline_packet: packet, + current_path_key: blinding_point, + }) + } else { + Ok(Self::TrampolineEntrypoint { + amt_to_forward, + outgoing_cltv_value, + multipath_trampoline_data, + trampoline_packet: packet, + }) + } } } impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundTrampolinePayload<'a> { @@ -277,7 +293,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(), @@ -306,8 +322,9 @@ 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, + _blinding_point: Option, ) -> Result { Err(APIError::InvalidRoute { err: "Trampoline onions cannot contain Trampoline entrypoints!".to_string(), @@ -408,9 +425,9 @@ 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, - starting_htlc_offset: u32, keysend_preimage: &Option, -) -> Result<(Vec>, u64, u32), APIError> { + blinded_tail: &'a BlindedTail, recipient_onion: &'a RecipientOnionFields, + cur_block_height: u32, keysend_preimage: &Option, +) -> Result<(Vec>, u64), APIError> { let mut res: Vec = Vec::with_capacity(blinded_tail.trampoline_hops.len() + blinded_tail.hops.len()); let blinded_tail_with_hop_iter = BlindedTailDetails::DirectEntry { @@ -420,12 +437,11 @@ pub(super) fn build_trampoline_onion_payloads<'a>( excess_final_cltv_expiry_delta: blinded_tail.excess_final_cltv_expiry_delta, }; - let (value_msat, cltv) = build_onion_payloads_callback( + let (value_msat, _) = build_onion_payloads_callback( blinded_tail.trampoline_hops.iter(), Some(blinded_tail_with_hop_iter), - total_msat, recipient_onion, - starting_htlc_offset, + cur_block_height, keysend_preimage, None, |action, payload| match action { @@ -433,15 +449,31 @@ pub(super) fn build_trampoline_onion_payloads<'a>( PayloadCallbackAction::PushFront => res.insert(0, payload), }, )?; - Ok((res, value_msat, cltv)) + Ok((res, value_msat)) } /// 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>, - trampoline_packet: Option, +#[cfg(any(test, feature = "_externalize_tests"))] +pub(crate) fn test_build_onion_payloads<'a>( + path: &'a Path, recipient_onion: &'a RecipientOnionFields, cur_block_height: u32, + keysend_preimage: &Option, invoice_request: Option<&'a InvoiceRequest>, + trampoline_packet: Option<(msgs::TrampolineOnionPacket, Option)>, +) -> Result<(Vec>, u64, u32), APIError> { + build_onion_payloads( + path, + recipient_onion, + cur_block_height, + 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, cur_block_height: u32, + keysend_preimage: &Option, invoice_request: Option<&'a InvoiceRequest>, + trampoline_packet: Option<(msgs::TrampolineOnionPacket, 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()), @@ -451,10 +483,11 @@ pub(super) fn build_onion_payloads<'a>( // means that the blinded path needs not be appended to the regular hops, and is only included // among the Trampoline onion payloads. let blinded_tail_with_hop_iter = path.blinded_tail.as_ref().map(|bt| { - if let Some(trampoline_packet) = trampoline_packet { + if let Some((trampoline_packet, blinding_point)) = trampoline_packet { return BlindedTailDetails::TrampolineEntry { trampoline_packet, final_value_msat: bt.final_value_msat, + blinding_point, }; } BlindedTailDetails::DirectEntry { @@ -468,9 +501,8 @@ 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, + cur_block_height, keysend_preimage, invoice_request, |action, payload| match action { @@ -491,6 +523,9 @@ enum BlindedTailDetails<'a, I: Iterator> { TrampolineEntry { trampoline_packet: msgs::TrampolineOnionPacket, final_value_msat: u64, + // If forwarding a trampoline payment inside of a blinded path, this blinding_point will + // be set for the trampoline to decrypt its inner onion. + blinding_point: Option, }, } @@ -499,8 +534,8 @@ enum PayloadCallbackAction { PushFront, } fn build_onion_payloads_callback<'a, 'b, H, B, F, OP>( - hops: H, mut blinded_tail: Option>, total_msat: u64, - recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32, + hops: H, mut blinded_tail: Option>, + recipient_onion: &'a RecipientOnionFields, cur_block_height: u32, keysend_preimage: &Option, invoice_request: Option<&'a InvoiceRequest>, mut callback: F, ) -> Result<(u64, u32), APIError> @@ -511,7 +546,7 @@ where OP: OnionPayload<'a, 'b, ReceiveType = OP>, { let mut cur_value_msat = 0u64; - let mut cur_cltv = starting_htlc_offset; + let mut cur_cltv = cur_block_height; let mut last_hop_id = None; for (idx, hop) in hops.rev().enumerate() { @@ -519,12 +554,8 @@ where // exactly as it should be (and the next hop isn't trying to probe to find out if we're // the intended recipient). let value_msat = if cur_value_msat == 0 { hop.fee_msat() } else { cur_value_msat }; - let cltv = if cur_cltv == starting_htlc_offset { - hop.cltv_expiry_delta().saturating_add(starting_htlc_offset) - } else { - cur_cltv - }; if idx == 0 { + let declared_incoming_cltv = hop.cltv_expiry_delta().saturating_add(cur_cltv); match blinded_tail.take() { Some(BlindedTailDetails::DirectEntry { blinding_point, @@ -542,8 +573,8 @@ where PayloadCallbackAction::PushBack, OP::new_blinded_receive( final_value_msat, - total_msat, - cur_cltv + excess_final_cltv_expiry_delta, + recipient_onion.total_mpp_amount_msat, + cur_block_height + excess_final_cltv_expiry_delta, &blinded_hop.encrypted_payload, blinding_point.take(), *keysend_preimage, @@ -565,16 +596,17 @@ where Some(BlindedTailDetails::TrampolineEntry { trampoline_packet, final_value_msat, + blinding_point, }) => { cur_value_msat += final_value_msat; callback( PayloadCallbackAction::PushBack, OP::new_trampoline_entry( - total_msat, final_value_msat + hop.fee_msat(), - cur_cltv, + declared_incoming_cltv, &recipient_onion, trampoline_packet, + blinding_point, )?, ); }, @@ -585,8 +617,7 @@ where &recipient_onion, *keysend_preimage, value_msat, - total_msat, - cltv, + declared_incoming_cltv, )?, ); }, @@ -597,7 +628,7 @@ where err: "Next hop ID must be known for non-final hops".to_string(), })?, value_msat, - cltv, + cur_cltv, ); callback(PayloadCallbackAction::PushFront, payload); } @@ -661,11 +692,14 @@ 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, @@ -975,9 +1009,7 @@ mod fuzzy_onion_utils { pub(crate) failed_within_blinded_path: bool, #[allow(dead_code)] pub(crate) hold_times: Vec, - #[cfg(any(test, feature = "_test_utils"))] pub(crate) onion_error_code: Option, - #[cfg(any(test, feature = "_test_utils"))] pub(crate) onion_error_data: Option>, #[cfg(test)] pub(crate) attribution_failed_channel: Option, @@ -987,12 +1019,32 @@ mod fuzzy_onion_utils { secp_ctx: &Secp256k1, logger: &L, htlc_source: &HTLCSource, encrypted_packet: OnionErrorPacket, ) -> DecodedOnionFailure { - let (path, session_priv) = match htlc_source { - HTLCSource::OutboundRoute { ref path, ref session_priv, .. } => (path, session_priv), + match htlc_source { + HTLCSource::OutboundRoute { ref path, ref session_priv, .. } => { + process_onion_failure_inner( + secp_ctx, + logger, + &path, + &session_priv, + None, + encrypted_packet, + ) + }, + HTLCSource::TrampolineForward { outbound_payment, .. } => { + let dispatch = outbound_payment.as_ref() + .expect("processing trampoline onion failure for forward with no outbound payment details"); + + process_onion_failure_inner( + secp_ctx, + logger, + &dispatch.path, + &dispatch.session_priv, + None, + encrypted_packet, + ) + }, _ => unreachable!(), - }; - - process_onion_failure_inner(secp_ctx, logger, path, &session_priv, None, encrypted_packet) + } } /// Decodes the attribution data that we got back from upstream on a payment we sent. @@ -1072,9 +1124,7 @@ fn process_onion_failure_inner( payment_failed_permanently: true, failed_within_blinded_path: false, hold_times: Vec::new(), - #[cfg(any(test, feature = "_test_utils"))] onion_error_code: None, - #[cfg(any(test, feature = "_test_utils"))] onion_error_data: None, #[cfg(test)] attribution_failed_channel: None, @@ -1462,9 +1512,7 @@ fn process_onion_failure_inner( payment_failed_permanently, failed_within_blinded_path, hold_times: hop_hold_times, - #[cfg(any(test, feature = "_test_utils"))] onion_error_code: _error_code_ret, - #[cfg(any(test, feature = "_test_utils"))] onion_error_data: _error_packet_ret, #[cfg(test)] attribution_failed_channel, @@ -1485,9 +1533,7 @@ fn process_onion_failure_inner( payment_failed_permanently: is_from_final_non_blinded_node, failed_within_blinded_path: false, hold_times: hop_hold_times, - #[cfg(any(test, feature = "_test_utils"))] onion_error_code: None, - #[cfg(any(test, feature = "_test_utils"))] onion_error_data: None, #[cfg(test)] attribution_failed_channel, @@ -1748,7 +1794,7 @@ impl LocalHTLCFailureReason { /// Returns true if the failure is only sent by the final recipient. Note that this function /// only checks [`LocalHTLCFailureReason`] variants that represent bolt 04 errors directly, /// as it's intended to analyze errors we've received as a sender. - fn is_recipient_failure(&self) -> bool { + pub(super) fn is_recipient_failure(&self) -> bool { self.failure_code() == LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code() || *self == LocalHTLCFailureReason::FinalIncorrectCLTVExpiry || *self == LocalHTLCFailureReason::FinalIncorrectHTLCAmount @@ -2118,6 +2164,21 @@ impl HTLCFailReason { pub(super) fn decode_onion_failure( &self, secp_ctx: &Secp256k1, logger: &L, htlc_source: &HTLCSource, ) -> DecodedOnionFailure { + macro_rules! decoded_onion_failure { + ($short_channel_id:expr, $failure_reason:expr, $data:expr) => { + DecodedOnionFailure { + network_update: None, + payment_failed_permanently: false, + short_channel_id: $short_channel_id, + failed_within_blinded_path: false, + hold_times: Vec::new(), + onion_error_code: Some($failure_reason), + onion_error_data: Some($data.clone()), + #[cfg(test)] + attribution_failed_channel: None, + } + }; + } match self.0 { HTLCFailReasonRepr::LightningError { ref err, .. } => { process_onion_failure(secp_ctx, logger, &htlc_source, err.clone()) @@ -2129,22 +2190,19 @@ impl HTLCFailReason { // failures here, but that would be insufficient as find_route // generally ignores its view of our own channels as we provide them via // ChannelDetails. - if let &HTLCSource::OutboundRoute { ref path, .. } = htlc_source { - DecodedOnionFailure { - network_update: None, - payment_failed_permanently: false, - short_channel_id: Some(path.hops[0].short_channel_id), - failed_within_blinded_path: false, - hold_times: Vec::new(), - #[cfg(any(test, feature = "_test_utils"))] - onion_error_code: Some(*failure_reason), - #[cfg(any(test, feature = "_test_utils"))] - onion_error_data: Some(data.clone()), - #[cfg(test)] - attribution_failed_channel: None, - } - } else { - unreachable!(); + match htlc_source { + &HTLCSource::OutboundRoute { ref path, .. } => { + decoded_onion_failure!( + (Some(path.hops[0].short_channel_id)), + *failure_reason, + data + ) + }, + &HTLCSource::TrampolineForward { ref outbound_payment, .. } => { + debug_assert!(outbound_payment.is_none()); + decoded_onion_failure!(None, *failure_reason, data) + }, + _ => unreachable!(), } }, } @@ -2402,7 +2460,10 @@ pub(crate) fn decode_next_payment_hop( &hop_data.trampoline_packet.hop_data, hop_data.trampoline_packet.hmac, Some(payment_hash), - (blinding_point, &node_signer), + // When we have a trampoline packet, the current_path_key in our outer onion + // payload plays the role of the update_add_htlc blinding_point for the inner + // onion. + (hop_data.current_path_key, node_signer), ); match decoded_trampoline_hop { Ok(( @@ -2586,7 +2647,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], @@ -2595,7 +2656,6 @@ pub fn create_payment_onion( secp_ctx, path, session_priv, - total_msat, recipient_onion, cur_block_height, payment_hash, @@ -2614,30 +2674,78 @@ pub(super) fn compute_trampoline_session_priv(outer_onion_session_priv: &SecretK SecretKey::from_slice(&session_priv_hash[..]).expect("You broke SHA-256!") } +/// Builds a payment onion for an inter-trampoline forward. +pub(crate) fn create_trampoline_forward_onion( + secp_ctx: &Secp256k1, path: &Path, session_priv: &SecretKey, payment_hash: &PaymentHash, + recipient_onion: &RecipientOnionFields, keysend_preimage: &Option, + trampoline_forward_info: &NextTrampolineHopInfo, prng_seed: [u8; 32], +) -> Result<(msgs::OnionPacket, u64, u32), APIError> { + // Inter-trampoline payments should always be cleartext because we need to know the node id + // that we need to route to. LDK does not currently support the legacy "trampoline to blinded + // path" approach, where we get a blinded path to pay inside of our trampoline onion. + debug_assert!(path.blinded_tail.is_none(), "trampoline should not be blinded"); + + let mut res: Vec = Vec::with_capacity(path.hops.len()); + + let blinded_tail_with_hop_iter: BlindedTailDetails<'_, core::iter::Empty<&BlindedHop>> = + BlindedTailDetails::TrampolineEntry { + trampoline_packet: trampoline_forward_info.onion_packet.clone(), + final_value_msat: 0, + blinding_point: trampoline_forward_info.blinding_point, + }; + let (value_msat, cltv) = build_onion_payloads_callback( + path.hops.iter(), + Some(blinded_tail_with_hop_iter), + recipient_onion, + // Note that we use the cltv expiry height that the next trampoline is expecting instead + // of the current block height. This is because we need to create an onion that terminates + // at the next trampoline with the cltv we've been told to give them. + trampoline_forward_info.cltv_expiry_height, + keysend_preimage, + None, + |action, payload| match action { + PayloadCallbackAction::PushBack => res.push(payload), + PayloadCallbackAction::PushFront => res.insert(0, payload), + }, + )?; + + let onion_keys = construct_onion_keys(&secp_ctx, &path, session_priv); + let onion_packet = + construct_onion_packet(res, onion_keys, prng_seed, payment_hash).map_err(|_| { + APIError::InvalidRoute { err: "Route size too large considering onion data".to_owned() } + })?; + Ok((onion_packet, value_msat, cltv)) +} + /// 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> { - 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; - (trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = - build_trampoline_onion_payloads( - &blinded_tail, - total_msat, - recipient_onion, - cur_block_height, - keysend_preimage, - )?; + let outer_total_msat; + (trampoline_payloads, outer_total_msat) = build_trampoline_onion_payloads( + &blinded_tail, + recipient_onion, + 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)); @@ -2656,19 +2764,20 @@ pub(crate) fn create_payment_onion_internal( err: "Route size too large considering onion data".to_owned(), })?; - trampoline_packet_option = Some(trampoline_packet); + trampoline_packet_option = Some((trampoline_packet, None)); + outer_onion = &trampoline_outer_onion; } } let (onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads( &path, - outer_total_msat, - recipient_onion, - outer_starting_htlc_offset, + outer_onion, + cur_block_height, keysend_preimage, invoice_request, trampoline_packet_option, )?; + debug_assert_eq!(htlc_cltv - cur_block_height, path.total_cltv_expiry_delta()); let onion_keys = construct_onion_keys(&secp_ctx, &path, session_priv); let onion_packet = construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash) @@ -4029,7 +4138,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 ea33bb5d263..be991dd3cf0 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -11,16 +11,17 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash; -use bitcoin::secp256k1::{self, Secp256k1, SecretKey}; +use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey}; use lightning_invoice::Bolt11Invoice; use crate::blinded_path::{IntroductionNode, NodeIdLookUp}; use crate::events::{self, PaidBolt12Invoice, PaymentFailureReason}; use crate::ln::channel_state::ChannelDetails; use crate::ln::channelmanager::{ - EventCompletionAction, HTLCSource, OptionalBolt11PaymentParams, PaymentCompleteUpdate, - PaymentId, + EventCompletionAction, HTLCPreviousHopData, HTLCSource, OptionalBolt11PaymentParams, + PaymentCompleteUpdate, PaymentId, }; +use crate::ln::msgs::{DecodeError, TrampolineOnionPacket}; 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`]. @@ -124,12 +127,20 @@ pub(crate) enum PendingOutboundPayment { // Storing the BOLT 12 invoice here to allow Proof of Payment after // the payment is made. bolt12_invoice: Option, + // Storing forward information for trampoline payments in order to build next hop info + // or build error or claims to the origin. + trampoline_forward_info: Option, custom_tlvs: Vec<(u64, Vec)>, pending_amt_msat: u64, /// Used to track the fee paid. Present iff the payment was serialized on 0.0.103+. 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, @@ -159,6 +170,46 @@ pub(crate) enum PendingOutboundPayment { }, } +#[derive(Clone, Eq, PartialEq)] +pub(crate) struct NextTrampolineHopInfo { + /// The Trampoline packet to include for the next Trampoline hop. + pub(crate) onion_packet: TrampolineOnionPacket, + /// If blinded, the current_path_key to set at the next Trampoline hop. + pub(crate) blinding_point: Option, + /// The amount that the next trampoline is expecting to receive. + pub(crate) amount_msat: u64, + /// The cltv expiry height that the next trampoline is expecting. + pub(crate) cltv_expiry_height: u32, +} + +impl_writeable_tlv_based!(NextTrampolineHopInfo, { + (1, onion_packet, required), + (3, blinding_point, option), + (5, amount_msat, required), + (7, cltv_expiry_height, required), +}); + +#[derive(Clone)] +pub(crate) struct TrampolineForwardInfo { + /// Information necessary to construct the onion packet for the next Trampoline hop. + pub(crate) next_hop_info: NextTrampolineHopInfo, + /// The incoming HTLCs that were forwarded to us, which need to be settled or failed once + /// our outbound payment has been completed. + pub(crate) previous_hop_data: Vec, + /// The shared secret from the incoming trampoline onion, needed for error encryption. + pub(crate) incoming_trampoline_shared_secret: [u8; 32], + /// The forwarding fee charged for this trampoline payment, persisted here so that we don't + /// need to look up the value of all our incoming/outgoing payments to calculate fee. + pub(crate) forwading_fee_msat: u64, +} + +impl_writeable_tlv_based!(TrampolineForwardInfo, { + (1, next_hop_info, required), + (3, previous_hop_data, required_vec), + (5, incoming_trampoline_shared_secret, required), + (7, forwading_fee_msat, required), +}); + #[derive(Clone)] pub(crate) struct RetryableInvoiceRequest { pub(crate) invoice_request: InvoiceRequest, @@ -619,7 +670,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 @@ -758,21 +813,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 +877,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 +936,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; @@ -857,12 +959,12 @@ 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, pub invoice_request: Option<&'a InvoiceRequest>, pub bolt12_invoice: Option<&'a PaidBolt12Invoice>, + pub trampoline_forward_info: Option<&'a TrampolineForwardInfo>, pub session_priv_bytes: [u8; 32], pub hold_htlc_at_next_hop: bool, } @@ -894,6 +996,30 @@ 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()); + } + + route.debug_assert_route_meets_params(logger)?; + + Ok(()) +} + impl OutboundPayments { #[rustfmt::skip] pub(super) fn send_payment( @@ -953,17 +1079,32 @@ 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), }; - 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()); + 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); @@ -1060,6 +1201,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, @@ -1085,7 +1227,7 @@ impl OutboundPayments { hash_map::Entry::Occupied(entry) => match entry.get() { PendingOutboundPayment::InvoiceReceived { .. } => { let (retryable_payment, onion_session_privs) = Self::create_pending_payment( - payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), &route, + payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), None, &route, Some(retry_strategy), payment_params, entropy_source, best_block_height, ); *entry.into_mut() = retryable_payment; @@ -1096,8 +1238,8 @@ impl OutboundPayments { invoice_request } else { unreachable!() }; let (retryable_payment, onion_session_privs) = Self::create_pending_payment( - payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), &route, - Some(retry_strategy), payment_params, entropy_source, best_block_height + payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), + None, &route, Some(retry_strategy), payment_params, entropy_source, best_block_height ); outbounds.insert(payment_id, retryable_payment); onion_session_privs @@ -1109,8 +1251,8 @@ impl OutboundPayments { core::mem::drop(outbounds); 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, + &route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), + None, payment_id, &onion_session_privs, hold_htlcs_at_next_hop, node_signer, best_block_height, &send_payment_along_path ); log_info!( @@ -1200,7 +1342,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, @@ -1462,12 +1604,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) } @@ -1497,7 +1635,7 @@ impl OutboundPayments { let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(), payment_id, keysend_preimage, &route, Some(retry_strategy), - Some(route_params.payment_params.clone()), entropy_source, best_block_height, None) + Some(route_params.payment_params.clone()), entropy_source, best_block_height, None, None) .map_err(|_| { log_error!(logger, "Payment with id {} is already pending. New payment had payment hash {}", payment_id, payment_hash); @@ -1505,7 +1643,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, 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); @@ -1519,6 +1657,120 @@ impl OutboundPayments { Ok(()) } + /// Errors immediately on [`RetryableSendFailure`] error conditions. Otherwise, further errors may + /// be surfaced asynchronously via [`Event::PaymentPathFailed`] and [`Event::PaymentFailed`]. + /// + /// [`Event::PaymentPathFailed`]: crate::events::Event::PaymentPathFailed + /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed + pub(super) fn send_payment_for_trampoline_forward< + R: Router, + NS: NodeSigner, + ES: EntropySource, + IH, + SP, + L: Logger, + >( + &self, payment_id: PaymentId, payment_hash: PaymentHash, + trampoline_forward_info: TrampolineForwardInfo, retry_strategy: Retry, + mut route_params: RouteParameters, router: &R, first_hops: Vec, + inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS, best_block_height: u32, + pending_events: &Mutex)>>, + send_payment_along_path: SP, logger: &WithContext, + ) -> Result<(), RetryableSendFailure> + where + IH: Fn() -> InFlightHtlcs, + SP: Fn(SendAlongPathArgs) -> Result<(), APIError>, + { + let inter_trampoline_payment_secret = + PaymentSecret(entropy_source.get_secure_random_bytes()); + let recipient_onion = RecipientOnionFields::secret_only( + inter_trampoline_payment_secret, + trampoline_forward_info.next_hop_info.amount_msat, + ); + + let route = self.find_initial_route( + payment_id, + payment_hash, + &recipient_onion, + None, + None, + &mut route_params, + router, + &first_hops, + &inflight_htlcs, + node_signer, + best_block_height, + logger, + )?; + + let onion_session_privs = self + .add_new_pending_payment( + payment_hash, + recipient_onion.clone(), + payment_id, + None, + &route, + Some(retry_strategy), + Some(route_params.payment_params.clone()), + entropy_source, + best_block_height, + None, + Some(trampoline_forward_info.clone()), + ) + .map_err(|_| { + log_error!( + logger, + "Payment with id {} is already pending. New payment had payment hash {}", + payment_id, + payment_hash + ); + RetryableSendFailure::DuplicatePayment + })?; + + let res = self.pay_route_internal( + &route, + payment_hash, + &recipient_onion, + None, + None, + None, + Some(&trampoline_forward_info), + 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 + ); + if let Err(e) = res { + self.handle_pay_route_err( + e, + payment_id, + payment_hash, + route, + route_params, + onion_session_privs, + router, + first_hops, + &inflight_htlcs, + entropy_source, + node_signer, + best_block_height, + pending_events, + &send_payment_along_path, + logger, + ); + } + Ok(()) + } + #[rustfmt::skip] fn find_route_and_send_payment( &self, payment_hash: PaymentHash, payment_id: PaymentId, route_params: RouteParameters, @@ -1552,18 +1804,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 { @@ -1581,14 +1824,14 @@ 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, trampoline_forward_info) = { let mut outbounds = self.pending_outbound_payments.lock().unwrap(); match outbounds.entry(payment_id) { hash_map::Entry::Occupied(mut payment) => { 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, trampoline_forward_info, onion_total_msat, .. } => { const RETRY_OVERFLOW_PERCENTAGE: u64 = 10; let retry_amt_msat = route.get_total_amount(); @@ -1604,11 +1847,12 @@ impl OutboundPayments { return } - let total_msat = *total_msat; + let trampoline_forward_info = trampoline_forward_info.clone(); let recipient_onion = RecipientOnionFields { payment_secret: *payment_secret, payment_metadata: payment_metadata.clone(), custom_tlvs: custom_tlvs.clone(), + total_mpp_amount_msat: *onion_total_msat, }; let keysend_preimage = *keysend_preimage; let invoice_request = invoice_request.clone(); @@ -1625,7 +1869,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(), trampoline_forward_info) }, PendingOutboundPayment::Legacy { .. } => { log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102"); @@ -1665,8 +1909,9 @@ 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), - &onion_session_privs, false, node_signer, best_block_height, &send_payment_along_path); + invoice_request.as_ref(), bolt12_invoice.as_ref(), trampoline_forward_info.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 { self.handle_pay_route_err( @@ -1813,17 +2058,18 @@ 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, - entropy_source, best_block_height, None + recipient_onion_fields.clone(), payment_id, None, &route, None, None, + entropy_source, best_block_height, None, 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, + None, None, None, None, payment_id, &onion_session_privs, false, node_signer, best_block_height, &send_payment_along_path ) { Ok(()) => Ok((payment_hash, payment_id)), @@ -1871,7 +2117,7 @@ impl OutboundPayments { &self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId, route: &Route, retry_strategy: Option, entropy_source: &ES, best_block_height: u32 ) -> Result, PaymentSendFailure> { - self.add_new_pending_payment(payment_hash, recipient_onion, payment_id, None, route, retry_strategy, None, entropy_source, best_block_height, None) + self.add_new_pending_payment(payment_hash, recipient_onion, payment_id, None, route, retry_strategy, None, entropy_source, best_block_height, None, None) } #[rustfmt::skip] @@ -1879,15 +2125,15 @@ impl OutboundPayments { &self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId, keysend_preimage: Option, route: &Route, retry_strategy: Option, payment_params: Option, entropy_source: &ES, best_block_height: u32, - bolt12_invoice: Option + bolt12_invoice: Option, trampoline_forward_info: Option ) -> Result, PaymentSendFailure> { let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap(); match pending_outbounds.entry(payment_id) { hash_map::Entry::Occupied(_) => Err(PaymentSendFailure::DuplicatePayment), hash_map::Entry::Vacant(entry) => { let (payment, onion_session_privs) = Self::create_pending_payment( - payment_hash, recipient_onion, keysend_preimage, None, bolt12_invoice, route, retry_strategy, - payment_params, entropy_source, best_block_height + payment_hash, recipient_onion, keysend_preimage, None, bolt12_invoice, trampoline_forward_info, + route, retry_strategy, payment_params, entropy_source, best_block_height ); entry.insert(payment); Ok(onion_session_privs) @@ -1899,7 +2145,8 @@ impl OutboundPayments { fn create_pending_payment( payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, keysend_preimage: Option, invoice_request: Option, - bolt12_invoice: Option, route: &Route, retry_strategy: Option, + bolt12_invoice: Option, trampoline_forward_info: Option, + route: &Route, retry_strategy: Option, payment_params: Option, entropy_source: &ES, best_block_height: u32 ) -> (PendingOutboundPayment, Vec<[u8; 32]>) { let mut onion_session_privs = Vec::with_capacity(route.paths.len()); @@ -1920,9 +2167,11 @@ impl OutboundPayments { keysend_preimage, invoice_request, bolt12_invoice, + trampoline_forward_info, 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), }; @@ -2068,7 +2317,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]>, + trampoline_forward_info: Option<&TrampolineForwardInfo>, 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 @@ -2082,7 +2331,9 @@ impl OutboundPayments { { return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_owned()})); } - let mut total_value = 0; + if trampoline_forward_info.is_some() && keysend_preimage.is_some() { + return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Trampoline forwards cannot include keysend preimage".to_owned()})); + } 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() { @@ -2105,24 +2356,20 @@ 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, + bolt12_invoice, trampoline_forward_info, hold_htlc_at_next_hop: hold_htlcs_at_next_hop, session_priv_bytes: *session_priv_bytes }); results.push(path_res); @@ -2181,7 +2428,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> @@ -2189,7 +2436,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, 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 }) } @@ -2376,6 +2623,116 @@ impl OutboundPayments { }); } + // Reports a failed HTLC that is part of an outgoing trampoline forward. Returns Some() if + // the incoming HTLC(s) associated with the trampoline should be failed back. + pub(super) fn trampoline_htlc_failed( + &self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, + secp_ctx: &Secp256k1, logger: &WithContext, + ) -> Option { + #[cfg(any(test, feature = "_test_utils"))] + let decoded_onion = onion_error.decode_onion_failure(secp_ctx, &logger, &source); + + #[cfg(not(any(test, feature = "_test_utils")))] + let decoded_onion = onion_error.decode_onion_failure(secp_ctx, &logger, &source); + + let (payment_id, path, session_priv) = match source { + // TODO: don't unwrap and handle error appropriately + HTLCSource::TrampolineForward { outbound_payment, .. } => { + let outbound_payment = outbound_payment.clone().unwrap(); + (outbound_payment.payment_id, outbound_payment.path, outbound_payment.session_priv) + }, + _ => panic!("can't fail trampoline forward with non-trampoline source"), + }; + + let mut session_priv_bytes = [0; 32]; + session_priv_bytes.copy_from_slice(&session_priv[..]); + let mut outbounds = self.pending_outbound_payments.lock().unwrap(); + + // If any payments already need retry, there's no need to generate a redundant + // `PendingHTLCsForwardable`. + let already_awaiting_retry = outbounds.iter().any(|(_, pmt)| { + let mut awaiting_retry = false; + if pmt.is_auto_retryable_now() { + if let PendingOutboundPayment::Retryable { pending_amt_msat, total_msat, .. } = pmt + { + if pending_amt_msat < total_msat { + awaiting_retry = true; + } + } + } + awaiting_retry + }); + + let attempts_remaining = + if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) { + if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) { + log_trace!( + logger, + "Received duplicative fail for HTLC with payment_hash {}", + &payment_hash + ); + return None; + } + if payment.get().is_fulfilled() { + log_trace!( + logger, + "Received failure of HTLC with payment_hash {} after payment completion", + &payment_hash + ); + return None; + } + let mut is_retryable_now = payment.get().is_auto_retryable_now(); + if let Some(scid) = decoded_onion.short_channel_id { + // TODO: If we decided to blame ourselves (or one of our channels) in + // process_onion_failure we should close that channel as it implies our + // next-hop is needlessly blaming us! + payment.get_mut().insert_previously_failed_scid(scid); + } + if decoded_onion.failed_within_blinded_path { + debug_assert!(decoded_onion.short_channel_id.is_none()); + if let Some(bt) = &path.blinded_tail { + payment.get_mut().insert_previously_failed_blinded_path(&bt); + } else { + debug_assert!(false); + } + } + + if !is_retryable_now || decoded_onion.payment_failed_permanently { + let reason = if decoded_onion.payment_failed_permanently { + PaymentFailureReason::RecipientRejected + } else { + PaymentFailureReason::RetriesExhausted + }; + payment.get_mut().mark_abandoned(reason); + is_retryable_now = false; + } + if payment.get().remaining_parts() == 0 { + if let PendingOutboundPayment::Abandoned { .. } = payment.get() { + payment.remove(); + return Some(decoded_onion); + } + } + is_retryable_now + } else { + log_trace!( + logger, + "Received fail for HTLC with payment_hash {} not found.", + &payment_hash + ); + return Some(decoded_onion); + }; + core::mem::drop(outbounds); + log_trace!(logger, "Failing Trampoline forward HTLC with payment_hash {}", &payment_hash); + + // If we miss abandoning the payment above, we *must* generate an event here or else the + // payment will sit in our outbounds forever. + if attempts_remaining && !already_awaiting_retry { + return None; + }; + + return Some(decoded_onion); + } + pub(super) fn fail_htlc( &self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, path: &Path, session_priv: &SecretKey, payment_id: &PaymentId, @@ -2383,24 +2740,26 @@ impl OutboundPayments { pending_events: &Mutex)>>, completion_action: &mut Option, logger: &WithContext, ) { - #[cfg(any(test, feature = "_test_utils"))] + #[cfg(test)] let DecodedOnionFailure { network_update, short_channel_id, payment_failed_permanently, - onion_error_code, - onion_error_data, failed_within_blinded_path, hold_times, + onion_error_code: _onion_code, + onion_error_data: _onion_data, .. } = onion_error.decode_onion_failure(secp_ctx, &logger, &source); - #[cfg(not(any(test, feature = "_test_utils")))] + #[cfg(not(test))] let DecodedOnionFailure { network_update, short_channel_id, payment_failed_permanently, failed_within_blinded_path, hold_times, + onion_error_code: _onion_code, + onion_error_data: _onion_data, .. } = onion_error.decode_onion_failure(secp_ctx, &logger, &source); @@ -2521,9 +2880,9 @@ impl OutboundPayments { path: path.clone(), short_channel_id, #[cfg(any(test, feature = "_test_utils"))] - error_code: onion_error_code.map(|f| f.failure_code()), + error_code: _onion_code.map(|f| f.failure_code()), #[cfg(any(test, feature = "_test_utils"))] - error_data: onion_error_data, + error_data: _onion_data, hold_times, } } @@ -2631,10 +2990,12 @@ impl OutboundPayments { keysend_preimage: None, // only used for retries, and we'll never retry on startup invoice_request: None, // only used for retries, and we'll never retry on startup bolt12_invoice: None, // only used for retries, and we'll never retry on startup! + trampoline_forward_info: None, // only used for retries, and we'll never retry on startup custom_tlvs: Vec::new(), // only used for retries, and we'll never retry on startup 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 } @@ -2673,6 +3034,20 @@ impl OutboundPayments { }, } } + + /// Looks up a trampoline forward by its payment id and returns the forwarding fee our node + /// earned, returning None if the payment is not found or it does not have trampoline forwading + /// information. + pub(crate) fn get_trampoline_forwarding_fee(&self, payment_id: &PaymentId) -> Option { + self.pending_outbound_payments.lock().unwrap().get(payment_id).and_then(|payment| { + match payment { + PendingOutboundPayment::Retryable { trampoline_forward_info, .. } => { + trampoline_forward_info.as_ref().map(|info| info.forwading_fee_msat) + }, + _ => None, + } + }) + } } /// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a @@ -2717,7 +3092,23 @@ 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), + (14, trampoline_forward_info, option), (15, bolt12_invoice, option), (not_written, retry_strategy, (static_value, None)), (not_written, attempts, (static_value, PaymentAttempts::new())), @@ -2836,7 +3227,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]), @@ -2884,10 +3275,10 @@ 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(); + &&keys_manager, 0, None, None).unwrap(); outbound_payments.find_route_and_send_payment( PaymentHash([0; 32]), PaymentId([0; 32]), expired_route_params, &&router, vec![], &|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &pending_events, @@ -2899,7 +3290,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"); } @@ -2930,10 +3321,10 @@ 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(); + &&keys_manager, 0, None, None).unwrap(); outbound_payments.find_route_and_send_payment( PaymentHash([0; 32]), PaymentId([0; 32]), route_params, &&router, vec![], &|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &pending_events, @@ -2943,7 +3334,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 { @@ -2967,7 +3358,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 +3366,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 }], @@ -2994,7 +3385,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(); @@ -3012,7 +3403,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(); @@ -3020,7 +3411,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 2b3a2633205..6d819bb0fe2 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,13 +139,14 @@ 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); 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 @@ -261,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 @@ -360,9 +363,10 @@ 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); + 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(); @@ -457,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(); @@ -507,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 = @@ -550,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 = @@ -620,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); @@ -668,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(); @@ -757,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 }, @@ -805,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); @@ -982,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 @@ -990,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); @@ -1160,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"), @@ -1180,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()); @@ -1197,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"), @@ -1225,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"), @@ -1516,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); @@ -1869,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 { @@ -1881,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), @@ -1925,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); @@ -1952,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 { @@ -1964,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), @@ -1994,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); @@ -2162,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); @@ -2258,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 = { @@ -2494,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(); @@ -2706,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(); @@ -2732,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 @@ -2757,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); @@ -2778,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(); @@ -2806,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); @@ -2840,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); @@ -3000,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(); @@ -3160,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(); @@ -3208,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); @@ -3324,7 +3328,7 @@ fn retry_multi_path_single_failed_payment() { scorer.expect_usage(chans[1].short_channel_id.unwrap(), usage); } - 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(); @@ -3405,7 +3409,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(); @@ -3544,7 +3548,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(); @@ -3789,7 +3793,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]); @@ -3991,7 +3995,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(); @@ -4290,7 +4294,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(); @@ -4459,6 +4463,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(); @@ -4539,7 +4544,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())); @@ -4671,6 +4676,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(); @@ -4679,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); @@ -4696,12 +4702,13 @@ 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]; 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); @@ -4820,6 +4827,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(); @@ -5014,7 +5022,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); @@ -5079,7 +5090,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"); @@ -5090,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, @@ -5174,7 +5184,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); @@ -5213,7 +5223,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(); @@ -5274,7 +5284,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(); @@ -5350,7 +5360,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(); @@ -5413,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:?}"); + } +} 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 a8206dfe850..61d11d664b0 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), session_privs).unwrap(); check_added_monitors(&nodes[0], 1); let updates = get_htlc_update_msgs(&nodes[0], &nodes[1].node.get_our_node_id()); @@ -1566,3 +1566,84 @@ fn test_peer_storage() { assert!(res.is_err()); } +#[test] +fn test_hold_completed_inflight_monitor_updates_upon_manager_reload() { + // Test that if a `ChannelMonitorUpdate` completes after the `ChannelManager` is serialized, + // but before it is deserialized, we hold any completed in-flight updates until background event + // processing. Previously, we would remove completed monitor updates from + // `in_flight_monitor_updates` during deserialization, relying on + // [`ChannelManager::process_background_events`] to eventually be called before the + // `ChannelManager` is serialized again such that the channel is resumed and further updates can + // be made. + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let (persister_a, persister_b); + let (chain_monitor_a, chain_monitor_b); + + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes_0_deserialized_a; + let nodes_0_deserialized_b; + + let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs); + let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2; + + send_payment(&nodes[0], &[&nodes[1]], 1_000_000); + + chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress); + + // Send a payment that will be pending due to an async monitor update. + 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, 1_000_000); + nodes[0].node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap(); + check_added_monitors(&nodes[0], 1); + + assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); + assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); + + // Serialize the ChannelManager while the monitor update is still in-flight. + let node_0_serialized = nodes[0].node.encode(); + + // Now complete the monitor update by calling force_channel_monitor_updated. + // This updates the monitor's state, but the ChannelManager still thinks it's pending. + let (_, latest_update_id) = nodes[0].chain_monitor.get_latest_mon_update_id(chan_id); + nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(chan_id, latest_update_id); + let monitor_serialized_updated = get_monitor!(nodes[0], chan_id).encode(); + + // Reload the node with the updated monitor. Upon deserialization, the ChannelManager will + // detect that the monitor update completed (monitor's update_id >= the in-flight update_id) + // and queue a `BackgroundEvent::MonitorUpdatesComplete`. + nodes[0].node.peer_disconnected(nodes[1].node.get_our_node_id()); + nodes[1].node.peer_disconnected(nodes[0].node.get_our_node_id()); + reload_node!( + nodes[0], + test_default_channel_config(), + &node_0_serialized, + &[&monitor_serialized_updated[..]], + persister_a, + chain_monitor_a, + nodes_0_deserialized_a + ); + + // If we serialize again, even though we haven't processed any background events yet, we should + // still see the `BackgroundEvent::MonitorUpdatesComplete` be regenerated on startup. + let node_0_serialized = nodes[0].node.encode(); + reload_node!( + nodes[0], + test_default_channel_config(), + &node_0_serialized, + &[&monitor_serialized_updated[..]], + persister_b, + chain_monitor_b, + nodes_0_deserialized_b + ); + + // Reconnect the nodes. We should finally see the `update_add_htlc` go out, as the reconnection + // should first process `BackgroundEvent::MonitorUpdatesComplete, allowing the channel to be + // resumed. + let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]); + reconnect_args.pending_htlc_adds = (0, 1); + reconnect_nodes(reconnect_args); +} + 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); diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index b27dee1a450..509beb9bc3e 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -512,6 +512,7 @@ pub struct RouteHop { /// to reach this node. pub channel_features: ChannelFeatures, /// The fee taken on this hop (for paying for the use of the *next* channel in the path). + /// /// If this is the last hop in [`Path::hops`]: /// * if we're sending to a [`BlindedPaymentPath`], this is the fee paid for use of the entire /// blinded path (including any Trampoline hops) @@ -557,8 +558,9 @@ pub struct TrampolineHop { /// the entire blinded path. pub fee_msat: u64, /// The CLTV delta added for this hop. + /// /// If this is the last Trampoline hop within [`BlindedTail`], this is the CLTV delta for the entire - /// blinded path. + /// blinded path (including the [`BlindedTail::excess_final_cltv_expiry_delta`]). pub cltv_expiry_delta: u32, } @@ -633,7 +635,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 { @@ -642,12 +644,23 @@ impl Path { } } + /// Gets the total CLTV expiry delta which will be added to the current block height (plus some + /// extra headroom) when sending the HTLC + pub fn total_cltv_expiry_delta(&self) -> u32 { + self.hops.iter().map(|hop| hop.cltv_expiry_delta).sum() + } + /// True if this [`Path`] has at least one Trampoline hop. pub fn has_trampoline_hops(&self) -> bool { self.blinded_tail.as_ref().is_some_and(|bt| !bt.trampoline_hops.is_empty()) } } +impl_writeable_tlv_based!(Path,{ + (1, hops, required_vec), + (3, blinded_tail, option), +}); + /// A route directs a payment from the sender (us) to the recipient. If the recipient supports MPP, /// it can take multiple paths. Each path is composed of one or more hops through the network. #[derive(Clone, Debug, Hash, PartialEq, Eq)] @@ -688,6 +701,111 @@ 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(()); + } + + let total_cltv_delta = path.total_cltv_expiry_delta(); + if total_cltv_delta > route_params.payment_params.max_total_cltv_expiry_delta { + let err = format!( + "Path had a total CLTV of {total_cltv_delta} which is greater than the maximum we're allowed {}", + route_params.payment_params.max_total_cltv_expiry_delta, + ); + 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. + } + + if let Some(tail) = &path.blinded_tail { + let trampoline_cltv_sum: u32 = + tail.trampoline_hops.iter().map(|hop| hop.cltv_expiry_delta).sum(); + let last_hop_cltv_delta = path.hops.last().unwrap().cltv_expiry_delta; + if trampoline_cltv_sum > last_hop_cltv_delta { + let err = format!( + "Path had a total trampoline CLTV of {trampoline_cltv_sum}, which is less than the total last-hop CLTV delta of {last_hop_cltv_delta}" + ); + debug_assert!(false, "{}", err); + log_error!(logger, "{}", err); + } + let last_trampoline_cltv = tail + .trampoline_hops + .last() + .map(|h| h.cltv_expiry_delta) + .unwrap_or(u32::MAX); + if tail.excess_final_cltv_expiry_delta > last_trampoline_cltv { + let err = format!( + "Last trampoline CLTV of {last_trampoline_cltv} is less than the excess blinded path cltv of {}", + tail.excess_final_cltv_expiry_delta + ); + //debug_assert!(false, "{}", err); + log_error!(logger, "{}", err); + } + if tail.excess_final_cltv_expiry_delta > last_hop_cltv_delta { + let err = format!( + "Last path hop CLTV of {last_hop_cltv_delta} is less than the excess blinded path cltv of {}", + tail.excess_final_cltv_expiry_delta + ); + debug_assert!(false, "{}", err); + log_error!(logger, "{}", err); + } + } + } + } + + Ok(()) + } } impl fmt::Display for Route { @@ -1204,7 +1322,7 @@ impl PaymentParameters { found_blinded_tail = true; } } - debug_assert!(found_blinded_tail); + //debug_assert!(found_blinded_tail); } } @@ -2351,7 +2469,7 @@ fn compute_fees(amount_msat: u64, channel_fees: RoutingFees) -> Option { /// Calculate the fees required to route the given amount over a channel with the given fees, /// saturating to [`u64::max_value`]. #[rustfmt::skip] -fn compute_fees_saturating(amount_msat: u64, channel_fees: RoutingFees) -> u64 { +pub(crate) fn compute_fees_saturating(amount_msat: u64, channel_fees: RoutingFees) -> u64 { amount_msat.checked_mul(channel_fees.proportional_millionths as u64) .map(|prop| prop / 1_000_000).unwrap_or(u64::max_value()) .saturating_add(channel_fees.base_msat as u64) @@ -2491,9 +2609,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) } diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index f821aa5afc0..24e535b70bb 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -1100,6 +1100,8 @@ impl_for_vec!(crate::routing::router::TrampolineHop); impl_for_vec_with_element_length_prefix!(crate::ln::msgs::UpdateAddHTLC); impl_writeable_for_vec_with_element_length_prefix!(&crate::ln::msgs::UpdateAddHTLC); impl_for_vec!(u32); +impl_for_vec!(crate::events::HTLCLocator); +impl_for_vec!(crate::ln::types::ChannelId); impl Writeable for Vec { #[inline] 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, /// ``` diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 18d003c7993..bcf39fde482 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -581,6 +581,11 @@ impl<'a> TestChainMonitor<'a> { self.added_monitors.lock().unwrap().push((channel_id, monitor)); self.chain_monitor.load_existing_monitor(channel_id, new_monitor) } + + pub fn get_latest_mon_update_id(&self, channel_id: ChannelId) -> (u64, u64) { + let monitor_id_state = self.latest_monitor_update_id.lock().unwrap(); + monitor_id_state.get(&channel_id).unwrap().clone() + } } impl<'a> chain::Watch for TestChainMonitor<'a> { fn watch_channel( diff --git a/pending_changelog/4304.txt b/pending_changelog/4304.txt new file mode 100644 index 00000000000..8c1580a2f4c --- /dev/null +++ b/pending_changelog/4304.txt @@ -0,0 +1,3 @@ +## Backwards Compatibility + +* Downgrade is not possible while the node has in-flight trampoline forwards.